This commit is contained in:
@@ -242,18 +242,19 @@
|
||||
<span>候選 PChome ${{ item.candidate_pc_price | int | number_format }}</span>
|
||||
{% endif %}
|
||||
{% if item.best_match_score %}
|
||||
<span>match {{ (item.best_match_score * 100) | round(0) | int }}%</span>
|
||||
<span>信心 {{ (item.best_match_score * 100) | round(0) | int }}%</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="dashboard-focus-sub">{{ item.action_label }}</div>
|
||||
{% if item.decision_envelope %}
|
||||
{% set envelope = item.decision_envelope %}
|
||||
{% set guardrails = envelope.guardrails or {} %}
|
||||
{% set data_quality = guardrails.data_quality | default('partial', true) | lower %}
|
||||
<div class="dashboard-review-envelope" aria-label="決策信封摘要">
|
||||
<span>{{ envelope.severity or 'P4' }}</span>
|
||||
<span>{{ guardrails.data_quality or 'partial' }}</span>
|
||||
<span>優先 {{ envelope.severity or 'P4' }}</span>
|
||||
<span>{{ {'complete': '證據完整', 'partial': '部分證據', 'missing': '證據不足'}.get(data_quality, '部分證據') }}</span>
|
||||
{% if guardrails.can_auto_execute == false %}
|
||||
<span>HITL</span>
|
||||
<span>需人工</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -452,33 +453,42 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="dashboard-table-wrap">
|
||||
<div class="dashboard-table-wrap {% if current_filter == 'pchome_review' %}is-review-wrap{% endif %}">
|
||||
<table class="dashboard-table {% if current_filter == 'ai_picks' %}is-ai-picks{% elif current_filter == 'pchome_review' %}is-review{% endif %}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>分類</th>
|
||||
<th>商品名稱</th>
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='price', order='asc' if current_sort == 'price' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">MOMO 價格</a>
|
||||
</th>
|
||||
<th class="text-end">PChome 價格</th>
|
||||
<th>競價判讀</th>
|
||||
{% if current_filter == 'ai_picks' %}
|
||||
<th>AI 建議</th>
|
||||
{% elif current_filter == 'pchome_review' %}
|
||||
<th>覆核動作</th>
|
||||
{% endif %}
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='yesterday_change', order='asc' if current_sort == 'yesterday_change' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">昨日漲跌</a>
|
||||
</th>
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='week_change', order='asc' if current_sort == 'week_change' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">週漲跌</a>
|
||||
</th>
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='timestamp', order='asc' if current_sort == 'timestamp' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">更新時間</a>
|
||||
</th>
|
||||
<th class="text-end">上架時間</th>
|
||||
</tr>
|
||||
{% if current_filter == 'pchome_review' %}
|
||||
<tr>
|
||||
<th>分類</th>
|
||||
<th>商品 / MOMO</th>
|
||||
<th>PChome 候選</th>
|
||||
<th>覆核判讀</th>
|
||||
<th>下一步</th>
|
||||
<th class="text-end">紀錄</th>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<th>分類</th>
|
||||
<th>商品名稱</th>
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='price', order='asc' if current_sort == 'price' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">MOMO 價格</a>
|
||||
</th>
|
||||
<th class="text-end">PChome 價格</th>
|
||||
<th>競價判讀</th>
|
||||
{% if current_filter == 'ai_picks' %}
|
||||
<th>AI 建議</th>
|
||||
{% endif %}
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='yesterday_change', order='asc' if current_sort == 'yesterday_change' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">昨日漲跌</a>
|
||||
</th>
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='week_change', order='asc' if current_sort == 'week_change' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">週漲跌</a>
|
||||
</th>
|
||||
<th class="text-end">
|
||||
<a href="{{ url_for('dashboard.index', page=1, sort_by='timestamp', order='asc' if current_sort == 'timestamp' and current_order == 'desc' else 'desc', category=current_category, filter=current_filter, q=search_query) }}">更新時間</a>
|
||||
</th>
|
||||
<th class="text-end">上架時間</th>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
@@ -487,6 +497,166 @@
|
||||
{% set decision = item.competitor_decision %}
|
||||
{% set match_status = item.pchome_match_status %}
|
||||
{% set image_url = product.image_url or ('https://m.momoshop.com.tw/moscdn/goods/' ~ product.i_code ~ '_m.webp') %}
|
||||
{% set safe_product_url = item.safe_momo_url or '#' %}
|
||||
{% if current_filter == 'pchome_review' %}
|
||||
{% set review = item.pchome_review %}
|
||||
{% set envelope = review.decision_envelope if review else {} %}
|
||||
{% set guardrails = envelope.guardrails if envelope else {} %}
|
||||
<tr class="dashboard-review-workbench-row" data-product-id="{{ product.id }}" data-product-name="{{ product.name|e }}">
|
||||
<td><span class="dashboard-category">{{ product.category or '未分類' }}</span></td>
|
||||
<td>
|
||||
<div class="dashboard-review-product-stack">
|
||||
<div class="dashboard-product-cell">
|
||||
<img class="dashboard-product-thumb" src="{{ image_url }}" alt="{{ product.name }}" loading="lazy" referrerpolicy="no-referrer">
|
||||
<div>
|
||||
<a class="dashboard-product-name momo-tracked-link" href="{{ safe_product_url or '#' }}" target="_blank" rel="noopener noreferrer"
|
||||
data-momo-original-url="{{ safe_product_url or '#' }}"
|
||||
data-track-platform="momo"
|
||||
data-track-source="dashboard-v2-pchome-review"
|
||||
data-track-product-id="{{ product.id }}"
|
||||
data-track-icode="{{ product.i_code }}"
|
||||
data-track-product-name="{{ product.name|e }}">{{ product.name }}</a>
|
||||
<div class="dashboard-review-product-meta">
|
||||
<span>MOMO {{ product.i_code }}</span>
|
||||
<span>${{ item.record.price | int | number_format }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="dashboard-review-candidate">
|
||||
{% if review and review.candidate_pc_id %}
|
||||
<a class="dashboard-review-candidate-title" href="https://24h.pchome.com.tw/prod/{{ review.candidate_pc_id }}" target="_blank" rel="noopener noreferrer">
|
||||
{{ review.candidate_pc_name or ('PChome ' ~ review.candidate_pc_id) }}
|
||||
</a>
|
||||
<div class="dashboard-review-candidate-meta">
|
||||
{% if review.candidate_pc_price %}
|
||||
<span>${{ review.candidate_pc_price | int | number_format }}</span>
|
||||
{% endif %}
|
||||
<span>PChome {{ review.candidate_pc_id }}</span>
|
||||
{% if review.candidate_count %}
|
||||
<span>{{ review.candidate_count }} 筆候選</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elif competitor and competitor.product_id %}
|
||||
<a class="dashboard-review-candidate-title" href="{{ competitor.product_url or ('https://24h.pchome.com.tw/prod/' ~ competitor.product_id) }}" target="_blank" rel="noopener noreferrer">
|
||||
{{ competitor.product_name or ('PChome ' ~ competitor.product_id) }}
|
||||
</a>
|
||||
<div class="dashboard-review-candidate-meta">
|
||||
{% if competitor.price %}
|
||||
<span>${{ competitor.price | int | number_format }}</span>
|
||||
{% endif %}
|
||||
<span>PChome {{ competitor.product_id }}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="dashboard-match-state is-{{ match_status.tone | default('neutral') }}">{{ match_status.label | default('尚未搜尋') }}</span>
|
||||
<div class="dashboard-review-note">{{ match_status.summary | default('尚未進入 PChome 補抓佇列') }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="dashboard-review-decision">
|
||||
<div class="dashboard-review-decision-head">
|
||||
<span class="dashboard-ai-pick-rank">{{ review.status_label if review else match_status.label }}</span>
|
||||
{% if review and review.best_match_score %}
|
||||
<span class="dashboard-ai-pick-confidence is-medium">信心 {{ (review.best_match_score * 100) | round(0) | int }}%</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="dashboard-ai-pick-reason">{{ review.action_label if review else match_status.summary }}</div>
|
||||
{% if review and review.identity_evidence_summary %}
|
||||
<div class="dashboard-review-note">證據:{{ review.identity_evidence_summary }}</div>
|
||||
{% endif %}
|
||||
{% if review and review.diagnostic_reasons %}
|
||||
<div class="dashboard-review-reasons" aria-label="比對差異與證據">
|
||||
{% for reason in review.diagnostic_reasons[:4] %}
|
||||
<span>{{ reason.label }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if review and review.unit_comparison and review.unit_comparison.summary %}
|
||||
<div class="dashboard-review-note momo-mono">{{ review.unit_comparison.summary }}</div>
|
||||
{% endif %}
|
||||
{% if review and review.unit_price_insight and review.unit_price_insight.summary %}
|
||||
<div class="dashboard-review-note">{{ review.unit_price_insight.summary }}</div>
|
||||
{% endif %}
|
||||
{% if envelope %}
|
||||
{% set data_quality = guardrails.data_quality | default('partial', true) | lower %}
|
||||
<div class="dashboard-review-envelope" aria-label="決策信封摘要">
|
||||
<span>優先 {{ envelope.severity or 'P4' }}</span>
|
||||
<span>{{ {'complete': '證據完整', 'partial': '部分證據', 'missing': '證據不足'}.get(data_quality, '部分證據') }}</span>
|
||||
{% if guardrails.can_auto_execute == false %}
|
||||
<span>需人工</span>
|
||||
{% endif %}
|
||||
{% if envelope.decision_id %}
|
||||
<span title="{{ envelope.decision_id }}">追蹤</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="dashboard-review-next-step">
|
||||
{% if review and review.catalog_review_guidance %}
|
||||
<div class="dashboard-review-next-title">{{ review.catalog_review_guidance.lane_label }}</div>
|
||||
<div class="dashboard-review-note">{{ review.catalog_review_guidance.action_hint }}</div>
|
||||
{% else %}
|
||||
<div class="dashboard-review-next-title">人工覆核</div>
|
||||
<div class="dashboard-review-note">{{ match_status.summary | default('確認候選是否同款,再決定是否採用。') }}</div>
|
||||
{% endif %}
|
||||
{% if review %}
|
||||
<div class="dashboard-review-actions" aria-label="人工覆核決策">
|
||||
{% if review.candidate_pc_id and review.candidate_pc_price %}
|
||||
<button class="dashboard-review-action is-accept" type="button"
|
||||
data-pchome-review-action
|
||||
data-review-action="accept_identity"
|
||||
data-review-sku="{{ review.sku }}"
|
||||
data-review-confirm="確認採用這筆 PChome 候選為正式同款配對?">
|
||||
採用同款
|
||||
</button>
|
||||
{% endif %}
|
||||
<button class="dashboard-review-action" type="button"
|
||||
data-pchome-review-action
|
||||
data-review-action="reject_identity"
|
||||
data-review-sku="{{ review.sku }}"
|
||||
data-review-confirm="確認否決這筆 PChome 候選?">
|
||||
否決候選
|
||||
</button>
|
||||
<button class="dashboard-review-action" type="button"
|
||||
data-pchome-review-action
|
||||
data-review-action="unit_price_required"
|
||||
data-review-sku="{{ review.sku }}"
|
||||
data-review-confirm="確認標記為需單位價比較?">
|
||||
標記單位價
|
||||
</button>
|
||||
<button class="dashboard-review-action is-research" type="button"
|
||||
data-pchome-review-action
|
||||
data-review-action="needs_research"
|
||||
data-review-sku="{{ review.sku }}"
|
||||
data-review-confirm="確認要求補搜尋詞或重新抓取?">
|
||||
補搜尋
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td class="dashboard-review-record text-end">
|
||||
{% if review and review.attempted_at %}
|
||||
<div class="momo-mono">{{ review.attempted_at }}</div>
|
||||
{% else %}
|
||||
<div class="momo-mono">{{ item.record.timestamp.strftime('%m-%d %H:%M') if item.record.timestamp else '--' }}</div>
|
||||
{% endif %}
|
||||
<div class="dashboard-review-record-tags">
|
||||
{% if review %}
|
||||
<span>覆核紀錄</span>
|
||||
{% endif %}
|
||||
{% if guardrails and guardrails.can_auto_execute == false %}
|
||||
<span>需人工</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="is-history-enabled" data-product-id="{{ product.id }}" data-product-name="{{ product.name|e }}" title="點擊查看歷史價格圖表">
|
||||
<td><span class="dashboard-category">{{ product.category or '未分類' }}</span></td>
|
||||
<td>
|
||||
@@ -565,7 +735,7 @@
|
||||
{% if competitor and competitor.price %}
|
||||
<div class="dashboard-pchome-price">${{ competitor.price | int | number_format }}</div>
|
||||
{% if competitor.match_score %}
|
||||
<div class="dashboard-price-sub">match {{ (competitor.match_score * 100) | round(0) | int }}%</div>
|
||||
<div class="dashboard-price-sub">信心 {{ (competitor.match_score * 100) | round(0) | int }}%</div>
|
||||
{% endif %}
|
||||
{% elif item.pchome_match_attempt and item.pchome_match_attempt.best_competitor_price and match_status.label == '需單位價比較' %}
|
||||
<div class="dashboard-pchome-price">${{ item.pchome_match_attempt.best_competitor_price | int | number_format }}</div>
|
||||
@@ -632,19 +802,20 @@
|
||||
<div class="dashboard-ai-pick-head">
|
||||
<span class="dashboard-ai-pick-rank">{{ review.status_label if review else match_status.label }}</span>
|
||||
{% if review and review.best_match_score %}
|
||||
<span class="dashboard-ai-pick-confidence is-medium">match {{ (review.best_match_score * 100) | round(0) | int }}%</span>
|
||||
<span class="dashboard-ai-pick-confidence is-medium">信心 {{ (review.best_match_score * 100) | round(0) | int }}%</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="dashboard-ai-pick-reason">{{ review.action_label if review else decision.summary }}</div>
|
||||
{% if envelope %}
|
||||
{% set data_quality = guardrails.data_quality | default('partial', true) | lower %}
|
||||
<div class="dashboard-review-envelope" aria-label="決策信封摘要">
|
||||
<span>{{ envelope.severity or 'P4' }}</span>
|
||||
<span>{{ guardrails.data_quality or 'partial' }}</span>
|
||||
<span>優先 {{ envelope.severity or 'P4' }}</span>
|
||||
<span>{{ {'complete': '證據完整', 'partial': '部分證據', 'missing': '證據不足'}.get(data_quality, '部分證據') }}</span>
|
||||
{% if guardrails.can_auto_execute == false %}
|
||||
<span>HITL</span>
|
||||
<span>需人工</span>
|
||||
{% endif %}
|
||||
{% if envelope.decision_id %}
|
||||
<span title="{{ envelope.decision_id }}">trace</span>
|
||||
<span title="{{ envelope.decision_id }}">追蹤</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -750,9 +921,10 @@
|
||||
{{ item.safe_created_at.strftime('%m-%d %H:%M') if item.safe_created_at else '--' }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ 10 if current_filter in ['ai_picks', 'pchome_review'] else 9 }}">
|
||||
<td colspan="{{ 6 if current_filter == 'pchome_review' else (10 if current_filter == 'ai_picks' else 9) }}">
|
||||
<div class="dashboard-empty">
|
||||
{% if search_query %}
|
||||
找不到與「{{ search_query }}」相關的商品
|
||||
|
||||
Reference in New Issue
Block a user