Try   HackMD

B-6 販售產品詳細頁

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-B-07 取得販售相關資訊 3 點選 Shipping tab
切換販售運費國家
7 URI-C-02 取得燈箱相關資訊 3 點選型錄認證/公司認證開啟燈箱
8 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
運費資料 DHL https://cs01.ttstaging.com.tw/front/24f1e33/tteng/images/icon/logo_ship_s_dhl.jpg
運費資料 FEDEX https://cs01.ttstaging.com.tw/front/24f1e33/tteng/images/icon/logo_ship_s_fedex.jpg
運費資料 TNT https://cs01.ttstaging.com.tw/front/24f1e33/tteng/images/icon/logo_ship_s_tnt.jpg
運費資料 EMS https://cs01.ttstaging.com.tw/front/24f1e33/tteng/images/icon/logo_ship_s_ems.jpg
運費資料 UPS https://cs01.ttstaging.com.tw/front/24f1e33/tteng/images/icon/logo_ship_s_ups.jpg
運費資料 Other https://cs01.ttstaging.com.tw/front/24f1e33/tteng/images/icon/logo_ship_s_other.jpg
payment term alipay https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/alipay.png
payment term paypal https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/paypal.png
payment term card https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/card.png
payment term tenpay https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/tenpay.png
payment term unionpay https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/unionpay.png
payment term hitrust https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/hitrust.png
payment term card_ae https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/card_ae.png
payment term googlepay https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/googlepay.png
payment term applepay https://cs01.ttstaging.com.tw/front/da74d3a/tteng/images/icon/applepay.png

原頁面網址

頁面輸入 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 →

頁面區塊說明

  • 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,顯示對應的廠商資訊
    • 參考運費計算 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 會切換到目前點選的項目

