# B-6 販售產品詳細頁 > [name=JasonWu] ###### tags: `CP頁` ------------------------------------------------------------------------ ## API 清單 No | API | Desc | 執行順序 | 執行條件 | 參考 ---|----------|---------------------|----------|--------------------|-------- 1 | [URI-A-02](https://hackmd.io/Cbw7V8AETEOzCk4s7TgfwA) | 多語標籤 | 1 | page init | 2 | [URI-B-03](https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA) | 取得型錄相關資料 | 1 | page init | 主要 api 3 | [URI-B-06C](https://hackmd.io/FDa_t2QwRxKoBimH6mW4qQ) | 商機Inquiry Product | 1 | page init | 無 4 | [URI-B-05](https://hackmd.io/ogBAYWnFQeGUTgYPo9aZPg) | 取得推薦相關型錄 | 2 | page init | 相關輸入條件會與 URI-B-03 有關 5 | [URI-B-04](https://hackmd.io/WTAzi5tXQBuakQV1tkgATw) | 取得廠商相關資料 | 3 | 點選廠商 tab | 呼叫廠商 api 6 | [URI-B-07](https://hackmd.io/03JtG4EZT8aAqqnddSgWTQ) | 取得販售相關資訊 | 3 | 點選 Shipping tab<br>切換販售運費國家 | 無 7 | [URI-C-02](https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w) | 取得燈箱相關資訊 | 3 | 點選型錄認證/公司認證開啟燈箱 | 無 8 | [URI046](https://hackmd.io/C0_27LhmTD6m9qV_DahhTQ) | 取得 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 ## 原頁面網址 - https://www.ttstaging.com.tw/product/1893926 ## 頁面輸入 URL 解析 - 進入頁面方式有兩種 - /product/{id} - 範例 : https://www.ttstaging.com.tw/product/1893926 - id : 型錄 id,為純數字 - /product/{safe-name}-{id}.html - 範例 : https://www.ttstaging.com.tw/product/tdk-blu-ray-dl-4x-50gb-809803.html - safe-name : 型錄名稱,為純文字,且型錄名稱會將英文轉為小寫,空白、特殊符號等字元替換為 - 號,例如 型錄名稱 為 **TDK Blu Ray DL 4X 50GB** 將會轉為 **tdk-blu-ray-dl-4x-50gb** - id : 型錄 id,為純數字 - id 為主要參數,會應用在後面的 api 中 ## 頁面說明 ![](https://i.imgur.com/1meSpHR.jpg) ![](https://imgur.com/HybBT3I.jpg) ### 頁面區塊說明 - B-06-01 - 共用Header Service元件, 參考[`L01`](https://docs.google.com/spreadsheets/d/1FfU1adulMCzdWNQNjagsm5rHYhokRwhKfzY-Lurvw9Q/edit#gid=1382704913) - B-06-02 - 麵包屑,請參考[URI-B-03](https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA?view#麵包屑) - B-06-03 - 型錄詳細資料,請參考[URI-B-03](https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA) - B-06-04 - 商機Inquiry Product,請參考[URI-B-06C](https://hackmd.io/FDa_t2QwRxKoBimH6mW4qQ?both) - B-06-05 - 推薦型錄&小額採購區,請參考[URI-B-05](https://hackmd.io/ogBAYWnFQeGUTgYPo9aZPg) - B-06-06 - 參考首頁Footer - SeoMeta 資訊 - 透過 api 取得頁面所需的 meta 資訊 - 麵包屑 - 透過 api 取得麵包屑 - 圖片與多媒體 - 透過 api 取得圖片、多媒體資訊 - 型錄主要資訊 - 透過 api 取得產品名稱, 型號資訊等資訊 - tab 區塊,切換顯示 - 透過 api 決定顯示那些顯示 tab - 詳細資料 - 透過 api 取得產品詳細資訊 - 參考廠商資訊 api,顯示對應的廠商資訊 - 參考運費計算 api,顯示對應的運費資訊 - 聯繫廠商 - 參考聯繫廠商 api,顯示相關畫面 - 推薦型錄 ### 頁面欄位說明 #### SeoMeta 資訊 - 舊版頁面相關資訊透過 xml 傳遞 ![](https://i.imgur.com/nd2g6gV.jpg) - 產出 html 頁面時,需要將相關資訊放入 meta tag 中 ![](https://i.imgur.com/Wt1DmGK.jpg) - ~~顯示邏輯~~ - ~~參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA?view#meta1~~ - ~~取得 data 中的 meta array~~ - ~~邏輯與格式~~ - ~~將 array 中的內容依次放入~~ - ~~格式固定為 <meta name="{meta.name}" content="{meta.content}">~~ - 透過 [URI046](https://hackmd.io/C0_27LhmTD6m9qV_DahhTQ) 取得頁面相關的 meta 資料 - 呼叫相關 api 路徑如下: ${TT-API網址}/domains/seo/{seoType}/{pageType}/{did}/{rowId} - 參數 seoType 帶入 "1" - 參數 pageType 帶入 "PRODUCT_DETAIL" - 參數 did 帶入頁面 {did} - 參數 rowId 帶入型錄 id {id} - 解析回傳的 json 內容,並寫入頁面原始檔中 #### 麵包屑 ![](https://i.imgur.com/yLAZQfN.jpg) - 顯示邏輯 - 參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA?view#%E9%BA%B5%E5%8C%85%E5%B1%911 - 取出 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 到對應的頁面 - 產品名稱不需要連結 #### 圖片與多媒體 ![](https://i.imgur.com/PmmFQZS.jpg) - 顯示邏輯 - 圖片部分參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E5%9C%96%E7%89%87%E8%B3%87%E6%96%99 - 多媒體部分參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E5%A4%9A%E5%AA%92%E9%AB%94%E8%B3%87%E6%96%99 - 取出 data 中的 sliderGalleryInfo array 與 multimediaInfo object - 邏輯與格式 - 1 區塊大圖 - 找出主要圖片 - 找出 sliderGalleryInfo 中 {sliderGalleryInfo.hasPrimary} = 1 的那一筆 - 顯示該筆資料的 {sliderGalleryInfo.img360_360Url} - 如果找不到對應的圖片,將會顯示預設圖 - 大圖 url : [`沒有圖片時的大圖`](#頁面相關路徑) - 小圖 url : [`沒有圖片時的小圖`](#頁面相關路徑) ![](https://i.imgur.com/Ijvi1Tf.jpg) - 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` ](https://www.addthis.com/) - 可參考 cms-front/src/main/webapp/site/tteng/xsl/share.xsl ![](https://i.imgur.com/MDzy6hB.png) - 操作邏輯 - 點選 多媒體3D 或 多媒體360 圖示,顯示相關操作燈箱 ![](https://i.imgur.com/D2yRXkr.jpg) - 帶入目前 multimediaInfo 內的資料 - 1 透過 iframe,將對應的 html 放入燈箱內 - 點選 3D 時,iframe 顯示 {multimediaInfo.multimedia_3d_url} - 點選 360 時,iframe 顯示 {multimediaInfo.multimedia_360_url} - 2 關閉燈箱按鈕,點選後關閉燈箱 - 3 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕 - 點選 多媒體 Video 圖示,顯示相關操作燈箱 ![](https://i.imgur.com/m2WzPOW.jpg) - 帶入目前 multimediaInfo 內的資料 - 1 透過 iframe,將對應的影片放入燈箱內 - 如果影片是 youtube,可使用 youtube - 如果不是,直接將對應內容放入 iframe - 2 關閉燈箱按鈕,點選後關閉燈箱 - 3 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕 - 當關閉燈箱時,如果影片正在撥放,此時需要停止撥放影片 - 目前舊版程式對 youtube 影片有做此處理,關閉燈箱時會呼叫 youtube 的 stopVideo 來停止撥放 - 點選 Enlarge,顯示相關操作燈箱 ![](https://i.imgur.com/ptCeR8F.jpg) - 帶入目前 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 的效果 ![](https://i.imgur.com/pukHhVx.jpg) - 首先判斷當前瀏覽器頁面的寬度是否 > 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 手動捲動效果 ![](https://i.imgur.com/WNORrEW.jpg) - slider 超過兩則圖片時,需要提供 slider 效果 - 舊版採用 slick slide 套件,載入 slick-utils.js - 點選向左或是向右,型錄小圖 slider 會捲動到前一筆或是後一筆 - 捲動的同時,上方型錄大圖也會跟著切換為當前第一張小圖對應的大圖 - 點選小圖,會將上面的大圖換為點選對應的大圖 - 點選下方小圖,會將上面的大圖換為點選對應的大圖 - 點選後,小圖 slider 的 index 會切換到目前點選的項目 #### 型錄主要資訊 ![](https://i.imgur.com/mWqpMlM.jpg) - 顯示邏輯 - 型錄相關內容(1,2,3,4,5,6,7,10,11,12,13,14,15區塊)參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E5%9E%8B%E9%8C%84%E8%B3%87%E6%96%99 - 價格表內容(8)參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E5%83%B9%E6%A0%BC%E8%A1%A8 - 規格屬性選項內容(9)參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E8%A6%8F%E6%A0%BC%E5%B1%AC%E6%80%A7%E9%81%B8%E9%A0%85 - 取出 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 ![](https://i.imgur.com/CBQyCIe.jpg) - 如果剩餘的時間在一日內,顯示剩餘的時分秒 ![](https://i.imgur.com/h5GdIrY.jpg) - Days Left 為多語,{lang.ez_daysleft} - 另外補上 {productInfo.isFreeShipping} 判斷,如果 {productInfo.isFreeShipping} == 1,顯示[免運費圖片](https://cs01.ttstaging.com.tw/front/497a7e5/tteng/images/icon_shipping2.png),如果要沿用,請將此圖檔下載後套用,多語請參考 {lang.ez_freeshipping} ![](https://i.imgur.com/rk0fpYC.jpg) - 2 型錄標題 - {productInfo.productName} - 3 型錄型號 - 標題為多語,{lang.modelno} - {productInfo.modelNo} - 沒有資料時不顯示本欄 - 4 產地 - 標題為多語,{lang.madein} - {productInfo.madeIn} - 沒有資料時不顯示本欄 - 5 廠商資訊 ![](https://i.imgur.com/9ylmV64.jpg) - 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 認證的圖片 ![](https://i.imgur.com/mWqpMlM.jpg) - 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 價格表 ![](https://i.imgur.com/dc3aF6E.jpg) - 以表格的方式顯示價格表,依次解析 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 規格屬性 ![](https://i.imgur.com/OxaXdHR.jpg) - 解析 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 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別 ![](https://i.imgur.com/mWqpMlM.jpg) - 11 Payment 區塊 - 顯示邏輯 ![](https://i.imgur.com/7IX4hkl.jpg) - 11-1 Payment 圖示 ~~- 取得 data 中的 paymentTermInfo array~~ ~~- 依照 {paymentTermInfo.code} 決定顯示什麼圖~~ ~~- [`Paypal`](#頁面相關路徑)~~ ~~- [`支付寶 alipay`](#頁面相關路徑)~~ ~~- [`信用卡 card`](#頁面相關路徑)~~ ~~- [`財付通 tenpay`](#頁面相關路徑)~~ ~~- [`銀聯卡 unionpay`](#頁面相關路徑)~~ ~~- [`網際威信 hitrust`](#頁面相關路徑)~~ ~~- [`信用卡(AE) card_ae`](#頁面相關路徑)~~ ~~- [`google pay`](#頁面相關路徑)~~ ~~- [`apple pay`](#頁面相關路徑)~~ - 11-1 Payment 圖示 - 取得 data 中的 paymentInfo object - 解析 {paymentInfo.paymentTerms} 決定顯示什麼圖:內容為透過逗號分隔的相關付款圖示,依照對應的格式顯示對應的圖 - [`Paypal`](#頁面相關路徑) - [`支付寶 alipay`](#頁面相關路徑) - [`信用卡 card`](#頁面相關路徑) - [`財付通 tenpay`](#頁面相關路徑) - [`銀聯卡 unionpay`](#頁面相關路徑) - [`網際威信 hitrust`](#頁面相關路徑) - [`信用卡(AE) card_ae`](#頁面相關路徑) - [`google pay`](#頁面相關路徑) - [`apple pay`](#頁面相關路徑) - 11-2 More - More 為多語,{lang.more} - 點選請參考下面的操作邏輯 - 11-3 詳細內容 - We also supports 目前是固定的 - 線下支付相關名稱取自於 {paymentInfo.offlineMethod} - for offline orders. 目前是固定的 - Min. Order: 為多語,{lang.ez_minorder} - 最小訂購量為 {paymentInfo.minOrderQuantity} - 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 代表販售,進入販售產品詳細頁 - 操作邏輯 - 規格屬性,下拉選單選擇 ![](https://i.imgur.com/q0Tou1k.jpg) - 對應頁面 9-2 所描述的操作方式 - 如果選取下拉選單項目對應的 {spec4SelectInfo.options.pcImgUrl} != '',代表這個規格屬性有對應圖片,要將此圖片加入 sliderGalleryInfo 中 - 例如原本該型錄有兩張圖,點選了一個有圖的規格屬性,此時型錄要顯示三張圖,前兩張是原本該型錄 sliderGalleryInfo 中的圖,要再額外加一張規格屬性的圖 - 上面的大圖對應 {spec4SelectInfo.options.pcImgUrl} - 新加入的圖同樣具備原本的圖片效果(Zoom in, Enlarge) - 目前系統會將 {spec4SelectInfo.options.pcImgUrl} 帶入,下方的 slider 沒有放入小圖,後續如果需要擴充加入小圖的功能,小圖可以使用 {spec4SelectInfo.options.mobileImgUrl} - 規格屬性,有價格表的情況下,改變 Quantity ![](https://i.imgur.com/hEDlxSb.jpg) - 對應頁面 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 按鈕 ![](https://i.imgur.com/rzJansD.jpg) - 先檢查該型錄是否有 規格屬性 ,如果有規格屬性,那必須每一項下拉選單都要有值,如果沒有值,顯示提示訊息 - 通過檢查,將會執行以下動作 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 - uat : https://header-service.ttstaging.com.tw/cart-receiver/add-item - prod : https://header-service.taiwantrade.com/cart-receiver/add-item - 透過 post 傳入相關參數,參數範例如下 ![](https://i.imgur.com/ahLb0C3.jpg) - productId 是要加入購物車的型錄 id,{productInfo.productId} - languageId 是該型錄的語系,{productInfo.languageId} - spec 是頁面參數,對應選擇下拉的 spec 的值 - cartSpecId : 對應選擇項目的 {spec4SelectInfo.sid} - cartSpecName : 對應選擇項目的 {spec4SelectInfo.name} - cartSpecDetailId : 對應選擇項目的 {spec4SelectInfo.options.id} - cartSpecDetailName : 對應選擇項目的 {spec4SelectInfo.options.name} - quantity 是頁面參數,對應頁面的 Quantity 值 - shippingMethod 與 shopToCountry 傳入 1 - domainId 對應參數 domain id - 解析回傳 json 內容,判斷是否成功加入購物車,範例如下 ![](https://i.imgur.com/X4Q6EAI.jpg) - 主要判斷 code, message, messageIntl 回傳的內容 - 如果 code = 200 代表 service 正常處理,反之代表呼叫 service 發生錯誤 - message 紀錄本次呼叫相關顯示訊息 - messageIntl 紀錄 debug 相關訊息 - data 的部分主要紀錄與型錄相關的訊息,並不影響顯示 - 點選 Add to favorite - 參考之前條列頁的加入最愛功能 - 加到我的最愛部分,這邊先貼上目前條列頁相同功能的程式碼,應該是可以直接套用的;條列頁程式有納入版本控制,後續開發如果碰到問題,這邊可以提供相關程式來參考;檢查型錄是否已經加入我的最愛、將型錄加入我的最愛、從我的最愛中移除型錄,在下面的 ts 中應該都有相對應的程式碼 - 相關連結 https://github.com/ttcloude/taitra-aem/blob/master/ui.frontend/src/app/library/product/product.component.ts - 相關程式請參考下方程式碼區塊 - 我的最愛程式碼區塊 ```=java 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 區塊,切換顯示 ![](https://i.imgur.com/m60pkjc.jpg) - 顯示邏輯 - 販售型錄的 tab 區塊分為五個對應 tab,預設顯示第一個 - 其中前兩個型錄詳目與公司詳目是固定一定會出現的;後三個與販售相關的區塊需要判斷 api 回傳的資料來決定是否要顯示 - Shipping & Packaging 請參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E9%81%8B%E8%B2%BB%E8%B3%87%E6%96%99 - Payment Terms 請參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E4%BB%98%E6%AC%BE%E5%96%AE%E4%BD%8D - Return Policy 請參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E7%9B%B8%E9%97%9C%E6%94%BF%E7%AD%96 - 取出 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) ![](https://i.imgur.com/hVlcjC3.jpg) - 顯示邏輯 - 取出 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 型錄認證/公司認證燈箱](https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w#%E7%87%88%E7%AE%B1%E5%85%83%E4%BB%B6) 取得對應 json 內容 - 傳入的 url 路徑 /certifications/prod/{certInfo.id}?lang={productInfo.languageId}&companyId={productInfo.supplierId} - 解析回傳的 certInfo object 內容並顯示於燈箱中 ![](https://i.imgur.com/id1t7B0.jpg) - 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 型錄認證相關型錄 ![](https://i.imgur.com/QF3j6lT.jpg) - 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://i.imgur.com/YeCuYdb.jpg) - 顯示邏輯 - 參考文件 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 中應該都有相對應的程式碼 - 相關連結 https://github.com/ttcloude/taitra-aem/blob/master/ui.frontend/src/app/library/supplier-list/supplier-list.component.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} - 條列頁我的最愛相關程式碼內容 ```=java 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://i.imgur.com/FukSQAh.jpg) - 顯示邏輯 - 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#%E7%9B%B8%E9%97%9C%E5%9E%8B%E9%8C%84 - 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://i.imgur.com/Gn2G6o3.jpg) - 顯示邏輯 - 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#%E5%B7%A5%E5%BB%A0%E8%B3%87%E6%96%99 - 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} - 操作邏輯 - 點選工廠圖片,透過燈箱顯示圖片 ![](https://i.imgur.com/snPARDi.jpg) - 1 點選的工廠圖片 - 2 開啟對應燈箱,顯示 {factoryInfo.imageList.imageURL} - 3 燈箱關閉按鈕 - 關閉燈箱按鈕,點選後關閉燈箱 - 4 燈箱遮罩 - 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕 ##### 公司認證與產品認證 ![](https://i.imgur.com/j5jmpkZ.jpg) - 顯示邏輯 - 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 型錄認證/公司認證燈箱](https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w#%E7%87%88%E7%AE%B1%E5%85%83%E4%BB%B6) 取得對應 json 內容 - 傳入的 url 路徑 /certifications/prod/{certInfo.id}?lang={productInfo.languageId}&companyId={productInfo.supplierId} - 解析回傳的 certInfo object 內容並顯示於燈箱中 ![](https://i.imgur.com/id1t7B0.jpg) - 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 型錄認證相關型錄 ![](https://i.imgur.com/QF3j6lT.jpg) - 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 型錄認證/公司認證燈箱](https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w#%E7%87%88%E7%AE%B1%E5%85%83%E4%BB%B6) 取得對應 json 內容 - 傳入的 url 路徑 /certifications/prod/{certInfo.id}?lang={productInfo.languageId}&companyId={productInfo.supplierId} - 解析回傳的 certInfo object 內容並顯示於燈箱中 ![](https://i.imgur.com/sFASn2z.jpg) - 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://i.imgur.com/LuuSZjm.jpg) - 顯示邏輯 - 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#%E5%BE%97%E7%8D%8E%E8%B3%87%E6%96%99 - 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://i.imgur.com/Jf0B3SJ.jpg) - 顯示邏輯 - 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#%E5%BE%97%E7%8D%8E%E8%B3%87%E6%96%99 - 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://i.imgur.com/5JhiZMQ.jpg) - 已登入 ![](https://i.imgur.com/zCuJJIt.jpg) - 顯示邏輯 ![](https://i.imgur.com/gfmtFal.jpg) - 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?both#%E5%85%AC%E5%8F%B8%E8%81%AF%E7%B5%A1%E8%B3%87%E8%A8%8A - 取得 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 聯絡人相關訊息 中有 聯絡人圖片,點選後會開啟燈箱顯示圖片 ![](https://i.imgur.com/53BkE8E.jpg) - 1 點選 聯絡人圖片 圖示 - 2 開啟燈箱內容,圖片部份顯示對應的 {contactUs.contactPicture} - 3 目前顯示 avatar 單字 - 4 燈箱關閉按鈕 - 關閉燈箱按鈕,點選後關閉燈箱 - 5 燈箱遮罩 - 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕 - 如果 2 聯絡人相關訊息 中有 聯絡人名片,點選後會開啟燈箱顯示名片 ![](https://i.imgur.com/LhMzWed.jpg) - 1 點選 聯絡人名片 圖示 - 2 開啟燈箱內容,圖片部份顯示對應的 {contactUs.contactCard} - 3 目前顯示 avatar 單字 - 4 燈箱關閉按鈕 - 關閉燈箱按鈕,點選後關閉燈箱 - 5 燈箱遮罩 - 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕 - 點選 3 Contact Supplier,會跳轉到本頁下面的 聯繫廠商 區塊 #### 聯繫廠商 - 商機Inquiry Product,請參考[URI-C-01](https://hackmd.io/Y-_iIT7rR4-uqwqN5ZGNXA?view) #### 詳細資料(Shipping & Packaging) ![](https://i.imgur.com/3V0Ofos.jpg) - 顯示邏輯 - 參考 [URI-B-07 Shipping & Packaging API](https://hackmd.io/03JtG4EZT8aAqqnddSgWTQ?view#%E8%BC%B8%E5%87%BA%E5%8F%83%E6%95%B8%E8%AA%AA%E6%98%8E) 取得對應 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} 來判斷顯示哪一張圖片 - [`DHL`](#頁面相關路徑) - [`EMS`](#頁面相關路徑) - [`FEDEX`](#頁面相關路徑) - [`TNT`](#頁面相關路徑) - [`UPS`](#頁面相關路徑) - [`Other`](#頁面相關路徑) - 第二欄顯示相關金額 - 先判斷 {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) ![](https://i.imgur.com/otBQ1Mk.jpg) - 顯示邏輯 - 參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E4%BB%98%E6%AC%BE%E5%96%AE%E4%BD%8D - 取得 data 中的 paymentTermInfo array - 邏輯與格式 - 1 Accepted payment method 為多語,{lang.ez_acceptedpaymentmethod} - 2 Currency 為多語,{lang.ez_currency} - 3 依照 {paymentTermInfo.code} 決定顯示什麼圖 - [`Paypal`](#頁面相關路徑) - [`支付寶 alipay`](#頁面相關路徑) - [`信用卡 card`](#頁面相關路徑) - [`財付通 tenpay`](#頁面相關路徑) - [`銀聯卡 unionpay`](#頁面相關路徑) - [`網際威信 hitrust`](#頁面相關路徑) - [`信用卡(AE) card_ae`](#頁面相關路徑) - [`google pay`](#頁面相關路徑) - [`apple pay`](#頁面相關路徑) - 4 顯示 {paymentTermInfo.currency} #### 詳細資料(Return Policy) ![](https://i.imgur.com/jWwlfcL.jpg) - 顯示邏輯 - 參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E7%9B%B8%E9%97%9C%E6%94%BF%E7%AD%96 - 取得 data 中的 returnPolicyInfo object - 邏輯與格式 - 顯示 {returnPolicyInfo.htmlContent} #### 聯繫廠商 - 商機Inquiry Product,請參考[B-06C](https://hackmd.io/FDa_t2QwRxKoBimH6mW4qQ?view) #### 推薦區塊 ![](https://i.imgur.com/cSNKeCe.jpg) - 顯示邏輯 - 參考 [URI-B-05 詳細頁 Cp Recommend API](https://hackmd.io/ogBAYWnFQeGUTgYPo9aZPg?both) 取得對應 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 頁 #### 警語相關邏輯 - 目前警語顯示規則如下: - 先透過 url 取得相關警語顯示的規則與顯示內容,範例 url 如下 - https://aem-apollo.taiwantrade.com/graphql?operationName=Warnings&variables=%7B%22did%22:2%7D&extensions=%7B%22persistedQuery%22:%7B%22version%22:1,%22sha256Hash%22:%22408620505421b126559cb52bcb72391e669697d6202176bdc261d6cd9e69143e%22%7D%7D - 其中 variable 中所傳遞的 did 對應目前頁面的 did - 取得的內容結構如下,html code 的警語範例如下,上圖是 url 實際畫面,下圖是結構化後的樣式 - ![](https://i.imgur.com/sC9lfI2.jpg) - ![](https://i.imgur.com/9ffnWfe.jpg) - 圖片結構化後的樣式如下 - ![](https://i.imgur.com/1lM8Xhe.jpg) - 分析取得的警語相關檔案的結構 - data.warnings 是 array 結構,紀錄所有要顯示警語的規則,每一個條件都要判斷,所以一個頁面可能會顯示多項警語 - data.warnings.displayMode 代表格式 - displayMode = 1 為 使用圖片 - displayMode = 2 為 使用 html code - data.warnings.cidList 代表標準 taitra code 條件 - cidList 是一個以逗號分隔的 string,例如 "cidList" : "543,123,456" - 本文件中有提到警語顯示相關的部分有 [型錄主要資訊](#%E5%9E%8B%E9%8C%84%E4%B8%BB%E8%A6%81%E8%B3%87%E8%A8%8A), [公司相關型錄](#%E5%85%AC%E5%8F%B8%E7%9B%B8%E9%97%9C%E5%9E%8B%E9%8C%84), [推薦區塊](#%E6%8E%A8%E8%96%A6%E5%8D%80%E5%A1%8A) - 如果這些區塊中的 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