Try   HackMD

B-5 非販售產品詳細頁

JasonWu

tags: CP頁

API 清單

No API Desc 執行順序 執行條件 參考
1 URI-A-02 多語標籤 1 page init
2 URI-B-03 取得型錄相關資料 1 page init 主要 api
3 URI-B-06C 商機Inquiry Product 1 page init
4 URI-B-05 取得推薦相關型錄 2 page init 相關輸入條件會與 URI-B-03 有關
5 URI-B-04 取得廠商相關資料 3 點選廠商 tab 呼叫廠商 api
6 URI-C-02 取得燈箱相關資訊 3 點選型錄認證/公司認證開啟燈箱
7 URI046 取得 SEO META 資料 4 SEO META 相關內容

頁面相關路徑

區塊 說明 相關路徑
圖片與多媒體 3D 小圖 https://cs01.ttstaging.com.tw/front/dea1e3c/tteng/images/icon_view3D_60.png
圖片與多媒體 360 小圖 https://cs01.ttstaging.com.tw/front/dea1e3c/tteng/images/icon_view360_60.png
圖片與多媒體 Video 小圖 https://cs01.ttstaging.com.tw/front/dea1e3c/tteng/images/icon_viewVideo_60.png
圖片與多媒體 沒有圖片時的大圖 https://cs01.ttstaging.com.tw/front/dea1e3c/tteng/images/public/360x360.png
圖片與多媒體 沒有圖片時的小圖 https://cs01.ttstaging.com.tw/front/dea1e3c/tteng/images/public/blank100x100.jpg

原頁面網址

頁面輸入 URL 解析

頁面說明

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • 型錄詳目頁來源主要透過URI-B-04取得相關資料
    • api 相關 url 如下:${TT-API網址}/products/detail/{did}/{id}?userAccessTicket={userAccessTicket}
      • did : domain id,與頁面及版型相關參數,例如英文主頁 did = 2
      • id : 公司 id,參考 http://www.ttstaging.com.tw/company/65 ,其中 "65" 就是公司 id
      • userAccessTicket 為登入者使用者Ticket,AEM 透過 header service 取得 userAccessTicket,如果有傳入,且經過解析後是正常的 ticket,後續 api 就會判斷此次呼叫是有登入的呼叫

頁面區塊說明

  • B-06-01
    • 共用Header Service元件, 參考L01
  • B-06-02
  • B-06-03
    • 型錄詳細資料,請參考URI-B-03
  • B-06-04
  • B-06-05
    • 推薦型錄&小額採購區,請參考URI-B-05
  • B-06-06
    • 參考首頁Footer
  • SeoMeta 資訊
    • 透過 api 取得頁面所需的 meta 資訊
  • 麵包屑
    • 透過 api 取得麵包屑
  • 圖片與多媒體
    • 透過 api 取得圖片、多媒體資訊
  • 型錄主要資訊
    • 透過 api 取得產品名稱, 型號資訊等資訊
  • tab 區塊,切換顯示
    • 透過 api 決定顯示那些顯示 tab
  • 詳細資料
    • 透過 api 取得產品詳細資訊
  • 聯繫廠商
    • 參考聯繫廠商 api,顯示相關畫面
  • 推薦型錄

頁面欄位說明

SeoMeta 資訊

  • 舊版頁面相關資訊透過 xml 傳遞
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 產出 html 頁面時,需要將相關資訊放入 meta tag 中
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 顯示邏輯
  • 透過 URI046 取得頁面相關的 meta 資料
    • 呼叫相關 api 路徑如下: ${TT-API網址}/domains/seo/{seoType}/{pageType}/{did}/{rowId}
    • 參數 seoType 帶入 "1"
    • 參數 pageType 帶入 "PRODUCT_DETAIL"
    • 參數 did 帶入頁面 {did}
    • 參數 rowId 帶入型錄 id {id}
    • 解析回傳的 json 內容,並寫入頁面原始檔中

麵包屑

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • 顯示邏輯
    • 參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA?view#麵包屑1
    • 取出 data 中的 breadInfo array
    • 邏輯與格式
      • 1 Home
        • 顯示 Home 連結
        • Home 為多語,{lang.home}
        • Home 的 url 是固定的,連結到該 domain 首頁
      • 2 路徑連結
        • 顯示 二碼四碼六碼 的連結
        • 依照 {breadInfo.seq} 順序,依次顯示 breadInfo 資料
        • 連結名稱套用 {breadInfo .name}
        • 點選後的 url 套用 {breadInfo.url}
      • 3 型錄的名稱
        • 顯示 型錄名稱
        • 套用 {productInfo.productName}
  • 操作邏輯
    • 點選麵包屑的連結,redirect 到對應的頁面
    • 產品名稱不需要連結

圖片與多媒體

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • 顯示邏輯

    • 圖片部分參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#圖片資料
    • 多媒體部分參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#多媒體資料
    • 取出 data 中的 sliderGalleryInfo array 與 multimediaInfo object
    • 邏輯與格式
      • 1 區塊大圖

        • 找出主要圖片
          • 找出 sliderGalleryInfo 中 {sliderGalleryInfo.hasPrimary} = 1 的那一筆
        • 顯示該筆資料的 {sliderGalleryInfo.img360_360Url}
        • 如果找不到對應的圖片,將會顯示預設圖
      • 2 Enlarge 按鈕

        • 顯示操作按鈕,供使用者點選
        • Enlarge 為多語,{lang.ez_enlarge}
      • 3 多媒體圖示

        • 顯示多媒體圖示,分為三大類:3D, 360, Video,對應上圖區塊 3 中由左至右的三個圖示
          • 如果 {multimediaInfo.multimedia_3d_url} != '',需要顯示 3D 的圖示 : 3D 小圖
          • 目前 3D 標題 {multimediaInfo.multi_media_3d_title} 並未使用,後續如果不需使用,此 api 欄位可移除
          • 如果 {multimediaInfo.multimedia_360_url} != '',需要顯示 360 的圖示 : 360 小圖
          • 目前 360 標題 {multimediaInfo.multi_media_360_title} 並未使用,後續如果不需使用,此 api 欄位可移除
          • 如果 {multimediaInfo.video_url} != '',需要顯示 Video 的圖示 : Video 小圖
      • 4 小圖 slider

        • 依次顯示 {sliderGalleryInfo.img100_100Url} 的小圖
        • 提供 slider 捲動效果,相關內容參考對應操作邏輯
      • 5 分享按鈕

        • 使用外部套件 addthis
        • 可參考 cms-front/src/main/webapp/site/tteng/xsl/share.xsl
  • 操作邏輯

    • 點選 多媒體3D 或 多媒體360 圖示,顯示相關操作燈箱

      • 帶入目前 multimediaInfo 內的資料
        • 1 透過 iframe,將對應的 html 放入燈箱內
          • 點選 3D 時,iframe 顯示 {multimediaInfo.multimedia_3d_url}
          • 點選 360 時,iframe 顯示 {multimediaInfo.multimedia_360_url}
        • 2 關閉燈箱按鈕,點選後關閉燈箱
        • 3 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
    • 點選 多媒體 Video 圖示,顯示相關操作燈箱

      • 帶入目前 multimediaInfo 內的資料
        • 1 透過 iframe,將對應的影片放入燈箱內
          • 如果影片是 youtube,可使用 youtube
          • 如果不是,直接將對應內容放入 iframe
        • 2 關閉燈箱按鈕,點選後關閉燈箱
        • 3 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
      • 當關閉燈箱時,如果影片正在撥放,此時需要停止撥放影片
        • 目前舊版程式對 youtube 影片有做此處理,關閉燈箱時會呼叫 youtube 的 stopVideo 來停止撥放
    • 點選 Enlarge,顯示相關操作燈箱

      • 帶入目前 sliderGalleryInfo 內的資料
        • 1 顯示目前該型錄的標題
        • 2 顯示 sliderGalleryInfo 中的所有 {sliderGalleryInfo.img100_100Url}
        • 3 顯示當前對應顯示的 {sliderGalleryInfo.imgOriUrl},預設是 {sliderGalleryInfo.hasPrimary} = 1 的原始圖
        • 4 此為 jquery.smoothZoom 套件 的效果,可局部放大原始圖片內容
        • 5 關閉燈箱按鈕,點選後關閉燈箱
        • 6 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
      • 舊版採用 jquery.smoothZoom 套件,載入 jquery.smoothZoom.css 與 jquery.smoothZoom.js
      • 當觸發了開啟 Enlarge 燈箱時,需要暫停型錄小圖 slider 的自動捲動效果;關閉時再恢復型錄小圖 slider 的自動捲動效果
    • 滑鼠移動到大圖上,會有 Zoom in 的效果

      • 首先判斷當前瀏覽器頁面的寬度是否 > 768 (rwd 不需要)
      • 再來判斷目前選取的圖片是否長寬都 > 360 (圖太小不需要)
      • 以上條件都符合時,才會觸發 Zoom in 效果
      • 帶入當前大圖 {sliderGalleryInfo.imgOriUrl} 當作縮放的原始圖
        1 透過 jquery.elevateZoom.cover 套件,在原始大圖的地方顯示放大區塊來源
        2 透過 jquery.elevateZoom.cover 套件,顯示對應放大區塊的圖
      • 舊版採用 jquery.elevateZoom.cover 套件,載入 jquery.elevateZoom.cover.js
      • 當觸發了 Zoom in 效果時,需要暫停型錄小圖 slider 的自動捲動效果;離開時再恢復型錄小圖 slider 的自動捲動效果
    • 型錄小圖 slider 自動捲動效果

      • slider 超過兩則圖片時,需要提供 slider 效果
      • 舊版採用 slick slide 套件,載入 slick-utils.js
      • 舊版設定開啟自動撥放 (autoplay: true)
      • 舊版設定自動捲動效果的時間是 4 秒 (autoplaySpeed: 4000)
      • 首尾相接的效果,到最後一張圖時,接在後面的是第一張圖 (infinite: true)
    • 型錄小圖 slider 手動捲動效果

      • slider 超過兩則圖片時,需要提供 slider 效果
      • 舊版採用 slick slide 套件,載入 slick-utils.js
      • 點選向左或是向右,型錄小圖 slider 會捲動到前一筆或是後一筆
      • 捲動的同時,上方型錄大圖也會跟著切換為當前第一張小圖對應的大圖
    • 點選小圖,會將上面的大圖換為點選對應的大圖

      • 點選下方小圖,會將上面的大圖換為點選對應的大圖
      • 點選後,小圖 slider 的 index 會切換到目前點選的項目

