refactor(storefront): 价格表去HTML化改bricks声明式控件(用户2026-09-18纠正:不用Html拼table)——core.py只回结构化headers/rows(单元格{t,k}),index.ui用VBox/HBox+Text(tabular-header-row/tabular-row框架双主题类)渲染,价格红/原价划线var(--ink-3)+line-through;Jinja真渲染验JSON合法

This commit is contained in:
yumoqing 2026-09-18 16:37:52 +08:00
parent 637c729e90
commit aee498de78
2 changed files with 38 additions and 36 deletions

View File

@ -1321,8 +1321,8 @@ class ProductManager:
全表同因子(如 flat=按次)时省略——价格列单位(元/张)已表达口径 全表同因子(如 flat=按次)时省略——价格列单位(元/张)已表达口径
Returns: Returns:
{'headers': [...], 'rows': [[要素值..., 价格文本]], 'html': '<table>…'} {'headers': [...], 'rows': [[要素值..., {'t': 价格文本, 'k': 'price'}...]]}
html 供 .ui 直接嵌入 Html 控件;让利(discount<1)时价格单元格附划线原价。 k='orig' 的单元格是划线原价(让利时附)。.ui 用声明式控件渲染(2026-09-18)。
""" """
cols = [] # [(key, header)] cols = [] # [(key, header)]
seen = set() seen = set()
@ -1350,18 +1350,9 @@ class ProductManager:
headers.append('价格') headers.append('价格')
rows = [] rows = []
html = ['<table style="border-collapse:collapse;width:100%;' # 结构化返回(2026-09-18 用户纠正:价格表用 bricks 声明式控件渲染,
'font-size:11px;line-height:1.5;">'] # 后端不拼 HTML——Html 控件内联样式是暗色治理死角且违背声明式铁律)。
# 主题感知(2026-09-18 暗色治理):内联样式可用 var(),跟 bricks.css 双主题变量 # 单元格 = {'t': 文本, 'k': 'price'|'orig'|''},.ui 按 k 上色/划线。
th_style = ('padding:3px 6px;border:1px solid var(--line);background:var(--bg-side);'
'color:var(--ink-3);font-weight:600;text-align:left;'
'white-space:nowrap;')
td_style = ('padding:3px 6px;border:1px solid var(--line);color:var(--ink-2);'
'white-space:nowrap;')
html.append('<tr>' + ''.join(
'<th style="%s">%s</th>' % (th_style, _html_escape(str(h)))
for h in headers) + '</tr>')
for p in prices: for p in prices:
vals = [] vals = []
for key, _h in cols: for key, _h in cols:
@ -1373,23 +1364,13 @@ class ProductManager:
else: else:
v = (p.get('filter_labels') or {}).get(key[2:], '') v = (p.get('filter_labels') or {}).get(key[2:], '')
vals.append(str(v if v is not None else '')) vals.append(str(v if v is not None else ''))
price_text = '¥%.4g %s' % (p['amount'], p.get('unit_label') or '元') cells = [{'t': v, 'k': ''} for v in vals]
cells.append({'t': '¥%.4g %s' % (p['amount'], p.get('unit_label') or '元'),
'k': 'price'})
if discount < 1.0 - 1e-9: if discount < 1.0 - 1e-9:
price_text += '(原价 ¥%.4g)' % p['original_price'] cells.append({'t': '¥%.4g' % p['original_price'], 'k': 'orig'})
rows.append(vals + [price_text]) rows.append(cells)
return {'headers': headers, 'rows': rows}
price_html = ('<span style="color:#e74c3c;font-weight:600;">%s</span>'
% _html_escape('¥%.4g %s' % (
p['amount'], p.get('unit_label') or '元')))
if discount < 1.0 - 1e-9:
price_html += (' <span style="color:#94a3b8;text-decoration:'
'line-through;">¥%.4g</span>' % p['original_price'])
html.append('<tr>' + ''.join(
'<td style="%s">%s</td>' % (td_style, _html_escape(v))
for v in vals)
+ '<td style="%s">%s</td></tr>' % (td_style, price_html))
html.append('</table>')
return {'headers': headers, 'rows': rows, 'html': ''.join(html)}
async def get_customer_price_display(self, product_id=None, product_code=None, async def get_customer_price_display(self, product_id=None, product_code=None,
user_org_id=None): user_org_id=None):
@ -1410,7 +1391,7 @@ class ProductManager:
{'success', 'prices': [{'label','unit_price','unit_label','amount','unit', {'success', 'prices': [{'label','unit_price','unit_label','amount','unit',
'filter_labels','original_price'}], 'filter_labels','original_price'}],
'pricing_text', 'discount', 'original_text', 'pricing_text', 'discount', 'original_text',
'price_table': {'headers','rows','html'} 或 None(多价目时给出)} 'price_table': {'headers': [...], 'rows': [[{'t','k'}...]]} 或 None(多价目时给出)}
""" """
iface, product, err = await self._get_product_interface( iface, product, err = await self._get_product_interface(
product_id, product_code) product_id, product_code)

View File

@ -162,12 +162,33 @@
}, },
{% else %} {% else %}
{% if price_table %} {% if price_table %}
{# 多价目:表格展示(要素列+价格列),产线包月/包年、模型分辨率×类型分档都走这里 #} {# 多价目:声明式表格(2026-09-18 用户纠正:不用 Html 拼 table,用 bricks 控件;
行类 tabular-header-row/tabular-row 框架双主题齐备;数据走 otext+i18n 回退原文) #}
{ {
"widgettype": "Html", "widgettype": "VBox",
"options": { "options": {"width": "100%", "gap": "0"},
"html": {{json.dumps(price_table.html, ensure_ascii=False)}} "subwidgets": [
} {
"widgettype": "HBox",
"options": {"css": "tabular-header-row", "width": "100%"},
"subwidgets": [
{% for h in price_table.headers %}
{"widgettype": "Text", "options": {"otext": {{json.dumps(h, ensure_ascii=False)}}, "text": {{json.dumps(h, ensure_ascii=False)}}, "i18n": true, "halign": "left", "css": "filler", "cfontsize": 0.75}}{{ "," if not loop.last }}
{% endfor %}
]
}{{ "," if price_table.rows }}
{% for row in price_table.rows %}
{
"widgettype": "HBox",
"options": {"css": "tabular-row", "width": "100%"},
"subwidgets": [
{% for c in row %}
{"widgettype": "Text", "options": {"otext": {{json.dumps(c.t, ensure_ascii=False)}}, "text": {{json.dumps(c.t, ensure_ascii=False)}}, "i18n": true, "halign": "left", "css": "filler", "cfontsize": 0.75{% if c.k == 'price' %}, "color": "#e74c3c"{% elif c.k == 'orig' %}, "color": "var(--ink-3)", "textDecoration": "line-through"{% else %}, "color": "var(--ink-2)"{% endif %}}}{{ "," if not loop.last }}
{% endfor %}
]
}{{ "," if not loop.last }}
{% endfor %}
]
}, },
{% else %} {% else %}
{ {