Files
guoju0808/ry-vue3/src/views/doctor/SubmissionNew.vue
T
郭庆泰 cf5790229a refactor: 合并 biz_support_unit/biz_execution_unit/biz_service_org 为 biz_org, 删除 2 张孤儿表, 改 biz_person.work_unit → org_id FK
主要改动:
- SQL: biz_support_unit → biz_org, 加 org_type, 加 business_nature; 删 biz_execution_unit, biz_service_org
- SQL: biz_project.support_unit_id/name + service_org_id/name → org_id/name/type
- SQL: biz_meeting.support_unit_name → org_name
- SQL: biz_person.work_unit → org_id (FK)
- 后端: 新 BizOrg entity/mapper/service/controller
- 后端: BizProject/BizMeeting 字段重命名
- 后端: 删 12 个 BizSupportUnit/BizExecutionUnit/BizServiceOrg Java 文件
- 后端: BizPersonImportVO.workUnit → orgName (导入时查 biz_org 取 org_id)
- 后端: BizAuthController.registerExecutor 完整实现 (原 registerSupplier stub)
- 前端: 新 admin/Orgs.vue + manager/Orgs.vue (原 SupportUnits)
- 前端: RegisterExecutor.vue (原 RegisterSupplier, 单页 2 步)
- 前端: sponsor/executor/manager 多个文件 workUnit → orgId/orgName 重命名
- 前端: 统一 supplier → executor, 业务命名 sponsor(赞助方) / executor(执行方=供应商)
- 前端: 全工程 execution → executor (company type / role / person unit_type)
2026-08-15 12:41:10 +08:00

280 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="submission-new">
<!-- 面包屑 -->
<div class="breadcrumb">
<a @click="goBack">我的项目设计投稿</a> &gt; {{ isEdit ? '修改项目设计投稿' : '新建项目设计投稿' }}
</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="title">
<el-input v-model="form.title" placeholder="请输入策划方案名称" maxlength="200" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目方向" prop="direction">
<el-select v-model="form.direction" placeholder="请选择" style="width:100%">
<el-option label="学术项目" value="学术项目" />
<el-option label="科研项目" value="科研项目" />
<el-option label="共识" value="共识" />
<el-option label="指南" value="指南" />
<el-option label="会议项目" value="会议项目" />
<el-option label="对外交流" value="对外交流" />
</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="projectCategory">
<el-select v-model="form.projectCategory" placeholder="请选择" style="width:100%">
<el-option label="学术会议类" value="学术会议类" />
<el-option label="专项科研类" value="专项科研类" />
<el-option label="调研征集类" value="调研征集类" />
<el-option label="慈善帮扶类" value="慈善帮扶类" />
<el-option label="标准制定类" value="标准制定类" />
<el-option label="患者援助类" value="患者援助类" />
<el-option label="专业培训类" value="专业培训类" />
</el-select>
</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="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>
<!-- 操作按钮 -->
<div class="form-actions">
<el-button type="primary" :loading="saving" @click="onSave">保存</el-button>
<el-button @click="confirmCancel">取消</el-button>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { useUserStore } from '@/store/user'
import request from '@/utils/request'
import { uploadToOss } from '@/utils/oss'
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const formRef = ref(null)
const saving = ref(false)
const loadingDetail = ref(false)
const designFileList = ref([])
const designUploading = ref(false)
const isEdit = !!route.params.subId
const form = reactive({
subId: null,
title: '',
direction: '',
projectForm: '',
projectCategory: '',
designFileUrl: '',
status: 'DRAFT',
remark: ''
})
const rules = {
title: [{ required: true, message: '请输入策划方案名称', trigger: 'blur' }],
direction: [{ required: true, message: '请选择项目方向', trigger: 'change' }],
projectForm: [{ required: true, message: '请选择项目形式', trigger: 'change' }],
projectCategory: [{ required: true, message: '请选择项目类别', trigger: 'change' }],
designFileUrl: [{ required: true, message: '请上传设计文件', trigger: 'change' }]
}
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 ext = '.' + (file.name.split('.').pop() || '').toLowerCase()
if (!accept.includes(ext)) { ElMessage.error('仅支持 PDF / Word / 图片格式'); return false }
return true
}
async function uploadDesign(opts) {
const file = opts.file
designUploading.value = true
try {
const url = await uploadToOss(file, 'ry8080/submission/design/')
form.designFileUrl = url
designFileList.value = [{ name: file.name, url }]
ElMessage.success('设计文件上传成功')
} catch (e) {
ElMessage.error('上传失败: ' + (e?.message || e))
// 上传失败时清空 file-list,避免 el-upload 保留失败文件
designFileList.value = []
} finally {
designUploading.value = false
}
}
function onDesignRemove() {
form.designFileUrl = ''
designFileList.value = []
}
async function loadDetail() {
const subId = route.params.subId
if (!subId) return
loadingDetail.value = true
try {
const res = await request.get(`/business/submission/${subId}`)
if (res?.code === 200 && res.data) {
const d = res.data
form.subId = d.subId
form.title = d.title || ''
form.direction = d.direction || ''
form.projectForm = d.projectForm || ''
form.projectCategory = d.projectCategory || ''
form.designFileUrl = d.designFileUrl || ''
form.status = d.status && String(d.status).trim() ? String(d.status).trim().toUpperCase() : 'DRAFT'
form.remark = d.remark || ''
// 已上传的设计文件回显到 file-list
if (d.designFileUrl) {
const name = d.designFileUrl.split('/').pop() || `${d.title || '设计文件'}.pdf`
designFileList.value = [{ name, url: d.designFileUrl }]
}
} else {
ElMessage.error(res?.msg || '加载失败')
goBack()
}
} catch (e) {
console.error('[submission-new/edit] loadDetail failed', e)
ElMessage.error(e?.msg || '加载失败')
goBack()
} finally {
loadingDetail.value = false
}
}
function goBack() {
router.push({ path: '/doctor/submissions', query: { _t: Date.now() } })
}
function confirmCancel() {
// 检查表单是否有内容
const hasContent = form.title || form.direction || form.projectForm || form.projectCategory || form.designFileUrl || form.remark
if (!hasContent) { goBack(); return }
ElMessageBox.confirm('确定取消新建?未保存的内容将丢失', '提示', { type: 'warning' })
.then(() => goBack())
.catch(() => {})
}
async function onSave() {
try {
await formRef.value.validate()
} catch (e) {
ElMessage.warning('请填写必填项')
return
}
saving.value = true
try {
let payload = { ...form }
if (!isEdit) {
// 新建: 强制设为 DRAFT(待提交)
payload.status = 'DRAFT'
// 写入当前登录用户信息
payload.submitterId = userStore.user?.userId || null
payload.submitterName = userStore.user?.userName || userStore.user?.nickName || ''
} else if (!payload.status) {
// 修改: 如果状态丢失, 兜底 DRAFT(待提交)
payload.status = 'DRAFT'
}
let res
if (isEdit) {
// 修改: 走 PUT /business/submission
res = await request.put('/business/submission', payload)
} else {
// 新建: 走 POST /business/submission
res = await request.post('/business/submission', payload)
}
if (res?.code === 200) {
ElMessage.success(isEdit ? '修改成功' : '新建成功')
goBack()
} else {
ElMessage.error(res?.msg || (isEdit ? '修改失败' : '保存失败'))
}
} catch (e) {
console.error('[submission-new] save failed', e)
ElMessage.error(e?.msg || (isEdit ? '修改失败' : '保存失败'))
} finally {
saving.value = false
}
}
onMounted(() => {
if (isEdit) loadDetail()
})
</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%; }
</style>