refactor(meetings): MeetingDetail + MeetingNew 挪到公共目录 views/meetings/
会议管理 3 个公共组件统一放在 views/meetings/ 下: - Meetings.vue (列表, 上轮 Option C 已挪) - MeetingDetail.vue ← 本次挪 - MeetingNew.vue ← 本次挪 理由: - 3 个组件都是 admin/manager 共用 (路由前缀对称指过去) - 目录名 'meetings' 精确表达 '会议公共组件' 语义 - 避免 grep 'manager/Meeting*' 误以为是 manager 专属 router/index.js 4 个 import 路径同步更新: - admin-meetings-detail / admin-meetings-new - manager-meetings-detail / manager-meetings-new git mv 保留文件历史 (rename detection), 无新增代码逻辑改动.
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="page-card manager-meeting-new">
|
||||
<!-- 面包屑: 标题随 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 对齐) -->
|
||||
<div class="form-grid">
|
||||
<!-- 左列 6 项只读快照 (来自 biz_project 派生) -->
|
||||
<el-form-item label="项目编号">
|
||||
<span class="form-value code">{{ snap.projectNo }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称">
|
||||
<span class="form-value">{{ snap.projectName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="总场次/总期数">
|
||||
<span class="form-value">{{ snap.totalSessions }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="支持单位">
|
||||
<span class="form-value">{{ snap.sponsorOrgName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<span class="form-value">{{ snap.createTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建人员">
|
||||
<span class="form-value">{{ snap.createUserName }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 右列 5 项: 1 只读 (会议名称 = 项目名) + 4 输入 -->
|
||||
<el-form-item label="会议名称">
|
||||
<span class="form-value">{{ form.meetingName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="期数" prop="periodNo">
|
||||
<el-input v-model="form.periodNo" placeholder="请填写第几期" />
|
||||
</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="选择开始时间" />
|
||||
</el-form-item>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮 (skill: 用 el-form-item 包裹, 不另起 div.form-actions) -->
|
||||
<el-form-item class="form-actions">
|
||||
<el-button type="primary" :loading="submitting" @click="onSave">保存</el-button>
|
||||
<el-button @click="goBack">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { bizAdd, bizGet, bizUpdate } from '@/api/public'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// ========== 角色感知 (admin/manager 共用此新建页, 按 URL 第 1 段识别) ==========
|
||||
const userStore = useUserStore()
|
||||
const roleSegment = computed(() => route.path.split('/')[1]) // 'admin' | 'manager'
|
||||
const isAdmin = computed(() => roleSegment.value === 'admin')
|
||||
const meetingsBase = computed(() => `/${roleSegment.value}/meetings`)
|
||||
const projectsBase = computed(() => `/${roleSegment.value}/projects`)
|
||||
|
||||
// ========== mode 判定 ==========
|
||||
// 3 种模式:
|
||||
// new: ?projectId=xxx → POST /business/meeting (新建)
|
||||
// edit: ?meetingId=xxx → PUT /business/meeting/{id} (修改)
|
||||
// copy: ?meetingId=xxx&mode=copy → POST /business/meeting (复制)
|
||||
// 修改/复制 URL 同时带 projectId 让左列快照能立即加载 (不必先等 bizGet meeting)
|
||||
const meetingId = route.query.meetingId || ''
|
||||
const urlProjectId = 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: '',
|
||||
totalSessions: '',
|
||||
sponsorOrgName: '',
|
||||
createTime: '',
|
||||
createUserName: ''
|
||||
})
|
||||
|
||||
// ========== 表单 (右列 5 项: meetingName 只读 + 4 输入) ==========
|
||||
const form = reactive({
|
||||
projectId: urlProjectId ? Number(urlProjectId) : null,
|
||||
projectNo: '',
|
||||
projectName: '',
|
||||
meetingName: '',
|
||||
periodNo: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
periodNo: [{ required: true, message: '请填写第几期', trigger: 'blur' }],
|
||||
startTime: [{ required: true, message: '请选择会议开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择会议结束时间', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// ========== 加载项目快照 ==========
|
||||
async function loadProjectSnapshot(pid) {
|
||||
if (!pid) return
|
||||
try {
|
||||
const r = await bizGet('project', pid)
|
||||
const d = (r && r.data) || {}
|
||||
snap.projectNo = d.projectNo || ''
|
||||
snap.projectName = d.projectName || ''
|
||||
snap.totalSessions = d.totalSessions ?? ''
|
||||
snap.sponsorOrgName = d.sponsorOrgName || ''
|
||||
snap.createTime = d.createTime || ''
|
||||
snap.createUserName = d.createUserName || d.createBy || ''
|
||||
} 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 || ''
|
||||
form.startTime = d.startTime || ''
|
||||
form.endTime = d.endTime || ''
|
||||
form.remark = d.remark || ''
|
||||
// 左列快照 projectNo/projectName 用 meeting 冗余字段 (即使 project_id 为 NULL 也能显示)
|
||||
snap.projectNo = d.projectNo || ''
|
||||
snap.projectName = d.projectName || ''
|
||||
// 总场次/支持单位/创建时间/创建人员 优先用 URL projectId, 其次 meeting.projectId
|
||||
const pid = urlProjectId || d.projectId
|
||||
if (pid) {
|
||||
await loadProjectSnapshot(pid)
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 保存 ==========
|
||||
async function onSave() {
|
||||
try { await formRef.value.validate() } catch (e) { return }
|
||||
submitting.value = true
|
||||
try {
|
||||
const payload = {
|
||||
projectId: form.projectId,
|
||||
projectNo: form.projectNo,
|
||||
projectName: form.projectName,
|
||||
meetingName: form.meetingName,
|
||||
periodNo: form.periodNo,
|
||||
startTime: form.startTime,
|
||||
endTime: form.endTime,
|
||||
remark: form.remark
|
||||
}
|
||||
if (mode.value === 'edit') {
|
||||
// 修改: 走 bizUpdate, 带 meetingId (后端 WHERE 用, 不能 delete)
|
||||
payload.meetingId = form.meetingId
|
||||
// __silentError: 拦截器不自动 toast, 错误统一由下方 catch 处理 (避免双 toast)
|
||||
await bizUpdate('meeting', payload, { __silentError: true })
|
||||
ElMessage.success('修改成功')
|
||||
} else {
|
||||
// 新建 / 复制: 走 bizAdd
|
||||
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() {
|
||||
// 跳回上游列表页 (按角色):
|
||||
// new (从 Projects 来) → /{role}/projects
|
||||
// edit/copy (从 Meetings 来) → /{role}/meetings
|
||||
if (mode.value === 'new') {
|
||||
router.push(projectsBase.value)
|
||||
} else {
|
||||
router.push(meetingsBase.value)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (mode.value === 'edit') {
|
||||
await loadMeeting(meetingId)
|
||||
} else if (mode.value === 'copy') {
|
||||
// 复制: 加载会议详情, name 加 " 副本", 期数 reset 让用户重填
|
||||
await loadMeeting(meetingId)
|
||||
form.meetingName = (form.meetingName || '') + ' 副本'
|
||||
form.periodNo = '' // 复制后让用户重新填第几期
|
||||
form.meetingId = null // 防御: 不带源 meetingId 走 bizAdd
|
||||
} else {
|
||||
// 新建: 加载项目快照, meetingName 默认 = 项目名
|
||||
if (urlProjectId) {
|
||||
await loadProjectSnapshot(urlProjectId)
|
||||
form.projectNo = snap.projectNo
|
||||
form.projectName = snap.projectName
|
||||
form.meetingName = snap.projectName
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* === 整页面板 (跟 Accounts.vue 一致: max-width: 1200px, 不居中) === */
|
||||
.manager-meeting-new {
|
||||
padding: 16px;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
/* 面包屑 (跟 executor/People.vue 一致) */
|
||||
.breadcrumb {
|
||||
font-size: 13px;
|
||||
color: #8c8c8c;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* === 2 列布局 (跟原型 .form-grid 一致, grid-auto-flow: column 让左 6 右 5 对齐) === */
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-auto-flow: column;
|
||||
grid-template-rows: repeat(6, auto);
|
||||
gap: 20px 32px;
|
||||
max-width: 900px;
|
||||
}
|
||||
/* grid 内的 el-form-item 不再加默认 margin-bottom (由 grid 控制间距) */
|
||||
.form-grid :deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 只读值样式 */
|
||||
.form-value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 32px;
|
||||
}
|
||||
.form-value.code {
|
||||
font-family: ui-monospace, "Courier New", monospace;
|
||||
color: var(--brand-primary);
|
||||
}
|
||||
|
||||
/* 底部按钮 (skill: 用 el-form-item 包裹, 不用 div.form-actions) */
|
||||
.form-actions {
|
||||
margin-top: 24px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
/* 按钮所在 form-item 不显示 label 列 (skill form-actions 包裹的标准做法) */
|
||||
.form-actions :deep(.el-form-item__label) {
|
||||
display: none;
|
||||
}
|
||||
.form-actions :deep(.el-form-item__content) {
|
||||
margin-left: 0 !important;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user