#!/usr/bin/env python3 # 为列表页自动注入「详情」按钮 + DetailDrawer 抽屉。 # 字段从已有 抽取(中文 label 复用,type:time 自动识别)。 import re, sys, os SKIP_LABELS = {'操作', '图片', '操作人', '附件', '明细', '记录', '步骤'} def parse_columns(tpl): fields = [] seen = set() col_re = re.compile(r']*?)(/?>)', re.S) i = 0 while True: m = col_re.search(tpl, i) if not m: break attrs = m.group(1) closing = m.group(2) if closing == '/>': block = m.group(0) i = m.end() else: k = tpl.find('', m.end()) if k < 0: i = m.end() continue block = tpl[m.start():k + len('')] i = k + len('') m_prop = re.search(r'prop="([^"]+)"', block) m_label = re.search(r'label="([^"]+)"', block) m_time = re.search(r'fmtTime\(row\.(\w+)\)', block) m_dynamic = re.search(r':label=', block) if not m_label or m_dynamic: continue label = m_label.group(1) if label in SKIP_LABELS: continue if m_time: key = m_time.group(1) if key not in seen: fields.append({'key': key, 'label': label, 'type': 'time'}) seen.add(key) continue if m_prop: key = m_prop.group(1) if key not in seen: fields.append({'key': key, 'label': label}) seen.add(key) return fields def fmt_field(f): if f.get('type') == 'time': return " { key: '%s', label: '%s', type: 'time' }," % (f['key'], f['label']) return " { key: '%s', label: '%s' }," % (f['key'], f['label']) def inject(path): s = open(path, encoding='utf-8').read() if 'DetailDrawer' in s: print(' SKIP (already has DetailDrawer):', path) return False if 'openDetail(' in s: print(' SKIP (already has openDetail):', path) return False # script + template m_script = re.search(r'', s, re.S) m_tpl = re.search(r'', s, re.S) if not m_script or not m_tpl: print(' SKIP (no script/template):', path) return False script = m_script.group(1) tpl = m_tpl.group(1) fields = parse_columns(tpl) if not fields: print(' SKIP (no columns parsed):', path) return False # 1) import imports = list(re.finditer(r"^\s*import .* from '[^']+'\s*$", script, re.M)) if not imports: print(' SKIP (no import lines):', path) return False last_import = imports[-1] ins_import = "import DetailDrawer from '../components/DetailDrawer.vue'\n" # 若路径是 ../../components(子目录页)则调整 if '../components/' not in script and '../../components/' in s: ins_import = "import DetailDrawer from '../../components/DetailDrawer.vue'\n" script_new = script[:last_import.end()] + "\n" + ins_import + script[last_import.end():] # 2) openDetail + detail state before fields_block = ",\n".join(fmt_field(f) for f in fields) open_detail = ( "\n// 详情抽屉\n" "const detail = reactive({ show: false, title: '', fields: [], row: null })\n" "function openDetail(row) {\n" " detail.row = row\n" " detail.title = `详情 · #${row.id}`\n" " detail.fields = [\n" + fields_block + "\n ]\n" " detail.show = true\n" "}\n" ) script_new = script_new.rstrip() if not script_new.endswith('}'): script_new = script_new + "\n" script_new = script_new + open_detail # 3) 详情 column right after \n" " \n" " \n" ) mt = re.search(r']*>', tpl) if not mt: print(' SKIP (no el-table):', path) return False # 插入到 之后(同一行或下一行) insert_pos = mt.end() tpl_new = tpl[:insert_pos] + "\n" + detail_col + tpl[insert_pos:] # 4) DetailDrawer before last in template last_div = tpl_new.rfind('') drawer = ( "\n \n" ) tpl_new = tpl_new[:last_div] + drawer + tpl_new[last_div:] new_s = s new_s = new_s[:m_script.start(1)] + script_new + new_s[m_script.end(1):] # 重新定位 template(script 长度变化不影响 template 区域位置,因 template 在 script 之后) new_s = new_s[:m_tpl.start(1)] + tpl_new + new_s[m_tpl.end(1):] open(path, 'w', encoding='utf-8').write(new_s) print(' OK fields=%d : %s' % (len(fields), path)) return True if __name__ == '__main__': for p in sys.argv[1:]: print('FILE', p) try: inject(p) except Exception as e: print(' ERROR', p, e)