型錄主要資訊

  • 顯示邏輯

    • 型錄相關內容參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#型錄資料
    • 邏輯與格式
      • 1 型錄標題
        • {productInfo.productName}
      • 2 型錄型號
        • 標題為多語,{lang.modelno}
        • {productInfo.modelNo}
        • 沒有資料時不顯示本欄
      • 3 產地
        • 標題為多語,{lang.madein}
        • {productInfo.madeIn}
        • 沒有資料時不顯示本欄
      • 4 廠商資訊
        • 4-1 標題為多語,{lang.supplier}
        • 4-2 顯示廠商名稱,{productInfo.supplierName}
        • 4-2 檢查 {productInfo.epUrl} 是否存在,如果存在,廠商名稱加上 url 連結,連結的 url = {productInfo.epUrl}
          • 檢查 {productInfo.supplierUrl} != '',有值代表該廠商相關公司網址,此時廠商名稱加上 url 連結,連結的 url = {productInfo.supplierUrl}
        • 4-3 檢查 {productInfo.epUrl} 是否存在,如果存在才顯示 More About This Product 項目,點選的 url = {productInfo.epUrl} + '/product/' + {productInfo.productId}
        • 4-3 檢查 {productInfo.epUrl} 是否存在,如果存在才顯示 More About This Product 項目,點選的 url = {productInfo.epUrl}
        • 4-3 More About This Product 為多語,{lang.ez_moreaboutproduct}
        • 目前 api 會額外回傳以下相關資料
          • 公司 ep 年資:{productInfo.supplierEpYear}
          • 公司認證小圖清單:{productInfo.supplierIcon}
          • 公司營業類型:{productInfo.supplierBusinessType}
      • 5 顯示 Request for Quotation 連結
        • 連結名稱為多語,{lang.requestforquotation}
        • 連結網址固定,格式為 "/latest-price/step1?productId=" + {productInfo.productId}
      • 6 Contact Supplier 按鈕
        • Contact Supplier 為多語,{lang.contactsupplier}
        • 點選相關邏輯請參考下面的操作邏輯
      • 7 Add to Favorites
        • Add to Favorites 為多語,{lang.addtofavorites}
        • 如果該型錄已經加入過 favorites 的話,要顯示 Go to Favorites,{lang.gotofavorites}
        • 點選的連結請參考條列頁的加到最愛與 redirect 到最愛的邏輯
      • 警語相關
        • 取得 {productInfo.catalogStandardCid},會影響到本頁是否要顯示警語
      • 其他欄位
        • 目前 api 會傳遞部分欄位,目前前台顯示並未特別輸出,但對應的 ep 畫面存在,這些欄位目前 api 還是會傳,再依後續邏輯判斷是否要保留
          • {productInfo.hasNewest},是否需要顯示 New Tag,0(否)/1(是)
          • {productInfo.hasFeatured},是否需要顯示 Featured Tag,0(否)/1(是)
        • {productInfo.hasSelling},是否販售,{productInfo.hasSelling} == 0 代表非販售,進入非販售產品詳細頁
  • 操作邏輯

    • 點選 Contact Supplier 按鈕

      • 跳轉到下面的 "聯繫廠商" 區塊
    • 點選 Add to favorite

      • 參考之前條列頁的加入最愛功能

      • 加到我的最愛部分,這邊先貼上目前條列頁相同功能的程式碼,應該是可以直接套用的;條列頁程式有納入版本控制,後續開發如果碰到問題,這邊可以提供相關程式來參考;檢查型錄是否已經加入我的最愛、將型錄加入我的最愛、從我的最愛中移除型錄,在下面的 ts 中應該都有相對應的程式碼

      • 我的最愛程式碼區塊

import { isPlatformServer } from '@angular/common';
import { Component, Inject, Input, PLATFORM_ID, ViewEncapsulation } from '@angular/core';
import { Currency } from 'src/app/models/currency.model';
import { ViewService } from 'src/app/service/view.service';
import { StatusService } from 'src/app/service/status.service';
import { AlertStore } from 'src/app/store/alert-store';
import { CurrencyStore } from 'src/app/store/currency-store';
import { BaseComponent } from '../../components/base.component';
import { ProductDisplay } from '../../models/product-display.model';
import { Product } from '../../models/product.model';
import { HttpService } from '../../service/http.service';
import { StringUtils } from '../../utils/string.utils';
import * as showModel from './product.js';
import { WINDOW } from '@ng-web-apis/common';
import { FavoriteProductStore } from '../../store/favorite-product-store';
import { ResetFavoriteListStore } from '../../store/reset-favorite-list-store';
import { ResetPermaCountStore } from '../../store/reset-perma-count-store';

@Component({
    selector: 'app-product',
    templateUrl: './product.component.html',
    encapsulation: ViewEncapsulation.None
})
export class ProductComponent extends BaseComponent {

    @Input()
    productDisplay: ProductDisplay;

    @Input()
    product: Product;

    @Input()
    index: number;

    @Input()
    languageId: number;

    @Input()
    did: number;

    @Input()
    alert = true;

    @Input()
    searchResultFlag = false;

    showProduct = false;

    showBadge = false;

    showBadgeRank = false;

    showBadgeNew = false;

    showBadgeTop = false;

    showMedia = false;

    showName = false;

    showCertification = false;

    showCompanyName = false;

    showCompanyType = false;

    showMinOrder = false;

    showCfaImg = false;

    showUnit = false;

    showDiscount = false;

    showOldPrice = false;

    showPrice = false;

    showOldTotalPrice = false;

    showTotalPrice = false;

    showIsFreeShipping = false;

    mediaIconClass = '';

    isCartProduct = false;

    currency: Currency;

    favoriteProductList: string[] = [];

    isAddedFavorite = false;

    resetFavoriteList = true;

