增加月计划填报管理页面

This commit is contained in:
BianLzhaoMin 2025-12-18 17:20:54 +08:00
parent eb8b82ab91
commit 00fc500e81
7 changed files with 813 additions and 158 deletions

View File

@ -26,149 +26,166 @@ import Layout from '@/layout'
// 公共路由
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
}
]
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/register',
component: () => import('@/views/register'),
hidden: true
},
{
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
},
{
path: '',
component: Layout,
redirect: '/index',
children: [
{
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
},
{
path: '/user',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'profile/:activeTab?',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' }
}
]
}
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue'),
},
],
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true,
},
{
path: '/register',
component: () => import('@/views/register'),
hidden: true,
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404'),
hidden: true,
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true,
},
{
path: '',
component: Layout,
redirect: '/index',
children: [
{
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true },
},
],
},
{
path: '/user',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'profile/:activeTab?',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' },
},
],
},
{
path: '/plan/monthlyPlanEdit',
component: Layout,
hidden: true,
children: [
{
path: 'index',
component: () => import('@/views/planMange/monthlyPlan/edit.vue'),
name: 'MonthlyPlanEdit',
meta: {
title: '月计划填报',
activeMenu: '/plan/monthlyPlan', // 保持左侧高亮在列表菜单
},
},
],
},
]
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' }
}
]
},
{
path: '/system/role-auth',
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' }
}
]
},
{
path: '/system/dict-data',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' }
}
]
},
{
path: '/monitor/job-log',
component: Layout,
hidden: true,
permissions: ['monitor:job:list'],
children: [
{
path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' }
}
]
},
{
path: '/tool/gen-edit',
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
}
]
}
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' },
},
],
},
{
path: '/system/role-auth',
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' },
},
],
},
{
path: '/system/dict-data',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' },
},
],
},
{
path: '/monitor/job-log',
component: Layout,
hidden: true,
permissions: ['monitor:job:list'],
children: [
{
path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' },
},
],
},
{
path: '/tool/gen-edit',
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' },
},
],
},
]
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
},
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
},
})
export default router

View File

@ -0,0 +1,145 @@
<template>
<div class="monthly-add-form">
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="auto"
size="large"
class="month-form"
>
<el-row :gutter="12" align="middle">
<el-col :span="8">
<el-form-item label="计划执行月份" prop="month">
<el-date-picker
v-model="formData.month"
type="month"
value-format="YYYY-MM"
placeholder="请选择月份"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="8" :offset="8">
<el-form-item label="" prop="keyword" class="keyword-item">
<el-input
v-model.trim="formData.keyword"
placeholder="输入关键字"
clearable
@keyup.enter="onSearch"
>
<template #suffix>
<el-icon @click="onSearch" style="cursor: pointer">
<Search />
</el-icon>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 任务列表 -->
<el-table
v-loading="loading"
:data="tableData"
border
height="480"
@selection-change="onSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="projectName" label="项目名称" min-width="260" />
<el-table-column prop="workTask" label="工作任务" min-width="360" />
</el-table>
</div>
</template>
<script setup name="MonthlyPlanAddForm">
import { ref, watch, getCurrentInstance } from 'vue'
import { Search } from '@element-plus/icons-vue'
const props = defineProps({
//
defaultMonth: {
type: String,
default: '',
},
//
loadTaskList: {
type: Function,
default: null,
},
})
const { proxy } = getCurrentInstance()
const formRef = ref(null)
const loading = ref(false)
const tableData = ref([])
const selectedRows = ref([])
const formData = ref({
month: props.defaultMonth || '',
keyword: '',
})
const rules = {
month: [{ required: true, message: '请选择计划执行月份', trigger: 'change' }],
}
// /
const fetchData = async () => {
if (!props.loadTaskList) return
await formRef.value.validate()
loading.value = true
try {
const params = {
month: formData.value.month,
keyword: formData.value.keyword,
}
const res = await props.loadTaskList(params)
tableData.value = res?.rows || res?.data?.rows || res?.data || []
} catch (e) {
console.error('加载计划任务列表失败:', e)
tableData.value = []
} finally {
loading.value = false
}
}
const onSearch = () => {
fetchData()
}
const onSelectionChange = (rows) => {
selectedRows.value = rows
}
//
watch(
() => props.defaultMonth,
(val) => {
if (val) formData.value.month = val
},
{ immediate: true },
)
//
defineExpose({
getSelectedTasks: () => selectedRows.value,
search: fetchData,
validate: () => formRef.value.validate(),
})
</script>
<style scoped lang="scss">
.monthly-add-form {
.month-form {
margin-bottom: 12px;
}
.keyword-item :deep(.el-form-item__content) {
justify-content: flex-end;
}
}
</style>

