refactor: 折扣设置改用Jinja2直建HBox列表+Form/UiFloat+changed事件, 废弃Tabular和inline_edit.js

This commit is contained in:
yumoqing 2026-06-29 11:54:12 +08:00
parent 1bf3528b6d
commit 2259b68789
4 changed files with 120 additions and 175 deletions

View File

@ -730,3 +730,44 @@ def load_discount():
env.bind_customer = bind_customer
env.get_customer_bind = get_customer_bind
env.assign_customer_to_sale = assign_customer_to_sale
# Discount setting products list
env.get_discount_setting_products = get_discount_setting_products
async def get_discount_setting_products(request):
"""Get products with their discount details for discount_setting page.
Used in Jinja2 template: {% set products = get_discount_setting_products(request) %}
"""
env = request._run_ns
discountid = (getattr(request, '_params_kw', {}) or {}).get('discountid', '')
user_orgid = await env.get_userorgid()
if not discountid or not user_orgid:
return []
dbname = env.get_module_dbname('discount')
db = DBPools()
config = getConfig()
db.databases = config.databases
sql = """
SELECT
p.id as productid,
p.product_code,
p.product_name,
p.category_id,
pc.name as category_name,
dd.id as detail_id,
dd.discount,
dd.discountid
FROM product p
LEFT JOIN product_category pc ON p.category_id = pc.id
LEFT JOIN discount_detail dd ON dd.productid = p.id
AND dd.discountid = ${discountid}$
WHERE p.org_id = ${org_id}$ AND p.status = '1'
ORDER BY pc.name, p.product_name
"""
async with db.sqlorContext(dbname) as sor:
recs = await sor.sqlExe(sql, {'discountid': discountid, 'org_id': user_orgid})
return recs if recs else []

View File

@ -95,7 +95,6 @@ for d in crud_defs:
PATHS_ANY = [
f"/{MOD}/menu.ui",
f"/{MOD}/discount_setting",
f"/{MOD}/discount_inline_edit.js",
]
# 每个 CRUD 定义 → any 别名目录 + logined 页面和脚本

View File

@ -1,116 +0,0 @@
/**
* Discount inline editor for discount_setting Tabular.
* Scans discount cells after render and enables click-to-edit.
*/
(function() {
var PROCESSED_CELLS = new WeakSet();
var SETUP_INTERVAL = 300;
function findTabular() {
if (typeof bricks === 'undefined') return null;
try {
return bricks.getWidgetById('product_discount_tbl', bricks.app.root);
} catch(e) {
return null;
}
}
function setupCell(cell) {
if (PROCESSED_CELLS.has(cell)) return;
var text = cell.textContent.trim();
if (text === '' || isNaN(parseFloat(text))) return;
PROCESSED_CELLS.add(cell);
cell.style.cursor = 'pointer';
cell.title = '点击编辑折扣';
cell.addEventListener('click', function(e) {
e.stopPropagation();
if (cell.querySelector('input')) return;
var oldVal = cell.textContent.trim();
var inp = document.createElement('input');
inp.type = 'number';
inp.step = '0.01';
inp.min = '0';
inp.max = '1';
inp.value = oldVal;
inp.style.cssText = 'width:100%;box-sizing:border-box;text-align:center;background:#1E293B;color:#F1F5F9;border:1px solid #3B82F6;border-radius:4px;padding:2px 4px;font-size:12px;';
cell.textContent = '';
cell.appendChild(inp);
inp.focus();
inp.select();
var saved = false;
var saveFn = async function() {
if (saved) return;
saved = true;
var newVal = inp.value.trim();
inp.disabled = true;
if (newVal === oldVal) {
cell.textContent = oldVal;
return;
}
try {
var url = new URL(window.location.href);
var discountid = url.searchParams.get('discountid') || '';
var rowEl = cell.closest('.tabular-row');
var productId = '';
var detailId = '';
var dv = findTabular();
if (dv && dv.scrollpanel) {
for (var i = 0; i < dv.scrollpanel.children.length; i++) {
var r = dv.scrollpanel.children[i];
if (r.dom_element === rowEl && r.user_data) {
productId = r.user_data.productid || '';
detailId = r.user_data.detail_id || '';
break;
}
}
}
var resp = await fetch(
url.origin + url.pathname.replace(/\/[^\/]*$/, '/save_discount.dspy'),
{
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
discountid: discountid,
productid: productId,
detail_id: detailId,
discount: newVal
})
}
);
if (resp.ok) {
cell.textContent = parseFloat(newVal).toString();
} else {
cell.textContent = oldVal;
}
} catch(ex) {
cell.textContent = oldVal;
}
};
inp.addEventListener('blur', saveFn);
inp.addEventListener('keydown', function(ev) {
if (ev.key === 'Enter') { ev.preventDefault(); inp.blur(); }
if (ev.key === 'Escape') { cell.textContent = oldVal; }
});
});
}
function scan() {
var dv = findTabular();
if (!dv || !dv.scrollpanel || !dv.scrollpanel.dom_element) return;
var cells = dv.scrollpanel.dom_element.querySelectorAll('.tabular-cell');
cells.forEach(setupCell);
}
// Keep scanning for new cells after each render
setInterval(scan, SETUP_INTERVAL);
})();

View File

