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)
This commit is contained in:
@@ -0,0 +1,504 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<!-- 面包屑 (与 sponsor 端一致: 首页 / XXX / 当前页) -->
|
||||
<div class="breadcrumb">
|
||||
首页 / 项目管理 / <span class="current">{{ isEdit ? '编辑项目' : '新建项目' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 第一区块:项目信息 -->
|
||||
<el-card shadow="never" class="new-card">
|
||||
<div class="new-card-title">项目信息</div>
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="form.projectName" placeholder="请输入项目名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目编号" prop="projectNo">
|
||||
<el-input v-model="form.projectNo" placeholder="请输入项目编号" />
|
||||
</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="totalSessions">
|
||||
<el-input-number v-model="form.totalSessions" :min="1" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="总金额(元)" prop="totalAmount">
|
||||
<el-input-number v-model="form.totalAmount" :min="0" :precision="2" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="管理费(元)">
|
||||
<el-input-number v-model="form.manageFee" :min="0" :precision="2" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker v-model="form.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker v-model="form.endTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 支持单位 -->
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提交截止(天)">
|
||||
<el-input-number v-model="form.submitDeadlineDays" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="支持单位">
|
||||
<el-select v-model="form.orgId" placeholder="请选择赞助公司" filterable clearable style="width:100%" @change="onSupporterPick">
|
||||
<el-option v-for="u in supporterOptions" :key="u.userId"
|
||||
:label="`${u.userName}${u.nickName ? ' (' + u.nickName + ')' : ''}`"
|
||||
:value="u.userId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 第二区块:角色劳务设置 -->
|
||||
<el-card shadow="never" class="new-card">
|
||||
<div class="new-card-title">角色劳务设置</div>
|
||||
<div class="hint-text">
|
||||
<p>*角色劳务设置针对该项目所有会议生效</p>
|
||||
<p>*专家劳务明细当会议劳务金额超过角色劳务设置时,该劳务金额为税后金额,则系统进行提示</p>
|
||||
</div>
|
||||
<div v-for="(r, idx) in form.roleRows" :key="idx" class="role-row">
|
||||
<el-select v-model="r.role" placeholder="请选择" style="width:160px" @change="onRoleSelectChange(r)">
|
||||
<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-input v-if="r.role === '其他'" v-model="r.customName" placeholder="角色名" class="role-custom" />
|
||||
<el-input-number v-model="r.amount" :min="0" :precision="2" placeholder="劳务金额" class="role-amount" />
|
||||
<!-- 行尾按钮: 每行末尾 [增加角色] + [删除本行] (不靠右, 紧贴字段) -->
|
||||
<div class="row-actions">
|
||||
<el-button link type="primary" @click="addRoleRow">增加角色</el-button>
|
||||
<el-button link type="danger" :disabled="form.roleRows.length<=1" @click="removeRoleRowAt(idx)">删除角色</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint-text">*当选择其他时,需手动填写角色名称,显示填写框</p>
|
||||
</el-card>
|
||||
|
||||
<!-- 第三区块:公告文件 -->
|
||||
<el-card shadow="never" class="new-card">
|
||||
<div class="new-card-title">公告文件</div>
|
||||
<p class="hint-text">*勾选公告类型后,上传对应文件;发布公告时可同时发布多个类型</p>
|
||||
<div class="notice-list">
|
||||
<div v-for="(n, idx) in form.notices" :key="idx" class="notice-row">
|
||||
<el-checkbox v-model="n.enabled" :label="n.key" class="notice-check">
|
||||
{{ n.label }}
|
||||
</el-checkbox>
|
||||
<el-upload
|
||||
v-if="n.enabled"
|
||||
class="notice-uploader"
|
||||
:show-file-list="true"
|
||||
:file-list="getFileList(n)"
|
||||
:before-upload="(file) => beforeUpload(file, n)"
|
||||
:http-request="(opts) => httpUpload(opts, n, idx)"
|
||||
:on-remove="(file) => onRemove(n, file)"
|
||||
:on-preview="(file) => onPreview(file)"
|
||||
accept=".pdf,.doc,.docx"
|
||||
drag
|
||||
action="#"
|
||||
>
|
||||
<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>
|
||||
<div v-else class="notice-disabled">勾选「{{ n.label }}」后可上传文件</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint-text">*名称规则:公告文件类型+取项目编号的后两段+项目名称</p>
|
||||
</el-card>
|
||||
|
||||
<!-- 底部按钮 (与 sponsor 工具栏风格一致) -->
|
||||
<div class="form-actions">
|
||||
<el-button @click="confirmCancel">取消</el-button>
|
||||
<el-button type="warning" :loading="submitting" @click="submit('publish')">保存并发布公告</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submit('save')">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { bizAdd, bizUpdate, bizGet } from '@/api/public'
|
||||
import { listExecutor, listSupporters } from '@/api/system'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { uploadToOss } from '@/utils/oss'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const isEdit = !!route.params.projectId
|
||||
const projectId = route.params.projectId || null
|
||||
const formRef = ref(null)
|
||||
const submitting = ref(false)
|
||||
const uploadingIdx = ref(-1)
|
||||
const currentUploadN = ref(null)
|
||||
const fileInputRef = ref(null)
|
||||
|
||||
function defaultRoleRow() { return { role: '', customName: '', amount: 0 } }
|
||||
function defaultNotice(key, label) { return { key, label, url: '', enabled: false, name: '' } }
|
||||
|
||||
// 支持方候选 (sponsor 角色)
|
||||
const supporterOptions = ref([])
|
||||
|
||||
const form = reactive({
|
||||
projectName: '', projectNo: '', projectForm: '',
|
||||
totalSessions: 0, totalAmount: 0, manageFee: 0,
|
||||
startTime: '', endTime: '',
|
||||
// 赞助公司 (TODO: 下拉源应改为 biz_org,orgType='sponsor', 当前是 sys_user)
|
||||
orgId: null,
|
||||
orgName: '',
|
||||
// 提交截止(天)
|
||||
submitDeadlineDays: 30,
|
||||
roleRows: Array(6).fill(0).map(() => defaultRoleRow()),
|
||||
// 公告附件(每个 notice 含 enabled/type/label/url/name)
|
||||
// enabled: 是否启用该公告类型(对应原型 checkbox 邀请函/支持函/通知/日程)
|
||||
notices: [
|
||||
{ key: 'invitation', label: '邀请函', enabled: false, url: '', name: '' },
|
||||
{ key: 'supportLetter', label: '支持函', enabled: false, url: '', name: '' },
|
||||
{ key: 'notice', label: '通知', enabled: false, url: '', name: '' },
|
||||
{ key: 'schedule', label: '日程', enabled: false, url: '', name: '' }
|
||||
]
|
||||
})
|
||||
|
||||
const rules = {
|
||||
projectName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
|
||||
projectNo: [{ required: true, message: '请输入项目编号', trigger: 'blur' }],
|
||||
projectForm: [{ required: true, message: '请选择项目形式', trigger: 'change' }],
|
||||
totalSessions: [{ required: true, message: '请输入总场次', trigger: 'blur' }],
|
||||
totalAmount: [{ required: true, message: '请输入总金额', trigger: 'blur' }],
|
||||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
|
||||
}
|
||||
|
||||
function onRoleSelectChange(r) {
|
||||
if (r.role !== '其他') r.customName = ''
|
||||
}
|
||||
function addRoleRow() {
|
||||
if (form.roleRows.length >= 20) { ElMessage.warning('最多 20 行角色'); return }
|
||||
form.roleRows.push(defaultRoleRow())
|
||||
}
|
||||
function removeRoleRowAt(idx) {
|
||||
if (form.roleRows.length <= 1) {
|
||||
ElMessage.warning('至少保留 1 行角色')
|
||||
return
|
||||
}
|
||||
form.roleRows.splice(idx, 1)
|
||||
}
|
||||
function syncProjectData() {
|
||||
ElMessage.info('同步数据:从外部数据源拉取最新项目信息 (后端支持时启用)')
|
||||
}
|
||||
|
||||
// 编辑模式:加载已有项目数据并反显到表单
|
||||
async function loadProject() {
|
||||
if (!isEdit || !projectId) return
|
||||
try {
|
||||
const res = await bizGet('project', projectId)
|
||||
// res 结构: {code:200, msg, data: BizProject} (单条 success 包了一层)
|
||||
const p = res?.data?.data || res?.data || res
|
||||
if (!p) return
|
||||
form.projectName = p.projectName || ''
|
||||
form.projectNo = p.projectNo || ''
|
||||
form.projectForm = p.projectForm || ''
|
||||
form.totalSessions = p.totalSessions || 0
|
||||
form.totalAmount = p.totalAmount || 0
|
||||
form.manageFee = p.manageFee || 0
|
||||
form.startTime = p.startTime || ''
|
||||
form.endTime = p.endTime || ''
|
||||
form.orgId = p.orgId || null
|
||||
form.orgName = p.orgName || ''
|
||||
form.submitDeadlineDays = p.submitDeadlineDays || 30
|
||||
// 公告文件反显: 优先 publicityFiles JSON 数组, 否则兼容旧字段
|
||||
const files = []
|
||||
if (Array.isArray(p.publicityFiles)) files.push(...p.publicityFiles)
|
||||
else if (typeof p.publicityFiles === 'string' && p.publicityFiles) {
|
||||
try { files.push(...JSON.parse(p.publicityFiles)) } catch (e) { /* ignore */ }
|
||||
}
|
||||
if (files.length) {
|
||||
// 按 type 反显到对应 notice 行
|
||||
for (const n of form.notices) {
|
||||
const m = files.find(f => f.type === n.key)
|
||||
if (m) { n.url = m.url || ''; n.name = m.name || ''; n.enabled = !!m.url }
|
||||
}
|
||||
} else {
|
||||
// 兼容老数据
|
||||
const noticeMap = {
|
||||
invitation: p.invitationUrl,
|
||||
supportLetter: p.supportLetterUrl,
|
||||
notice: p.noticeUrl,
|
||||
schedule: p.scheduleUrl,
|
||||
publish: p.publishUrl
|
||||
}
|
||||
for (const n of form.notices) {
|
||||
n.url = noticeMap[n.key] || ''
|
||||
n.name = n.url ? (n.url.split('/').pop() || '') : ''
|
||||
n.enabled = !!n.url
|
||||
}
|
||||
}
|
||||
// 加载已有执行方分配 (暂不实现, 留 hook)
|
||||
// await loadAssigns(projectId)
|
||||
} catch (e) {
|
||||
ElMessage.error('加载项目数据失败: ' + (e?.msg || e?.message || e))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function loadSupporters(query = '') {
|
||||
try {
|
||||
const params = { roleType: 'sponsor', pageNum: 1, pageSize: 50 }
|
||||
if (query) params.userName = query
|
||||
const r = await listSupporters(params)
|
||||
supporterOptions.value = r.rows || r.data || []
|
||||
} catch (e) { supporterOptions.value = [] }
|
||||
}
|
||||
|
||||
function onSupporterPick(userId) {
|
||||
const u = supporterOptions.value.find(x => x.userId === userId)
|
||||
if (u) form.orgName = u.userName || ''
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
loadProject()
|
||||
loadSupporters('')
|
||||
})
|
||||
function beforeUpload(file, n) {
|
||||
// 文件大小校验: 限制 20MB
|
||||
const maxSize = 20 * 1024 * 1024
|
||||
if (file.size > maxSize) {
|
||||
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 httpUpload(opts, n, idx) {
|
||||
const file = opts.file
|
||||
uploadingIdx.value = idx
|
||||
currentUploadN.value = n
|
||||
const dir = 'ry8080/project/' + n.key + '/'
|
||||
try {
|
||||
const url = await uploadToOss(file, dir)
|
||||
n.url = url
|
||||
n.name = file.name
|
||||
n.enabled = true
|
||||
ElMessage.success(`${n.label} 上传成功`)
|
||||
} catch (err) {
|
||||
ElMessage.error(`上传失败: ${err.message || err}`)
|
||||
n.url = ''
|
||||
n.name = ''
|
||||
} finally {
|
||||
uploadingIdx.value = -1
|
||||
currentUploadN.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// el-upload 内置 file-list 需要从 n.url 转换成 {name, url} 对象
|
||||
function getFileList(n) {
|
||||
if (!n.url) return []
|
||||
// 从 url 截取文件名
|
||||
const url = n.url
|
||||
const name = url.split('/').pop() || `${n.label}.pdf`
|
||||
return [{ name, url }]
|
||||
}
|
||||
|
||||
function onPreview(file) {
|
||||
if (file.url) window.open(file.url, '_blank')
|
||||
}
|
||||
|
||||
function onRemove(n, file) {
|
||||
n.url = ''
|
||||
}
|
||||
function previewFile(n) {
|
||||
if (n.url) window.open(n.url, '_blank')
|
||||
}
|
||||
function downloadFile(n) {
|
||||
if (!n.url) return
|
||||
// 触发浏览器下载
|
||||
const a = document.createElement('a')
|
||||
a.href = n.url
|
||||
a.download = n.name || (n.label + '.pdf')
|
||||
a.target = '_blank'
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
}
|
||||
function removeNotice(idx) {
|
||||
const n = form.notices[idx]
|
||||
if (!n) return
|
||||
ElMessageBox.confirm(`确定删除"${n.label}"附件?`, '提示', { type: 'warning' })
|
||||
.then(() => { n.url = ''; ElMessage.success('已删除') })
|
||||
.catch(() => {})
|
||||
}
|
||||
function goBack() {
|
||||
// 带 _t 强制 Projects.vue watch 触发 reload, 即便路由一样也重载
|
||||
router.push({ path: '/manager/projects', query: { _t: Date.now() } })
|
||||
}
|
||||
function confirmCancel() {
|
||||
ElMessageBox.confirm(`确定取消${isEdit ? '编辑' : '新建'}项目?未保存的内容将丢失`, '提示', { type: 'warning' })
|
||||
.then(() => goBack())
|
||||
.catch(() => {})
|
||||
}
|
||||
async function submit(mode = 'save') {
|
||||
await formRef.value.validate()
|
||||
const uploaded = form.notices.filter(n => n.url)
|
||||
const payload = {
|
||||
projectName: form.projectName,
|
||||
projectNo: form.projectNo,
|
||||
projectForm: form.projectForm,
|
||||
totalSessions: form.totalSessions,
|
||||
totalAmount: form.totalAmount,
|
||||
manageFee: form.manageFee,
|
||||
startTime: form.startTime,
|
||||
endTime: form.endTime,
|
||||
orgId: form.orgId,
|
||||
orgName: form.orgName,
|
||||
submitDeadlineDays: form.submitDeadlineDays
|
||||
}
|
||||
// 公告文件以 publicityFiles JSON 数组存储 (替代旧 invitationUrl/supportLetterUrl/noticeUrl/scheduleUrl)
|
||||
payload.publicityFiles = JSON.stringify(uploaded.map(n => ({
|
||||
type: n.key,
|
||||
label: n.label,
|
||||
url: n.url,
|
||||
name: n.name || (n.url ? n.url.split('/').pop() || '' : '')
|
||||
})))
|
||||
// 兼容旧字段: 同步写一份 (保持后端字段兼容, 新逻辑以 publicityFiles 为准)
|
||||
for (const n of uploaded) {
|
||||
if (n.key === 'invitation') payload.invitationUrl = n.url
|
||||
else if (n.key === 'supportLetter') payload.supportLetterUrl = n.url
|
||||
else if (n.key === 'notice') payload.noticeUrl = n.url
|
||||
else if (n.key === 'schedule') payload.scheduleUrl = n.url
|
||||
}
|
||||
// 发布时设置 is_published='1' + publish_time (同步后端字段)
|
||||
if (mode === 'publish') {
|
||||
payload.isPublished = '1'
|
||||
payload.publishTime = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
||||
// announcement_type 取第一个已上传的文件类型
|
||||
const firstUploaded = uploaded[0]
|
||||
if (firstUploaded) {
|
||||
payload.announcementType = firstUploaded.key
|
||||
}
|
||||
}
|
||||
// 多执行方分配已移至"项目分配"页面单独管理
|
||||
try {
|
||||
submitting.value = true
|
||||
let savedProjectId = projectId
|
||||
if (isEdit) {
|
||||
payload.projectId = projectId
|
||||
await bizUpdate('project', payload)
|
||||
} else {
|
||||
const r = await bizAdd('project', payload)
|
||||
// bizAdd 返回 { code, msg, data: { projectId: '...' } } 或 直接 data
|
||||
savedProjectId = r?.data?.projectId || r?.projectId || r?.data
|
||||
}
|
||||
const roleText = form.roleRows.filter(r => r.role).map(r => `${r.role === '其他' ? (r.customName || '其他') : r.role}=${r.amount}`).join(', ')
|
||||
if (roleText) console.info('[new-project.roleLabor]', form.projectNo, roleText)
|
||||
const successText = isEdit
|
||||
? (mode === 'publish' ? '更新并发布公告成功' : '更新成功')
|
||||
: (mode === 'publish' ? '保存并发布公告成功' : '保存成功')
|
||||
ElMessage.success(successText)
|
||||
goBack()
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.msg || (isEdit ? '更新失败' : '新建失败'))
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 1:1 抄 sponsor/SponsorProjects.vue */
|
||||
.page-card { background: #fff; padding: 16px 20px; border-radius: 6px; border: 1px solid #f0f0f0; }
|
||||
.breadcrumb { font-size: 13px; color: #8c8c8c; margin-bottom: 12px; }
|
||||
.breadcrumb .current { color: #262626; font-weight: 500; }
|
||||
|
||||
/* 卡片分块 (与 sponsor 一致: 左边竖线标识 + 标题) */
|
||||
.new-card { margin-bottom: 14px; border: 1px solid #f0f0f0; border-radius: 4px; }
|
||||
.new-card :deep(.el-card__body) { padding: 16px 20px; }
|
||||
.new-card-title {
|
||||
font-size: 14px; font-weight: 600; color: #262626; margin-bottom: 12px;
|
||||
padding-left: 8px; border-left: 3px solid var(--brand-primary, #1890ff); line-height: 1;
|
||||
}
|
||||
|
||||
.hint-text { font-size: 12px; color: #909399; margin: 8px 0; line-height: 1.6; }
|
||||
.hint-text p { margin-bottom: 4px; }
|
||||
.hint-text p:last-child { margin-bottom: 0; }
|
||||
.role-row { display: flex; align-items: center; gap: 12px; padding: 6px 0; }
|
||||
.role-custom { flex: 1; max-width: 180px; }
|
||||
.role-amount { width: 180px; }
|
||||
.row-actions { display: flex; gap: 8px; }
|
||||
.notice-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
.notice-row { display: flex; align-items: stretch; gap: 16px; margin-bottom: 16px; }
|
||||
.notice-label {
|
||||
width: 100px;
|
||||
display: flex; align-items: center; justify-content: flex-end;
|
||||
color: #606266; font-size: 14px;
|
||||
}
|
||||
.notice-uploader { flex: 1; }
|
||||
.notice-uploader :deep(.el-upload) { width: 100%; }
|
||||
.notice-uploader :deep(.el-upload-dragger) {
|
||||
padding: 12px 16px;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
}
|
||||
.notice-uploader :deep(.el-upload-dragger .el-upload__text) {
|
||||
font-size: 13px; color: #606266;
|
||||
}
|
||||
.notice-uploader :deep(.el-upload-dragger .el-upload__tip) {
|
||||
font-size: 12px; color: #909399; margin-top: 2px;
|
||||
}
|
||||
.notice-uploader :deep(.el-upload__tip) { margin-top: 0; }
|
||||
.notice-uploader :deep(.el-upload-list) { margin: 0; }
|
||||
.notice-actions { display: flex; flex-direction: column; justify-content: center; gap: 4px; min-width: 70px; }
|
||||
.notice-actions .el-button { padding: 2px 6px; font-size: 12px; }
|
||||
.notice-disabled { flex: 1; color: #c0c4cc; font-size: 13px; padding: 8px 0; }
|
||||
|
||||
/* 底部按钮 (居中, 与 sponsor 风格一致) */
|
||||
.form-actions { display: flex; justify-content: center; gap: 16px; padding: 20px 0 4px; border-top: 1px solid #f0f0f0; margin-top: 8px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user