    constructor(
        private httpService: HttpService,
        currencyStore: CurrencyStore,
        private favoriteProductStore: FavoriteProductStore,
        private resetFavoriteListStore: ResetFavoriteListStore,
        private resetPermaCountStore: ResetPermaCountStore,
        private statusService: StatusService,
        @Inject(ViewService) private viewService: ViewService,
        private alertStore: AlertStore,
        @Inject(PLATFORM_ID) private platformId: any,
        @Inject(WINDOW) readonly windowRef: any) {
        super();
        this.log = false;
        this.addStore(currencyStore, (currency) => {
            this.currency = currency;
            this.setPriceAndCurrency();
        }, false);
        this.addStore(favoriteProductStore, (favoriteList) => {
            this.favoriteProductList = favoriteList;
            this.resetFavoriteList = false;
            if (this.product && this.favoriteProductList.includes(this.product.productId)) {
                this.isAddedFavorite = true;
            } else {
                this.isAddedFavorite = false;
            }
        }, false);
    }


    onInit(): void {

        // SSR忽略
        if (isPlatformServer(this.platformId)) {
            return;
        }

        this.initShowFlag();

        if (this.product) {

            this.showProduct = true;

            if (this.alert) {
                this.alertStore.addProduct(this.product);
            } else {
                this.debug('No alert for ' + this.product.productNameAlt);
            }

            if (this.resetFavoriteList) {

                // Notify Header set favorit list
                this.resetFavoriteListStore.resetFavoriteType('PRODUCT');

            } else {

                if (this.favoriteProductList.includes(this.product.productId)) {
                    this.isAddedFavorite = true;
                } else {
                    this.isAddedFavorite = false;
                }
            }

            // Show Discount: 前台顯示邏輯更正:若是「不可販售」,就不顯示「免運圖示」、「折扣」、minorder、金額
            if (this.product.isSelling && StringUtils.isNotBlank(this.product.discount) && this.product.discount !== '0') {

                this.showDiscount = true;
            }

            // Show Unit
            if (StringUtils.isNotBlank(this.product.unit)) {

                this.showUnit = true;
            }

            // Show Old Price: 前台顯示邏輯更正:若是「不可販售」,就不顯示「免運圖示」、「折扣」、minorder、金額
            if (this.product.isSelling && this.showDiscount
                && StringUtils.isNotBlank(this.product.minOldPrice) && StringUtils.isNotBlank(this.product.maxOldPrice)) {

                this.showOldPrice = true;
            }

            // Show Price: 前台顯示邏輯更正:若是「不可販售」,就不顯示「免運圖示」、「折扣」、minorder、金額
            if (this.product.isSelling && StringUtils.isNotBlank(this.product.minPrice) && StringUtils.isNotBlank(this.product.maxPrice)) {

                this.showPrice = true;
            }

            // Show Cart Product
            if (StringUtils.isNotBlank(this.product.totalPrice) && this.product.quantity) {

                this.isCartProduct = true;
            }

            // Show Old Total Price: 前台顯示邏輯更正:若是「不可販售」,就不顯示「免運圖示」、「折扣」、minorder、金額
            if (this.product.isSelling && this.isCartProduct && StringUtils.isNotBlank(this.product.oldTotalPrice)) {

                this.showOldTotalPrice = true;
            }

            // Show Total Price: 前台顯示邏輯更正:若是「不可販售」,就不顯示「免運圖示」、「折扣」、minorder、金額
            if (this.product.isSelling && this.isCartProduct) {

                this.showTotalPrice = true;
            }

            // Check Currency and Price
            if (this.currency && this.currency.currency !== this.product.currency) {

                this.setPriceAndCurrency();
            }

            if (this.productDisplay) {

                this.checkCustomDisplayCondition();
            }
        }

    }

    checkCustomDisplayCondition(): void {

        // Show Badge
        if (StringUtils.isNotBlank(this.productDisplay.showBadge) && this.productDisplay.showBadge === 'rank') {

            this.showBadge = true;
            this.showBadgeRank = true;

        } else if (StringUtils.isNotBlank(this.productDisplay.showBadge) && this.productDisplay.showBadge === 'new') {

            this.showBadge = true;
            this.showBadgeNew = true;

        } else if (StringUtils.isNotBlank(this.productDisplay.showBadge) && this.productDisplay.showBadge === 'top') {

            this.showBadge = true;
            this.showBadgeTop = true;
        }

        // Show Media
        if (this.productDisplay.showMedia && StringUtils.isNotBlank(this.product.mediaIcon)) {

            this.showMedia = true;
            this.mediaIconClass = this.product.mediaIcon;
        }

        // Show Name
        if (this.productDisplay.showName) {
            if (!this.searchResultFlag && StringUtils.isNotBlank(this.product.productNameAlt)) {
                this.showName = true;
            } else if (this.searchResultFlag && StringUtils.isNotBlank(this.product.productName)) {
                this.showName = true;
            }
        }

        // Show Certification
        if (this.productDisplay.showCertification && StringUtils.isNotBlank(this.product.productCertification)) {

            this.showCertification = true;
        }

        // Show CompanyName
        if (this.productDisplay.showCompanyName && StringUtils.isNotBlank(this.product.companyNameAlt)) {

            this.showCompanyName = true;
        }

        // Show CompanyType
        if (this.productDisplay.showCompanyType && StringUtils.isNotBlank(this.product.companyType)) {

            this.showCompanyType = true;
        }

        // Show MinOrder: 前台顯示邏輯更正:若是「不可販售」,就不顯示「免運圖示」、「折扣」、minorder、金額
        if (this.product.isSelling && this.productDisplay.showMinOrder &&
            StringUtils.isNotBlank(this.product.minOrder) &&
            StringUtils.isNotBlank(this.product.unit)) {

            this.showMinOrder = true;
        }

        // Show CfaImg
        if (this.productDisplay.showCfaImg && this.product.cfaImg && this.product.cfaImg.length > 0) {

            this.showCfaImg = true;
        }

        // Show IsFreeShipping: 前台顯示邏輯更正:若是「不可販售」,就不顯示「免運圖示」、「折扣」、minorder、金額
        this.showIsFreeShipping = this.product.isSelling && this.productDisplay.showIsFreeShipping && this.product.isFreeShipping;
    }

    initShowFlag(): void {

        this.showProduct = false;
        this.showBadge = false;
        this.showBadgeRank = false;
        this.showBadgeNew = false;
        this.showBadgeTop = false;
        this.showMedia = false;
        this.showName = false;
        this.showCertification = false;
        this.showCompanyName = false;
        this.showCompanyType = false;
        this.showMinOrder = false;
        this.showCfaImg = false;
        this.showUnit = false;
        this.showDiscount = false;
        this.showOldPrice = false;
        this.showPrice = false;
        this.showTotalPrice = false;
        this.showIsFreeShipping = false;
        this.mediaIconClass = '';
    }

    setPriceAndCurrency(): void {

        const exchangeRate = this.currency.exchangeRate;
        const currency = this.currency.currency;

        if (this.product && this.product.currency !== currency) {
            this.product.currency = currency;
            this.product.minOldPrice = this.getPrice(this.product.minOldPrice, exchangeRate);
            this.product.maxOldPrice = this.getPrice(this.product.maxOldPrice, exchangeRate);
            this.product.minPrice = this.getPrice(this.product.minPrice, exchangeRate);
            this.product.maxPrice = this.getPrice(this.product.maxPrice, exchangeRate);
            this.product.oldTotalPrice = this.getPrice(this.product.oldTotalPrice, exchangeRate);
            this.product.totalPrice = this.getPrice(this.product.totalPrice, exchangeRate);
        }

    }

    getPrice(srcPrice: string, exchangeRate: number): string {

        if (!StringUtils.isNumeric(exchangeRate)) {
            return srcPrice;
        }

        if (StringUtils.isNumeric(srcPrice)) {

            const price = parseFloat(srcPrice);

            const result = price * exchangeRate;

            return result.toFixed(2);

        }

        return srcPrice;
    }


    async addFavoriteClick(): Promise<void> {

        this.debug('### click addFavoriteClick ###');

        try {

            this.statusService.mask();

            if (this.isAddedFavorite) {
                this.debug('### remove favorite:', this.product.productId);
                const response = await this.windowRef.headerEntry.removeMyFavorites(this.product.productId, 'PRODUCT', this.languageId);
                if (response.code === '200') {
                    this.debug('### remove success ###');
                    this.favoriteProductStore.removeProductId(this.product.productId);
                }
            } else {
                this.debug('### add favorite:', this.product.productId);
                const response = await this.windowRef.headerEntry.addMyFavorites(this.product.productId, 'PRODUCT', this.languageId, this.did, 'FRONT');
                if (response.code === '200') {
                    this.debug('### add success ###');
                    this.favoriteProductStore.addProductId(this.product.productId);
                }
            }

            this.resetPermaCountStore.updateType('PRODUCT');

        } catch (e) {

            this.error(e);

        } finally {

            this.statusService.unMask();
        }
    }

