refactor(meetings): 修改/复制跳独立页 /manager/meetings/new?meetingId=xxx[&mode=copy], 移除列表页 edit dialog

This commit is contained in:
郭庆泰
2026-08-18 23:36:08 +08:00
parent 580410f70f
commit f2830196a1
2 changed files with 141 additions and 148 deletions
+133 -53
View File
@@ -1,13 +1,13 @@
<template>
<div class="page-card manager-meeting-new">
<!-- 面包屑 -->
<div class="breadcrumb">首页 / 会议管理 / 新建会议</div>
<!-- 面包屑: 标题随 mode (新建/修改/复制) -->
<div class="breadcrumb">首页 / 会议管理 / {{ pageTitle }}</div>
<!-- el-form 包裹 + label-width 110px (跟原型一致) -->
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<!-- 2 列布局 (跟原型 .form-grid 一致: grid-auto-flow: column 让左 6 5 对齐) -->
<!-- 2 列布局 (跟原型 .form-grid 一致: grid-auto-flow: column 让左 6 7 对齐) -->
<div class="form-grid">
<!-- 左列 6 项只读快照 (来自 biz_project 派生) -->
<!-- 左列 6 项只读快照 (来自 biz_project 派生, 来自 meeting.projectId) -->
<el-form-item label="项目编号">
<span class="form-value code">{{ snap.projectNo }}</span>
</el-form-item>
@@ -27,12 +27,20 @@
<span class="form-value">{{ snap.createUserName }}</span>
</el-form-item>
<!-- 右列 5 : 1 只读 + 4 输入 -->
<el-form-item label="会议名称">
<span class="form-value">{{ form.meetingName }}</span>
<!-- 右列 7 可编辑: meetingName/periodNo/projectForm/startTime/endTime/totalPeriods/remark -->
<el-form-item label="会议名称" prop="meetingName">
<el-input v-model="form.meetingName" placeholder="请输入会议名称" />
</el-form-item>
<el-form-item label="期数" prop="periodNo">
<el-input v-model="form.periodNo" placeholder="请填写第几期" />
<el-input-number v-model="form.periodNo" :min="1" :precision="0" placeholder="请填写第几期" />
</el-form-item>
<el-form-item label="项目形式" prop="projectForm">
<el-select v-model="form.projectForm" placeholder="请选择项目形式" style="width:100%">
<el-option label="线上" value="线上" />
<el-option label="线下" value="线下" />
<el-option label="线上+线下" value="线上+线下" />
<el-option label="其他" value="其他" />
</el-select>
</el-form-item>
<el-form-item label="会议开始时间" prop="startTime">
<el-date-picker v-model="form.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择开始时间" />
@@ -40,14 +48,17 @@
<el-form-item label="会议结束时间" prop="endTime">
<el-date-picker v-model="form.endTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择结束时间" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" placeholder="请输入备注" />
<el-form-item label="总期数" prop="totalPeriods">
<el-input-number v-model="form.totalPeriods" :min="1" :precision="0" />
</el-form-item>
<el-form-item label="备注" class="full-row">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</div>
<!-- 底部按钮 (skill: el-form-item 包裹, 不另起 div.form-actions) -->
<el-form-item class="form-actions">
<el-button type="primary" @click="onSave">保存</el-button>
<el-button type="primary" :loading="submitting" @click="onSave">保存</el-button>
<el-button @click="goBack">取消</el-button>
</el-form-item>
</el-form>
@@ -55,17 +66,32 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { bizAdd, bizGet } from '@/api/public'
import { bizAdd, bizGet, bizUpdate } from '@/api/public'
import { ElMessage } from 'element-plus'
const route = useRoute()
const router = useRouter()
const preselectProjectId = route.query.projectId || ''
const formRef = ref(null)
// 项目只读快照 (左列 6 项)
// ========== mode 判定 ==========
// 3 种模式:
// new: ?projectId=xxx → POST /business/meeting (新建)
// edit: ?meetingId=xxx → PUT /business/meeting/{id} (修改)
// copy: ?meetingId=xxx&mode=copy → POST /business/meeting (复制)
const meetingId = route.query.meetingId || ''
const projectId = route.query.projectId || ''
const mode = computed(() => {
if (meetingId && route.query.mode === 'copy') return 'copy'
if (meetingId) return 'edit'
return 'new'
})
const pageTitle = computed(() => ({ new: '新建会议', edit: '修改会议', copy: '复制会议' }[mode.value]))
const formRef = ref(null)
const submitting = ref(false)
// ========== 项目只读快照 (左列 6 项) ==========
const snap = reactive({
projectNo: '',
projectName: '',
@@ -75,51 +101,34 @@ const snap = reactive({
createUserName: ''
})
// 表单 (右列 5: meetingName 只读 + 4 输入)
// ========== 表单 (右列 7可编辑) ==========
const form = reactive({
projectId: preselectProjectId ? Number(preselectProjectId) : null,
projectId: null,
projectNo: '',
projectName: '',
meetingName: '',
periodNo: '',
periodNo: 1,
projectForm: '',
startTime: '',
endTime: '',
totalPeriods: 1,
remark: ''
})
const rules = {
meetingName: [{ required: true, message: '请输入会议名称', trigger: 'blur' }],
periodNo: [{ required: true, message: '请填写第几期', trigger: 'blur' }],
projectForm: [{ required: true, message: '请选择项目形式', trigger: 'change' }],
startTime: [{ required: true, message: '请选择会议开始时间', trigger: 'change' }],
endTime: [{ required: true, message: '请选择会议结束时间', trigger: 'change' }]
endTime: [{ required: true, message: '请选择会议结束时间', trigger: 'change' }],
totalPeriods: [{ required: true, message: '请填写总期数', trigger: 'blur' }]
}
async function onSave() {
try { await formRef.value.validate() } catch (e) { return }
// ========== 加载项目快照 ==========
async function loadProjectSnapshot(pid) {
if (!pid) return
try {
// __silentError: 拦截器不自动 toast, 错误统一由下方 catch 处理 (避免双 toast)
await bizAdd('meeting', {
projectId: form.projectId,
projectNo: form.projectNo,
projectName: form.projectName,
meetingName: form.meetingName,
periodNo: form.periodNo,
startTime: form.startTime,
endTime: form.endTime,
remark: form.remark
}, { __silentError: true })
ElMessage.success('会议创建成功')
goBack()
} catch (e) {
ElMessage.error(e?.msg || '保存失败')
}
}
function goBack() { router.push('/manager/projects') }
onMounted(async () => {
if (!preselectProjectId) return
try {
const r = await bizGet('project', preselectProjectId)
const r = await bizGet('project', pid)
const d = (r && r.data) || {}
snap.projectNo = d.projectNo || ''
snap.projectName = d.projectName || ''
@@ -127,11 +136,78 @@ onMounted(async () => {
snap.sponsorOrgName = d.sponsorOrgName || ''
snap.createTime = d.createTime || ''
snap.createUserName = d.createUserName || d.createBy || ''
// 会议名称默认 = 项目名称 (跟红字提示一致)
form.projectNo = d.projectNo || ''
form.projectName = d.projectName || ''
form.meetingName = d.projectName || ''
} catch (e) {}
} catch (e) { /* 静默, 不阻塞主流程 */ }
}
// ========== 加载会议详情 (edit/copy 模式用) ==========
async function loadMeeting(mid) {
if (!mid) return
const r = await bizGet('meeting', mid)
const d = (r && r.data) || {}
form.meetingId = d.meetingId
form.projectId = d.projectId ?? null
form.projectNo = d.projectNo || ''
form.projectName = d.projectName || ''
form.meetingName = d.meetingName || ''
form.periodNo = d.periodNo || 1
form.projectForm = d.projectForm || ''
form.startTime = d.startTime || ''
form.endTime = d.endTime || ''
form.totalPeriods = d.totalPeriods || 1
form.remark = d.remark || ''
// 左列快照来自 meeting.projectId
await loadProjectSnapshot(d.projectId)
}
// ========== 保存 ==========
async function onSave() {
try { await formRef.value.validate() } catch (e) { return }
submitting.value = true
try {
const payload = { ...form }
if (mode.value === 'edit') {
// 修改: 走 bizUpdate, 带 meetingId (后端 WHERE 用, 不能 delete)
// __silentError: 拦截器不自动 toast, 错误统一由下方 catch 处理 (避免双 toast)
await bizUpdate('meeting', payload, { __silentError: true })
ElMessage.success('修改成功')
} else {
// 新建 / 复制: 走 bizAdd, 不传 meetingId (AUTO_INCREMENT)
delete payload.meetingId
await bizAdd('meeting', payload, { __silentError: true })
ElMessage.success(mode.value === 'copy' ? '复制成功' : '创建成功')
}
goBack()
} catch (e) {
ElMessage.error(e?.msg || (mode.value === 'edit' ? '修改失败' : '保存失败'))
} finally {
submitting.value = false
}
}
function goBack() {
// 跳回列表 (用 name 而非 path, hash 模式 + 路由可复用)
router.push({ name: 'manager-meetings' })
}
onMounted(async () => {
if (mode.value === 'edit') {
// 修改: 加载会议详情 (左列项目快照由 meeting.projectId 派生)
await loadMeeting(meetingId)
} else if (mode.value === 'copy') {
// 复制: 加载会议详情, name 加 " 副本", periodNo reset 为 1 (用户填)
await loadMeeting(meetingId)
form.meetingName = (form.meetingName || '') + ' 副本'
form.periodNo = 1 // 复制后让用户重新填期数
} else {
// 新建: 加载项目快照, meetingName 默认 = 项目名
if (projectId) {
form.projectId = Number(projectId) || null
await loadProjectSnapshot(projectId)
form.projectNo = snap.projectNo
form.projectName = snap.projectName
form.meetingName = snap.projectName // 默认会议名 = 项目名
}
}
})
</script>
@@ -149,12 +225,12 @@ onMounted(async () => {
margin-bottom: 24px;
}
/* === 2 列布局 (跟原型 .form-grid 一致, grid-auto-flow: column 让左 6 右 5 对齐) === */
/* === 2 列布局 (跟原型 .form-grid 一致, grid-auto-flow: column 让左 6 右 7 对齐) === */
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-flow: column;
grid-template-rows: repeat(6, auto);
grid-template-rows: repeat(7, auto);
gap: 20px 32px;
max-width: 900px;
}
@@ -162,6 +238,10 @@ onMounted(async () => {
.form-grid :deep(.el-form-item) {
margin-bottom: 0;
}
/* 备注占满整行 (第 7 项右列最后一行, 跨 2 列) */
.form-grid :deep(.full-row) {
grid-column: 2 / 3;
}
/* 只读值样式 */
.form-value {
+8 -95
View File
@@ -103,38 +103,14 @@
</el-descriptions>
</el-dialog>
<!-- 修改 / 复制 dialog (editMode 区分, 复制走 bizAdd, 修改走 bizUpdate) -->
<el-dialog v-model="editOpen" :title="editMode === 'copy' ? '复制会议' : '修改会议'" width="640px">
<el-form :model="editForm" label-width="100px">
<el-form-item label="会议名称"><el-input v-model="editForm.meetingName" /></el-form-item>
<el-form-item label="项目形式">
<el-select v-model="editForm.projectForm">
<el-option label="线上" value="线上" />
<el-option label="线下" value="线下" />
<el-option label="线上+线下" value="线上+线下" />
<el-option label="其他" value="其他" />
</el-select>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker v-model="editForm.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" />
</el-form-item>
<el-form-item label="结束时间">
<el-date-picker v-model="editForm.endTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" />
</el-form-item>
<el-form-item label="总期数"><el-input-number v-model="editForm.totalPeriods" :min="1" /></el-form-item>
<el-form-item label="备注"><el-input v-model="editForm.remark" type="textarea" :rows="2" /></el-form-item>
</el-form>
<template #footer>
<el-button @click="editOpen = false">取消</el-button>
<el-button type="primary" :loading="editSubmitting" @click="submitEdit">确定</el-button>
</template>
</el-dialog>
<!-- 修改 / 复制: 跳转到 /manager/meetings/new?meetingId=xxx[&mode=copy] ( MeetingNew.vue 独立页处理) -->
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { bizList, bizUpdate, bizAdd } from '@/api/public'
import { useRouter } from 'vue-router'
import { bizList, bizUpdate } from '@/api/public'
import { ElMessage, ElMessageBox } from 'element-plus'
// ========== 筛选 (按实际 8 项: 项目编号/会议ID/会议名称/第?期/会议时间/项目形式/当前阶段/备注) ==========
@@ -216,56 +192,13 @@ const detailOpen = ref(false)
const currentRow = ref(null)
function onView(row) { currentRow.value = row; detailOpen.value = true }
// ========== 修改 / 复制 ==========
const editOpen = ref(false)
const editSubmitting = ref(false)
// editMode 区分当前 dialog 是 "修改" 还是 "复制", 决定 submitEdit 走 bizUpdate 还是 bizAdd
const editMode = ref('edit') // 'edit' | 'copy'
const editForm = reactive({
meetingId: '', meetingName: '',
projectId: null, projectNo: '', projectName: '',
projectForm: '', startTime: '', endTime: '',
totalPeriods: 1, currentStage: '未执行',
remark: ''
})
// ========== 修改 / 复制 (跳 MeetingNew.vue 独立页, ?meetingId=xxx[&mode=copy]) ==========
const router = useRouter()
function onEdit(row) {
editMode.value = 'edit'
Object.assign(editForm, {
meetingId: row.meetingId,
projectId: row.projectId ?? null,
projectNo: row.projectNo || '',
projectName: row.projectName || '',
meetingName: row.meetingName || '',
projectForm: row.projectForm || '',
startTime: row.startTime || '',
endTime: row.endTime || '',
totalPeriods: row.totalPeriods || 1,
currentStage: row.currentStage || '未执行',
remark: row.remark || ''
})
editOpen.value = true
router.push({ name: 'manager-meetings-new', query: { meetingId: row.meetingId } })
}
async function submitEdit() {
editSubmitting.value = true
const isCopy = editMode.value === 'copy'
try {
const payload = { ...editForm }
if (isCopy) {
// 复制: 走 bizAdd, 不传 meetingId (AUTO_INCREMENT) 和 businessId (后端兜底)
delete payload.meetingId
await bizAdd('meeting', payload, { __silentError: true })
} else {
// 修改: 走 bizUpdate
await bizUpdate('meeting', payload, { __silentError: true })
}
ElMessage.success(isCopy ? '复制成功' : '修改成功')
editOpen.value = false
load()
} catch (e) {
ElMessage.error(e?.msg || (isCopy ? '复制失败' : '修改失败'))
} finally {
editSubmitting.value = false
}
function onCopy(row) {
router.push({ name: 'manager-meetings-new', query: { meetingId: row.meetingId, mode: 'copy' } })
}
// ========== 提交 (单条) ==========
@@ -278,26 +211,6 @@ async function onSubmit(row) {
} catch (e) { ElMessage.error(e?.msg || '提交失败') }
}
// ========== 复制 (单条) ==========
function onCopy(row) {
// 复制行到会议名 + ' 副本' 打开 dialog, 走 bizAdd 创建新会议 (跟 onEdit 共用同一 dialog, editMode='copy')
editMode.value = 'copy'
Object.assign(editForm, {
meetingId: '',
projectId: row.projectId ?? null,
projectNo: row.projectNo || '',
projectName: row.projectName || '',
meetingName: (row.meetingName || '') + ' 副本',
projectForm: row.projectForm || '',
startTime: row.startTime || '',
endTime: row.endTime || '',
totalPeriods: row.totalPeriods || 1,
currentStage: '未执行',
remark: row.remark || ''
})
editOpen.value = true
}
// ========== 批量提交 ==========
async function onBatchSubmit() {
if (!selectedIds.value.length) return