Markdown/HTML Card
Markdown/HTML Card 위젯은 대시보드에 Markdown이나 HTML 내용을 렌더링합니다. 정적 정보 표시, 템플릿 기반 데이터 카드, CSS로 꾸민 완전한 사용자 지정 HTML 레이아웃에 씁니다.
주요 기능
섹션 제목: “주요 기능”- 실시간 엔티티 데이터로 바뀌는
${key}자리표시자를 넣어 정적 Markdown이나 HTML을 표시합니다. - JavaScript 값 함수로 내용을 코드로 만듭니다. 조건부 서식을 적용하거나, 여러 키를 합치거나, 복잡한 HTML을 구성할 수 있습니다.
- 위젯 범위로 한정된 사용자 지정 CSS로 카드를 꾸밉니다.
<tb-dashboard-state>로 다른 대시보드 상태를 카드 안에 넣습니다. 공통 위젯을 복제하지 않고 탭 레이아웃을 만들 때 씁니다.
위젯 추가
섹션 제목: “위젯 추가”- 대시보드를 수정 모드로 엽니다. 위쪽 툴바의 위젯 추가를 클릭하거나, 빈 대시보드 가운데의 위젯 추가 아이콘을 클릭합니다.
- 위젯 번들 선택 대화상자에서 HTML widgets를 찾아 클릭합니다.
- Markdown/HTML Card 위젯을 선택합니다.
- 데이터 소스와 모양을 설정한 다음 추가를 클릭합니다.
위젯은 세 탭에서 설정합니다: 데이터(데이터 소스), 모양(HTML 내용과 스타일), 위젯 카드(위젯 카드 설정).
데이터 소스
섹션 제목: “데이터 소스”${key} 자리표시자를 채우거나 값 함수에 넘길 데이터의 소스를 선택합니다:
- 장치 — 특정 HMI 장치. 위젯이 이 HMI 장치의 최신 속성과 텔레메트리 데이터로 자리표시자를 채웁니다.
- 개체 별명 — 엔티티 별칭에 맞는 엔티티 집합. 대상 엔티티를 실행 중에 동적으로 고를 때 씁니다.
- 기능 — JavaScript 함수로 데이터를 만듭니다. 시험할 때나 실제 HMI 장치가 필요 없을 때 씁니다.
데이터 소스를 설정하지 않으면 위젯은 실시간 데이터를 넣지 않은 정적 템플릿을 렌더링합니다.
정적 Markdown/HTML 템플릿
섹션 제목: “정적 Markdown/HTML 템플릿”마크다운/HTML 패턴 필드에 Markdown이나 HTML을 직접 입력합니다. ${key}로 실시간 값을 넣고, ${key:N}으로 소수 자릿수를 정합니다.
### Temperature value card- **Current entity**: ${entityName}- **Current temperature**: ${temperature:1} °C동적 Markdown/HTML 내용
섹션 제목: “동적 Markdown/HTML 내용”내용을 코드로 만들려면 마크다운/HTML 값 함수 사용 스위치를 켭니다. 함수는 엔티티별 최신 값이 담긴 data 배열을 받아 HTML 문자열을 돌려줘야 합니다.
const entity = data[0];const color = entity.temperature > 25 ? "red" : entity.temperature > 20 ? "green" : "blue";return `### Temperature value card- Current entity: <span>${entity.entityName}</span>- Current temperature: <span style="color:${color};">${entity.temperature.toFixed(1)} °C</span>`;내용 스타일(CSS)
섹션 제목: “내용 스타일(CSS)”위젯의 CSS 탭에 사용자 지정 CSS를 추가합니다. Markdown/HTML 템플릿에서 쓴 클래스 이름으로 스타일을 지정할 수 있습니다.
마크다운/HTML 패턴:
<div class="office-card"> <h3>${entityName}</h3> <p class="blue-box"> <strong>Current temperature:</strong> <span class="temp-value">${temperature:1}</span> °C </p> <p class="green-box"> <strong>Current humidity:</strong> <span class="hum-value">${humidity:0}</span> % </p></div>마크다운/HTML CSS:
.office-card { box-sizing: border-box; padding: 15px; margin: 6px 0; text-align: center; font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, "Noto Sans";}.office-card h3 { margin: 0 0 12px; font-size: 24px;}.blue-box { background: #2196F31A; border: 2px solid #2196F3; border-radius: 10px; padding: 10px; margin: 8px 0; font-weight: 700;}.green-box { background: #4CAF501A; border: 2px solid #4CAF50; border-radius: 10px; padding: 10px; margin: 6px 0; font-weight: 700;}카드 모양
섹션 제목: “카드 모양”카드 수준 설정은 위젯 카드 탭에서 합니다.
| 설정 | 설명 |
|---|---|
| 제목 | 위젯 헤더에 표시되는 글자. |
| 카드 아이콘 | 제목 옆에 표시하는 아이콘. |
| 배경 색 | 카드 배경. CSS로 배경을 정할 때는 #FFFFFF00(투명)으로 정합니다. |
| 카드 모서리 반경 | 카드 모서리의 둥근 정도. |
| 카드 패딩 | 카드 내용과 테두리 사이 간격. HTML 템플릿이 자체 패딩을 관리하면 0으로 정합니다. |
| 카드 버튼 표시 | 전체화면을 켜고 끕니다. Data export는 PE, PaaS, PaaS/EU에서만 쓸 수 있습니다. |
Markdown/HTML Card는 다음 소스에서 위젯 동작을 지원합니다:
- 카드 클릭 시 — 사용자가 카드의 아무 곳이나 클릭하면 실행됩니다.
- 위젯 헤더 버튼 — 위젯 헤더 표시줄에 사용자 지정 버튼을 추가합니다.
사무실 정보 카드
섹션 제목: “사무실 정보 카드”목표: 사무실 자산의 주소, 관리자, 전화, 이메일 속성을 아이콘이 있는 정보 카드에 꾸며서 표시합니다.
1단계. 엔티티 준비. 이 CSV로 사무실 자산을 가져옵니다: office_a_asset.csv. 자산에는 서버 속성 address, officeManager, phone, email이 있어야 합니다.
2단계. 위젯 추가. 대시보드를 수정 모드로 열고 위젯 추가를 클릭하고 HTML widgets를 선택한 다음 Markdown/HTML Card를 고릅니다.
3단계. 데이터 소스 설정. 데이터 탭에서 데이터 소스를 사무실 자산 엔티티로 정합니다. 데이터 키로 address, officeManager, phone, email을 추가합니다.
4단계. 값 함수 설정. 모양 탭에서 마크다운/HTML 값 함수 사용을 켜고 JavaScript 함수를 붙여 넣습니다:
if (data.length) { const buildingAttributes = ['address', 'officeManager', 'phone', 'email']; let information = { entityName: data[0].entityName ? data[0].entityName : 'Not found', buildingAttributesField: {} }; for (let key of buildingAttributes) { information.buildingAttributesField[key] = data[0][key] ? data[0][key] : 'Not found'; } const attributesNotFound = Object.values(information.buildingAttributesField) .every(value => value === 'Not found'); if (attributesNotFound) { return '<div class="no-data-card">' + '<h1 class="card-title">Information on ' + information.entityName + '</h1>' + '<div class="no-data-block"><div class="no-data-content">' + '<p class="no-data-title">There is no information about the building</p>' + '</div></div></div>'; } else { return '<div class="card">' + '<h1 class="card-title">Information on ' + information.entityName + '</h1>' + '<div class="attributes">' + '<div class="attribute_container"><div class="icon_wrapper"><tb-icon class="icon">place</tb-icon></div>' + '<div><div class="attribute_label">Address</div><div class="attribute">' + information.buildingAttributesField.address + '</div></div></div>' + '<div class="attribute_container"><div class="icon_wrapper"><tb-icon class="icon">person</tb-icon></div>' + '<div><div class="attribute_label">Manager</div><div class="attribute">' + information.buildingAttributesField.officeManager + '</div></div></div>' + '<div class="attribute_container"><div class="icon_wrapper"><tb-icon class="icon">call</tb-icon></div>' + '<div><div class="attribute_label">Phone</div><div class="attribute">' + information.buildingAttributesField.phone + '</div></div></div>' + '<div class="attribute_container"><div class="icon_wrapper"><tb-icon class="icon">mail_outline</tb-icon></div>' + '<div><div class="attribute_label">Email</div><div class="attribute">' + information.buildingAttributesField.email + '</div></div></div>' + '</div></div>'; }}5단계. CSS 추가. 마크다운/HTML CSS 필드에 CSS 스타일을 붙여 넣습니다:
.card, .no-data-card { padding: 0px; height: 100%; }.card-title { position: sticky; top: 0; z-index: 2; font-size: 16px; letter-spacing: 0.25px; padding: 16px; }.attribute_container { display: flex; align-items: center; gap: 20px; padding: 16px; border-bottom: 1px solid #0000000d; }.icon { z-index: 1; }.icon_wrapper { display: flex; position: relative; align-items: center; padding: 8px; border: 1px solid var(--tb-primary-100, #305680); border-radius: 4px; }.icon_wrapper:before { content: ''; z-index: 0; position: absolute; opacity: 0.1; background-color: var(--tb-primary-200, #305680); width: 100%; height: 100%; left: 0; top: 0; }.attribute, .attribute_label { font-size: 14px; line-height: 20px; }.attribute_label { color: #00000061; }.no-data-card { display: flex; flex-direction: column; }.no-data-block { display: flex; flex-direction: column; justify-content: center; height: 100%; max-width: 345px; padding: 0 15px; margin: 0 auto; text-align: center; }.no-data-content { transform: translateY(-30%); }.no-data-block p { font-weight: 500; }.no-data-title { color: rgba(0, 0, 0, 0.54); }6단계. 저장. 추가를 클릭하고 필요하면 크기와 위치를 조정한 다음 대시보드 툴바의 저장을 클릭합니다.
결과: 카드에 사무실 이름, 주소, 관리자, 전화, 이메일이 아이콘과 함께 표시됩니다. 속성이 없으면 그 필드에 “Not found”가 표시됩니다.
문제 해결
섹션 제목: “문제 해결”위젯 내용이 비어 있음
| 원인 | 해결 방법 |
|---|---|
| 자리표시자 키를 찾을 수 없음 | ${key} 이름이 데이터 소스의 데이터 키 라벨과 정확히 같은지 확인합니다. 키 이름은 대소문자를 구분합니다. |
| 데이터 소스가 설정되지 않음 | ${key} 자리표시자를 쓰면 맞는 데이터 키가 있는 데이터 소스를 설정해야 합니다. |
| HMI 장치가 데이터를 보내지 않음 | 장치 → 최근 데이터를 열고 키에 최근 값이 있는지 확인합니다. |
값 함수가 아무것도 출력하지 않거나 오류를 냄
| 원인 | 해결 방법 |
|---|---|
return 문이 빠짐 | 함수는 HTML 문자열을 명시적으로 돌려줘야 합니다. 모든 코드 경로가 값을 돌려주는지 확인합니다. |
data[0]이 undefined임 | 데이터 소스에 결과가 없으면 함수가 빈 data 배열을 받습니다. 보호 코드를 추가합니다: if (!data.length) return ''; |
| JavaScript 문법 오류 | 브라우저 콘솔을 열어 정확한 오류와 줄 번호를 확인합니다. |
CSS 스타일이 적용되지 않음
| 원인 | 해결 방법 |
|---|---|
| 클래스 이름이 맞지 않음 | CSS의 클래스 이름이 HTML 템플릿의 클래스 이름과 정확히 같은지 확인합니다. |
| 위젯 기본 스타일이 스타일을 덮어씀 | 더 구체적인 CSS 선택자를 쓰거나, 최후의 수단으로 !important를 추가합니다. |