批量推送代码

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-29 17:52:05 +08:00
co-authored by Claude
parent 36528b6e0c
commit e47f76478b
130 changed files with 3972 additions and 2546 deletions
+78 -83
View File
@@ -13,28 +13,41 @@
<el-form-item label="项目名称">
<span class="form-value">{{ snap.projectName }}</span>
</el-form-item>
<el-form-item label="会议名称">
<el-input v-if="canEditName" v-model="form.meetingName" placeholder="请输入会议名称" />
<span v-else class="form-value">{{ form.meetingName }}</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.totalSessions }}</span>
</el-form-item>
<el-form-item label="项目创建人">
<span class="form-value">{{ snap.createUserName }}</span>
</el-form-item>
<el-form-item label="项目创建时间">
<span class="form-value">{{ snap.createTime }}</span>
</el-form-item>
</div>
<!-- 待填写 (放下面) -->
<div class="form-grid form-grid-inputs">
<el-form-item label="会议名称">
<el-input v-if="canEditName" v-model="form.meetingName" placeholder="请输入会议名称" />
<span v-else class="form-value">{{ form.meetingName }}</span>
</el-form-item>
<el-form-item label="期数" prop="periodNo">
<!--
不用 el-input-number: 移动端其 +/- 按钮 absolute 定位在容器最右,
容器 100% 后按钮飘到行尾. 改用 type="number" 原生 input, 浏览器自带步进按钮在 input 内右贴边
-->
<el-input v-model.number="form.periodNo" type="number" :min="1" :max="totalSessionsNum" 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="期数" prop="periodNo">
<el-input v-model="form.periodNo" placeholder="请填写第几期" />
<el-form-item label="会议地点" prop="address">
<el-input v-model="form.address" placeholder="请输入会议地点" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" placeholder="请输入备注" />
@@ -95,7 +108,15 @@ const snap = reactive({
projectNo: '',
projectName: '',
totalSessions: '',
sponsorOrgName: ''
sponsorOrgName: '',
createUserName: '',
createTime: ''
})
// 期数上限 = 总场次/总期数 (executor 快照 totalSessions 已换成分配给本机构的场次口径)
const totalSessionsNum = computed(() => {
const n = Number(snap.totalSessions)
return Number.isNaN(n) || n <= 0 ? undefined : n
})
// ========== 表单 (右列 5 项: meetingName 只读 + 4 输入) ==========
@@ -104,16 +125,18 @@ const form = reactive({
projectNo: '',
projectName: '',
meetingName: '',
periodNo: '',
periodNo: null,
startTime: '',
endTime: '',
address: '',
remark: ''
})
const rules = {
periodNo: [{ required: true, message: '请填写第几期', trigger: 'blur' }],
periodNo: [{ required: true, message: '请填写期数', trigger: 'blur' }],
startTime: [{ required: true, message: '请选择会议开始时间', trigger: 'change' }],
endTime: [{ required: true, message: '请选择会议结束时间', trigger: 'change' }]
endTime: [{ required: true, message: '请选择会议结束时间', trigger: 'change' }],
address: [{ required: true, message: '请填写会议地点', trigger: 'blur' }]
}
// ========== 加载项目快照 ==========
@@ -126,6 +149,8 @@ async function loadProjectSnapshot(pid) {
snap.projectName = d.projectName || ''
snap.totalSessions = d.totalSessions ?? ''
snap.sponsorOrgName = d.sponsorOrgName || ''
snap.createUserName = d.createUserName || ''
snap.createTime = d.createTime || ''
// executor: "总场次" = 分配给本执行方(公司)的场次, 不是项目总场次
if (roleSegment.value === 'executor') {
try {
@@ -147,9 +172,10 @@ async function loadMeeting(mid) {
form.projectNo = d.projectNo || ''
form.projectName = d.projectName || ''
form.meetingName = d.meetingName || ''
form.periodNo = d.periodNo || ''
form.periodNo = d.periodNo ?? null
form.startTime = d.startTime || ''
form.endTime = d.endTime || ''
form.address = d.address || ''
form.remark = d.remark || ''
// 左列快照 projectNo/projectName 用 meeting 冗余字段 (即使 project_id 为 NULL 也能显示)
snap.projectNo = d.projectNo || ''
@@ -170,14 +196,12 @@ async function onSave() {
ElMessage.error('会议开始时间不能晚于会议结束时间')
return
}
// executor: 期数不得超过分配给本机构的场次 (快照 totalSessions 已换成 executor 口径)
if (roleSegment.value === 'executor') {
const pn = Number(form.periodNo)
const total = Number(snap.totalSessions)
if (!Number.isNaN(pn) && !Number.isNaN(total) && pn > total) {
ElMessage.error(`期数不能超过分配给本机构的场次 (共 ${total} 场)`)
return
}
// 期数不得高于总场次/总期数 (executor 快照 totalSessions 已换成分配给本机构的场次口径)
const pn = Number(form.periodNo)
const total = Number(snap.totalSessions)
if (!Number.isNaN(pn) && !Number.isNaN(total) && pn > total) {
ElMessage.error(`期数不能高于总场次/总期数 (共 ${total} 场)`)
return
}
submitting.value = true
try {
@@ -189,6 +213,7 @@ async function onSave() {
periodNo: form.periodNo,
startTime: form.startTime,
endTime: form.endTime,
address: form.address,
remark: form.remark
}
if (mode.value === 'edit') {
@@ -226,10 +251,10 @@ onMounted(async () => {
if (mode.value === 'edit') {
await loadMeeting(meetingId)
} else if (mode.value === 'copy') {
// 复制: 加载会议详情, name 加 " 副本", 期数 reset 让用户重填
// 复制: 加载会议详情, 会议名用项目名称 (而非 "xx 副本"), 期数 reset 让用户重填
await loadMeeting(meetingId)
form.meetingName = (form.meetingName || '') + ' 副本'
form.periodNo = '' // 复制后让用户重新填第几期
form.meetingName = snap.projectName || form.projectName || form.meetingName
form.periodNo = null // 复制后让用户重新填第几期
form.meetingId = null // 防御: 不带源 meetingId 走 bizAdd
} else {
// 新建: 加载项目快照, meetingName 默认 = 项目名
@@ -303,85 +328,55 @@ onMounted(async () => {
}
/* ========================================
/* ========================================
移动端适配 (≤768px)
- 卡片 padding 收窄
- 工具栏横向滚动
- filter-form: label 与输入框横向
- 表格字号收紧
- 只调整本页面布局 (form-grid / form-actions / form-value + form-item 容器),
不强制覆盖 Element Plus 控件内部样式 (.el-input / .el-date-editor / .el-input__wrapper)
======================================== */
@media (max-width: 768px) {
/* 卡片 padding */
.page-card { padding: 12px !important; border-radius: 4px !important; }
.breadcrumb { font-size: 12px !important; margin-bottom: 8px !important; }
.breadcrumb { font-size: 12px !important; margin-bottom: 12px !important; }
/* 工具栏: 横向滚动 */
.toolbar {
flex-wrap: nowrap !important;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding-bottom: 6px;
margin-bottom: 8px !important;
scrollbar-width: thin;
}
.toolbar::-webkit-scrollbar { height: 4px; }
.toolbar::-webkit-scrollbar-thumb { background: var(--brand-slate-200); border-radius: 2px; }
.toolbar :deep(.action-btn),
.toolbar :deep(.el-button) {
flex-shrink: 0;
font-size: 12px !important;
padding: 0 10px !important;
height: 30px !important;
/* 双列 grid → 单列, gap 加大保证 form-item 之间有足够空间显示 error message */
.form-grid {
grid-template-columns: 1fr !important;
gap: 22px !important;
max-width: 100% !important;
}
.form-grid-inputs { margin-top: 16px !important; padding-top: 16px !important; }
/* filter-form: 横向 (label 左 + input 右) */
.filter-form {
/* form-item 容器: 让 label + content 横向并排 (label-width=110px 桌面默认, mobile 仍生效)
align-items: flex-start 让 .el-form-item__error (validate 错误提示) 能在 content 下方独立占行,
不被垂直居中挤掉 */
.manager-meeting-new :deep(.el-form-item) {
display: flex !important;
flex-direction: column !important;
align-items: stretch !important;
gap: 10px !important;
}
.filter-form :deep(.el-form-item) {
display: flex !important;
align-items: center !important;
align-items: flex-start !important;
margin-right: 0 !important;
margin-bottom: 0 !important;
}
.filter-form :deep(.el-form-item__label) {
float: none !important;
width: auto !important;
min-width: 80px !important;
text-align: right !important;
padding: 0 8px 0 0 !important;
font-size: 13px !important;
color: var(--el-text-color-regular) !important;
line-height: 32px !important;
height: 32px !important;
}
.filter-form :deep(.el-form-item__content) {
margin-left: 0 !important;
line-height: 32px !important;
.manager-meeting-new :deep(.el-form-item__content) {
flex: 1 !important;
min-width: 0 !important;
}
/* 强制所有控件全宽 */
.filter-form :deep(.el-select),
.filter-form :deep(.el-input),
.filter-form :deep(.el-date-editor),
.filter-form :deep(.el-button),
.filter-form :deep(.el-cascader) {
width: 100% !important;
min-width: 0 !important;
/* 只读值容器 (form-value) 也走 flex:1 让其与 input 一致 */
.form-value { flex: 1; min-width: 0; line-height: 32px; }
/* 底部按钮: 左右并排等宽 */
.form-actions { margin-top: 16px !important; padding-top: 16px !important; }
.form-actions :deep(.el-form-item__content) {
display: flex !important;
flex-wrap: wrap !important;
gap: 0 !important;
}
.form-actions :deep(.el-button) {
flex: 1 1 0 !important;
width: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
display: block !important;
}
.filter-form :deep(.el-button + .el-button) {
margin-top: 8px !important;
.form-actions :deep(.el-button + .el-button) {
margin-left: 8px !important;
}
/* 表格字号收紧 */
:deep(.el-table) { font-size: 12px !important; }
}
</style>