    mediaIconClick(event: MouseEvent): void {

        this.debug('### click media icon ###');
        this.viewService.getProductPopup().refreshProduct(this.product, this.searchResultFlag);
        showModel(event);
    }

    removeProductClick(): void {

        this.debug('### click remove product ###');
    }
}

tab 區塊,切換顯示

  • 顯示邏輯
    • 未販售型錄的兩個 tab 是型錄詳目與公司詳目
      • 1 Product Detail
        • 標題為多語,{lang.productdetail}
      • 2 Company Profile
        • 標題為多語,{lang.companyprofile}
  • 操作邏輯
    • 透過 tab 去切換顯示對應的區塊

詳細資料(Product Detail)

  • 顯示邏輯
    • 取出 data 中的 productInfo object, specInfo array, paymentInfo object, attachInfo array, certInfo array 來顯示資料
    • 取出 data 中的 productInfo object, specInfo array, attachInfo array, certInfo array 來顯示資料
      • 1 廣告詞
        • {productInfo.adSlogan}
        • 沒有的話不顯示此區塊
      • 2 規格列表
        • Spec 為多語,{lang.spec}
        • 內容取自 specInfo array,依次顯示
          • 每一行固定顯示格式為 {specInfo .name} + ":" + {specInfo.value}
        • 沒有的話不顯示此區塊
      • 3 Made in Taiwan 與 Design in Taiwan 的圖示
        • 判斷 {productInfo.logoType}
          • {productInfo.logoType} = 0 顯示 Design In Taiwan Logo
          • {productInfo.logoType} = 1 顯示 Made In Taiwan Logo
          • {productInfo.logoType} = -1 不顯示
        • 沒有的話不顯示此區塊
      • 4 Key Features
        • Key Features 為多語,{lang.keyfeatures}
        • {productInfo.keyFaturesHtml}
        • 沒有的話不顯示此區塊
      • 5 產品認證
        • Product Certification 為多語,{lang.productcertification}
        • 內容取自 certInfo array,依次顯示
          • 顯示對應的認證圖,圖片 url 為 {certInfo.imgUrl},圖片 title 為 {certInfo.imgTitle},圖片 alt 為 {certInfo.imgAlt}
        • 點選認證圖後會開啟燈箱顯示認證詳目資料
          • 燈箱 url 為 {certInfo.lightboxUrl}
          • 相關燈箱操作請參考下面的操作邏輯
      • 7 附件列表
        • Attachment 為多語,{lang.attachment}
        • 內容取自 attachInfo array,依次顯示
          • 以 url 方式顯示檔案連結
            • 連結 href = {attachInfo.fileUrl}
            • 連結名稱顯示檔案名稱與檔案大小,檔案名稱為 {attachInfo.fileName},檔案大小為 {attachInfo.fileSize}
        • 沒有的話不顯示此區塊
      • 8 影片內容
        • Video 為多語,{lang.ez_video}
        • 內容取自 {productInfo.videoUrl}, {productInfo.videoTitle}, {productInfo.videoTypeName}
        • 影片類型 {productInfo.videoType} 啟動的類型只有 youtube 類型
        • 沒有的話不顯示此區塊
      • 9 最後更新時間
        • Last Update 為多語,{lang.lastupdate}
        • 內容顯示 {productInfo.updateDate}
  • 操作邏輯
    • 點選產品型錄認證燈箱
      • 參考 URI-C-02 型錄認證/公司認證燈箱 取得對應 json 內容
        • 傳入的 url 路徑 /certifications/prod/{certInfo.id}?lang={productInfo.languageId}&companyId={productInfo.supplierId}
      • 解析回傳的 certInfo object 內容並顯示於燈箱中
      • 1 燈箱名稱
        • Product Certification 為多語,{lang.productcertification}
      • 2 型錄認證名稱
        • 顯示 {certInfo .name}
      • 3 型錄認證圖片
        • 顯示 {certInfo.imgUrl}
      • 4 型錄認證編號
        • Certification No 為多語,{lang.certificationno}
        • 顯示 {certInfo.serial}
        • 如果沒有就不顯示此欄位
      • 5 型錄認證生效起始日期
        • Certification Start Time 為多語,{lang.certificationstarttime}
        • 顯示 {certInfo.startTime}
        • 如果沒有就不顯示此欄位
      • 6 型錄認證生效結束日期
        • Certification End Time 為多語,{lang.certificationendtime}
        • 顯示 {certInfo.endTime}
        • 如果沒有就不顯示此欄位
      • 7 型錄認證說明
        • Certification description 為多語,{lang.certificationdesc}
        • 顯示 {certInfo.desc}
        • 如果沒有就不顯示此欄位
      • 8 型錄認證附件檔案
        • Attachments 為多語,{lang.certificationattachments}
        • 顯示 url 連結
          • href 顯示 {certInfo.fileUrl}
          • 連結名稱顯示 {certInfo.fileName}
          • 檔案大小顯示 {certInfo.fileSize}
        • 如果沒有就不顯示此欄位
      • 9 型錄認證相關型錄
        • Product 為多語,{lang.product}
        • 解析 certInfo.refProductRows array,依次顯示內容
          • 9-1 顯示 {certInfo.refProductRows.imgUrl}
          • 9-2 對應的型錄詳目頁 url 連結
            • href 顯示 {certInfo.refProductRows.productCpUrl}
            • 連結名稱顯示 {certInfo.refProductRows.productName}
        • 如果沒有就不顯示此區塊
      • 10 燈箱關閉按鈕
        • 關閉燈箱按鈕,點選後關閉燈箱
      • 11 燈箱遮罩
        • 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕

詳細資料(Company Profile)

  • 顯示邏輯
    • 參考 URI-B-04 取得對應 json 內容
    • 呼叫 url 參數為 /companies/detail/{did}/{id}?productId={productInfo.productId}&userId={loginUserId}
    • {loginUserId} 為登入人員的 userId,未登入時不需傳遞
    • 呼叫 url 參數為 /companies/detail/{did}/{id}?productId={productInfo.productId}&userAccessTicket={userAccessTicke}
    • 如果輸入參數 userAccessTicket 有值,代表使用者已登入,以參數方式傳給 companies detail api
    • 主要分為 公司基本資料, 公司相關型錄, 公司工廠資料, 公司認證與產品認證, 公司得獎紀錄、大事記, 公司聯絡資訊 等區塊
