From 29578196a1b9acc9033302e39d018bcbbdf5b013 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 24 Jun 2026 20:42:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=8B=AC=E7=AB=8B=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E6=8A=98=E6=89=A3=E8=AE=BE=E7=BD=AE=E9=A1=B5=E9=9D=A2=EF=BC=88?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E4=BA=A7=E5=93=81=E5=88=97=E8=A1=A8+?= =?UTF-8?q?=E8=A1=8C=E5=86=85upsert=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- json/discount_list.json | 4 +- wwwroot/api/get_product_discount_list.dspy | 44 ++++++++++++ .../get_products_with_discount.dspy | 43 +++++++++++ wwwroot/discount_setting/index.ui | 71 +++++++++++++++++++ wwwroot/discount_setting/save_discount.dspy | 54 ++++++++++++++ 5 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 wwwroot/api/get_product_discount_list.dspy create mode 100644 wwwroot/discount_setting/get_products_with_discount.dspy create mode 100644 wwwroot/discount_setting/index.ui create mode 100644 wwwroot/discount_setting/save_discount.dspy diff --git a/json/discount_list.json b/json/discount_list.json index 5c64521..3c69fe1 100644 --- a/json/discount_list.json +++ b/json/discount_list.json @@ -32,8 +32,8 @@ "subtables": [ { "field": "discountid", - "title": "折扣产品明细", - "subtable": "discount_detail_list" + "title": "设置产品折扣", + "subtable": "discount_setting" } ] } diff --git a/wwwroot/api/get_product_discount_list.dspy b/wwwroot/api/get_product_discount_list.dspy new file mode 100644 index 0000000..22f62da --- /dev/null +++ b/wwwroot/api/get_product_discount_list.dspy @@ -0,0 +1,44 @@ +# 获取用户机构的所有产品及其折扣信息 +# 用于折扣明细设置页面 + +ns = params_kw.copy() +discountid = ns.get('discountid') + +if not discountid: + return {"total": 0, "rows": []} + +user_orgid = await get_userorgid() +if not user_orgid: + return {"total": 0, "rows": []} + +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + ns['sort'] = 'category_id, product_name' + +# 查询用户机构的所有产品,LEFT JOIN折扣明细 +sql = ''' +SELECT + p.id as productid, + p.product_name, + p.product_code, + p.category_id, + pc.name as category_name, + COALESCE(dd.id, '') as detail_id, + COALESCE(dd.discount, 0) as discount +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 p.category_id, p.product_name +''' + +ns['org_id'] = user_orgid +ns['discountid'] = discountid + +db = DBPools() +dbname = get_module_dbname('discount') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + debug(f'get_product_discount_list: {len(r.get("rows", []))} products') + return r diff --git a/wwwroot/discount_setting/get_products_with_discount.dspy b/wwwroot/discount_setting/get_products_with_discount.dspy new file mode 100644 index 0000000..faf6ca4 --- /dev/null +++ b/wwwroot/discount_setting/get_products_with_discount.dspy @@ -0,0 +1,43 @@ +# 查询用户所在机构的所有产品及其折扣信息 +# 参数: discountid (必需) + +discountid = params_kw.get('discountid') +if not discountid: + return {"total": 0, "rows": []} + +user_orgid = await get_userorgid() +if not user_orgid: + return {"total": 0, "rows": []} + +ns = params_kw.copy() +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + ns['sort'] = 'category_name, product_name' + +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 +''' + +ns['org_id'] = user_orgid + +db = DBPools() +dbname = get_module_dbname('discount') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + debug(f'get_products_with_discount: {len(r.get("rows", []))} products for org {user_orgid}') + return r diff --git a/wwwroot/discount_setting/index.ui b/wwwroot/discount_setting/index.ui new file mode 100644 index 0000000..987cc4f --- /dev/null +++ b/wwwroot/discount_setting/index.ui @@ -0,0 +1,71 @@ +{ + "widgettype": "VBox", + "options": {"height": "100%", "width": "100%"}, + "subwidgets": [ + { + "id": "product_discount_tbl", + "widgettype": "Tabular", + "options": { + "width": "100%", + "height": "100%", + "title": "产品折扣设置", + "description": "选中产品后点击工具栏按钮设置折扣", + "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_method": "GET", + "data_params": {}, + "row_options": { + "idField": "productid", + "checkField": "checked", + "fields": [ + { + "name": "category_name", + "title": "产品分类", + "type": "str", + "cwidth": 2 + }, + { + "name": "product_name", + "title": "产品名称", + "type": "str", + "cwidth": 4 + }, + { + "name": "product_code", + "title": "产品编码", + "type": "str", + "cwidth": 3 + }, + { + "name": "discount", + "title": "当前折扣", + "type": "float", + "cwidth": 2 + } + ] + }, + "page_rows": 100, + "cache_limit": 5 + }, + "binds": [ + { + "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({}); });" + } + ] + } + ] +} diff --git a/wwwroot/discount_setting/save_discount.dspy b/wwwroot/discount_setting/save_discount.dspy new file mode 100644 index 0000000..7028430 --- /dev/null +++ b/wwwroot/discount_setting/save_discount.dspy @@ -0,0 +1,54 @@ +# Upsert折扣明细:有detail_id则更新,无则新增 +# 参数: discountid, productid, category_id, discount, detail_id(可选) + +result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}} + +try: + discountid = params_kw.get('discountid') + productid = params_kw.get('productid') + category_id = params_kw.get('category_id') + discount_val = params_kw.get('discount') + detail_id = params_kw.get('detail_id') + + if not discountid or not productid: + raise Exception('缺少必要参数: discountid, productid') + + # discount为0或空时,如果已有记录则删除 + if not discount_val or float(discount_val) == 0: + if detail_id: + dbname = get_module_dbname('discount') + async with DBPools().sqlorContext(dbname) as sor: + await sor.D('discount_detail', {'id': detail_id}) + result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '折扣已清除', 'type': 'success'}} + else: + result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': 'ok', 'type': 'success'}} + return result + + dbname = get_module_dbname('discount') + user_orgid = await get_userorgid() + + async with DBPools().sqlorContext(dbname) as sor: + if detail_id: + # 更新 + await sor.U('discount_detail', { + 'id': detail_id, + 'discount': float(discount_val) + }) + else: + # 新增 + await sor.C('discount_detail', { + 'id': uuid(), + 'discountid': discountid, + 'resellerid': user_orgid, + 'prodtypeid': category_id, + 'productid': productid, + 'discount': float(discount_val) + }) + + result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '折扣保存成功', 'type': 'success'}} + +except Exception as e: + debug(f'save_discount error: {e}') + result['options'] = {'title': 'Error', 'message': f'保存失败: {str(e)}', 'type': 'error'} + +return result