View File

@ -0,0 +1,80 @@
import { reactive } from 'vue'
// 静态选项(可后续替换为接口下拉)
const stationOptions = [
{ label: '昆明运检站', value: 1 },
{ label: '大理运检站', value: 2 },
]
const majorOptions = [
{ label: '输电专业', value: 1 },
{ label: '变电专业', value: 2 },
]
const businessTypeOptions = [
{ label: '日常巡视', value: 1 },
{ label: '缺陷处理', value: 2 },
]
const riskLevelOptions = [
{ label: '低风险', value: 1 },
{ label: '中风险', value: 2 },
{ label: '高风险', value: 3 },
]
// 月计划列表搜索表单配置
export const buildFormColumns = () => [
{
type: 'month',
prop: 'month',
placeholder: '请选择月份',
},
{
type: 'select',
prop: 'stationId',
placeholder: '请选择运检站',
options: stationOptions,
},
{
type: 'input',
prop: 'keyword',
placeholder: '请输入关键字',
},
{
type: 'select',
prop: 'majorId',
placeholder: '请选择专业',
options: majorOptions,
},
{
type: 'select',
prop: 'businessTypeId',
placeholder: '请选择业务类型',
options: businessTypeOptions,
},
{
type: 'select',
prop: 'riskLevel',
placeholder: '请选择风险等级',
options: riskLevelOptions,
},
]
// 月计划列表表格列
export const tableColumns = [
{ prop: 'stationName', label: '运检站' },
{ prop: 'majorName', label: '专业' },
{ prop: 'businessTypeName', label: '业务类型' },
{ prop: 'projectName', label: '项目名称' },
{ prop: 'workTask', label: '工作任务' },
{ prop: 'riskLevelName', label: '风险等级' },
{ prop: 'categoryName', label: '类别' },
{ prop: 'workAmount', label: '工作量' },
{ prop: 'baseCount', label: '基数' },
{ prop: 'planStartDate', label: '计划开始时间' },
]
export default {
tableColumns,
buildFormColumns,
}

View File