@ -1,67 +1,88 @@
{% set products = get_discount_setting_products(request) %}
{
"id": "product_discount_tbl",
"widgettype": "Tabular",
"widgettype": "VBox",
"options": {
"width": "100%",
"height": "100%",
"title": "产品折扣设置",
"description": "选中行后点击「设置折扣」编辑折扣值留空或填0可清除折扣",
"css": "card",
"toolbar": {
"tools": [
{
"name": "set_discount",
"label": "设置折扣",
"selected_row": true,
"icon": "{{entire_url('/bricks/imgs/edit.svg')}}"
}
]
},
"data_url": "{{entire_url('./get_products_with_discount.dspy')}}",
"data_params": {
"discountid": "{{params_kw.discountid}}"
},
"data_method": "GET",
"row_options": {
"idField": "productid",
"checkField": "checked",
"fields": [
{
"name": "category_name",
"title": "产品分类",
"type": "str",
"cwidth": 3
},
{
"name": "product_name",
"title": "产品名称",
"type": "str",
"cwidth": 5
},
{
"name": "product_code",
"title": "产品编码",
"type": "str",
"cwidth": 3
},
{
"name": "discount",
"title": "当前折扣",
"type": "float",
"cwidth": 3
}
]
},
"page_rows": 100,
"cache_limit": 5
"css": "filler"
},
"binds": [
"subwidgets": [
{
"wid": "set_discount",
"event": "click",
"actiontype": "script",
"target": "self",
"script": "var dv = bricks.getWidgetById('product_discount_tbl', bricks.app.root); if (!dv || !dv.select_row || !dv.select_row.user_data) { alert('请先选中一个产品'); return; } var row = dv.select_row.user_data; var discountid = new URLSearchParams(window.location.search).get('discountid') || ''; var mf = new bricks.ModalForm({title: '设置折扣 - ' + row.product_name, width: '400px', auto_open: true, fields: [{name: 'discountid', uitype: 'hide', value: discountid}, {name: 'productid', uitype: 'hide', value: row.productid}, {name: 'category_id', uitype: 'hide', value: row.category_id}, {name: 'detail_id', uitype: 'hide', value: row.detail_id || ''}, {name: 'product_name', label: '产品', uitype: 'text', value: row.product_name}, {name: 'category_name', label: '分类', uitype: 'text', value: row.category_name}, {name: 'discount', label: '折扣(0-1)', uitype: 'float', value: row.discount || '', tip: '0到1之间如0.8表示8折留空或0表示清除折扣'}], submit_url: '{{entire_url('./save_discount.dspy')}}'}); mf.bind('submited', async function(params) { await bricks.show_resp_message_or_error(params); await dv.render({}); });"
"widgettype": "Text",
"options": {
"text": "产品折扣设置",
"fontSize": "16px",
"fontWeight": "600",
"color": "#F1F5F9",
"padding": "8px 12px"
}
},
{
"widgettype": "HBox",
"options": {
"bgcolor": "#0F172A",
"padding": "8px 12px",
"borderRadius": "6px 6px 0 0",
"alignItems": "center"
},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "产品分类", "cwidth": 3, "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8"}},
{"widgettype": "Text", "options": {"text": "产品名称", "cwidth": 5, "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8"}},
{"widgettype": "Text", "options": {"text": "产品编码", "cwidth": 3, "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8"}},
{"widgettype": "Text", "options": {"text": "折扣(可编辑)", "cwidth": 3, "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8"}}
]
},
{
"widgettype": "VBox",
"id": "discount_rows",
"options": {
"width": "100%",
"css": "childrensize"
},
"subwidgets": [
{% for p in products %}
{
"widgettype": "HBox",
"options": {
"padding": "6px 12px",
"border": "0 0 1px 0",
"borderColor": "#334155",
"alignItems": "center",
"width": "100%"
},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "{{p.category_name or ''}}", "cwidth": 3, "fontSize": "12px", "color": "#E2E8F0"}},
{"widgettype": "Text", "options": {"text": "{{p.product_name or ''}}", "cwidth": 5, "fontSize": "12px", "color": "#F1F5F9"}},
{"widgettype": "Text", "options": {"text": "{{p.product_code or ''}}", "cwidth": 3, "fontSize": "12px", "color": "#E2E8F0"}},
{
"widgettype": "Form",
"id": "discount_form_{{loop.index}}",
"options": {
"cwidth": 3,
"show_label": false,
"css": "childrensize",
"submit_url": "{{entire_url('./save_discount.dspy')}}",
"fields": [
{"name": "discountid", "uitype": "hide", "value": "{{params_kw.discountid}}"},
{"name": "productid", "uitype": "hide", "value": "{{p.productid}}"},
{"name": "detail_id", "uitype": "hide", "value": "{{p.detail_id or ''}}"},
{"name": "discount", "uitype": "float", "value": "{{p.discount or ''}}", "placeholder": "0~1", "dec_len": 2}
]
},
"binds": [
{
"wid": "self",
"event": "changed",
"actiontype": "script",
"target": "self",
"script": "var form = this.target; var d = form.getValue(); var old = form._last_val; if (d.discount === old) return; form._last_val = d.discount; fetch(form.options.submit_url, {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(d)}).then(function(r){ return r.json(); }).then(function(resp){ if (resp.widgettype === 'Error') bricks.show_error(resp.options); else console.log('discount saved'); }).catch(function(e){ console.error(e); });"
}
]
}
]
}{% if not loop.last %},{% endif %}
{% endfor %}
]
}
]
}