增加月计划填报 日计划填报页面

This commit is contained in:
BianLzhaoMin 2025-12-18 17:38:03 +08:00
parent 00fc500e81
commit 83334bc090
7 changed files with 689 additions and 27 deletions

View File

@ -0,0 +1,143 @@
<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-row>
</el-form>
<el-form-item prop="keyword" class="keyword-item">
<el-input
placeholder="输入关键字"
clearable
v-model.trim="formData.keyword"
@keyup.enter="onSearch"
style="width: 240px"
>
<template #suffix>
<el-icon @click="onSearch" style="cursor: pointer">
<Search />
</el-icon>
</template>
</el-input>
</el-form-item>
<!-- 任务列表 -->
<el-table
border
:data="tableData"
v-loading="loading"
@selection-change="onSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" prop="projectName" label="项目名称" min-width="260" />
<el-table-column align="center" 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,82 @@
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: 'input',
prop: 'keyword',
placeholder: '请输入关键字',
},
{
type: 'input',
prop: 'keyword',
placeholder: '请输入项目名称',
},
{
type: 'input',
prop: 'keyword',
placeholder: '请输入工作任务',
},
{
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 const dialogConfig = reactive({
outerVisible: false,
outerTitle: '新增计划',
outerWidth: '70%', // 根据图片缩小宽度更美观
minHeight: '70vh',
maxHeight: '90vh',
})
export default {
tableColumns,
dialogConfig,
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,10 +1,112 @@
<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>
<AddForm ref="addFormRef" />
<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="DailyPlan"></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'
import AddForm from './addForm.vue'
<style></style>
const router = useRouter()
const { proxy } = getCurrentInstance()
const { tableColumns, dialogConfig, buildFormColumns } = config
const comTableRef = ref(null)
const addFormRef = 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 = () => {
dialogConfig.outerVisible = true
}
//
const onHandleCancel = () => {
dialogConfig.outerVisible = false
}
//
const onHandleSave = () => {
addFormRef.value.validate()
}
//
const onCloseDialogOuter = (visible) => {
dialogConfig.outerVisible = visible
}
</script>
<style scoped></style>

View File

@ -20,36 +20,34 @@
/>
</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-form-item prop="keyword" class="keyword-item">
<el-input
placeholder="输入关键字"
clearable
v-model.trim="formData.keyword"
@keyup.enter="onSearch"
style="width: 240px"
>
<template #suffix>
<el-icon @click="onSearch" style="cursor: pointer">
<Search />
</el-icon>
</template>
</el-input>
</el-form-item>
<!-- 任务列表 -->
<el-table
v-loading="loading"
:data="tableData"
border
height="480"
:data="tableData"
v-loading="loading"
@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-column align="center" prop="projectName" label="项目名称" min-width="260" />
<el-table-column align="center" prop="workTask" label="工作任务" min-width="360" />
</el-table>
</div>
</template>

View File

@ -74,7 +74,16 @@ export const tableColumns = [
{ prop: 'planStartDate', label: '计划开始时间' },
]
export const dialogConfig = reactive({
outerVisible: false,
outerTitle: '新增计划',
outerWidth: '70%', // 根据图片缩小宽度更美观
minHeight: '70vh',
maxHeight: '90vh',
})
export default {
tableColumns,
dialogConfig,
buildFormColumns,
}

View File

@ -17,6 +17,7 @@
<ComDialog :dialog-config="dialogConfig" @closeDialogOuter="onCloseDialogOuter">
<template #outerContent>
<AddForm ref="addFormRef" />
<el-row class="common-btn-row">
<ComButton plain type="info" @click="onHandleCancel">取消</ComButton>
<ComButton @click="onHandleSave">保存</ComButton>
@ -34,13 +35,15 @@ 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'
import AddForm from './addForm.vue'
const router = useRouter()
const { proxy } = getCurrentInstance()
const { tableColumns, buildFormColumns } = config
const { tableColumns, dialogConfig, buildFormColumns } = config
const comTableRef = ref(null)
const addFormRef = ref(null)
// 使
const formColumns = computed(() => buildFormColumns())
@ -86,7 +89,24 @@ const actionColumns = [
]
//
const onHandleAdd = () => {}
const onHandleAdd = () => {
dialogConfig.outerVisible = true
}
//
const onHandleCancel = () => {
dialogConfig.outerVisible = false
}
//
const onHandleSave = () => {
addFormRef.value.validate()
}
//
const onCloseDialogOuter = (visible) => {
dialogConfig.outerVisible = visible
}
</script>
<style scoped></style>