@ -0,0 +1,308 @@
<template>
<div class="app-container monthly-plan-edit">
<!-- 顶部返回 + 标题 -->
<el-page-header @back="onBack" :content="pageTitle" class="page-header" />
<el-card shadow="never" class="card-section">
<template #header>
<div class="card-header">基本信息</div>
</template>
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="auto"
:disabled="isDetail"
>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="运检站" prop="stationId">
<el-select
v-model="formData.stationId"
placeholder="请选择运检站"
clearable
filterable
>
<el-option
v-for="item in stationOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="专业" prop="majorId">
<el-select
v-model="formData.majorId"
placeholder="请选择专业"
clearable
>
<el-option
v-for="item in majorOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="业务类型" prop="businessTypeId">
<el-select
v-model="formData.businessTypeId"
placeholder="请选择业务类型"
clearable
>
<el-option
v-for="item in businessTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model.trim="formData.projectName"
placeholder="请输入项目名称"
maxlength="50"
show-word-limit
clearable
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="风险等级" prop="riskLevel">
<el-select
v-model="formData.riskLevel"
placeholder="请选择风险等级"
clearable
>
<el-option
v-for="item in riskLevelOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="计划执行月份" prop="month">
<el-date-picker
v-model="formData.month"
type="month"
value-format="YYYY-MM"
placeholder="请选择月份"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="作业任务" prop="workContent">
<el-input
type="textarea"
v-model.trim="formData.workContent"
placeholder="请输入作业内容"
maxlength="500"
show-word-limit
:autosize="{ minRows: 3, maxRows: 6 }"
/>
</el-form-item>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="计划开始时间" prop="planStartDate">
<el-date-picker
v-model="formData.planStartDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择计划开始时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="计划结束时间" prop="planEndDate">
<el-date-picker
v-model="formData.planEndDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择计划结束时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注" prop="remark">
<el-input
type="textarea"
v-model.trim="formData.remark"
placeholder="请输入备注"
maxlength="200"
show-word-limit
:autosize="{ minRows: 3, maxRows: 6 }"
/>
</el-form-item>
</el-form>
</el-card>
<!-- 人员排班区域先留出结构后续可按业务补充 -->
<el-card shadow="never" class="card-section">
<template #header>
<div class="card-header">人员排班</div>
</template>
<div class="calendar-placeholder">
<span>这里展示月份日历和每日人员排班待与后端字段对接后完善</span>
</div>
</el-card>
<!-- 底部操作按钮详情模式下隐藏 -->
<div v-if="!isDetail" class="page-footer">
<ComButton plain type="info" @click="onBack">取消</ComButton>
<ComButton type="primary" @click="onSubmit">保存</ComButton>
</div>
</div>
</template>
<script setup name="MonthlyPlanEdit">
import { ref, computed, getCurrentInstance } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { addPlanAPI, updatePlanAPI } from '@/api/planMange/plan.js'
import ComButton from '@/components/ComButton/index.vue'
const route = useRoute()
const router = useRouter()
const { proxy } = getCurrentInstance()
const mode = computed(() => route.query.mode || 'add')
const isDetail = computed(() => mode.value === 'detail')
const pageTitle = computed(() => {
if (mode.value === 'edit') return '编辑月计划'
if (mode.value === 'detail') return '月计划详情'
return '新增月计划'
})
const formRef = ref(null)
const getInitFormData = () => ({
planId: null,
stationId: null,
majorId: null,
businessTypeId: null,
projectName: '',
workContent: '',
riskLevel: null,
month: '',
planStartDate: '',
planEndDate: '',
remark: '',
})
const formData = ref(getInitFormData())
//
const stationOptions = [
{ label: '昆明运检站', value: 1 },
{ label: '大理运检站', value: 2 },
]
const majorOptions = [
{ label: '输电专业', value: 1 },
{ label: '变电专业', value: 2 },
]
const businessTypeOptions = [
{ label: '日常巡视', value: 1 },
{ label: '缺陷处理', value: 2 },
]
const riskLevelOptions = [
{ label: '低风险', value: 1 },
{ label: '中风险', value: 2 },
{ label: '高风险', value: 3 },
]
const rules = {
stationId: [{ required: true, message: '请选择运检站', trigger: 'change' }],
majorId: [{ required: true, message: '请选择专业', trigger: 'change' }],
businessTypeId: [{ required: true, message: '请选择业务类型', trigger: 'change' }],
projectName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
workContent: [{ required: true, message: '请输入作业内容', trigger: 'blur' }],
riskLevel: [{ required: true, message: '请选择风险等级', trigger: 'change' }],
month: [{ required: true, message: '请选择计划执行月份', trigger: 'change' }],
planStartDate: [{ required: true, message: '请选择计划开始时间', trigger: 'change' }],
planEndDate: [{ required: true, message: '请选择计划结束时间', trigger: 'change' }],
}
const onBack = () => {
router.back()
}
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (!valid) return
const API = mode.value === 'edit' ? updatePlanAPI : addPlanAPI
const payload = { ...formData.value }
try {
const res = await API(payload)
if (res.code === 200) {
proxy.$modal.msgSuccess(mode.value === 'edit' ? '编辑成功' : '新增成功')
onBack()
}
} catch (error) {
console.error('保存月计划失败:', error)
}
})
}
</script>
<style scoped lang="scss">
.monthly-plan-edit {
.page-header {
margin-bottom: 16px;
}
.card-section {
margin-bottom: 16px;
}
.card-header {
font-weight: 600;
font-size: 14px;
}
.calendar-placeholder {
min-height: 240px;
display: flex;
align-items: center;
justify-content: center;
color: #9ca3af;
border: 1px dashed #e5e7eb;
border-radius: 8px;
background-color: #f9fafb;
}
.page-footer {
margin-top: 12px;
display: flex;
justify-content: flex-end;
gap: 12px;
}
}
</style>

View File

