- 给 24 个有 filter-form 但无 media query 的列表页统一加 @media (max-width:768px) - 卡片 padding 收窄 (12px, radius 4px) - 工具栏横向滚动 (6+ 按钮铺不下) - filter-form 保持横向布局 (label 左 80px 右对齐 + input 右占满) - 查找/重置按钮垂直堆叠不偏移 - 表格字号 12px - 13 个宽表 (≥10 列) 替换为 GrTable 组件: - sponsor/Projects, executor/Projects, sponsor/SponsorProjects - sponsor/SponsorPeople, expert/Experts, executor/People - meetings/Meetings, sponsor/Meetings, executor/Meetings - manager/ExecIntent, manager/SupportIntent, manager/Plans - admin/Users - 每个页面的 mainCols 取业务主键 (项目编号+名称 / 账号+姓名 等) - 手机端次要列折叠到展开行, 主列+操作列保留 - 修复 executor/Projects.vue 多行 el-table 标签导致的 GrTable 替换错误 全 95 个 vue 编译 200, 0 错误
303 lines
12 KiB
Vue
303 lines
12 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; }
|
|
|
|
|
|
/* ========================================
|
|
移动端适配 (≤768px)
|
|
- 卡片 padding 收窄
|
|
- 工具栏横向滚动 (6 个按钮铺不下, 用 overflow-x)
|
|
- filter-form: 保持 label 左 + input 右横向布局
|
|
- 表格字号收紧
|
|
======================================== */
|
|
@media (max-width: 768px) {
|
|
/* 卡片 padding */
|
|
.page-card { padding: 12px !important; border-radius: 4px !important; }
|
|
.breadcrumb { font-size: 12px !important; margin-bottom: 8px !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;
|
|
}
|
|
|
|
/* filter-form: label 与输入框横向 (label 左, input 右) */
|
|
.filter-form {
|
|
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;
|
|
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;
|
|
flex: 1 !important;
|
|
min-width: 0 !important;
|
|
}
|
|
|
|
/* 强制所有控件全宽, 覆盖 inline 260px / 140px / 120px */
|
|
.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;
|
|
margin-left: 0 !important;
|
|
margin-right: 0 !important;
|
|
display: block !important;
|
|
}
|
|
.filter-form :deep(.el-button + .el-button) {
|
|
margin-top: 8px !important;
|
|
}
|
|
|
|
/* 表格字号收紧 */
|
|
:deep(.el-table) { font-size: 12px !important; }
|
|
}
|
|
</style> |