# B-5 非販售產品詳細頁
> [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-C-02](https://hackmd.io/OFJ68AIxTfePBEeKqlGa-w) | 取得燈箱相關資訊 | 3 | 點選型錄認證/公司認證開啟燈箱 | 無
7 | [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
## 原頁面網址
- http://www.ttstaging.com.tw/product/1895999
## 頁面輸入 URL 解析
- 進入頁面方式有兩種
- /product/{id}
- 範例 : http://www.ttstaging.com.tw/product/1895999
- id : 型錄 id,為純數字
- /product/{safe-name}-{id}.html
- 範例 : https://www.ttstaging.com.tw/product/%E6%9C%AA%E8%B2%A9%E5%94%AE%E5%9E%8B%E9%8C%84-1895999.html
- safe-name : 型錄名稱,為純文字,且型錄名稱會將英文轉為小寫,空白、特殊符號等字元替換為 - 號,例如 型錄名稱 為 **TDK Blu Ray DL 4X 50GB** 將會轉為 **tdk-blu-ray-dl-4x-50gb**
- id : 型錄 id,為純數字
- id 為主要參數,會應用在後面的 api 中
## 頁面說明


- 型錄詳目頁來源主要透過[URI-B-04](https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA)取得相關資料
- api 相關 url 如下:${TT-API網址}/products/detail/{did}/{id}?userAccessTicket={userAccessTicket}
- did : domain id,與頁面及版型相關參數,例如英文主頁 did = 2
- id : 公司 id,參考 http://www.ttstaging.com.tw/company/65 ,其中 "65" 就是公司 id
- userAccessTicket 為登入者使用者Ticket,AEM 透過 header service 取得 userAccessTicket,如果有傳入,且經過解析後是正常的 ticket,後續 api 就會判斷此次呼叫是有登入的呼叫
### 頁面區塊說明
- B-06-01
- 共用Header Service元件, 參考[`L01`](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,顯示相關畫面
- 推薦型錄
### 頁面欄位說明
#### SeoMeta 資訊
- 舊版頁面相關資訊透過 xml 傳遞

- 產出 html 頁面時,需要將相關資訊放入 meta tag 中

- ~~顯示邏輯~~
- ~~參考 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://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://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 : [`沒有圖片時的小圖`](#頁面相關路徑)

- 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

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

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

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

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

- 首先判斷當前瀏覽器頁面的寬度是否 > 768 (rwd 不需要)
- 再來判斷目前選取的圖片是否長寬都 > 360 (圖太小不需要)
- 以上條件都符合時,才會觸發 Zoom in 效果
- 帶入當前大圖 {sliderGalleryInfo.imgOriUrl} 當作縮放的原始圖
1 透過 jquery.elevateZoom.cover 套件,在原始大圖的地方顯示放大區塊來源
2 透過 jquery.elevateZoom.cover 套件,顯示對應放大區塊的圖
- 舊版採用 jquery.elevateZoom.cover 套件,載入 jquery.elevateZoom.cover.js
- 當觸發了 Zoom in 效果時,需要暫停型錄小圖 slider 的自動捲動效果;離開時再恢復型錄小圖 slider 的自動捲動效果
- 型錄小圖 slider 自動捲動效果
- slider 超過兩則圖片時,需要提供 slider 效果
- 舊版採用 slick slide 套件,載入 slick-utils.js
- 舊版設定開啟自動撥放 (autoplay: true)
- 舊版設定自動捲動效果的時間是 4 秒 (autoplaySpeed: 4000)
- 首尾相接的效果,到最後一張圖時,接在後面的是第一張圖 (infinite: true)
- 型錄小圖 slider 手動捲動效果

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

- 顯示邏輯
- 型錄相關內容參考 https://hackmd.io/nlZlemkdTRCC7ua4Uup8UA#%E5%9E%8B%E9%8C%84%E8%B3%87%E6%96%99
- 邏輯與格式
- 1 型錄標題
- {productInfo.productName}
- 2 型錄型號
- 標題為多語,{lang.modelno}
- {productInfo.modelNo}
- 沒有資料時不顯示本欄
- 3 產地
- 標題為多語,{lang.madein}
- {productInfo.madeIn}
- 沒有資料時不顯示本欄
- 4 廠商資訊

- 4-1 標題為多語,{lang.supplier}
- 4-2 顯示廠商名稱,{productInfo.supplierName}
- ~~4-2 檢查 {productInfo.epUrl} 是否存在,如果存在,廠商名稱加上 url 連結,連結的 url = {productInfo.epUrl}~~
- 檢查 {productInfo.supplierUrl} != '',有值代表該廠商相關公司網址,此時廠商名稱加上 url 連結,連結的 url = {productInfo.supplierUrl}
- ~~4-3 檢查 {productInfo.epUrl} 是否存在,如果存在才顯示 More About This Product 項目,點選的 url = {productInfo.epUrl} + '/product/' + {productInfo.productId}~~
- 4-3 檢查 {productInfo.epUrl} 是否存在,如果存在才顯示 More About This Product 項目,點選的 url = {productInfo.epUrl}
- 4-3 More About This Product 為多語,{lang.ez_moreaboutproduct}
- 目前 api 會額外回傳以下相關資料
- 公司 ep 年資:{productInfo.supplierEpYear}
- 公司認證小圖清單:{productInfo.supplierIcon}
- 公司營業類型:{productInfo.supplierBusinessType}
- 5 顯示 Request for Quotation 連結
- 連結名稱為多語,{lang.requestforquotation}
- 連結網址固定,格式為 "/latest-price/step1?productId=" + {productInfo.productId}
- 6 Contact Supplier 按鈕
- Contact Supplier 為多語,{lang.contactsupplier}
- 點選相關邏輯請參考下面的操作邏輯
- 7 Add to Favorites
- Add to Favorites 為多語,{lang.addtofavorites}
- 如果該型錄已經加入過 favorites 的話,要顯示 Go to Favorites,{lang.gotofavorites}
- 點選的連結請參考條列頁的加到最愛與 redirect 到最愛的邏輯
- 警語相關
- 取得 {productInfo.catalogStandardCid},會影響到本頁是否要顯示警語
- 其他欄位
- ~~目前 api 會傳遞部分欄位,目前前台顯示並未特別輸出,但對應的 ep 畫面存在,這些欄位目前 api 還是會傳,再依後續邏輯判斷是否要保留~~
- ~~{productInfo.hasNewest},是否需要顯示 New Tag,0(否)/1(是)~~
- ~~{productInfo.hasFeatured},是否需要顯示 Featured Tag,0(否)/1(是)~~
- {productInfo.hasSelling},是否販售,{productInfo.hasSelling} == 0 代表非販售,進入非販售產品詳細頁
- 操作邏輯
- 點選 Contact Supplier 按鈕
- 跳轉到下面的 "聯繫廠商" 區塊
- 點選 Add to favorite
- 參考之前條列頁的加入最愛功能
- 加到我的最愛部分,這邊先貼上目前條列頁相同功能的程式碼,應該是可以直接套用的;條列頁程式有納入版本控制,後續開發如果碰到問題,這邊可以提供相關程式來參考;檢查型錄是否已經加入我的最愛、將型錄加入我的最愛、從我的最愛中移除型錄,在下面的 ts 中應該都有相對應的程式碼
- 相關連結 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 區塊,切換顯示

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

- 顯示邏輯
- ~~取出 data 中的 productInfo object, specInfo array, paymentInfo object, attachInfo array, certInfo array 來顯示資料~~
- 取出 data 中的 productInfo object, specInfo array, attachInfo array, certInfo array 來顯示資料
- 1 廣告詞
- {productInfo.adSlogan}
- 沒有的話不顯示此區塊
- 2 規格列表
- Spec 為多語,{lang.spec}
- 內容取自 specInfo array,依次顯示
- 每一行固定顯示格式為 {specInfo .name} + ":" + {specInfo.value}
- 沒有的話不顯示此區塊
- 3 Made in Taiwan 與 Design in Taiwan 的圖示
- 判斷 {productInfo.logoType}
- {productInfo.logoType} = 0 顯示 Design In Taiwan Logo
- {productInfo.logoType} = 1 顯示 Made In Taiwan Logo
- {productInfo.logoType} = -1 不顯示
- 沒有的話不顯示此區塊
- 4 Key Features
- Key Features 為多語,{lang.keyfeatures}
- {productInfo.keyFaturesHtml}
- 沒有的話不顯示此區塊
- 5 產品認證
- Product Certification 為多語,{lang.productcertification}
- 內容取自 certInfo array,依次顯示
- 顯示對應的認證圖,圖片 url 為 {certInfo.imgUrl},圖片 title 為 {certInfo.imgTitle},圖片 alt 為 {certInfo.imgAlt}
- 點選認證圖後會開啟燈箱顯示認證詳目資料
- 燈箱 url 為 {certInfo.lightboxUrl}
- 相關燈箱操作請參考下面的操作邏輯
- 7 附件列表
- Attachment 為多語,{lang.attachment}
- 內容取自 attachInfo array,依次顯示
- 以 url 方式顯示檔案連結
- 連結 href = {attachInfo.fileUrl}
- 連結名稱顯示檔案名稱與檔案大小,檔案名稱為 {attachInfo.fileName},檔案大小為 {attachInfo.fileSize}
- 沒有的話不顯示此區塊
- 8 影片內容
- Video 為多語,{lang.ez_video}
- 內容取自 {productInfo.videoUrl}, {productInfo.videoTitle}, {productInfo.videoTypeName}
- 影片類型 {productInfo.videoType} 啟動的類型只有 youtube 類型
- 沒有的話不顯示此區塊
- 9 最後更新時間
- Last Update 為多語,{lang.lastupdate}
- 內容顯示 {productInfo.updateDate}
- 操作邏輯
- 點選產品型錄認證燈箱
- 參考 [URI-C-02 型錄認證/公司認證燈箱](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 內容並顯示於燈箱中

- 1 燈箱名稱
- Product Certification 為多語,{lang.productcertification}
- 2 型錄認證名稱
- 顯示 {certInfo .name}
- 3 型錄認證圖片
- 顯示 {certInfo.imgUrl}
- 4 型錄認證編號
- Certification No 為多語,{lang.certificationno}
- 顯示 {certInfo.serial}
- 如果沒有就不顯示此欄位
- 5 型錄認證生效起始日期
- Certification Start Time 為多語,{lang.certificationstarttime}
- 顯示 {certInfo.startTime}
- 如果沒有就不顯示此欄位
- 6 型錄認證生效結束日期
- Certification End Time 為多語,{lang.certificationendtime}
- 顯示 {certInfo.endTime}
- 如果沒有就不顯示此欄位
- 7 型錄認證說明
- Certification description 為多語,{lang.certificationdesc}
- 顯示 {certInfo.desc}
- 如果沒有就不顯示此欄位
- 8 型錄認證附件檔案
- Attachments 為多語,{lang.certificationattachments}
- 顯示 url 連結
- href 顯示 {certInfo.fileUrl}
- 連結名稱顯示 {certInfo.fileName}
- 檔案大小顯示 {certInfo.fileSize}
- 如果沒有就不顯示此欄位
- 9 型錄認證相關型錄

- Product 為多語,{lang.product}
- 解析 certInfo.refProductRows array,依次顯示內容
- 9-1 顯示 {certInfo.refProductRows.imgUrl}
- 9-2 對應的型錄詳目頁 url 連結
- href 顯示 {certInfo.refProductRows.productCpUrl}
- 連結名稱顯示 {certInfo.refProductRows.productName}
- 如果沒有就不顯示此區塊
- 10 燈箱關閉按鈕
- 關閉燈箱按鈕,點選後關閉燈箱
- 11 燈箱遮罩
- 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
#### 詳細資料(Company Profile)

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

- 顯示邏輯
- 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#廠商資料
- 取得 data 中的 companyProfile object
- 1 公司名稱
- ~~如果 {companyProfile.companyWebSiteUrl} != '',顯示為聯結的型態,名稱是 {companyProfile.companyName},聯結是 {companyProfile.companyWebSiteUrl}~~
- ~~如果 {companyProfile.companyWebSiteUrl} == '',單純顯示 {companyProfile.companyName} 即可~~
- 如果 {companyProfile.companyWebSiteUrl} != null,顯示為聯結的型態,名稱是 {companyProfile.companyName},聯結是 {companyProfile.companyWebSiteUrl}
- 如果 {companyProfile.companyWebSiteUrl} == null,且 {companyProfile.ecCompanyWebSiteUrl} != null,顯示為聯結的型態,名稱是 {companyProfile.companyName},聯結是 {companyProfile.ecCompanyWebSiteUrl}
- 如果 {companyProfile.companyWebSiteUrl} == null,且 {companyProfile.ecCompanyWebSiteUrl} == null,單純顯示 {companyProfile.companyName} 即可
- 2 Add to favorites
- Add to favorites 為多語,{lang.addtofavorites}
- 如果該公司已經加入過 favorites 的話,要顯示 Go to Favorites,{lang.gotofavorites}
- 點選的連結請參考條列頁的加到最愛與 redirect 到最愛的邏輯
- 加到我的最愛部分,這邊先貼上目前條列頁相同功能的程式碼,應該是可以直接套用的;條列頁程式有納入版本控制,後續開發如果碰到問題,這邊可以提供相關程式來參考;檢查公司是否已經加入我的最愛、將公司加入我的最愛、從我的最愛中移除公司,在下面的 ts 中應該都有相對應的程式碼
- 相關連結 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://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} 跟今天日期的比較,判斷是否在折扣日內
- 再判斷 {productlist.discount} 是否有值且 {productlist.discount} > 0.0
- 上面三點都成立,代表有折扣
- 如果沒有折扣,顯示 {productlist.priceCurrency} + {productlist.price} + {productlist.unit}
- 如果有折扣,顯示 {productlist.discount} + {productlist.priceCurrency} + ({productlist.price} * {productlist.discount}) + {productlist.unit}
- json 提供的是 USD 金額,如果頁面的幣別有替換,那顯示時需要將金額從 USD 換算為對硬幣別的金額,幣別顯示也要改為頁面的幣別
- 6 Free Shipping
- 判斷是否免運費,如果 {productlist.isFreeShipping} == 1,顯示 free shipping 的小圖
- 7 Min Order
- ~~顯示 {productlist.minOrder} + {productlist.unit}~~
- 顯示 {productlist.minOrder}
- 警語相關
- 取得 {productlist.catalogStandardCid},會影響到本頁是否要顯示警語
- 8 更多型錄圖示
- 判斷以下邏輯
- 目前型錄清單最多顯示 5 則,需判斷 {productCount} > 5
- ~~確定公司是否有 ep 網頁,{companyProfile.companyWebSiteUrl} != '' 代表是 ep~~
- ~~如果以上兩個條件都符合,顯示更多型錄~~
- ~~點選的聯結格式如下:{companyProfile.companyWebSiteUrl}/product-catalog/all-products.html~~
- 更多型錄圖示點選的 url = {productMoreUrl}
- 9 更多型錄文字
- 如果 8 有顯示,就要顯示對應的文字
- All Products 為多語,{lang.allproducts}
- All Products 後面的數量是 {productCount}
##### 公司工廠資料

- 顯示邏輯
- 參考文件 https://hackmd.io/WTAzi5tXQBuakQV1tkgATw?view#%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}
- 操作邏輯
- 點選工廠圖片,透過燈箱顯示圖片

- 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 型錄認證/公司認證燈箱](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 內容並顯示於燈箱中

- 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 型錄認證/公司認證燈箱](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 內容並顯示於燈箱中

- 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#%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://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://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}
- Last Update 資料請參考 {contactUs.lastUpdateTime},該欄位回傳的日期是 long 格式的數字,套版時可以將數字轉換為對應時區的字串
- 操作邏輯
- 如果 2 聯絡人相關訊息 中有 聯絡人圖片,點選後會開啟燈箱顯示圖片

- 1 點選 聯絡人圖片 圖示
- 2 開啟燈箱內容,圖片部份顯示對應的 {contactUs.contactPicture}
- 3 目前顯示 avatar 單字
- 4 燈箱關閉按鈕
- 關閉燈箱按鈕,點選後關閉燈箱
- 5 燈箱遮罩
- 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
- 如果 2 聯絡人相關訊息 中有 聯絡人名片,點選後會開啟燈箱顯示名片

- 1 點選 聯絡人名片 圖示
- 2 開啟燈箱內容,圖片部份顯示對應的 {contactUs.contactCard}
- 3 目前顯示 avatar 單字
- 4 燈箱關閉按鈕
- 關閉燈箱按鈕,點選後關閉燈箱
- 5 燈箱遮罩
- 燈箱背景會加上 mask,點選 mask 效果等同於點選關閉燈箱按鈕
- 點選 3 Contact Supplier,會跳轉到本頁下面的 聯繫廠商 區塊
#### 聯繫廠商
- 商機Inquiry Product,請參考[URI-K-32](https://hackmd.io/Y-_iIT7rR4-uqwqN5ZGNXA?view)
#### 推薦區塊

- 顯示邏輯
- 參考 [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},會影響到本頁是否要顯示警語
#### 警語相關邏輯
- 目前警語顯示規則如下:
- 先透過 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 實際畫面,下圖是結構化後的樣式
- 
- 
- 圖片結構化後的樣式如下
- 
- 分析取得的警語相關檔案的結構
- 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.modelno | 型錄型號
型錄主要資訊 | lang.madein | 型錄產地
型錄主要資訊 | lang.supplier | 廠商名稱
型錄主要資訊 | lang.ez_moreaboutproduct | More About This Product
型錄主要資訊 | lang.requestforquotation | Request For Quotation
型錄主要資訊 | lang.ez_pleaseselect | Please Select
型錄主要資訊 | lang.ez_available | Available
型錄主要資訊 | lang.contactsupplier | Contact Supplier
型錄主要資訊 | lang.addtofavorites | Add to favorites
型錄主要資訊 | lang.gotofavorites | Go to favorites
tab 區塊 | lang.productdetail | Product Detail
tab 區塊 | lang.companyprofile | Company Profile
型錄 Detail | lang.spec | Spec
型錄 Detail | lang.keyfeatures | Key Features
型錄 Detail | lang.productcertification | Product Certification
型錄 Detail | lang.paymentdetails | Payment Details for Offline Orders
型錄 Detail | lang.paymentterms | Payment Terms
型錄 Detail | lang.minimumorde | Minium Order
型錄 Detail | lang.attachment | Attachment
型錄 Detail | lang.ez_video | Video
型錄 Detail | lang.lastupdate | Last Update
公司基本資料 | lang.addtofavorites | Add to favorite
公司基本資料 | lang.gotofavorites | Go to favorite
公司基本資料 | lang.basicinformation | Basic Information
公司基本資料 | lang.companyname | Company Name
公司基本資料 | lang.companiesbusinesstype | Business Type
公司基本資料 | lang.yearestablished | Year Establish
公司基本資料 | lang.factorylocation | Factory Location
公司基本資料 | lang.totalrevenue | Total Revenue
公司基本資料 | lang.capitial | Capital
公司基本資料 | lang.employee | Employee No.
公司基本資料 | lang.mainproducts | Main Products
公司基本資料 | lang.mainexportmarket | Main Export Market
公司相關型錄 | lang.otherproducts | Other Products
公司相關型錄 | lang.allproducts | All Products
公司工廠資料 | lang.factory | Factory
認證 | lang.certification | Certification
認證 | lang.companycertification | Company Certification
認證 | lang.allcertification | All Certification
認證 | lang.productcertification | Product Certification
認證燈箱 | lang.certificationno | Certification No
認證燈箱 | lang.certificationstarttime | Certification Start Time
認證燈箱 | lang.certificationendtime | Certification End Time
認證燈箱 | lang.certificationdesc | Certification Description
認證燈箱 | lang.certificationattachments | Attachments
得獎紀錄 | lang.awards | Awards
得獎紀錄 | lang.officialrecords | Official Records
Contact Us | lang.logintoviewdetails | Login in to View Details
Contact Us | lang.contactinformation | Contact Information
Contact Us | lang.owner | Owner
Contact Us | lang.contactperson | Contact Person
Contact Us | lang.tel1 | Contact Phone No.
Contact Us | lang.contactsupplier | Contact Supplier
Contact Us | lang.fax | Office Fax
Contact Us | lang.phonenumber | Office Phone No.
Contact Us | lang.officeaddress | Office Address
Contact Us | lang.officehours | Office Hours
Contact Us | lang.companywebsite | Company Website
Contact Us | lang.onlineshop | Online Shop
Contact Us | lang.mobilesite | Mobile Site
Contact Us | lang.lastupdate | Last Update
推薦型錄 | lang.recommendation | Recommendation
推薦型錄 | lang.ez_smallrecommendation | Small Order Recommendation