公司基本資料

  • 顯示邏輯
    • 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#廠商資料
    • 取得 data 中的 companyProfile object
    • 1 公司名稱
      • 如果 {companyProfile.companyWebSiteUrl} != '',顯示為聯結的型態,名稱是 {companyProfile.companyName},聯結是 {companyProfile.companyWebSiteUrl}
      • 如果 {companyProfile.companyWebSiteUrl} == '',單純顯示 {companyProfile.companyName} 即可
      • 如果 {companyProfile.companyWebSiteUrl} != null,顯示為聯結的型態,名稱是 {companyProfile.companyName},聯結是 {companyProfile.companyWebSiteUrl}
      • 如果 {companyProfile.companyWebSiteUrl} == null,且 {companyProfile.ecCompanyWebSiteUrl} != null,顯示為聯結的型態,名稱是 {companyProfile.companyName},聯結是 {companyProfile.ecCompanyWebSiteUrl}
      • 如果 {companyProfile.companyWebSiteUrl} == null,且 {companyProfile.ecCompanyWebSiteUrl} == null,單純顯示 {companyProfile.companyName} 即可
    • 2 Add to favorites
      • Add to favorites 為多語,{lang.addtofavorites}
      • 如果該公司已經加入過 favorites 的話,要顯示 Go to Favorites,{lang.gotofavorites}
      • 點選的連結請參考條列頁的加到最愛與 redirect 到最愛的邏輯
    • 加到我的最愛部分,這邊先貼上目前條列頁相同功能的程式碼,應該是可以直接套用的;條列頁程式有納入版本控制,後續開發如果碰到問題,這邊可以提供相關程式來參考;檢查公司是否已經加入我的最愛、將公司加入我的最愛、從我的最愛中移除公司,在下面的 ts 中應該都有相對應的程式碼
    • 3 Basic Information 為多語,{lang.basicinformation}
    • 4 取得 data 中的 companyProfile object 解析資料,如果 companyProfile 沒有對應欄位,該行不顯示
      • Company Name
        • Company Name 為多語,{lang.companyname}
        • 顯示 {companyProfile.companyName}
      • Business Type
        • Business Type 為多語,{lang.companiesbusinesstype}
        • 顯示 {companyProfile.businessTypeListDesc}
      • Year Established
        • Year Established 為多語,{lang.yearestablished}
        • 顯示 {companyProfile.yearEstablished}
      • Factory Location
        • Factory Location 為多語,{lang.factorylocation}
        • 顯示 {companyProfile.factoryLocation}
      • Total Revenue
        • Total Revenue 為多語,{lang.totalrevenue}
        • 顯示 {companyProfile.totalRevenue}
      • Capitial
        • Capitial 為多語,{lang.capitial}
        • 先判斷 {companyProfile.showCapital} == 1 才會顯示資本額
        • 顯示 {companyProfile.capital}
      • No. of Employee
        • No. of Employee 為多語,{lang.employee}
        • 顯示 {companyProfile.employeeRank}
      • Main Product
        • Main Product 為多語,{lang.mainproducts}
        • 顯示 {companyProfile.mainExportProductName}
      • Main Export Market
        • Main Export Market 為多語,{lang.mainexportmarket}
        • 顯示 {companyProfile.mainExportMarketListDesc}
    • 條列頁我的最愛相關程式碼內容
