diff --git a/xls2ddl/tmpls.py b/xls2ddl/tmpls.py index 04c190b..1d97e65 100644 --- a/xls2ddl/tmpls.py +++ b/xls2ddl/tmpls.py @@ -73,8 +73,7 @@ data_browser_tmpl = """ {% if content_view %} "content_view":{{json.dumps(content_view, indent=4, ensure_ascii=False)}}, {% endif %} -{% if search_fields %} - "search_fields":{{json.dumps(search_fields, indent=4, ensure_ascii=False)}}, +{% if data_filter %} "search_form":{ "css":"card", "padding":"8px", @@ -157,30 +156,14 @@ ns['sort'] = '{{relation.outter_field}}_text' {% endif %} sql = '''{{sql}}''' {% if not relation %} -filterjson = params_kw.get('data_filter') -if filterjson and isinstance(filterjson, str): - try: - filterjson = json.loads(filterjson) - except (json.JSONDecodeError, TypeError): - filterjson = None +{% if data_filter %} +filterjson = {{json.dumps(data_filter, ensure_ascii=False)}} +{% else %} +filterjson = None fields_str=r'''{{json.dumps(fields, indent=4, ensure_ascii=False)}}''' ori_fields = json.loads(fields_str) -if not filterjson: - fields = [ f['name'] for f in ori_fields ] - filterjson = default_filterjson(fields, ns) -{% if search_fields %} -# 应用 search_fields 中显式声明的 op(如 LIKE) -__sf_ops = {f['field']: f.get('op') for f in {{json.dumps(search_fields, ensure_ascii=False)}} if f.get('op')} -if filterjson and __sf_ops: - if not isinstance(filterjson, dict) or 'AND' not in filterjson: - filterjson = {'AND': [filterjson] if filterjson else []} - for fj in filterjson['AND']: - if fj.get('field') in __sf_ops: - fj['op'] = __sf_ops[fj['field']] - if fj['op'] == 'LIKE': - var = fj.get('var') - if var and var in ns and ns[var]: - ns[var] = f'%{ns[var]}%' +fields = [ f['name'] for f in ori_fields ] +filterjson = default_filterjson(fields, ns) {% endif %} {% if logined_userorgid or logined_userid %} # 确保 logined 过滤条件始终生效 diff --git a/xls2ddl/xls2crud.py b/xls2ddl/xls2crud.py index 71684d4..2cd359f 100644 --- a/xls2ddl/xls2crud.py +++ b/xls2ddl/xls2crud.py @@ -255,50 +255,95 @@ def filter_backslash(s): ls = s.split('\\/') return '/'.join(ls) +def extract_filter_vars(filterjson, field_map): + """递归提取 data_filter 中的 var 和对应的 field,返回 {var: field_info}""" + if not filterjson: + return {} + + result = {} + + if isinstance(filterjson, dict): + # 逻辑操作符:AND, OR, NOT + for key in ['AND', 'OR', 'NOT', 'and', 'or', 'not']: + if key in filterjson: + sub = filterjson[key] + if isinstance(sub, list): + for item in sub: + result.update(extract_filter_vars(item, field_map)) + elif isinstance(sub, dict): + result.update(extract_filter_vars(sub, field_map)) + + # 单个条件:field + op + var + if 'field' in filterjson and 'var' in filterjson: + var_name = filterjson['var'] + field_name = filterjson['field'] + if field_name in field_map: + result[var_name] = field_map[field_name].copy() + + elif isinstance(filterjson, list): + for item in filterjson: + result.update(extract_filter_vars(item, field_map)) + + return result + def build_filter_field_list(desc) -> list: - """Build enriched filter field list for InlineForm search form. - When a search_fields field has uitype='code', auto-populate - valueField/textField/params/dataurl from the model's codes definition.""" - if not desc.search_fields: + """从 data_filter 提取所有 var,根据 field 从 fields/alters 获取字段定义, + 构造 InlineForm 搜索字段。field name 用 var。""" + if not desc.data_filter: return [] + + # 构建 field name -> field info 的映射 + field_map = {} + for f in field_list(desc): + field_map[f['name']] = f + + # 从 data_filter 提取 var -> field info + var_field_map = extract_filter_vars(desc.data_filter, field_map) + codes_map = {} if desc.codes: for c in desc.codes: cfield = c.field if isinstance(c, dict) else getattr(c, 'field', None) if cfield: codes_map[cfield] = c + modulename = getattr(desc, 'modulename', '') result = [] - for f in desc.search_fields: - field_name = f.field if isinstance(f, dict) else getattr(f, 'field', '') - field_title = f.title if isinstance(f, dict) else getattr(f, 'title', '') - field_uitype = f.uitype if isinstance(f, dict) else getattr(f, 'uitype', 'str') + + for var_name, field_info in var_field_map.items(): + field_name = field_info['name'] + field_title = field_info.get('title', field_name) + field_uitype = field_info.get('uitype', 'str') + field_def = { - 'name': field_name, + 'name': var_name, 'uitype': field_uitype, 'placeholder': field_title, 'cwidth': 15 } + if field_uitype == 'code' and field_name in codes_map: c = codes_map[field_name] ctable = c.table if isinstance(c, dict) else getattr(c, 'table', '') cvaluefield = c.valuefield if isinstance(c, dict) else getattr(c, 'valuefield', '') ctextfield = c.textfield if isinstance(c, dict) else getattr(c, 'textfield', '') ccond = (c.cond if isinstance(c, dict) else getattr(c, 'cond', None)) - field_def['valueField'] = field_name - field_def['textField'] = field_name + '_text' + field_def['valueField'] = var_name + field_def['textField'] = var_name + '_text' field_def['params'] = { 'dbname': "{{get_module_dbname('" + modulename + "')}}", 'table': ctable, 'tblvalue': cvaluefield, 'tbltext': ctextfield, - 'valueField': field_name, - 'textField': field_name + '_text' + 'valueField': var_name, + 'textField': var_name + '_text' } if ccond: field_def['params']['cond'] = ccond field_def['dataurl'] = "{{entire_url('/appbase/get_code.dspy')}}?prepend_all=1" + result.append(field_def) + return result def build_data_browser(pat: str, desc: dict):