1. 工序工位支持0号上线位,更新解析逻辑与注释 2. 物料清单模块重命名为产品物料清单并优化文案 3. WMS与工位端新增用户/角色详情抽屉组件 4. 工单模块新增自动生成工单号、工位分组选择器 5. 日排产支持批量生成与详情查看 6. 工单列表重构操作菜单与表单优化
147 lines
5.4 KiB
Python
147 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
||
# 为列表页自动注入「详情」按钮 + DetailDrawer 抽屉。
|
||
# 字段从已有 <el-table-column> 抽取(中文 label 复用,type:time 自动识别)。
|
||
import re, sys, os
|
||
|
||
SKIP_LABELS = {'操作', '图片', '操作人', '附件', '明细', '记录', '步骤'}
|
||
|
||
def parse_columns(tpl):
|
||
fields = []
|
||
seen = set()
|
||
col_re = re.compile(r'<el-table-column\b([^>]*?)(/?>)', 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('</el-table-column>', m.end())
|
||
if k < 0:
|
||
i = m.end()
|
||
continue
|
||
block = tpl[m.start():k + len('</el-table-column>')]
|
||
i = k + len('</el-table-column>')
|
||
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'<script setup>(.*?)</script>', s, re.S)
|
||
m_tpl = re.search(r'<template>(.*?)</template>', 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 </script>
|
||
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 <el-table
|
||
detail_col = (
|
||
" <el-table-column label=\"详情\" width=\"80\" align=\"center\">\n"
|
||
" <template #default=\"{ row }\">\n"
|
||
" <el-button link type=\"primary\" size=\"small\" @click=\"openDetail(row)\">详情</el-button>\n"
|
||
" </template>\n"
|
||
" </el-table-column>\n"
|
||
)
|
||
mt = re.search(r'<el-table\b[^>]*>', tpl)
|
||
if not mt:
|
||
print(' SKIP (no el-table):', path)
|
||
return False
|
||
# 插入到 <el-table ...> 之后(同一行或下一行)
|
||
insert_pos = mt.end()
|
||
tpl_new = tpl[:insert_pos] + "\n" + detail_col + tpl[insert_pos:]
|
||
|
||
# 4) DetailDrawer before last </div> in template
|
||
last_div = tpl_new.rfind('</div>')
|
||
drawer = (
|
||
"\n <DetailDrawer :visible=\"detail.show\" @update:visible=\"detail.show = $event\"\n"
|
||
" :title=\"detail.title\" :fields=\"detail.fields\" :data=\"detail.row\" />\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)
|