import { Component, ElementRef, Inject, Input, QueryList, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
import { WINDOW } from '@ng-web-apis/common';
import { Subscription } from 'rxjs';
import { Product } from '../../models/product.model';
import { BaseComponent } from '../../components/base.component';
import { ProductDisplay } from '../../models/product-display.model';
import { SearchCompany } from '../../models/search-company.model';
import { UiUtils } from 'src/app/utils/ui.utils';
import { FavoriteSupplierStore } from '../../store/favorite-supplier-store';
import { ResetFavoriteListStore } from '../../store/reset-favorite-list-store';
import { ResetPermaCountStore } from '../../store/reset-perma-count-store';
import { StatusService } from 'src/app/service/status.service';
import { initSwiper } from './supplierListSwiper';
import { destroy } from '../../components/baseSwiper';

@Component({
    selector: 'app-supplier-list',
    styleUrls: ['./supplier-list.component.css'],
    templateUrl: './supplier-list.component.html',
    encapsulation: ViewEncapsulation.None
})
export class SupplierListComponent extends BaseComponent {

    @ViewChildren('appProducts', { read: ElementRef })
    appProducts: QueryList<ElementRef>;

    @ViewChild('viewAll', { read: ElementRef })
    viewAllSilder: ElementRef;

    @Input()
    productDisplay: ProductDisplay;

    @Input()
    index: number;

    @Input()
    languageId: number;

    @Input()
    did: number;

    subscription: Subscription;

    searchCompany: SearchCompany;

    supplierProducts: Product[];

    showViewAll = false;

    sliderTotalCount = 0;

    sliderCount = 0;

    isAddedFavorite = false;

    favoriteSupplier: string[] = [];

    resetFavoriteList = true;

    constructor(
        @Inject(WINDOW) readonly windowRef: any,
        private favoriteSupplierStore: FavoriteSupplierStore,
        private resetFavoriteListStore: ResetFavoriteListStore,
        private resetPermaCountStore: ResetPermaCountStore,
        private statusService: StatusService) {
        super();
        this.addStore(favoriteSupplierStore, (favoriteList) => {
            this.favoriteSupplier = favoriteList;
            this.resetFavoriteList = false;
            if (this.searchCompany && this.favoriteSupplier.includes(this.searchCompany.companyId)) {
                this.isAddedFavorite = true;
            } else {
                this.isAddedFavorite = false;
            }
        }, false);
    }

    async onRefresh() {
        this.supplierProducts = [];
        destroy(this.searchCompany?.id);
        this.sliderCount = 0;

        if (this.resetFavoriteList) {

            // Notify Header set favorit list
            this.resetFavoriteListStore.resetFavoriteType('SUPPLIER');

        } else {

            if (this.searchCompany && this.favoriteSupplier.includes(this.searchCompany.companyId)) {
                this.isAddedFavorite = true;
            } else {
                this.isAddedFavorite = false;
            }
        }
    }

    @Input() set company(searchCompany: SearchCompany) {
        this.searchCompany = searchCompany;
    }

    @Input() set products(products: Product[]) {
        this.supplierProducts = products;
        if (products) {
            this.sliderTotalCount = products.length;
        }
    }

    onDomChange(mutation: MutationRecord): void {

        this.debug('onDomChange ' + mutation + ' , liElementCount=' + this.sliderCount);

        if (!UiUtils.isSwiperSlideDivElement(mutation)) {
            return;
        }

        this.sliderCount++;

        this.debug('onDomChange sliderCount=' + this.sliderCount + ' , sliderTotalCount=' + this.sliderTotalCount);

        if (this.sliderCount === this.sliderTotalCount) {
            if (this.searchCompany.totalProducts > 4) {
                this.showViewAll = true;
            } else {
                this.debug('onDomChange initSupplierProdsSwiperConfig');
                initSwiper(this.searchCompany?.id);
            }
        } else if (this.sliderCount > this.sliderTotalCount) {
            this.debug('onDomChange initSupplierProdsSwiperConfig');
            initSwiper(this.searchCompany?.id);
        }

    }

    getViewAllHeight(): number {

        if (!this.showViewAll) {
            return 0;
        }

        let viewAllHeight: number = this.viewAllSilder ? this.viewAllSilder.nativeElement.height : 0;
        if (this.windowRef.innerWidth >= 768) {
            if (this.appProducts) {
                const appProduct: ElementRef = this.appProducts.first;
                if (appProduct) {
                    const height = appProduct.nativeElement.getElementsByClassName('prod_img_wrapper')[0].getBoundingClientRect().height;
                    viewAllHeight = height;
                }
            }
        }

        this.debug('SupplierListComponent getViewAllHeight=' + viewAllHeight);
        return viewAllHeight;
    }

    async addFavoriteClick(): Promise<void> {

        this.debug('### click addFavoriteClick ###');

        try {

            this.statusService.mask();
            const companyId = this.searchCompany.companyId;
            if (this.isAddedFavorite) {
                this.debug('### remove favorite:', companyId);
                const response = await this.windowRef.headerEntry.removeMyFavorites(companyId, 'SUPPLIER', this.languageId);
                if (response.code === '200') {
                    this.debug('### remove success ###');
                    this.favoriteSupplierStore.removeCompanyId(companyId);
                }
            } else {
                this.debug('### add favorite:', companyId);
                const response = await this.windowRef.headerEntry.addMyFavorites(companyId, 'SUPPLIER', this.languageId, this.did, 'FRONT');
                if (response.code === '200') {
                    this.debug('### add success ###');
                    this.favoriteSupplierStore.addCompanyId(companyId);
                }
            }

            this.resetPermaCountStore.updateType('SUPPLIER');

        } catch (e) {

            this.error(e);

        } finally {

            this.statusService.unMask();
        }
    }

}
公司相關型錄

  • 顯示邏輯
    • 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#相關型錄
      • 1 標題 Other Products 為多語,{lang.otherproducts}
      • 取得 data 中的 productlist array,依次解析資料
      • 2 圖片
        • 顯示 {productlist.imageURL}
      • 3 New tag
        • 如果 {productlist.newest} == 1,需要顯示 New 標籤
      • 4 型錄名稱聯結
        • 名稱為 {productlist.productName},聯結為 {productlist.productUrl}
      • 5 型錄價格
        • 先判斷是否有販售,{productlist.isSelling} == 1,如果沒有販售就不用顯示價格
        • 再來判斷是否有折扣
          • 先判斷 {productlist.isDiscount} == 1(有折扣)
          • 再判斷 {productlist.discountStartDate} 與 {productlist.discountEndDate} 跟今天日期的比較,判斷是否在折扣日內
          • 再判斷 {productlist.discount} 是否有值且 {productlist.discount} > 0.0
          • 上面三點都成立,代表有折扣
        • 如果沒有折扣,顯示 {productlist.priceCurrency} + {productlist.price} + {productlist.unit}
        • 如果有折扣,顯示 {productlist.discount} + {productlist.priceCurrency} + ({productlist.price} * {productlist.discount}) + {productlist.unit}
        • json 提供的是 USD 金額,如果頁面的幣別有替換,那顯示時需要將金額從 USD 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別
      • 6 Free Shipping
        • 判斷是否免運費,如果 {productlist.isFreeShipping} == 1,顯示 free shipping 的小圖
      • 7 Min Order
        • 顯示 {productlist.minOrder} + {productlist.unit}
        • 顯示 {productlist.minOrder}
      • 警語相關
        • 取得 {productlist.catalogStandardCid},會影響到本頁是否要顯示警語
      • 8 更多型錄圖示
        • 判斷以下邏輯
          • 目前型錄清單最多顯示 5 則,需判斷 {productCount} > 5
          • 確定公司是否有 ep 網頁,{companyProfile.companyWebSiteUrl} != '' 代表是 ep
          • 如果以上兩個條件都符合,顯示更多型錄
          • 點選的聯結格式如下:{companyProfile.companyWebSiteUrl}/product-catalog/all-products.html
          • 更多型錄圖示點選的 url = {productMoreUrl}
      • 9 更多型錄文字
        • 如果 8 有顯示,就要顯示對應的文字
        • All Products 為多語,{lang.allproducts}
        • All Products 後面的數量是 {productCount}
公司工廠資料

  • 顯示邏輯
    • 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#工廠資料
    • 1 標題 Factory 為多語,{lang.factory}
    • 取得 data 中的 factoryInfo.videoList array 與 factoryInfo.imageList array,依次解析資料
    • 先解析 factoryInfo.videoList array,依次顯示
      • 2 顯示影片,{factoryInfo.videoList.videoURL}
      • 3 顯示影片名稱,{factoryInfo.videoList.videoTitle}
    • 再來解析 factoryInfo.imageList array,依次顯示
      • 4 顯示圖片,{factoryInfo.imageList.imageURL},圖片的 title 跟 alt 顯示 {factoryInfo.imageList.imageTitle}
      • 5 顯示圖片名稱,{factoryInfo.imageList.imageTitle}
  • 操作邏輯
    • 點選工廠圖片,透過燈箱顯示圖片
      • 1 點選的工廠圖片
      • 2 開啟對應燈箱,顯示 {factoryInfo.imageList.imageURL}
      • 3 燈箱關閉按鈕
        • 關閉燈箱按鈕,點選後關閉燈箱
      • 4 燈箱遮罩
        • 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
公司認證與產品認證

  • 顯示邏輯

    • 1 Certification 為多語,{lang.certification}
    • 取得 data 中的 certInfo.thirdPartyCert array, certInfo.companyCert array, certInfo.productCert array
    • 2 解析第三方認證顯示邏輯(thirdPartyCert)
      • 依次分析 {certInfo.thirdPartyCert} 資料
      • 第三方認證的 tts 認證與 advanced 認證圖片是固定的路徑,因此回傳的 json 只記錄是否顯示,以及對應認證顯示的名稱,結構與其他認證不同
      • 如果 {certInfo.ttsCert} == 1,代表要顯示 tts 的認證圖片,另外名稱顯示 certInfo.ttsCertName 的值,點選 tts 認證不會開啟燈箱
      • 如果 certInfo.advancedCert == 1,代表要顯示 advanced 的認證圖片,另外名稱顯示 {certInfo.advancedCertName},點選 advanced 認證不會開啟燈箱
      • 其他類型的認證,需要解析 {certInfo.thirdPartyCert.isUseHtml} 欄位值
        • 如果 {certInfo.thirdPartyCert.isUseHtml} == 1,代表認證的內容要顯示 {certInfo.thirdPartyCert.informationHtml} 內的 html code
        • 反之,依照一般認證顯示規則,點選後開啟燈箱,顯示相關內容,認證燈箱部分請參考 https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w
    • 3 Company Certification 為多語,{lang.companycertification}
    • 4 解析 certInfo.companyCert 資料,依次顯示
      • 如果 {certInfo.companyCert.isUseHtml} == 1,代表認證的內容要顯示 {certInfo.companyCert.informationHtml} 內的 html code
      • 反之,依照一般認證顯示規則,點選後開啟燈箱,顯示相關內容,認證燈箱部分請參考 https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w
    • 5 解析以下條件,如果符合,顯示按鈕讓操作人員可以開啟視窗,並列出所有相關的公司認證
      • 判斷數量 {certInfo.companyCert.total} > 0
      • 判斷開啟的 url 非空,對應的 url 為{certInfo.companyCert.allCertHref} != ''
    • 6 當第五項要顯示時,對應的名稱 All Certifications 為多語,{lang.allcertification}
      • 顯示 All Certifications,後面加上 certInfo.companyCert.total 數量
    • 7 Product Certification 為多語,{lang.productcertification}
    • 8 解析 certInfo.productCert 資料,依次顯示
      • 如果 {certInfo.productCert.isUseHtml} == 1,代表認證的內容要顯示 {certInfo.productCert.informationHtml} 內的 html code
      • 反之,依照一般認證顯示規則,點選後開啟燈箱,顯示相關內容,認證燈箱部分請參考 https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w
    • 9 解析以下條件,如果符合,顯示按鈕讓操作人員可以開啟視窗,並列出所有相關的公司認證
      • 判斷數量 {certInfo.productCert.total} > 0
      • 判斷開啟的 url 非空,對應的 url 為{certInfo.productCert.allCertHref} != ''
    • 10 當第九項要顯示時,對應的名稱 All Certifications 為多語,{lang.allcertification}
      • 顯示 All Certifications,後面加上 {certInfo.productCert.total} 數量
  • 操作邏輯

    • 點選產品型錄認證燈箱

      • 參考 URI-C-02 型錄認證/公司認證燈箱 取得對應 json 內容
        • 傳入的 url 路徑 /certifications/prod/{certInfo.id}?lang={productInfo.languageId}&companyId={productInfo.supplierId}
      • 解析回傳的 certInfo object 內容並顯示於燈箱中
      • 1 燈箱名稱
        • Product Certification 為多語,{lang.productcertification}
      • 2 型錄認證名稱
        • 顯示 {certInfo .name}
      • 3 型錄認證圖片
        • 顯示 {certInfo.imgUrl}
      • 4 型錄認證編號
        • Certification No 為多語,{lang.certificationno}
        • 顯示 {certInfo.serial}
        • 如果沒有就不顯示此欄位
      • 5 型錄認證生效起始日期
        • Certification Start Time 為多語,{lang.certificationstarttime}
        • 顯示 {certInfo.startTime}
        • 如果沒有就不顯示此欄位
      • 6 型錄認證生效結束日期
        • Certification End Time 為多語,{lang.certificationendtime}
        • 顯示 {certInfo.endTime}
        • 如果沒有就不顯示此欄位
      • 7 型錄認證說明
        • Certification description 為多語,{lang.certificationdesc}
        • 顯示 {certInfo.desc}
        • 如果沒有就不顯示此欄位
      • 8 型錄認證附件檔案
        • Attachments 為多語,{lang.certificationattachments}
        • 顯示 url 連結
          • href 顯示 {certInfo.fileUrl}
          • 連結名稱顯示 {certInfo.fileName}
          • 檔案大小顯示 {certInfo.fileSize}
        • 如果沒有就不顯示此欄位
      • 9 型錄認證相關型錄
        • Product 為多語,{lang.product}
        • 解析 certInfo.refProductRows array,依次顯示內容
          • 9-1 顯示 {certInfo.refProductRows.imgUrl}
          • 9-2 對應的型錄詳目頁 url 連結
            • href 顯示 {certInfo.refProductRows.productCpUrl}
            • 連結名稱顯示 {certInfo.refProductRows.productName}
        • 如果沒有就不顯示此區塊
      • 10 燈箱關閉按鈕
        • 關閉燈箱按鈕,點選後關閉燈箱
      • 11 燈箱遮罩
        • 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
    • 點選公司型錄認證燈箱

      • 參考 URI-C-02 型錄認證/公司認證燈箱 取得對應 json 內容
        • 傳入的 url 路徑 /certifications/prod/{certInfo.id}?lang={productInfo.languageId}&companyId={productInfo.supplierId}
      • 解析回傳的 certInfo object 內容並顯示於燈箱中
      • 1 燈箱名稱
        • Company Certification 為多語,{lang.companycertification}
      • 2 公司名稱
        • 顯示 {companyInfo.companyName}
      • 3 認證名稱
        • 顯示 {certInfo .name}
      • 4 認證圖片
        • 顯示 {certInfo.imgUrl}
      • 5 認證相關顯示內容
        • 認證編號
          • Certification No 為多語,{lang.certificationno}
          • 顯示 {certInfo.serial}
          • 如果沒有就不顯示此欄位
        • 認證生效起始日期
          • Certification Start Time 為多語,{lang.certificationstarttime}
          • 顯示 {certInfo.startTime}
          • 如果沒有就不顯示此欄位
        • 認證生效結束日期
          • Certification End Time 為多語,{lang.certificationendtime}
          • 顯示 {certInfo.endTime}
          • 如果沒有就不顯示此欄位
        • 認證說明
          • Certification description 為多語,{lang.certificationdesc}
          • 顯示 {certInfo.desc}
          • 如果沒有就不顯示此欄位
        • 認證附件檔案
          • Attachments 為多語,{lang.certificationattachments}
          • 顯示 url 連結
            • href 顯示 {certInfo.fileUrl}
            • 連結名稱顯示 {certInfo.fileName}
            • 檔案大小顯示 {certInfo.fileSize}
          • 如果沒有就不顯示此欄位
      • 6 燈箱關閉按鈕
        • 關閉燈箱按鈕,點選後關閉燈箱
      • 7 燈箱遮罩
        • 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
公司得獎紀錄

  • 顯示邏輯
    • 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#得獎資料
    • 1 標題 Award 為多語,{lang.awards}
    • 2 標題 Award from {companyProfile.companyName}
      • Award 為多語,{lang.awards}
      • from 為固定顯示
      • 後面需要接上 {companyProfile.companyName}
    • 取得 data 中的 awardInfo.awardList array,依次解析資料
      • 3 顯示圖片,{awardInfo.awardList.imgSrc}
      • 4 顯示年分,{awardInfo.awardList.startYear}
      • 5 顯示得獎名稱與聯結,名稱為 {awardInfo.awardList.title},點選的聯結是 {awardInfo.awardList.href}
      • 6 顯示說明,{awardInfo.awardList.description}
公司大事記

  • 顯示邏輯
    • 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#得獎資料
    • 1 標題 Official Records 為多語,{lang.officialrecords}
    • 2 Official Records from Taiwan International Trade Shows 中的 Official Records 為多語,from Taiwan International Trade Shows 為固定顯示
    • 取得 data 中的 awardInfo.officialRecordsFromTTSList array,依次解析資料
      • 3 顯示圖片,{awardInfo.officialRecordsFromTTSList.logoUrl}
      • 4 顯示年月,{awardInfo.officialRecordsFromTTSList.showYearMonth}
      • 5 顯示名稱與聯結,名稱為 {awardInfo.officialRecordsFromTTSList.displayTitle},點選的聯結是 {awardInfo.officialRecordsFromTTSList.sUrl}
      • 6 顯示序號,Booth No. 為固定顯示,後方序號為 {awardInfo.officialRecordsFromTTSList.showBoots}
    • 7 Official Records from {公司名稱},Official Records 為多語,{lang.officialrecords},from 為固定顯示
      • 後面需要接上 {companyProfile.companyName}
    • 取得 data 中的 awardInfo.officialRecordList array,依次解析資料
      • 8 顯示圖片,{awardInfo.officialRecordList.imgSrc}
      • 9 顯示年分,{awardInfo.officialRecordList.startYear}
      • 10 顯示名稱與聯結,名稱為 {awardInfo.officialRecordList.title},點選的聯結是 {awardInfo.officialRecordList.href}
      • 11 顯示說明,{awardInfo.officialRecordList.description}

公司聯絡資訊

  • 未登入

  • 已登入

  • 顯示邏輯

    • 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?both#公司聯絡資訊
    • 取得 data 中的 contactUs object
    • 1 Contact Information 為多語,{lang.contactinformation}
    • 2 聯絡人相關訊息
      • 如果有登入,依次顯示資訊
        • Owner 為多語,{lang.owner}
          • 顯示 {contactUs.owner}
        • Contact Person 為多語,{lang.contactperson}
          • 顯示 {contactUs.contactFullName}
          • 分析 {contactUs.contactPicture},如果有值,顯示聯絡人圖片
          • 分析 {contactUs.contactCard},如果有值,顯示聯絡人名片圖
        • Business Phone Number 為多語,{lang.tel1}
          • 顯示 {contactUs.contactPhone}
      • 如果沒有登入,owner 等資訊會是空的,此時請顯示 Log in to view details,多語資料 {lang.logintoviewdetails}
        • Owner 為多語,{lang.owner}
        • Contact Person 為多語,{lang.contactperson}
        • Business Phone Number 為多語,{lang.tel1}
    • 3 Contact Supplier 為多語,{lang.contactsupplier}
    • 4 其他相關資訊
      • 依次判斷以下 contactUs object 的欄位
        • Company Fax Number 為多語,{lang.fax}
          • 對應欄位 {contactUs.officeFax}
        • Phone Number 為多語,{lang.phonenumber}
          • 對應欄位 {contactUs.contactPhone}
        • Office Address 為多語,{lang.officeaddress}
          • 對應以下欄位
            • {contactUs.officePostal}
            • {contactUs.officeAddress}
            • {contactUs.officeAddressTw}
          • 如果 {contactUs.officeAddressTw} != '' 顯示 {contactUs.officePostal} + {contactUs.officeAddressTw}
          • 如果 {contactUs.officeAddressTw} == '' and {contactUs.officeAddress} != '' 顯示 {contactUs.officePostal} + {contactUs.officeAddress}
          • 後面加上 google map 的聯結,帶入對應的地址
          • 欄位顯示 {contactUs.officePostal} + {contactUs.officeAddress}
          • 後方的 google map 連結,其中的地址需要執行以下的判斷
            • 如果 {contactUs.officeAddressTw} != null,此時 google map 的地址使用 {contactUs.officeAddressTw}
            • 反之則不顯示 google map 按鈕
        • Office Hours 為多語,{lang.officehours}
          • 對應欄位 {contactUs.officeHour}
        • Company Website 為多語,{lang.companywebsite}
          • 顯示聯結
            • 聯結名稱對應欄位 {companyProfile.companyName}
            • 聯結 url 對應欄位 {companyProfile.companyWebSiteUrl}
            • 連結 url 對應欄位 {contactUs.websiteUrl}
        • Online Shop 為多語,{lang.onlineshop}
          • 顯示聯結
            • 聯結名稱對應欄位 {lang.onlineshop}
            • 聯結 url {contactUs.onlineShop}
        • Mobile Site 為多語,{lang.mobilesite}
          • 顯示 qr code 圖,對應 url : {contactUs.qrcodeUrl}
    • 5 Last Update 為多語,{lang.lastupdate}
      • Last Update 資料請參考 {contactUs.lastUpdateTime},該欄位回傳的日期是 long 格式的數字,套版時可以將數字轉換為對應時區的字串
  • 操作邏輯

    • 如果 2 聯絡人相關訊息 中有 聯絡人圖片,點選後會開啟燈箱顯示圖片
      • 1 點選 聯絡人圖片 圖示
      • 2 開啟燈箱內容,圖片部份顯示對應的 {contactUs.contactPicture}
      • 3 目前顯示 avatar 單字
      • 4 燈箱關閉按鈕
        • 關閉燈箱按鈕,點選後關閉燈箱
      • 5 燈箱遮罩
        • 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
    • 如果 2 聯絡人相關訊息 中有 聯絡人名片,點選後會開啟燈箱顯示名片
      • 1 點選 聯絡人名片 圖示
      • 2 開啟燈箱內容,圖片部份顯示對應的 {contactUs.contactCard}
      • 3 目前顯示 avatar 單字
      • 4 燈箱關閉按鈕
        • 關閉燈箱按鈕,點選後關閉燈箱
      • 5 燈箱遮罩
        • 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
    • 點選 3 Contact Supplier,會跳轉到本頁下面的 聯繫廠商 區塊

聯繫廠商

  • 商機Inquiry Product,請參考URI-K-32

推薦區塊

  • 顯示邏輯
    • 參考 URI-B-05 詳細頁 Cp Recommend API 取得對應 json 內容
    • 取得 Recommends 方式如下
      • 傳入的 url 路徑 /products/detail/recommends/{did}?id={pid}&type=product
      • did 傳入進入本 cp 頁一開始的 did
      • pid 傳入 {productInfo.productId}
      • 解析 json 內容,取得 products array 中的內容,依次顯示
        • 1 Recommendations 為多語,{lang.recommendation}
        • 2 顯示型錄圖片,{products.productPicture100}
        • 3 顯示型錄名稱,{products.productNameAlt},點選對應的 cp url 為 {products.productUrl}
      • 警語相關
        • 取得 {products.catalogStandardCid},會影響到本頁是否要顯示警語
    • 取得 Small Order Recommendation 方式如下
      • 傳入的 url 路徑 /products/detail/small-orders/{did}?id={pid}&type=product
      • did 傳入進入本 cp 頁一開始的 did
      • pid 傳入 productInfo.productId
      • 解析 json 內容,取得 products array 中的內容,依次顯示
        • 4 Small Order Recommendation 為多語,{lang.ez_smallrecommendation}
        • 5 顯示型錄圖片,{products.productPicture100}
        • 6 顯示型錄名稱,{products.productNameAlt},點選對應的 cp url 為 {products.productUrl}
        • 7 顯示型錄銷售金額,small order 必定是販售型錄
          • 先判斷 {products.discount} 是否有值且 {products.discount} > 0.0,符合代表有折扣
          • 如果沒有折扣,金額顯示原價;如果有折扣,金額顯示折扣價
          • 折扣顯示金額請用 ({products.discount} * {products.minPrice}) + '-' + ({products.discount} * {products.maxPrice})
          • 後方加上 {products.unit}
          • json 提供的是 USD 金額,如果頁面的幣別有替換,那顯示時需要將金額從 USD 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別
        • 8 判斷顯示 free shipping 小圖與最小訂購量
          • 如果第 7 點判斷有折扣,需要顯示折扣資訊
          • 如果 {products.isFreeShipping} == 1,代表需要顯示免運費圖示
          • 如果 {products.minOrder} != 0,顯示最小訂購量,{products.minOrder} + {products.unit}
      • 警語相關
        • 取得 {products.catalogStandardCid},會影響到本頁是否要顯示警語

警語相關邏輯

  • 目前警語顯示規則如下:

  • 分析取得的警語相關檔案的結構

    • data.warnings 是 array 結構,紀錄所有要顯示警語的規則,每一個條件都要判斷,所以一個頁面可能會顯示多項警語
    • data.warnings.displayMode 代表格式
      • displayMode = 1 為 使用圖片
      • displayMode = 2 為 使用 html code
    • data.warnings.cidList 代表標準 taitra code 條件
      • cidList 是一個以逗號分隔的 string,例如 "cidList" : "543,123,456"
      • 本文件中有提到警語顯示相關的部分有 型錄主要資訊, 公司相關型錄, 推薦區塊
      • 如果這些區塊中的 catalogStandardCid 值存在於 cidList 中,代表對應的型錄符合這一項警語的規則,所以要顯示此區塊的警語
      • 以 "cidList" : "543,123,456" 為例,如果本頁面中的任何一個區塊的 catalogStandardCid = 543 or catalogStandardCid = 123 or catalogStandardCid = 456,都要顯示這一項 data.warnings 的警語
    • data.warnings.dPictureUrl, data.warnings.mPictureUrl
      • data.warnings.displayMode == 1,代表使用圖片,警語直接顯示對應的圖片
      • data.warnings.dPictureUrl 為大圖 url,用在一般頁面
      • data.warnings.mPictureUrl 為小圖 url,用在行動版頁面
    • data.warnings.dPictureHeight, data.warnings.mPictureHeight
      • data.warnings.displayMode == 1,代表使用圖片,警語直接顯示對應的圖片
      • 由於一次可能要顯示多張警語圖片,未來套版可能需要先計算出預計的圖片高度,可以透過這兩個欄位取得一般頁面與行動版頁面的圖片高度
      • 資料格式為 number
    • data.warnings.htmlCode
      • data.warnings.displayMode == 2,代表使用 html code
      • 警語顯示一段 html 內容,顯示的 html code = {data.warnings.htmlCode}
    • data.warnings.backgroundColor
      • data.warnings.displayMode == 2,代表使用 html code
      • 如果有設定內容,代表警語的 html 區塊的背景顏色
      • 例如上圖範例,data.warnings.backgroundColor = '#080404',代表 html code 的背景顏色會是 黑色

多語清單

區塊 多語代碼 說明
麵包屑 lang.home 麵包屑 Home
圖片與多媒體 lang.ez_enlarge enlarge 按鈕名稱
型錄主要資訊 lang.modelno 型錄型號
型錄主要資訊 lang.madein 型錄產地
型錄主要資訊 lang.supplier 廠商名稱
型錄主要資訊 lang.ez_moreaboutproduct More About This Product
型錄主要資訊 lang.requestforquotation Request For Quotation
型錄主要資訊 lang.ez_pleaseselect Please Select
型錄主要資訊 lang.ez_available Available
型錄主要資訊 lang.contactsupplier Contact Supplier
型錄主要資訊 lang.addtofavorites Add to favorites
型錄主要資訊 lang.gotofavorites Go to favorites
tab 區塊 lang.productdetail Product Detail
tab 區塊 lang.companyprofile Company Profile
型錄 Detail lang.spec Spec
型錄 Detail lang.keyfeatures Key Features
型錄 Detail lang.productcertification Product Certification
型錄 Detail lang.paymentdetails Payment Details for Offline Orders
型錄 Detail lang.paymentterms Payment Terms
型錄 Detail lang.minimumorde Minium Order
型錄 Detail lang.attachment Attachment
型錄 Detail lang.ez_video Video
型錄 Detail lang.lastupdate Last Update
公司基本資料 lang.addtofavorites Add to favorite
公司基本資料 lang.gotofavorites Go to favorite
公司基本資料 lang.basicinformation Basic Information
公司基本資料 lang.companyname Company Name
公司基本資料 lang.companiesbusinesstype Business Type
公司基本資料 lang.yearestablished Year Establish
公司基本資料 lang.factorylocation Factory Location
公司基本資料 lang.totalrevenue Total Revenue
公司基本資料 lang.capitial Capital
公司基本資料 lang.employee Employee No.
公司基本資料 lang.mainproducts Main Products
公司基本資料 lang.mainexportmarket Main Export Market
公司相關型錄 lang.otherproducts Other Products
公司相關型錄 lang.allproducts All Products
公司工廠資料 lang.factory Factory
認證 lang.certification Certification
認證 lang.companycertification Company Certification
認證 lang.allcertification All Certification
認證 lang.productcertification Product Certification
認證燈箱 lang.certificationno Certification No
認證燈箱 lang.certificationstarttime Certification Start Time
認證燈箱 lang.certificationendtime Certification End Time
認證燈箱 lang.certificationdesc Certification Description
認證燈箱 lang.certificationattachments Attachments
得獎紀錄 lang.awards Awards
得獎紀錄 lang.officialrecords Official Records
Contact Us lang.logintoviewdetails Login in to View Details
Contact Us lang.contactinformation Contact Information
Contact Us lang.owner Owner
Contact Us lang.contactperson Contact Person
Contact Us lang.tel1 Contact Phone No.
Contact Us lang.contactsupplier Contact Supplier
Contact Us lang.fax Office Fax
Contact Us lang.phonenumber Office Phone No.
Contact Us lang.officeaddress Office Address
Contact Us lang.officehours Office Hours
Contact Us lang.companywebsite Company Website
Contact Us lang.onlineshop Online Shop
Contact Us lang.mobilesite Mobile Site
Contact Us lang.lastupdate Last Update
推薦型錄 lang.recommendation Recommendation
推薦型錄 lang.ez_smallrecommendation Small Order Recommendation