1d3dac3bec
1) 검색 입력란이 거대했던 버그 .cf24-filters 가 세로 flex 인데 .cf24-search 에 flex:0 1 320px 을 줬다. 세로 방향에서는 flex-basis 가 '높이'로 적용돼 입력란이 320px 짜리 상자가 됐다. height:32px 로 한 줄에 고정했고, 그만큼 목록이 더 보인다. 2) 목록 550px 요청대로 왼쪽을 550px 로 넓혔다. 남은 폭(약 290px)이 상품명 몫이라 대부분 한 줄에 들어가고, 수정일도 월-일 시:분까지 보여준다. 컬럼은 번호·상품명·진열·판매·수정 5개 그대로다. 3) 문법 강조 편집기 색칠된 <pre> 위에 투명한 <textarea> 를 겹치는 방식으로 직접 구현했다. 외부 라이브러리를 쓰지 않는 이유는 자체 호스팅 원칙이다(CDN 의존 금지). 태그·속성이름· 속성값·주석·기호를 색으로 구분하고 Tab 은 들여쓰기로 쓴다. 두 층의 글자가 어긋나지 않으려면 폰트·줄높이·padding·줄바꿈 규칙이 완전히 같아야 한다. 특히 높이는 <pre> 의 scrollHeight 를 기준으로 textarea 에 지정한다 — textarea 의 scrollHeight 를 쓰면 두 줄쯤 더 잡혀 어긋난다(실측 830 vs 792, 브라우저에서 확인 후 수정). 20만 자를 넘으면 강조를 끈다. 4) 소스 정리(포맷)와 저장 반영 store.format_html 을 추가했다. 화면 표시와 저장에 같은 함수를 쓰므로 화면에서 본 정리된 소스가 그대로 카페24에 저장된다. 렌더링을 바꾸지 않는 것을 최우선으로 했다. HTML 에서 공백은 의미가 있어서 인라인 요소 사이에 줄바꿈을 넣으면 화면에 공백이 생긴다 — 이미지 사이가 벌어지는 고전적인 사고다. 그래서 블록 요소 경계에서만 줄을 나누고 img·br·span·a 는 블록 목록에서 일부러 뺐다. <style>·<script>·<pre>·<textarea> 안쪽은 한 글자도 건드리지 않는다. 내용이 한 줄뿐인 짧은 블록은 다시 한 줄로 합친다. 멱등성을 테스트로 고정했다. 처음 구현은 <style> 안 빈 줄이 실행마다 한 줄씩 늘어나 멱등이 깨졌고(테스트가 잡음), 앞뒤 빈 줄을 버리도록 고쳤다. 편집하지 않고 다시 적용해도 저장값이 계속 달라지면 버전 이력이 의미를 잃는다. 닫는 태그가 빠진 HTML 이 흔하므로 들여쓰기 상한(12)을 뒀고, 어떤 이유로든 실패하면 원본을 그대로 돌려준다. 검증: 유닛테스트 41개 통과(신규 8개 — 블록 분리·인라인 보존(이미지 붙음)·style 원문 보존·멱등·짧은 블록 합치기·깨진 HTML 내성·속성값 미변경·정리+인코딩 왕복). 브라우저 실측: 검색란 32px, 목록 550px/편집기 750px, 오버레이 두 층 높이 일치 (편집 전 792=792, 20줄 추가 후 1175=1175), 토큰 색상 적용, 가로 스크롤 없음. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
115 lines
5.4 KiB
HTML
115 lines
5.4 KiB
HTML
{# 오른쪽 편집기 조각.
|
|
전체 페이지(products.html)가 include 하고, JS 가 /products/{no}/pane 으로
|
|
같은 조각만 다시 받아 끼워 넣는다. 그래서 여기에는 <script> 를 두지 않는다
|
|
(innerHTML 로 삽입된 script 는 실행되지 않는다 — JS 는 products.html 에 있고
|
|
삽입 후 cf24BindEditor() 로 다시 연결한다). #}
|
|
|
|
{% if editor_error %}
|
|
<div class="cf24-flash cf24-flash-err">
|
|
카페24 조회에 실패했습니다: {{ editor_error }}<br />
|
|
<a href="/cafe24/system">시스템 화면에서 연결 상태를 확인하세요.</a>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="cf24-editor-head">
|
|
<div>
|
|
<h3 class="cf24-editor-title">{{ info.product_name or '상품' }}</h3>
|
|
<p class="cf24-editor-sub">
|
|
상품번호 {{ product_no }}
|
|
{% if info.product_code %}· <code>{{ info.product_code }}</code>{% endif %}
|
|
{% if info.price %}· {{ info.price }}{% endif %}
|
|
{% if info.updated_date %}· 최근 수정 {{ info.updated_date }}{% endif %}
|
|
</p>
|
|
</div>
|
|
<div class="cf24-editor-badges">
|
|
{% if info.display %}<span class="erp-badge cf24-badge-ok">진열</span>
|
|
{% else %}<span class="erp-badge cf24-badge-off">미진열</span>{% endif %}
|
|
{% if info.selling %}<span class="erp-badge cf24-badge-ok">판매</span>
|
|
{% else %}<span class="erp-badge cf24-badge-off">중지</span>{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% if desc %}
|
|
<p class="cf24-note">
|
|
{% if desc.separated_mobile %}
|
|
<strong>PC/모바일 분리 사용 상품입니다.</strong> 아래 적용은 PC 만 바꿉니다 —
|
|
모바일({{ desc.mobile_description | length }}자)은 카페24 관리자에서 따로 반영해야 합니다.
|
|
{% else %}
|
|
모바일은 PC와 동일 설정이라 적용 시 <strong>함께 반영</strong>됩니다.
|
|
{% endif %}
|
|
{% if desc.mobile_differs %}<span class="cf24-warn">현재 PC/모바일 내용이 다릅니다.</span>{% endif %}
|
|
<br />
|
|
소스는 <strong>태그마다 줄을 나눠 정리</strong>해서 보여주며, <strong>적용하면 정리된 소스가
|
|
그대로 카페24에 저장</strong>됩니다. 줄바꿈·들여쓰기만 바뀌고 태그 구조는 그대로이며,
|
|
이미지가 벌어지지 않도록 <code>img</code>·<code>span</code> 같은 인라인 요소와
|
|
<code><style></code> 안쪽은 건드리지 않습니다.
|
|
<br />
|
|
이미지 경로의 한글 파일명은 카페24에 <code>%EC%9A%A9…</code> 로 저장되어 있습니다.
|
|
여기서는 한글로 보여주고, 적용할 때 원래 형식으로 되돌립니다.
|
|
</p>
|
|
|
|
<form class="cf24-editor-form" method="post"
|
|
action="/cafe24/products/{{ product_no }}/apply"
|
|
data-confirm="카페24 쇼핑몰에 바로 반영됩니다. 적용할까요? 직전 내용은 자동으로 백업되어 되돌릴 수 있습니다.">
|
|
<input type="hidden" name="base_fingerprint" value="{{ fingerprint }}" />
|
|
<input type="hidden" name="list_query" value="{{ list_query }}" />
|
|
|
|
<div class="cf24-editor-bar">
|
|
<span class="cf24-muted">PC 상세설명 HTML · {{ desc.description | length }}자</span>
|
|
<span class="cf24-editor-bar-right">
|
|
<input class="cf24-memo" type="text" name="memo" maxlength="200"
|
|
placeholder="변경 메모 (버전 이력에 남습니다)" />
|
|
<button class="erp-btn erp-btn-outline" type="button" data-copy="cf24-html-pc">복사</button>
|
|
<button class="erp-btn erp-btn-primary" type="submit">카페24에 적용</button>
|
|
</span>
|
|
</div>
|
|
|
|
{# 색칠된 <pre> 위에 투명한 <textarea> 를 겹쳐 문법 강조를 만든다.
|
|
두 요소의 글자 위치가 어긋나면 안 되므로 폰트·여백은 CSS 에서 함께 관리한다. #}
|
|
<div class="cf24-code" id="cf24-code-pc">
|
|
<pre class="cf24-code-hl" aria-hidden="true"><code id="cf24-hl-pc"></code></pre>
|
|
<textarea id="cf24-html-pc" class="cf24-code-input" name="html"
|
|
spellcheck="false" autocapitalize="off" autocorrect="off"
|
|
wrap="soft">{{ html_pc }}</textarea>
|
|
</div>
|
|
</form>
|
|
|
|
{% if desc.separated_mobile or desc.mobile_differs %}
|
|
<details class="cf24-details">
|
|
<summary>모바일 상세설명 HTML 보기 (읽기 전용 · {{ desc.mobile_description | length }}자)</summary>
|
|
<textarea id="cf24-html-mo" class="cf24-html" rows="12" readonly
|
|
spellcheck="false">{{ html_mobile }}</textarea>
|
|
<div class="cf24-actions">
|
|
<button type="button" class="erp-btn erp-btn-outline" data-copy="cf24-html-mo">복사</button>
|
|
</div>
|
|
</details>
|
|
{% endif %}
|
|
|
|
<details class="cf24-details">
|
|
<summary>버전 이력 {% if revisions %}({{ revisions | length }}건){% endif %}</summary>
|
|
{% if revisions %}
|
|
<div class="cf24-scroll">
|
|
<table class="erp-table cf24-compact">
|
|
<thead>
|
|
<tr><th>시각</th><th>유형</th><th>길이</th><th>작업자</th><th>메모</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for rev in revisions %}
|
|
<tr>
|
|
<td class="cf24-nowrap">{{ rev.created_at }}</td>
|
|
<td class="cf24-nowrap"><code>{{ rev.revision_type }}</code></td>
|
|
<td class="cf24-nowrap">{{ rev.html_length }}자</td>
|
|
<td class="cf24-nowrap">{{ rev.created_by or '—' }}</td>
|
|
<td>{{ rev.memo or '' }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p class="cf24-muted">버전 선택 복원은 Phase 6 에서 붙습니다. 내용은 모두 보관됩니다.</p>
|
|
{% else %}
|
|
<p class="cf24-muted">아직 이 상품의 변경 이력이 없습니다.</p>
|
|
{% endif %}
|
|
</details>
|
|
{% endif %}
|