型錄主要資訊

  • 顯示邏輯

    • 型錄相關內容(1,2,3,4,5,6,7,10,11,12,13,14,15區塊)參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#型錄資料
    • 價格表內容(8)參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#價格表
    • 規格屬性選項內容(9)參考
      https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#規格屬性選項
    • 取出 data 中的 productInfo object, priceRangeInfo array, 與 spec4SelectInfo array
    • 邏輯與格式
      • 1 折扣與折扣剩餘日期
        • 先判斷 {productInfo.hasDiscount} == 1(有折扣)
        • 再判斷 {productInfo.discountStartDate} 與 {productInfo.discountEndDate} 跟今天日期的比較,判斷是否在折扣日內
        • {productInfo.discountStartDate} 與 {productInfo.discountEndDate} 回傳的資料型態為 long,對應 linux timestamp,例如 1633332979000,代表台灣時間 2021-10-04 15:36:19
        • 再判斷 {productInfo.discount} 是否有值且 {productInfo.discount} > 0.0
        • 上面三點都成立,代表有折扣,折扣值 = {productInfo.discount}
        • 接下來判斷 剩餘日期,{productInfo.discountDayLeft} 是否有值,有的話才顯示 XX Days Left
        • 如果有折扣,且 {productInfo.discountEndDate} != null,此時需要計算對應的折扣剩餘日期
          • 用目前的時間 - {productInfo.discountEndDate} 來計算,如果剩餘的時間超過一日以上,顯示 xx Days Left
          • 如果剩餘的時間在一日內,顯示剩餘的時分秒
          • Days Left 為多語,{lang.ez_daysleft}
        • 另外補上 {productInfo.isFreeShipping} 判斷,如果 {productInfo.isFreeShipping} == 1,顯示免運費圖片,如果要沿用,請將此圖檔下載後套用,多語請參考 {lang.ez_freeshipping}
      • 2 型錄標題
        • {productInfo.productName}
      • 3 型錄型號
        • 標題為多語,{lang.modelno}
        • {productInfo.modelNo}
        • 沒有資料時不顯示本欄
      • 4 產地
        • 標題為多語,{lang.madein}
        • {productInfo.madeIn}
        • 沒有資料時不顯示本欄
      • 5 廠商資訊
        • 5-1 標題為多語,{lang.supplier}
        • 5-2 顯示廠商名稱,{productInfo.supplierName}
        • 5-2 檢查 {productInfo.epUrl} 是否存在,如果存在,廠商名稱加上 url 連結,連結的 url = {productInfo.epUrl}
          • 檢查 {productInfo.supplierUrl} != '',有值代表該廠商相關公司網址,此時廠商名稱加上 url 連結,連結的 url = {productInfo.supplierUrl}
        • 5-3 檢查 {productInfo.epUrl} 是否存在,如果存在才顯示 More About This Product 項目,點選的 url = {productInfo.epUrl} + '/product/' + {productInfo.productId}
        • 5-3 檢查 {productInfo.epUrl} 是否存在,如果存在才顯示 More About This Product 項目,點選的 url = {productInfo.epUrl}
        • 5-3 More About This Product 為多語,{lang.ez_moreaboutproduct}
        • 目前 api 會額外回傳以下相關資料
          • 公司 ep 年資:{productInfo.supplierEpYear}
          • 公司認證小圖清單:{productInfo.supplierIcon}
          • 公司營業類型:{productInfo.supplierBusinessType}
          • 公司是否有 tts 認證:{productInfo.supplierTTSIcon}
            • 如果 {productInfo.supplierTTSIcon} == 1,代表對應的公司有 tts 認證,此時需要顯示 tts 認證的圖片
      • 6 原始價格
        • 標題為多語,{lang.ez_unitprice}
        • 顯示 {productInfo.priceCurrencyName} + {productInfo.priceMin} + '~' + {productInfo.priceMax} + '/' + {productInfo.unitName}
        • 參考項目 1 的判斷,如果該型錄有折扣,需要在價格上標註刪除線,代表原價有打折
        • json 提供的是 USD 金額,如果頁面的幣別有替換,那顯示時需要將金額從 USD 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別
      • 7 折扣價格
        • 標題為多語,{lang.ez_discountprice}
        • 參考項目 1 的判斷,如果該型錄沒有折扣,本項不顯示
        • 顯示 {productInfo.priceCurrencyName} + ({productInfo.priceMin} * {productInfo.discount}) + '~' + ({productInfo.priceMax} * {productInfo.discount}) + '/' + {productInfo.unitName}
        • json 提供的是 USD 金額,如果頁面的幣別有替換,那顯示時需要將金額從 USD 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別
      • 8 價格表
        • 以表格的方式顯示價格表,依次解析 priceRangeInfo array 的內容
        • 如果 priceRangeInfo 沒有資料,這個區塊不顯示
        • 8-1 標題為多語,{lang.price}
        • 8-2 表格標題多語列表如下
          • Quantity : {lang.ez_quantity}
          • Price : {lang.price}
          • Processing Time : {lang.ez_processingtime}
        • 8-3 表格內容對應欄位放置對應如下
          • Quantity : {priceRangeInfo.quantityRange}
          • Price : {priceRangeInfo.price}
          • Processing Time : {priceRangeInfo.prepareDay} + {lang.ez_days}
        • {priceRangeInfo.price} 內容包含幣別與金額,如果頁面的幣別有替換,那顯示時需要將金額從 USD 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別
        • 8-4 顯示 Request for Quotation 連結
          • 連結名稱為多語,{lang.requestforquotation}
          • 連結網址固定,格式為 "/latest-price/step1?productId=" + {productInfo.productId}
      • 9 規格屬性
        • 解析 spec4SelectInfo array 中的內容,組成對應的下拉選單
        • 如果 spec4SelectInfo 沒有資料,這個區塊不顯示
        • 9-1 顯示名稱 {spec4SelectInfo .name}
        • 9-2 產出對應的下拉選單
          • 預設選項(Please Select)為多語,{lang.ez_pleaseselect}
          • 每個項目的 value = {spec4SelectInfo.options .id}
          • 每個項目的 name = {spec4SelectInfo.options .name}
          • 每個項目有可能有圖片,對應大圖圖片 = {spec4SelectInfo.options.pcImgUrl},對應小圖圖片 ={spec4SelectInfo.options.mobileImgUrl},對應原圖圖片 = {spec4SelectInfo.options.oriImgUrl}
          • 圖片的用途請參考下面的操作邏輯
        • 9-3 Quantity 為多語,{lang.ez_quantity}
        • 9-4 可以透過 + 跟 - 的按鈕調整 Quantity 的數量,調整 Quantity 數量的邏輯請參考下面的操作邏輯
        • 9-5 包含可以訂購的數量, UnitName 與 available 的多語
          • 可訂購數量為 {productInfo.ezMaxOrderCount}
          • unitName 為 {productInfo.unitName}
          • available 為多語,{lang.ez_available}
        • 9-5 顯示可訂購數量, 單位名稱 與 available 文字的多語
          • 可訂購數量為 {productInfo.ezMaxOrderCount}
          • 單位名稱 為 {productInfo.unitName}
          • available 為多語,{lang.ez_available}
      • 10 Total Price
        • 標題為多語,{lang.ez_totalprice}
        • 金額顯示格式為 幣別 X 個數 = 金額 / 單位
          • 幣別 : {productInfo.priceCurrencyName}
          • 個數 : 對應欄位 9 的 Quantity 數量,預設為 1
          • 金額 : 透過計算而得,相關邏輯請參考下面的操作邏輯
          • 單位 : {productInfo.unitName}
        • 如果頁面的幣別有替換,那顯示時需要將金額從 USD 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別
      • 11 Payment 區塊
      • 12 Buy Now 按鈕
        • Buy Now 為多語,{lang.ez_buynow}
        • 點選相關邏輯請參考下面的操作邏輯
      • 13 Contact Supplier 按鈕
        • Contact Supplier 為多語,{lang.contactsupplier}
        • 點選相關邏輯請參考下面的操作邏輯
      • 14 Add to Cart
        • Add to Cart 為多語,{lang.ez_addtocart}
        • 點選相關邏輯請參考下面的操作邏輯
      • 15 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 == 1 代表販售,進入販售產品詳細頁
  • 操作邏輯

    • 規格屬性,下拉選單選擇

      • 對應頁面 9-2 所描述的操作方式
      • 如果選取下拉選單項目對應的 {spec4SelectInfo.options.pcImgUrl} != '',代表這個規格屬性有對應圖片,要將此圖片加入 sliderGalleryInfo 中
      • 例如原本該型錄有兩張圖,點選了一個有圖的規格屬性,此時型錄要顯示三張圖,前兩張是原本該型錄 sliderGalleryInfo 中的圖,要再額外加一張規格屬性的圖
      • 上面的大圖對應 {spec4SelectInfo.options.pcImgUrl}
      • 新加入的圖同樣具備原本的圖片效果(Zoom in, Enlarge)
      • 目前系統會將 {spec4SelectInfo.options.pcImgUrl} 帶入,下方的 slider 沒有放入小圖,後續如果需要擴充加入小圖的功能,小圖可以使用 {spec4SelectInfo.options.mobileImgUrl}
    • 規格屬性,有價格表的情況下,改變 Quantity

      • 對應頁面 9-4 所描述的操作方式
      • 先參考上方的 price 表,判斷 quantity 介於哪個區間內,然後取得金額
        • 以上圖為例,當 quantity = 5,對應的金額是 USD 105.56
        • 以上圖為例,當 quantity = 11,對應的金額是 USD 94.53
      • quantity 需要做最大值的檢查,不能超過最大值
        • 以上圖為例,quantity 的最大值 = 100
      • 變更 quantity 時,Total Price 區塊需要重新計算,金額會從對應的 price 表取得
        • 以上圖為例,當 quantity = 5,此時 Total Price = USD 105.56 X 5 = USD 527.80
        • 以上圖為例,當 quantity = 11,此時 Total Price = USD 94.53 X 11 = USD 1,039.83
    • 規格屬性,沒有價格表的情況下,改變 Quantity

      • 對應頁面 9-4 所描述的操作方式
      • quantity 需要做最大值的檢查,不能超過最大值
      • 變更 quantity 時,Total Price 區塊需要重新計算
        • 由於沒有價格表,代表此型錄的產品是單一價格,所以修改 quantity 時,直接使用 {productInfo.priceMin} 來計算 Total Price
    • 點選 Buy Now 按鈕

      • 先檢查該型錄是否有 規格屬性 ,如果有規格屬性,那必須每一項下拉選單都要有值,如果沒有值,顯示提示訊息
      • 通過檢查,將會執行以下動作
        1. 將型錄加入購物車(Add to cart)
        2. redirect 到固定網址:{shopping domain}/secured/shoppingcart/step1
    • 點選 11-2 的 More

      • 顯示 11-3 的資訊
    • 點選 Contact Supplier 按鈕

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

      • 參考之前條列頁的加入購物車功能
      • 加入購物車功能,參考 header service 的 add to cart 部分
        • 呼叫 header service,相關 post url
        • 透過 post 傳入相關參數,參數範例如下
          • productId 是要加入購物車的型錄 id,{productInfo.productId}
          • languageId 是該型錄的語系,{productInfo.languageId}
          • spec 是頁面參數,對應選擇下拉的 spec 的值
          • quantity 是頁面參數,對應頁面的 Quantity 值
          • shippingMethod 與 shopToCountry 傳入 1
          • domainId 對應參數 domain id
        • 解析回傳 json 內容,判斷是否成功加入購物車,範例如下
          • 主要判斷 code, message, messageIntl 回傳的內容
            • 如果 code = 200 代表 service 正常處理,反之代表呼叫 service 發生錯誤
            • message 紀錄本次呼叫相關顯示訊息
            • messageIntl 紀錄 debug 相關訊息
          • data 的部分主要紀錄與型錄相關的訊息,並不影響顯示
    • 點選 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 區塊分為五個對應 tab,預設顯示第一個
    • 其中前兩個型錄詳目與公司詳目是固定一定會出現的;後三個與販售相關的區塊需要判斷 api 回傳的資料來決定是否要顯示
    • Shipping & Packaging 請參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#運費資料
    • Payment Terms 請參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#付款單位
    • Return Policy 請參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#相關政策
    • 取出 data 中的 shippingInfo object, paymentTermInfo array 與 returnPolicyInfo object
    • 邏輯與格式
      • 1 Product Detail
        • 標題為多語,{lang.productdetail}
      • 2 Company Profile
        • 標題為多語,{lang.companyprofile}
      • 3 Shipping & Packaging
        • 標題為多語,{lang.ez_shippingpackaging}
        • 如果 data 中的 shippingInfo object 非空,顯示此 tab
        • 如果使用者有登入,此處需要傳遞相關使用者資訊到後台,這樣才能正確取得對應的 shippingCountryId
      • 4 Payment Terms
        • 標題為多語,{lang.paymentterms}
        • 如果 data 中的 paymentTermInfo array 非空,顯示此 tab
      • 5 Return Policy
        • 標題為多語,{lang.ez_return_policy}
        • 如果 data 中的 returnPolicyInfo object 非空,顯示此 tab
  • 操作邏輯
    • 透過 tab 去切換顯示對應的區塊

