Files
guoju0808/ry-vue3/src/views/doctor/Submissions.vue
T
guoju0808 d2fe5a4035 style: 重塑政务风格 + 操作列统一 el-link + 表格紧凑化
业务背景
- ry-vue3 为政务/卫健委风合规管理系统 (医疗机构合规系统), 不要可爱配色、不要插画
- 品牌色: 主色 #15803D (低调深绿) + 次色 #0E7490 (青蓝), 不要蓝色系主色

变更概要
1. 全局地基
   - theme.scss: Element Plus 主色改为 #15803D, slate 9 档, 联动组件库
   - main.scss: 字体/页面骨架/筛选栏/表格/控件/滚动条 全套样式
   - AdminLayout.vue: 政务顶栏 + 浅色侧栏
   - Workbench.vue: 替换原绿底大字卡为 KPI 白底色条 + 快捷入口 + 进度条

2. 表格操作列改造
   - el-button link 全面替换为 el-link (:underline="false" + type="primary/danger")
   - 36 文件 132 处替换, 操作列外加 .table-actions 容器 (内含分隔线)
   - 修复 el-link 不支持的属性 (size="small"/text/loading)

3. 筛选栏
   - 弹性布局 + gap 14px 16px + align-items: flex-start + min-height 32px
   - 删除所有 scoped .filter-form 覆盖 (28 处) 让全局样式生效
   - 控件宽度 180px 统一, 标签 32px 居中, 折行不紧贴

4. 表格紧凑化
   - cell-padding 10px → 6px, 表头高度 36px
   - 行高 ~46px → ~36px, 字号 13px

5. 每页默认 20 条
   - 21 文件 pageSize: 10 → 20

6. meetings 操作列折叠
   - 11 操作 → 外置 3 个主操作 + 1 个"更多" el-dropdown (8 个次操作)
   - width 500px → 260px
   - 配 .op-dropdown / .op-caret 样式 (hover 旋转)
   - 新增 onMoreAction dispatch 函数 (复用原 handler)

7. 操作列文字防拆行
   - .table-actions flex-wrap: nowrap + white-space: nowrap
   - el-link padding 0 8px → 0 6px, flex-shrink: 0

8. 环境配置
   - .env.development: 直连远端 https://risingdoctor.com/hg-api (绕过 vite proxy 卡顿)
   - vite.config.js: proxy fallback 保留
2026-08-26 11:04:15 +00:00

221 lines
8.9 KiB
Vue

