diff --git a/product_management/core.py b/product_management/core.py
index a501e4c..bf0f7f2 100644
--- a/product_management/core.py
+++ b/product_management/core.py
@@ -1321,8 +1321,8 @@ class ProductManager:
全表同因子(如 flat=按次)时省略——价格列单位(元/张)已表达口径
Returns:
- {'headers': [...], 'rows': [[要素值..., 价格文本]], 'html': '
…'}
- html 供 .ui 直接嵌入 Html 控件;让利(discount<1)时价格单元格附划线原价。
+ {'headers': [...], 'rows': [[要素值..., {'t': 价格文本, 'k': 'price'}...]]}
+ k='orig' 的单元格是划线原价(让利时附)。.ui 用声明式控件渲染(2026-09-18)。
"""
cols = [] # [(key, header)]
seen = set()
@@ -1350,18 +1350,9 @@ class ProductManager:
headers.append('价格')
rows = []
- html = ['']
- # 主题感知(2026-09-18 暗色治理):内联样式可用 var(),跟 bricks.css 双主题变量
- 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('' + ''.join(
- '| %s | ' % (th_style, _html_escape(str(h)))
- for h in headers) + '
')
-
+ # 结构化返回(2026-09-18 用户纠正:价格表用 bricks 声明式控件渲染,
+ # 后端不拼 HTML——Html 控件内联样式是暗色治理死角且违背声明式铁律)。
+ # 单元格 = {'t': 文本, 'k': 'price'|'orig'|''},.ui 按 k 上色/划线。
for p in prices:
vals = []
for key, _h in cols:
@@ -1373,23 +1364,13 @@ class ProductManager:
else:
v = (p.get('filter_labels') or {}).get(key[2:], '')
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:
- price_text += '(原价 ¥%.4g)' % p['original_price']
- rows.append(vals + [price_text])
-
- price_html = ('%s'
- % _html_escape('¥%.4g %s' % (
- p['amount'], p.get('unit_label') or '元')))
- if discount < 1.0 - 1e-9:
- price_html += (' ¥%.4g' % p['original_price'])
- html.append('' + ''.join(
- '| %s | ' % (td_style, _html_escape(v))
- for v in vals)
- + '%s |
' % (td_style, price_html))
- html.append('
')
- return {'headers': headers, 'rows': rows, 'html': ''.join(html)}
+ cells.append({'t': '¥%.4g' % p['original_price'], 'k': 'orig'})
+ rows.append(cells)
+ return {'headers': headers, 'rows': rows}
async def get_customer_price_display(self, product_id=None, product_code=None,
user_org_id=None):
@@ -1410,7 +1391,7 @@ class ProductManager:
{'success', 'prices': [{'label','unit_price','unit_label','amount','unit',
'filter_labels','original_price'}],
'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(
product_id, product_code)
diff --git a/wwwroot/storefront/index.ui b/wwwroot/storefront/index.ui
index 53b740b..3a3a6b1 100644
--- a/wwwroot/storefront/index.ui
+++ b/wwwroot/storefront/index.ui
@@ -162,12 +162,33 @@
},
{% else %}
{% if price_table %}
-{# 多价目:表格展示(要素列+价格列),产线包月/包年、模型分辨率×类型分档都走这里 #}
+{# 多价目:声明式表格(2026-09-18 用户纠正:不用 Html 拼 table,用 bricks 控件;
+ 行类 tabular-header-row/tabular-row 框架双主题齐备;数据走 otext+i18n 回退原文) #}
{
- "widgettype": "Html",
- "options": {
- "html": {{json.dumps(price_table.html, ensure_ascii=False)}}
- }
+ "widgettype": "VBox",
+ "options": {"width": "100%", "gap": "0"},
+ "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 %}
{