詳細資料(Product Detail)

  • 顯示邏輯

    • 取出 data 中的 productInfo object, specInfo array, paymentInfo object, 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)

  • 顯示邏輯
    • 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#廠商資料
    • 呼叫 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
    • 主要分為 公司基本資料, 公司相關型錄, 公司工廠資料, 公司認證與產品認證, 公司得獎紀錄、大事記, 公司聯絡資訊 等區塊
    • 取得 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} 跟今天日期的比較,判斷是否在折扣日內
          • {productInfo.discountStartDate} 與 {productInfo.discountEndDate} 回傳的資料型態為 long,對應 linux timestamp,例如 1633332979000,代表台灣時間 2021-10-04 15:36:19
          • 再判斷 {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 更多型錄圖示
        • 判斷以下邏輯
          • 在產品company profile下,型錄清單最多顯示 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}
  • 操作邏輯

    • 如果 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-C-01

詳細資料(Shipping & Packaging)

  • 顯示邏輯

    • 參考 URI-B-07 Shipping & Packaging API 取得對應 json 內容
      • 傳入的 url 路徑 /products/shipping/{did}/{id}/{countryId}
      • 傳入的 url 路徑 /products/shipping/{did}/{id}/{countryId}?userId={loginUserId}
      • 傳入的 url 路徑 /products/shipping/{did}/{id}/{countryId}?userAccessTicket={userAccessTicket}
      • id 傳入 {productInfo.productId}
      • countryId 一開始的預設值是 {shippingInfo.shippingCountryId}
      • 之後如果使用者變更了國家(參考圖中的區塊 6),此時 countryId 傳入區塊 6 的值
      • 如果使用者有登入,呼叫 api 時需要傳遞登入者的 loginUserId,此 userId 參數非必填,未登入就不需傳遞
      • 如果使用者有登入,呼叫 api 時需要傳遞登入者的 userAccessTicket,此 useruserAccessTicketId 參數非必填,未登入就不需傳遞
    • 解析回傳的 data object 內容
      • 1 Calculate your shipping cost by countr/region and quantity 為多語,{lang.ez_calshippingcost}
      • 2 Quantity 為多語,{lang.ez_quantity}
      • 3 數量,輸入框,可輸入數字,預設為 1
      • 4 Units 為多語,{lang.ez_unit}
      • 5 Ship to 為多語,{lang.ez_shipto}
      • 6 下拉選單,解析 data.shippingCountryList 的資料,依照順序顯示
        • 下拉值:{data.shippingCountryList.code}
        • 下拉名稱 : {data.shippingCountryList .name}
      • 7 下拉區域,解析 data.shippingAreaList 的資料,依照順序顯示
        • 下拉值:{data.shippingAreaList.code}
        • 下拉名稱 : {data.shippingAreaList .name}
      • 8 Zip Code 輸入框,目前舊版程式未特別處理
      • 9 Shipping Company 為多語,{lang.ez_shippingcompany}
      • 10 Shipping Cost 為多語,{lang.ez_shippingcost}
      • 11 Estimated Delivery Time 為多語,{lang.ez_deliverytime}
      • 12 價格列表
        • 解析 data.shippingInfo.shippingDataList 的資料,依照順序顯示
        • 第一欄顯示對應的圖片,依照 {data.shippingInfo.shippingDataList.deliveryCompanyCode} 來判斷顯示哪一張圖片
        • 第二欄顯示相關金額
          • 先判斷 {data.shippingInfo.shippingDataList.shippingType} 值
          • shippingType == 0 代表免運費,顯示 Free Shipping 圖片與 Free Shipping 多語 {lang.ez_freeshipping},Free Shipping
          • shippingType == 1 代表自訂運費,透過以下欄位計算金額
            • {data.shippingInfo.shippingDataList.customPortage} 自訂運費需要有值
            • {data.shippingInfo.shippingDataList.customAmount} 自訂運費,每多 n 件多收的金額
            • {data.shippingInfo.shippingDataList.customOverAmount} 自訂運費,結合 customOverPortage 的應用,紀錄 每多 n 件的件數
            • 顯示金額為 customAmount + customOverAmount * (欄位 3 的 Quantity)
            • 自訂運費對應的欄位內容格式為 運送設置
              {data.shippingInfo.shippingDataList.customAmount} 件之內,運費 {data.shippingInfo.shippingDataList.customPortage}。每超過 {data.shippingInfo.shippingDataList.customOverAmount} 件,增加運費 {data.shippingInfo.shippingDataList.customOverPortage}。
            • 計算方式如下(以下的 quantity 代表欄位三的值):
              • 如果運送數量未達加收運費的件數(quantity <= {data.shippingInfo.shippingDataList.customAmount}),此時運費 = {data.shippingInfo.shippingDataList.customPortage}
              • 運送數量達到加收運費的狀況(quantity > {data.shippingInfo.shippingDataList.customAmount}),此時先計算加收運費單位(overFee),這只是一個暫存的參數,後面的程式計算會用到;首先檢查 "每超過 {data.shippingInfo.shippingDataList.customOverAmount} 件,增加運費 {data.shippingInfo.shippingDataList.customOverPortage}" 中的 {data.shippingInfo.shippingDataList.customOverAmount} 與 {data.shippingInfo.shippingDataList.customOverPortage} 是否都有值
                • 都有值,代表加收運費正常,overFee = (quantity - 不需加收的件數) / (每超過 n 件增加運費),得到的結果向上取整數,代表有多少單位需要加收運費
                • 值有缺,此時 overFee = 0
              • 運費 = 運費 + (加收運費件數 * 多收的單位運費)
              • 程式範例如下:
                var fee = 0; // 最後的運費
                if (quantity <= customAmount) {
                  fee = customPortage;
                } else {
                  var overFee = 0;
                  if (customOverAmount && customOverAmount > 0) {
                    overFee = Math.ceil((quantity - customAmount) / customOverAmount);
                  }
                  fee = customPortage + overFee * customOverPortage;
                }
          • shippingType == 2 代表固定運費,固定顯示 {data.shippingInfo.shippingDataList.fixFee}
          • shippingType == 3 代表 UPS 優惠運費,固定顯示 {data.shippingInfo.shippingDataList.monetaryValue} * quantity;如果回傳 null 或是 0,也是代表免運費的意思
        • 第三欄顯示運送時間
          • 如果有 durationStart 跟 durationEnd
            • 顯示 {{ durationStart }} - {{ durationEnd }} days,多語 {lang.days}
          • 如果只有 durationStart
            • 顯示 {{ durationStart }} days,多語 {lang.days}
          • 如果兩者都沒有
            • 顯示 Expected delivery time contact suppliers,多語 {lang.ez_expecteddeliverytime}
  • 操作邏輯

    • 修改 Quantity 時,如果是自訂運費,要重新顯示對應的金額
      • 參考上方 shippingType == 2 的情況
    • 下拉選單修改 Country 的值,對應區塊 6
      • 重新呼叫 api,將回傳結果解析並放在畫面上