<template>
<div class="page-card">
<div class="breadcrumb">首页 / 我的项目策划方案</div>
<el-form inline :model="q" class="filter-form">
<el-form-item label="策划方案名称"><el-input v-model="q.planName" placeholder="输入策划方案名称" clearable style="width: 200px" /></el-form-item>
<el-form-item label="项目方向">
<el-select v-model="q.planDirectionId" placeholder="请选择" clearable style="width: 200px">
<el-option v-for="opt in planDirectionOptions" :key="opt.id" :label="opt.title" :value="opt.id" />
</el-select>
</el-form-item>
<el-form-item label="项目类别">
<dict-select v-model="q.planCategory" dict-type="biz_project_category" value-field="label" style="width: 160px" />
</el-form-item>
<el-form-item label="形式">
<el-select v-model="q.projectForm" placeholder="请选择" clearable style="width: 140px">
<el-option label="线上" value="线上" />
<el-option label="线下" value="线下" />
<el-option label="线上+线下" value="线上+线下" />
<el-option label="其他" value="其他" />
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="q.status" placeholder="请选择" clearable style="width: 140px">
<el-option label="待提交" value="0" />
<el-option label="待审核" value="1" />
<el-option label="通过" value="2" />
<el-option label="拒绝" value="3" />
</el-select>
</el-form-item>
<el-form-item label="备注"><el-input v-model="q.remark" placeholder="输入备注" clearable style="width: 200px" /></el-form-item>
<el-form-item><el-button type="primary" @click="load">查找</el-button><el-button @click="reset">重置</el-button></el-form-item>
</el-form>
<div class="toolbar">
<el-button type="primary" @click="onCreate">新建投稿</el-button>
<el-button @click="onBatch" :disabled="!selected.length">批量提交</el-button>
</div>
<el-table :data="rows" v-loading="loading" stripe border @selection-change="onSelectionChange">
<el-table-column type="selection" width="48" />
<el-table-column prop="planName" label="投稿名称" min-width="220" show-overflow-tooltip />
<el-table-column label="项目方向" min-width="220" show-overflow-tooltip>
<template #default="{ row }">{{ row.planDirectionTitle || row.planDirection || '-' }}</template>
</el-table-column>
<el-table-column prop="planCategory" label="项目类别" width="120" />
<el-table-column prop="projectForm" label="形式" width="100" />
<el-table-column label="设计文件" width="140">
<template #default="{ row }">
<template v-if="row.designFileUrl">
<el-link :underline="false" type="primary" @click="openPreview(row.designFileUrl, '设计文件')">查看</el-link>
<el-link type="primary" :href="row.designFileUrl" target="_blank">下载</el-link>
</template>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">
<audit-status-tag :status="row.status" />
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }"><div class="table-actions">
<el-link :underline="false" type="primary" @click="onView(row)">查看</el-link>
<el-link :underline="false" v-if="canModify(row)" type="primary" @click="onEdit(row)">修改</el-link>
<el-link :underline="false" v-if="canSubmit(row)" type="primary" @click="onSubmit(row)">提交</el-link>
</div></template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="page.pageNum"
v-model:page-size="page.pageSize"
:total="page.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="load"
@size-change="load"
style="margin-top: 12px; text-align: right;"
/>
<Preview v-model="previewOpen" :url="previewUrl" :title="previewTitle" />
</div>
</template>
<script setup>
import { reactive, ref, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import request from '@/utils/request'
import { bizList, bizUpdate } from '@/api/public'
import DictSelect from '@/components/DictSelect.vue'
import AuditStatusTag from '@/components/AuditStatusTag.vue'
import Preview from '@/components/Preview.vue'
const router = useRouter()
const route = useRoute()
// 投稿模块路径前缀: 按当前角色 (doctor/executor/sponsor) 拼, 避免串到别的角色侧栏
const base = computed(() => `/${route.meta.role || 'doctor'}`)
const q = reactive({
planName: '',
planDirectionId: '',
planCategory: '',
projectForm: '',
status: '',
remark: ''
})
const page = reactive({ pageNum: 1, pageSize: 20, total: 0 })
const rows = ref([])
const loading = ref(false)
const selected = ref([])
// 设计文件预览
const previewOpen = ref(false)
const previewUrl = ref('')
const previewTitle = ref('附件预览')
// 项目方向 options (复用 manager 侧接口)
const planDirectionOptions = ref([])
async function loadPlanDirectionOptions() {
try {
const { data } = await request.get('/business/specialPlan/options')
planDirectionOptions.value = (data && data.data) || data || []
} catch { planDirectionOptions.value = [] }
}
async function load() {
loading.value = true
try {
const { data } = await bizList('projectPlan', { ...q, pageNum: page.pageNum, pageSize: page.pageSize })
rows.value = data?.rows || []
page.total = data?.total || 0
} catch (e) { rows.value = []; page.total = 0 }
finally { loading.value = false }
}
function reset() {
q.planName = ''
q.planDirectionId = ''
q.planCategory = ''
q.projectForm = ''
q.status = ''
q.remark = ''
page.pageNum = 1
load()
}
// 锁定的状态 (待审核 '1' / 通过 '2') 不可修改/提交; 未提交 '0' / 拒绝 '3' 可操作
function canModify(row) {
return row.status !== '1' && row.status !== '2'
}
function canSubmit(row) {
// 已通过 '2' 无需再提交; 待审核 '1' 也不允许重复提交; 只有未提交 '0' / 拒绝 '3' 可提交
return row.status === '0' || row.status === '3'
}
function onSelectionChange(arr) { selected.value = arr }
function onCreate() {
router.push({ path: `${base.value}/submission/new` })
}
function onView(row) {
router.push({ path: `${base.value}/submission/detail/${row.planId}` })
}
function onEdit(row) {
router.push({ path: `${base.value}/submission/edit/${row.planId}` })
}
async function onSubmit(row) {
try {
await ElMessageBox.confirm(`确定要提交投稿「${row.planName}」吗?提交后将进入审核流程`, '提交确认', { type: 'warning' })
} catch { return }
try {
// 提交后状态变为 '1' (待审核)
await bizUpdate('projectPlan', { planId: row.planId, status: '1' })
ElMessage.success('已提交,等待审核')
load()
} catch (e) { ElMessage.error(e?.msg || '提交失败') }
}
async function onBatch() {
if (!selected.value.length) { ElMessage.warning('请先勾选要提交的投稿'); return }
try {
await ElMessageBox.confirm(`确定要批量提交选中的 ${selected.value.length} 条投稿吗?`, '批量提交', { type: 'warning' })
} catch { return }
let ok = 0
for (const r of selected.value) {
if (!canSubmit(r)) continue
try { await bizUpdate('projectPlan', { planId: r.planId, status: '1' }); ok++ } catch {}
}
ElMessage.success(`已提交 ${ok} 条`)
load()
}
function openPreview(url, title) {
previewUrl.value = url
previewTitle.value = title || '附件预览'
previewOpen.value = true
}
onMounted(() => {
loadPlanDirectionOptions()
load()
})
</script>
<style scoped>
.page-card { background: #fff; padding: 16px 20px; border-radius: 6px; border: 1px solid #f0f0f0; }
.breadcrumb { font-size: 13px; color: #8c8c8c; margin-bottom: 12px; }
.toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.status { font-size: 13px; color: #595959; }
: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); border-color: var(--brand-primary-deep); }
:deep(.el-table .el-button) { border-radius: 4px; }
:deep(.el-table .el-button.is-link) { color: var(--brand-primary); background: transparent; border: none; }
:deep(.el-table .el-button.is-link:hover) { color: var(--brand-primary-deep); background: transparent; }
:deep(.el-table .el-button.is-link.is-disabled) { color: #c0c4cc; background: transparent; }
</style>