writeOff/refactor.py
haomingming 815aa04fe8 first
2026-05-20 18:21:39 +08:00

347 lines
11 KiB
Python

import sys
import os
filepath = r'd:\\haomi\\cursor_projects\\writeOff\\frontend\\src\\views\\modules\\meeting-page\\MeetingMaterialDrawer.vue'
with open(filepath, 'r', encoding='utf-8') as f:
text = f.read()
# 1. Update Scripts
script_idx = text.rfind('defineEmits<{')
insert_script = """const emit = defineEmits<{
materialTabChange: [name: string];
saveMaterial: [];
submitMaterial: [];
}>();
const handleSidebarMenuClick = async (moduleCode: string) => {
if (selectedModuleCode.value === moduleCode) return;
const canLeave = await handleBeforeTabLeave(moduleCode, selectedModuleCode.value);
if (canLeave) {
selectedModuleCode.value = moduleCode;
emit('materialTabChange', moduleCode);
}
};
const displayModuleTitle = computed(() => {
const map: Record<string, string> = {
BASIC_INFO: '会议基本信息',
WRITE_OFF_DOCS: '核销材料',
EXPERT_PROFILE: '专家简介/串场',
EXPERT_LIST: '专家配置及材料',
MEETING_INVOICE: '会议各项发票'
};
return map[selectedModuleCode.value] || '会议资料';
});
"""
end_emits_idx = text.find('}();', script_idx) + 5
if end_emits_idx < script_idx + 5: # fallback
end_emits_idx = text.find('}>();', script_idx) + 5
text = text[:script_idx] + insert_script + text[end_emits_idx:]
# 2. Top layout replacement
basic_info_idx = text.find("<el-form v-if=\"selectedModuleCode === 'BASIC_INFO'\"")
template_idx = text.rfind('<template>', 0, basic_info_idx)
sidebar_replacement = """<template>
<el-drawer v-model="materialDialogVisible" size="1200px" :with-header="false" :before-close="handleBeforeCloseDrawer" destroy-on-close class="custom-material-drawer">
<div class="material-layout">
<div class="material-sidebar">
<div class="sidebar-header">
<div class="sidebar-title">{{ materialDialogTitle }}</div>
</div>
<ul class="sidebar-menu">
<li :class="{ 'is-active': selectedModuleCode === 'BASIC_INFO'}" @click="handleSidebarMenuClick('BASIC_INFO')">会议基本信息</li>
<li :class="{ 'is-active': selectedModuleCode === 'WRITE_OFF_DOCS'}" @click="handleSidebarMenuClick('WRITE_OFF_DOCS')">核销材料</li>
<li :class="{ 'is-active': selectedModuleCode === 'EXPERT_PROFILE'}" @click="handleSidebarMenuClick('EXPERT_PROFILE')">专家简介/串场</li>
<li :class="{ 'is-active': selectedModuleCode === 'EXPERT_LIST'}" @click="handleSidebarMenuClick('EXPERT_LIST')">专家列表</li>
<li :class="{ 'is-active': selectedModuleCode === 'MEETING_INVOICE'}" @click="handleSidebarMenuClick('MEETING_INVOICE')">会议发票</li>
</ul>
<div class="sidebar-budget-card" v-if="materialBudgetSummary.ready">
<div class="budget-title">预算看板</div>
<div class="budget-item"><span class="text-secondary">设定预算</span> <span>¥ {{ toYuan(materialBudgetSummary.budgetCent) }}</span></div>
<div class="budget-item"><span class="text-secondary">劳务总计</span> <span>¥ {{ toYuan(materialBudgetSummary.laborTotalCent) }}</span></div>
<div class="budget-item"><span class="text-secondary">发票总计</span> <span>¥ {{ toYuan(materialBudgetSummary.meetingInvoiceTotalCent) }}</span></div>
<div class="budget-item"><span class="text-secondary">已用额度</span> <span>¥ {{ toYuan(materialBudgetSummary.usedTotalCent) }}</span></div>
<div class="budget-divider"></div>
<div class="budget-item" :class="materialBudgetSummary.overCent > 0 ? 'text-danger fw-bold' : 'text-success fw-bold'">
<span>{{ materialBudgetSummary.overCent > 0 ? '超发预算' : '剩余额度' }}</span>
<span>¥ {{ toYuan(materialBudgetSummary.overCent > 0 ? materialBudgetSummary.overCent : materialBudgetSummary.remainCent) }}</span>
</div>
</div>
<div v-if="materialReviewNotice" class="sidebar-notice-card">
<div class="notice-title">审核通知</div>
<div class="notice-content">{{ materialReviewNotice }}</div>
</div>
</div>
<div class="material-main">
<div class="main-header">
<div class="main-title">{{ displayModuleTitle }}</div>
<div class="main-actions">
<el-button @click="handleCancelClick" round>关闭页面</el-button>
<el-button v-if="canMaterialSave" type="primary" @click="emit('saveMaterial')" round>保存更改</el-button>
</div>
</div>
<div class="main-content scrollbar-custom">
<div class="material-module-card" v-if="selectedModuleCode === 'BASIC_INFO'">
<el-form label-position="left" :label-width="LABEL_WIDTH.lg">
"""
text = text[:template_idx] + sidebar_replacement + text[basic_info_idx + len('<el-form v-if="selectedModuleCode === \'BASIC_INFO\'" label-position="left" :label-width="LABEL_WIDTH.lg">'):]
# 3. Replace v-else-ifs
text = text.replace(
'<el-form v-else-if="selectedModuleCode === \'WRITE_OFF_DOCS\'" label-position="left" :label-width="LABEL_WIDTH.lg">',
'</div>\n <div class="material-module-card" v-else-if="selectedModuleCode === \'WRITE_OFF_DOCS\'">\n <el-form label-position="left" :label-width="LABEL_WIDTH.lg">'
)
text = text.replace(
'<el-form v-else-if="selectedModuleCode === \'EXPERT_PROFILE\'" label-position="left" :label-width="LABEL_WIDTH.lg">',
'</div>\n <div class="material-module-card" v-else-if="selectedModuleCode === \'EXPERT_PROFILE\'">\n <el-form label-position="left" :label-width="LABEL_WIDTH.lg">'
)
text = text.replace(
'<div v-else-if="selectedModuleCode === \'EXPERT_LIST\'">',
'</div>\n <div class="material-module-card p-0" v-else-if="selectedModuleCode === \'EXPERT_LIST\'">\n <div class="expert-list-container">'
)
text = text.replace(
'<el-form v-else-if="selectedModuleCode === \'MEETING_INVOICE\'" label-position="left" :label-width="LABEL_WIDTH.lg">',
'</div>\n <div class="material-module-card" v-else-if="selectedModuleCode === \'MEETING_INVOICE\'">\n <el-form label-position="left" :label-width="LABEL_WIDTH.lg">'
)
# Replace EXPERT_LIST structure
# <div class="expert-list-container"> wrapper has been added. So the last closing tag needs adjustment
text = text.replace(
'</el-drawer>\n</template>',
' </div>\n </div>\n </div>\n </div>\n</el-drawer>\n</template>'
)
# We also have <template #footer> ... </template> which we just delete entirely.
start_footer = text.find('<template #footer>')
end_footer = text.find('</template>', start_footer) + len('</template>')
if start_footer != -1:
text = text[:start_footer] + '<!-- Footer replaced by main-header -->' + text[end_footer:]
text = text.replace('</div>\n <!-- Footer replaced by main-header -->', '</div>\n </div>\n </div>\n </div>\n </div>\n</el-drawer>')
# 4. Segmented Control for EXPERT_LIST internal tabs:
text = text.replace(
''' <el-tabs v-model="expertSubModule" class="mb-md">
<el-tab-pane label="现场照片" name="ONSITE_PHOTO" />
<el-tab-pane label="劳务协议" name="LABOR_PROTOCOL" />
</el-tabs>''',
''' <div class="custom-segmented-control mb-md">
<div class="segmented-item" :class="{ 'is-active': expertSubModule === 'ONSITE_PHOTO' }" @click="expertSubModule = 'ONSITE_PHOTO'">现场照片</div>
<div class="segmented-item" :class="{ 'is-active': expertSubModule === 'LABOR_PROTOCOL' }" @click="expertSubModule = 'LABOR_PROTOCOL'">劳务协议</div>
</div>'''
)
# 5. Add custom CSS at the end
style_idx = text.rfind('</style>')
custom_css = """
/* Custom Material Drawer Styles */
:global(.custom-material-drawer .el-drawer__body) {
padding: 0 !important;
background-color: var(--wo-bg-light, #f5f7fa);
}
.material-layout {
display: flex;
height: 100%;
width: 100%;
overflow: hidden;
}
.material-sidebar {
width: 280px;
background-color: var(--wo-bg-side, #ffffff);
border-right: 1px solid var(--wo-border-light, #e4e7ed);
display: flex;
flex-direction: column;
flex-shrink: 0;
padding: 24px 0;
z-index: 10;
}
.sidebar-header {
padding: 0 24px;
margin-bottom: 24px;
}
.sidebar-title {
font-size: 20px;
font-weight: 600;
color: var(--wo-text-primary, #303133);
}
.sidebar-menu {
list-style: none;
padding: 0 12px;
margin: 0;
flex: 1;
}
.sidebar-menu li {
padding: 12px 16px;
margin-bottom: 8px;
border-radius: 8px;
cursor: pointer;
color: var(--wo-text-regular, #606266);
font-size: 14px;
transition: all 0.2s ease;
font-weight: 500;
}
.sidebar-menu li:hover {
background-color: var(--wo-bg-light, #f5f7fa);
color: var(--wo-text-primary, #303133);
}
.sidebar-menu li.is-active {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
font-weight: 600;
}
.sidebar-budget-card {
margin: 20px 16px 0;
padding: 16px;
border-radius: 12px;
background: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);
border: 1px solid rgba(0,0,0,0.05);
box-shadow: 0 4px 12px rgba(0,0,0,0.03);
}
.sidebar-notice-card {
margin: 16px;
padding: 16px;
border-radius: 12px;
background-color: var(--el-color-info-light-9);
border: 1px solid var(--el-color-info-light-7);
}
.notice-title {
color: var(--el-color-info);
font-weight: 600;
font-size: 13px;
margin-bottom: 8px;
}
.notice-content {
font-size: 13px;
color: var(--wo-text-regular);
line-height: 1.5;
}
.budget-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: var(--wo-text-primary);
}
.budget-item {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 13px;
margin-bottom: 8px;
}
.budget-divider {
height: 1px;
background-color: rgba(0,0,0,0.06);
margin: 12px 0;
}
.material-main {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
background-color: #f7f8fa;
}
.main-header {
height: 72px;
padding: 0 32px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #ffffff;
border-bottom: 1px solid var(--wo-border-light, #e4e7ed);
flex-shrink: 0;
}
.main-title {
font-size: 18px;
font-weight: 600;
color: var(--wo-text-primary);
}
.main-content {
flex: 1;
padding: 32px;
overflow-y: auto;
}
.material-module-card {
background-color: #ffffff;
border-radius: 12px;
padding: 32px;
box-shadow: 0 2px 12px rgba(0,0,0,0.02);
border: 1px solid var(--wo-border-light, #e4e7ed);
}
.material-module-card.p-0 {
padding: 0;
background: transparent;
box-shadow: none;
border: none;
}
/* Custom Segmented Control */
.custom-segmented-control {
display: inline-flex;
background-color: var(--wo-bg-light, #f5f7fa);
padding: 4px;
border-radius: 8px;
}
.segmented-item {
padding: 8px 24px;
font-size: 14px;
font-weight: 500;
color: var(--wo-text-regular);
cursor: pointer;
border-radius: 6px;
transition: all 0.2s ease;
}
.segmented-item.is-active {
background-color: #ffffff;
color: var(--el-color-primary);
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.expert-list-container {
display: flex;
gap: 16px;
height: 100%;
}
.expert-list-container .section-card-sm,
.expert-list-container .section-card {
background-color: #ffffff;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 12px rgba(0,0,0,0.02);
border: 1px solid var(--wo-border-light, #e4e7ed);
}
"""
if style_idx != -1:
text = text[:style_idx] + custom_css + text[style_idx:]
with open(filepath, 'w', encoding='utf-8') as f:
f.write(text)
print('Done!')