@ -1,9 +1,92 @@
<template>
<div>
<h1>月计划填报管理</h1>
<div class="app-container">
<!-- 月计划填报管理列表 -->
<ComTable
ref="comTableRef"
:form-columns="formColumns"
:table-columns="tableColumns"
:load-data="listPlanAPI"
:show-toolbar="true"
:show-action="true"
:action-columns="actionColumns"
>
<template #toolbar>
<ComButton type="primary" icon="Plus" @click="onHandleAdd">新增月计划</ComButton>
</template>
</ComTable>
<ComDialog :dialog-config="dialogConfig" @closeDialogOuter="onCloseDialogOuter">
<template #outerContent>
<el-row class="common-btn-row">
<ComButton plain type="info" @click="onHandleCancel">取消</ComButton>
<ComButton @click="onHandleSave">保存</ComButton>
</el-row>
</template>
</ComDialog>
</div>
</template>
<script setup name="MonthlyPlan"></script>
<script setup name="MonthlyPlan">
import { ref, computed, getCurrentInstance } from 'vue'
import { useRouter } from 'vue-router'
import { listPlanAPI, delPlanAPI } from '@/api/planMange/plan.js'
import config from './config'
import ComTable from '@/components/ComTable/index.vue'
import ComButton from '@/components/ComButton/index.vue'
import ComDialog from '@/components/ComDialog/index.vue'
<style></style>
const router = useRouter()
const { proxy } = getCurrentInstance()
const { tableColumns, buildFormColumns } = config
const comTableRef = ref(null)
// 使
const formColumns = computed(() => buildFormColumns())
//
const actionColumns = [
{
label: '详情',
type: 'primary',
link: true,
handler: (row) => {
router.push({
path: '/plan/monthlyPlanEdit/index',
query: { id: row.planId, mode: 'detail' },
})
},
},
{
label: '编辑',
type: 'primary',
link: true,
handler: (row) => {
router.push({
path: '/plan/monthlyPlanEdit/index',
query: { id: row.planId, mode: 'detail' },
})
},
},
{
label: '删除',
type: 'danger',
link: true,
handler: (row) => {
proxy.$modal.confirm('是否确认删除该月计划?').then(async () => {
const result = await delPlanAPI({ planId: row.planId })
if (result.code === 200) {
proxy.$modal.msgSuccess('删除成功')
comTableRef.value?.refresh()
}
})
},
},
]
//
const onHandleAdd = () => {}
</script>
<style scoped></style>

View File

@ -1,6 +1,28 @@
import { reactive } from 'vue'
// 搜索表单配置(计划管理)
// 静态选项(后续可替换为接口下拉)
const stationOptions = [
{ label: '昆明运检站', value: 1 },
{ label: '大理运检站', value: 2 },
]
const majorOptions = [
{ label: '输电专业', value: 1 },
{ label: '变电专业', value: 2 },
]
const businessTypeOptions = [
{ label: '日常巡视', value: 1 },
{ label: '缺陷处理', value: 2 },
]
const riskLevelOptions = [
{ label: '低风险', value: 1 },
{ label: '中风险', value: 2 },
{ label: '高风险', value: 3 },
]
// 搜索表单配置(计划管理列表筛选)
export const buildFormColumns = () => [
{
type: 'month',
@ -8,33 +30,33 @@ export const buildFormColumns = () => [
placeholder: '请选择月份',
},
{
type: 'input',
prop: 'projectName',
placeholder: '请输入项目名称',
type: 'select',
prop: 'stationId',
placeholder: '请选择运检站',
options: stationOptions,
},
{
type: 'input',
prop: 'workContent',
placeholder: '请输入作业内容关键字',
prop: 'keyword',
placeholder: '请输入关键字',
},
{
type: 'select',
prop: 'deptId',
placeholder: '请选择实施部门',
options: [
{ label: '实施部门一', value: 1 },
{ label: '实施部门二', value: 2 },
],
prop: 'majorId',
placeholder: '请选择专业',
options: majorOptions,
},
{
type: 'select',
prop: 'businessTypeId',
placeholder: '请选择业务类型',
options: businessTypeOptions,
},
{
type: 'select',
prop: 'riskLevel',
placeholder: '请选择风险等级',
options: [
{ label: '低风险', value: 1 },
{ label: '中风险', value: 2 },
{ label: '高风险', value: 3 },
],
options: riskLevelOptions,
},
]

View File

@ -12,7 +12,7 @@
>
<template #toolbar>
<ComButton type="primary" icon="Plus" @click="onHandleAdd">新建计划</ComButton>
<ComButton type="info" icon="Plus" pain @click="onHandleAdd">导入计划</ComButton>
<ComButton type="info" icon="Upload" pain @click="onHandleAdd">导入计划</ComButton>
</template>
</ComTable>