refactor(submissions): 方案A — mapper submitter_name + 页面样式对齐 + 紧急迁移
- mapper: selectFields 加 COALESCE(bp.name, u.user_name), LEFT JOIN biz_person (uk_person_user_id 防笛卡尔积) - mapper: insert 补 7 字段 (plan_name/plan_direction/plan_category/project_form/design_file_url/is_settled/project_no), plan_name NOT NULL 缺默认 - SQL: 紧急迁移脚本 add submitter_id + idx + biz_person UNIQUE (ALTER, 不 DROP) - SubmissionNew: page-card + breadcrumb + max-width 1200px, accept=.pdf/.png/.jpg/.jpeg, __silentError 防双 toast - SubmissionNew: el-upload drag 模式 A (对齐 Meetings.vue, 删 design-uploader 自定义 CSS) - SubmissionDetail: 删状态/投稿人/投稿时间/审计, 改 Preview 组件展示设计文件
This commit is contained in:
@@ -56,15 +56,29 @@
|
||||
insert into biz_project_plan
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="planId != null and planId != ''">plan_id,</if>
|
||||
<if test="planName != null and planName != ''">plan_name,</if>
|
||||
<if test="planDirection != null and planDirection != ''">plan_direction,</if>
|
||||
<if test="planDirectionId != null">plan_direction_id,</if>
|
||||
<if test="planCategory != null and planCategory != ''">plan_category,</if>
|
||||
<if test="projectForm != null and projectForm != ''">project_form,</if>
|
||||
<if test="designFileUrl != null and designFileUrl != ''">design_file_url,</if>
|
||||
<if test="status != null">status,</if>
|
||||
<if test="isSettled != null and isSettled != ''">is_settled,</if>
|
||||
<if test="projectNo != null and projectNo != ''">project_no,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="submitterId != null">submitter_id,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="planId != null and planId != ''">#{planId},</if>
|
||||
<if test="planName != null and planName != ''">#{planName},</if>
|
||||
<if test="planDirection != null and planDirection != ''">#{planDirection},</if>
|
||||
<if test="planDirectionId != null">#{planDirectionId},</if>
|
||||
<if test="planCategory != null and planCategory != ''">#{planCategory},</if>
|
||||
<if test="projectForm != null and projectForm != ''">#{projectForm},</if>
|
||||
<if test="designFileUrl != null and designFileUrl != ''">#{designFileUrl},</if>
|
||||
<if test="status != null">#{status},</if>
|
||||
<if test="isSettled != null and isSettled != ''">#{isSettled},</if>
|
||||
<if test="projectNo != null and projectNo != ''">#{projectNo},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="submitterId != null">#{submitterId},</if>
|
||||
</trim>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
-- ============================================================
|
||||
-- 紧急修复: 医生首页报 Unknown column 'p.submitter_id'
|
||||
-- 时间: 2026-08-18
|
||||
-- 说明:
|
||||
-- biz_project_plan 加 submitter_id 列 + 索引 (代码已先行, DB 跟上)
|
||||
-- biz_person.user_id 加 UNIQUE (LEFT JOIN 1:1 保证, 避免笛卡尔积)
|
||||
-- ⚠️ 不 DROP 任何表 (DROP 走单独决策)
|
||||
-- ============================================================
|
||||
|
||||
USE guoju0808;
|
||||
|
||||
-- ALTER 1: 给 biz_project_plan 加投稿人字段
|
||||
ALTER TABLE biz_project_plan
|
||||
ADD COLUMN submitter_id BIGINT NULL COMMENT '投稿人用户ID (医生侧投稿归属, 经理/admin 录入为 NULL)';
|
||||
|
||||
-- ALTER 2: 给 submitter_id 加索引 (加速 WHERE 过滤)
|
||||
CREATE INDEX idx_plan_submitter ON biz_project_plan(submitter_id);
|
||||
|
||||
-- ALTER 3: biz_person.user_id 加 UNIQUE (1:1 LEFT JOIN 无笛卡尔积)
|
||||
-- 当前 dump 已确认 biz_person.user_id 无重复 (12 条数据, user_id 非空 2 条且 user_id=106/107 各一条)
|
||||
ALTER TABLE biz_person
|
||||
ADD UNIQUE KEY uk_person_user_id (user_id);
|
||||
|
||||
-- 验证
|
||||
SELECT 'biz_project_plan.submitter_id' AS check_item, COUNT(*) AS col_exists
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='guoju0808' AND TABLE_NAME='biz_project_plan' AND COLUMN_NAME='submitter_id';
|
||||
|
||||
SELECT 'biz_person.uk_person_user_id' AS check_item, COUNT(*) AS idx_exists
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA='guoju0808' AND TABLE_NAME='biz_person' AND INDEX_NAME='uk_person_user_id';
|
||||
@@ -39,8 +39,7 @@
|
||||
<el-form-item label="设计文件">
|
||||
<div class="readonly-cell">
|
||||
<template v-if="detail.designFileUrl">
|
||||
<el-link type="primary" :href="detail.designFileUrl" target="_blank">查看</el-link>
|
||||
<el-link type="primary" style="margin-left: 12px" :href="detail.designFileUrl" :download="downloadName">下载</el-link>
|
||||
<el-button link type="primary" @click="openPreview(detail.designFileUrl, detail.planName || '设计文件')">查看</el-button>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</div>
|
||||
@@ -55,25 +54,6 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="状态">
|
||||
<audit-status-tag v-if="detail.status" :status="detail.status" />
|
||||
<span v-else>-</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="投稿人">
|
||||
<el-input :model-value="display.submitterName" readonly />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="投稿时间">
|
||||
<el-input :model-value="display.createTime" readonly />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
@@ -81,6 +61,9 @@
|
||||
<div class="detail-actions">
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 附件预览 (图片/PDF 直接预览, 其他类型给下载链接) -->
|
||||
<Preview v-model="previewOpen" :url="previewUrl" :title="previewTitle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -89,7 +72,7 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '@/utils/request'
|
||||
import AuditStatusTag from '@/components/AuditStatusTag.vue'
|
||||
import Preview from '@/components/Preview.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -97,42 +80,41 @@ const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const detail = ref({})
|
||||
|
||||
// 显示用: 空值显示 "-", 时间格式化
|
||||
const previewOpen = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewTitle = ref('设计文件')
|
||||
|
||||
function openPreview(url, title) {
|
||||
if (!url) { ElMessage.warning('暂无附件'); return }
|
||||
previewUrl.value = url
|
||||
previewTitle.value = title || '设计文件'
|
||||
previewOpen.value = true
|
||||
}
|
||||
|
||||
// 显示用: 空值显示 "-"
|
||||
function fmt(v) {
|
||||
if (v === null || v === undefined || v === '') return '-'
|
||||
return v
|
||||
}
|
||||
function fmtTime(t) {
|
||||
if (!t) return '-'
|
||||
try { return new Date(t).toLocaleString('zh-CN', { hour12: false }) } catch { return t }
|
||||
}
|
||||
|
||||
const display = computed(() => ({
|
||||
planName: fmt(detail.value.planName),
|
||||
planDirectionTitle: fmt(detail.value.planDirectionTitle || detail.value.planDirection),
|
||||
projectForm: fmt(detail.value.projectForm),
|
||||
planCategory: fmt(detail.value.planCategory),
|
||||
remark: fmt(detail.value.remark),
|
||||
submitterName: fmt(detail.value.submitterName),
|
||||
createTime: fmtTime(detail.value.createTime)
|
||||
remark: fmt(detail.value.remark)
|
||||
}))
|
||||
|
||||
function goBack() {
|
||||
router.push({ path: '/doctor/submissions', query: { _t: Date.now() } })
|
||||
}
|
||||
|
||||
function downloadName() {
|
||||
const url = detail.value.designFileUrl || ''
|
||||
const last = url.split('/').pop()
|
||||
return last || `submission-${detail.value.planId}`
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const planId = route.params.planId
|
||||
if (!planId) { ElMessage.warning('参数缺失'); goBack(); return }
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await request.get(`/business/projectPlan/${planId}`)
|
||||
const res = await request.get(`/business/projectPlan/${planId}`, { __silentError: true })
|
||||
if (res?.code === 200) {
|
||||
detail.value = res.data || {}
|
||||
} else {
|
||||
|
||||
@@ -1,83 +1,77 @@
|
||||
<template>
|
||||
<div class="submission-new">
|
||||
<!-- 面包屑 -->
|
||||
<div v-loading="loadingDetail" class="page-card doctor-submission-new">
|
||||
<div class="breadcrumb">
|
||||
<a @click="goBack">我的项目设计投稿</a> > {{ isEdit ? '修改项目设计投稿' : '新建项目设计投稿' }}
|
||||
<span>首页 / 我的项目设计投稿 / {{ isEdit ? '修改项目设计投稿' : '新建项目设计投稿' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 表单卡片 -->
|
||||
<el-card v-loading="loadingDetail" shadow="never" class="form-card">
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="策划方案名称" prop="planName">
|
||||
<el-input v-model="form.planName" placeholder="请输入策划方案名称" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目方向" prop="planDirectionId">
|
||||
<el-select v-model="form.planDirectionId" placeholder="请选择" style="width:100%">
|
||||
<el-option v-for="opt in planDirectionOptions" :key="opt.id" :label="opt.title" :value="opt.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="submission-form">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="策划方案名称" prop="planName">
|
||||
<el-input v-model="form.planName" placeholder="请输入策划方案名称" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目方向" prop="planDirectionId">
|
||||
<el-select v-model="form.planDirectionId" placeholder="请选择" style="width:100%">
|
||||
<el-option v-for="opt in planDirectionOptions" :key="opt.id" :label="opt.title" :value="opt.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<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-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目类别" prop="planCategory">
|
||||
<dict-select v-model="form.planCategory" dict-type="biz_project_category" value-field="label" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<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-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目类别" prop="planCategory">
|
||||
<dict-select v-model="form.planCategory" dict-type="biz_project_category" value-field="label" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="设计文件" prop="designFileUrl">
|
||||
<el-upload
|
||||
class="design-uploader"
|
||||
drag
|
||||
action="#"
|
||||
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg"
|
||||
:show-file-list="true"
|
||||
:file-list="designFileList"
|
||||
:before-upload="beforeDesignUpload"
|
||||
:http-request="uploadDesign"
|
||||
:on-remove="onDesignRemove"
|
||||
>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将设计文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">支持 PDF / Word / 图片, 单文件 ≤ 20MB</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="设计文件" prop="designFileUrl">
|
||||
<el-upload
|
||||
drag
|
||||
action="#"
|
||||
accept=".pdf,.png,.jpg,.jpeg"
|
||||
:show-file-list="true"
|
||||
:file-list="designFileList"
|
||||
:before-upload="beforeDesignUpload"
|
||||
:http-request="uploadDesign"
|
||||
:on-remove="onDesignRemove"
|
||||
>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将设计文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">支持 PDF / 图片, 单文件 ≤ 20MB</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="4" placeholder="请输入备注 (0/500)" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="4" placeholder="请输入备注 (0/500)" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="form-actions">
|
||||
<el-button type="primary" :loading="saving" @click="onSave">保存</el-button>
|
||||
<el-button @click="confirmCancel">取消</el-button>
|
||||
</div>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="onSave">保存</el-button>
|
||||
<el-button @click="confirmCancel">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -133,9 +127,9 @@ const rules = {
|
||||
function beforeDesignUpload(file) {
|
||||
const max = 20 * 1024 * 1024
|
||||
if (file.size > max) { ElMessage.error('设计文件不能超过 20MB'); return false }
|
||||
const accept = ['.pdf', '.doc', '.docx', '.png', '.jpg', '.jpeg']
|
||||
const accept = ['.pdf', '.png', '.jpg', '.jpeg']
|
||||
const ext = '.' + (file.name.split('.').pop() || '').toLowerCase()
|
||||
if (!accept.includes(ext)) { ElMessage.error('仅支持 PDF / Word / 图片格式'); return false }
|
||||
if (!accept.includes(ext)) { ElMessage.error('仅支持 PDF / 图片格式'); return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -223,9 +217,9 @@ async function onSave() {
|
||||
payload.status = '0'
|
||||
}
|
||||
if (isEdit) {
|
||||
await bizUpdate('projectPlan', payload)
|
||||
await bizUpdate('projectPlan', payload, { __silentError: true })
|
||||
} else {
|
||||
await bizAdd('projectPlan', payload)
|
||||
await bizAdd('projectPlan', payload, { __silentError: true })
|
||||
}
|
||||
ElMessage.success(isEdit ? '修改成功' : '新建成功')
|
||||
goBack()
|
||||
@@ -244,23 +238,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.submission-new { max-width: 1000px; }
|
||||
.breadcrumb { font-size: 13px; color: #8c8c8c; margin-bottom: 16px; }
|
||||
.breadcrumb a { color: var(--brand-primary); cursor: pointer; text-decoration: none; }
|
||||
.breadcrumb a:hover { color: var(--brand-primary-deep, var(--brand-primary)); }
|
||||
|
||||
.form-card { border-radius: 6px; }
|
||||
:deep(.form-card .el-card__body) { padding: 24px 28px; }
|
||||
|
||||
.form-actions { margin-top: 16px; display: flex; gap: 8px; }
|
||||
|
||||
:deep(.el-button--primary) { background: var(--brand-primary); border-color: var(--brand-primary); border-radius: 4px; }
|
||||
:deep(.el-button--primary:hover) { background: var(--brand-primary-deep, var(--brand-primary)); border-color: var(--brand-primary-deep, var(--brand-primary)); }
|
||||
:deep(.el-button) { border-radius: 4px; }
|
||||
:deep(.el-input__wrapper), :deep(.el-textarea__inner) { border-radius: 4px; }
|
||||
|
||||
/* 设计文件上传控件: 占满表单行宽 */
|
||||
.design-uploader { width: 100%; }
|
||||
.design-uploader :deep(.el-upload) { width: 100%; }
|
||||
.design-uploader :deep(.el-upload-dragger) { width: 100%; }
|
||||
/* 整页面板 (与 admin/ExpertNew.vue 一致: max-width 1200px) */
|
||||
.doctor-submission-new { max-width: 1200px; padding: 16px; }
|
||||
.breadcrumb { font-size: 13px; color: #8c8c8c; margin-bottom: 12px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user