詳細資料(Payment Terms)

詳細資料(Return Policy)

聯繫廠商

  • 商機Inquiry Product,請參考B-06C

推薦區塊

  • 顯示邏輯

    • 參考 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},會影響到本頁是否要顯示警語
  • 操作邏輯

    • 點選型錄圖片與聯結時,將會 redirect 到該型錄的 cp 頁

警語相關邏輯

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

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

    • 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.ez_daysleft 折扣剩餘天數
型錄主要資訊 lang.modelno 型錄型號
型錄主要資訊 lang.madein 型錄產地
型錄主要資訊 lang.supplier 廠商名稱
型錄主要資訊 lang.ez_moreaboutproduct More About This Product
型錄主要資訊 lang.ez_unitprice Unit Price
型錄主要資訊 lang.ez_discountprice Discount Price
型錄主要資訊 lang.price Price
型錄主要資訊 lang.ez_quantity Quantity
型錄主要資訊 lang.ez_processingtime Processing Time
型錄主要資訊 lang.ez_days Days
型錄主要資訊 lang.requestforquotation Request For Quotation
型錄主要資訊 lang.ez_pleaseselect Please Select
型錄主要資訊 lang.ez_available Available
型錄主要資訊 lang.ez_totalprice Total Price
型錄主要資訊 lang.more More
型錄主要資訊 lang.ez_minorder Min Order.
型錄主要資訊 lang.ez_buynow Buy Now
型錄主要資訊 lang.contactsupplier Contact Supplier
型錄主要資訊 lang.ez_addtocart Add to Cart
型錄主要資訊 lang.addtofavorites Add to favorites
型錄主要資訊 lang.gotofavorites Go to favorites
tab 區塊 lang.productdetail Product Detail
tab 區塊 lang.companyprofile Company Profile
tab 區塊 lang.ez_shippingpackaging Shipping & Packaging
tab 區塊 lang.paymentterms Payment Terms
tab 區塊 lang.ez_return_policy Return Policy
型錄 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.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.product Product
公司基本資料 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.product Product
運費區塊 lang.ez_calshippingcost Calculate your shipping cost by countr/region
運費區塊 lang.ez_quantity Quantity
運費區塊 lang.ez_unit Units
運費區塊 lang.ez_shipto Ship to
運費區塊 lang.ez_shippingcompany Shipping Company
運費區塊 lang.ez_shippingcost Shipping Cost
運費區塊 lang.ez_deliverytime Estimated Delivery Time
運費區塊 lang.ez_freeshipping Free Shipping
運費區塊 lang.days Days
運費區塊 lang.ez_expecteddeliverytime Expected delivery time contact suppliers
payment term lang.ez_acceptedpaymentmethod Accepted payment method
payment term lang.ez_currency Currency
推薦型錄 lang.recommendation Recommendation
推薦型錄 lang.ez_smallrecommendation Small Order Recommendation