feat: 新增后勤模块功能及页面

新增后勤模块相关页面和功能,包括:
1. 项目总览查询页面及详情弹窗组件
2. 风险管控卡配置页面及详情页
3. 账户管理页面及用户弹窗
4. 个人中心页面
5. 新增相关图标资源
6. 更新路由配置
7. 修复签发人管理页面查询表单字段
This commit is contained in:
liangbin 2026-01-13 16:20:35 +08:00
parent 8ee7c82eee
commit 8643d4174f
19 changed files with 2222 additions and 246 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

View File

@ -150,7 +150,7 @@ export const constantRoutes = [
name: "FinishedProject",
meta: { title: "项目完工复核管理", icon: "table" },
},
{
path: "/Tenement/FinishedProject/particulars",
component: () =>
@ -179,7 +179,7 @@ export const constantRoutes = [
name: "PropertyPermission",
meta: { title: "权限配置", icon: "table" },
},
]
],
},
{
@ -194,11 +194,10 @@ export const constantRoutes = [
name: "PropertyPersonalCenter",
meta: { title: "个人中心(物业)", icon: "table" },
},
]
],
},
// 物业模块END
// 业主模块START
{
path: "",
@ -212,7 +211,7 @@ export const constantRoutes = [
name: "SignerManagement",
meta: { title: "签发人管理", icon: "table" },
},
]
],
},
{
path: "",
@ -226,7 +225,7 @@ export const constantRoutes = [
name: "TicketIssueAudit",
meta: { title: "票证签发审核", icon: "table" },
},
]
],
},
{
@ -241,7 +240,7 @@ export const constantRoutes = [
name: "EntryExitPermitIssue",
meta: { title: "出入证签发", icon: "table" },
},
]
],
},
{
@ -251,12 +250,11 @@ export const constantRoutes = [
children: [
{
path: "/proprietor/ProjectOverview",
component: () =>
import("@/views/proprietor/ProjectOverview/index.vue"),
component: () => import("@/views/proprietor/ProjectOverview/index.vue"),
name: "ProjectOverview",
meta: { title: "项目总览", icon: "table" },
},
]
],
},
{
@ -271,7 +269,7 @@ export const constantRoutes = [
name: "OwnerPersonalCenter",
meta: { title: "个人中心(业主)", icon: "table" },
},
]
],
},
// 业主模块END
@ -289,7 +287,7 @@ export const constantRoutes = [
name: "TreeRatingManagement",
meta: { title: "树形层级管理", icon: "table" },
},
]
],
},
{
@ -299,15 +297,76 @@ export const constantRoutes = [
children: [
{
path: "/logistics/FilingReview",
component: () =>
import("@/views/Logistics/FilingReview/index.vue"),
component: () => import("@/views/Logistics/FilingReview/index.vue"),
name: "FilingReview",
meta: { title: "建档审核中心", icon: "table" },
},
]
],
},
{
path: "",
component: Layout,
redirect: "/index",
children: [
{
path: "/logistics/UserManagement",
component: () => import("@/views/Logistics/UserManagement/index.vue"),
name: "UserManagement",
meta: { title: "账户管理", icon: "table" },
},
],
},
{
path: "",
component: Layout,
redirect: "/index",
children: [
{
path: "/logistics/RiskManagement",
component: () => import("@/views/Logistics/RiskManagement/index.vue"),
name: "RiskManagement",
meta: { title: "风险管控卡配置", icon: "table" },
},
{
path: "/logistics/RiskManagement/Details",
component: () =>
import("@/views/Logistics/RiskManagement/Details/index.vue"),
name: "RiskManagementDetails",
hidden: true,
meta: { title: "风险管控卡配置详情", icon: "table" },
},
],
},
{
path: "",
component: Layout,
redirect: "/index",
children: [
{
path: "/logistics/ProjectOverviewQuery",
component: () =>
import("@/views/Logistics/ProjectOverviewQuery/index.vue"),
name: "ProjectOverviewQuery",
meta: { title: "项目总览查询", icon: "table" },
},
],
},
{
path: "",
component: Layout,
redirect: "/index",
children: [
{
path: "/logistics/logisticsPersonalCenter",
component: () =>
import("@/views/Logistics/logisticsPersonalCenter/index.vue"),
name: " logisticsPersonalCenter",
meta: { title: "个人中心(后勤)", icon: "table" },
},
],
},
// 后勤模块END
// 后勤模块END
{
path: "/user",

View File

@ -0,0 +1,129 @@
<!-- 后勤模块- 节点审核弹窗 -->
<template>
<el-dialog title="审核节点信息" v-model="props.show" @close="handleClose" width="600px">
<div class="Cneter-box">
<el-form :model="formData" label-width="120px" class="review-form" label-position="top">
<div class="section-title">基础信息</div>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="节点名称">
<el-input v-model="formData.nodeName" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="节点类型">
<el-tag type="primary" size="large">部门</el-tag>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属上级">
<el-input v-model="formData.parentNode" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="创建时间">
<el-input v-model="formData.createTime" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="提交人">
<el-input v-model="formData.submitter" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式">
<el-input v-model="formData.contact" disabled />
</el-form-item>
</el-col>
</el-row>
<div class="section-title">审核操作</div>
<el-form-item label="审核结果">
<el-radio-group v-model="formData.reviewResult">
<el-radio label="通过">通过</el-radio>
<el-radio label="退回">退回</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审核意见">
<el-input
v-model="formData.reviewComment"
type="textarea"
:rows="4"
placeholder="请输入审核意见 (退回时必填)"
/>
</el-form-item>
</el-form>
</div>
<!-- 底部按钮 -->
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">确认审核</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
show: {
type: Boolean,
default: false
},
CloseDialog: {
type: Function,
default: () => { }
}
})
//
const handleClose = () => {
props.CloseDialog()
}
//
const formData = ref({
nodeName: '后勤保障部 - 设备科',
parentNode: '后勤保障部',
submitter: '李建国',
contact: '138****8888',
createTime: '2023-10-15 14:25',
reviewResult: '通过',
reviewComment: ''
})
const handleSubmit = () => {
//
console.log('提交分配信息', formData.value)
props.CloseDialog()
}
</script>
<style scoped lang="scss">
.TipsBox{
font-size: 12px;
color: #999;
margin-top: 10px;
}
.review-form {
max-width: 800px;
}
.section-title {
font-size: 16px;
font-weight: 600;
margin: 20px 0 15px 0;
color: #303133;
border-bottom: 1px solid #e6e6e6;
padding-bottom: 5px;
}
</style>

View File

@ -2,219 +2,171 @@
<template>
<div class="MainBox">
<el-row :gutter="20">
<!-- 左侧树形结构 -->
<el-col :span="8">
<div class="CardBox">
<div class="LeftTitle">组织架构树</div>
<div class="TreeBox">
<el-tree :data="treeData" :props="treeProps" show-line default-expand-all highlight-current
@current-change="handleTreeChange" />
</div>
<div class="tree-actions">
<el-button type="primary" icon="Plus">新增节点</el-button>
<el-button icon="Edit">编辑节点</el-button>
<el-button icon="Delete">删除节点</el-button>
</div>
</div>
<el-form :model="queryForm" inline class="card-box mb-4">
<el-form-item label="节点名称">
<el-input v-model="queryForm.nodeName" placeholder="请输入节点名称"></el-input>
</el-form-item>
<el-form-item label="节点类型">
<el-select v-model="queryForm.nodeType" placeholder="请选择节点类型" clearable>
<el-option v-for="item in nodeTypeList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="提交时间">
<el-date-picker v-model="queryForm.dateRange" type="daterange" value-format="yyyy-MM-dd"
range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
</el-col>
<el-form-item>
<el-button size="default">重置</el-button>
<el-button type="primary" size="default">查询</el-button>
<el-button type="primary" plain size="default">导出Excel</el-button>
<el-button type="primary" plain size="default">打印</el-button>
</el-form-item>
</el-form>
<!-- 右侧节点详情 -->
<el-col :span="16">
<el-card class="detail-card" shadow="hover">
<template #header>
<span>节点详情</span>
<div class="card-box">
<el-table :data="tableData" class="mt-2">
<el-table-column prop="userName" label="节点名称"></el-table-column>
<el-table-column prop="departmentName" label="节点类型"></el-table-column>
<el-table-column prop="roleName" label="所属上级"></el-table-column>
<el-table-column prop="projectScope" label="提交人"></el-table-column>
<el-table-column prop="phone" label="提交时间"></el-table-column>
<el-table-column prop="permissionDesc" label="权限说明"></el-table-column>
<el-table-column prop="reviewStatus" label="审核状态">
<template #default="scope">
<el-tag :type="scope.row.reviewStatus === '待审核' ? 'primary' : 'danger'">
{{ scope.row.reviewStatus === '待审核' ? '待审核' : '拒绝' }}
</el-tag>
</template>
<el-form :model="formData" label-width="120px" label-position="top">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="节点名称">
<el-input v-model="formData.nodeName" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="节点编码">
<el-input v-model="formData.nodeCode" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="上级节点">
<el-input v-model="formData.parentNode" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="创建时间">
<el-input v-model="formData.createTime" disabled />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-tag :type="scope.row.status === '1' ? 'success' : 'danger'">
{{ scope.row.status === '1' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleEdit(scope.row)">审核</el-button>
<el-button size="small" type="primary" link v-if="scope.row.status === '2'">启用</el-button>
<el-button size="small" type="primary" link v-else>禁用</el-button>
</template>
</el-table-column>
</el-table>
<!-- 关联账户 -->
<el-card class="account-card" shadow="hover" style="margin-top: 20px;">
<template #header>
<span>关联账户</span>
</template>
<el-table :data="accountList" border>
<el-table-column prop="accountName" label="账户名" />
<el-table-column prop="realName" label="姓名" />
<el-table-column prop="unit" label="所属单位" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="getStatusTagType(row.status)">{{ row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
<el-pagination layout="prev, pager, next" :total="25" :page-size="10" :current-page="1"
class="float-right" />
</el-card>
</el-col>
</el-row>
<!-- 账户列表 -->
<el-row :gutter="20">
<el-col :span="24">
<el-card class="account-card" shadow="hover" style="margin-top: 20px;">
<template #header>
<span>账户列表</span>
<el-button type="primary" icon="Plus" class="float-right" size="mini">新建账户</el-button>
</template>
<el-table :data="accountList" border>
<el-table-column prop="accountName" label="账户名" />
<el-table-column prop="realName" label="姓名" />
<el-table-column prop="unit" label="所属单位" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="getStatusTagType(row.status)">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button type="text" icon="Edit">编辑</el-button>
<el-button type="text" icon="Delete">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination layout="prev, pager, next" :total="25" :page-size="10" :current-page="1"
class="float-right" />
</el-card>
</el-col>
</el-row>
<div class="pagination-container">
<span class="total-count">显示第 {{ (pageNum - 1) * pageSize + 1 }} {{ pageNum * pageSize }} 条记录 {{
total }}
</span>
<el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[5, 10, 20]"
:total="total" layout="prev, pager, next"></el-pagination>
</div>
</div>
<!-- 节点审核弹窗 -->
<DialogBox v-if="dialogShow" ref="dialogRef" :show="dialogShow" :CloseDialog="handleCancel" />
</div>
</template>
<script setup>
import { ref } from 'vue'
<script setup name="Index">
import { ref } from "vue";
import DialogBox from "./DialogBox.vue";
//
const queryForm = ref({
dateRange: [],
nodeName: "",
nodeType: ""
});
//
const nodeTypeList = ref([
//
const treeData = ref([
{
label: '江苏省电力公司',
children: [
{
label: '南京市供电公司',
children: [
{
label: '城东片区',
children: [
{ label: 'A办公楼' },
{ label: 'B办公楼' }
]
},
{
label: '城西片区',
children: [
{ label: 'C办公楼' }
]
}
]
}
]
}
])
//
const treeProps = {
children: 'children',
label: 'label'
}
//
const formData = ref({
nodeName: '江苏省电力公司',
nodeCode: 'JS001',
parentNode: '-',
createTime: '2023-05-15'
})
//
const relatedAccounts = ref([
{
accountName: 'NJGD_ZH',
realName: '赵华',
phone: '138****1234',
role: '项目经理',
status: '已生效'
label: "部门",
value: "1"
},
{
accountName: 'NJGD_LW',
realName: '李伟',
phone: '139****5678',
role: '审核人',
status: '待审核'
}
])
//
const accountList = ref([
{
accountName: 'NJGD_ZH',
realName: '赵华',
unit: '南京市供电公司 - 城东片区 - A办公楼',
phone: '138****1234',
role: '项目经理',
status: '已生效'
label: "楼层",
value: "2"
},
{
accountName: 'NJGD_LW',
realName: '李伟',
unit: '南京市供电公司 - 城西片区 - C办公楼',
phone: '139****5678',
role: '审核人',
status: '待审核'
label: "班组",
value: "3"
},
]);
//
const tableData = ref([
{
id: 1,
needBallheadNum: "1001",
projectName: "项目1",
returnStatus: "待审核",
returnUserName: "用户1",
submitEndTime: "2023-08-01 10:00:00",
userName: "张三",
departmentName: "工程部",
roleName: "工作票签发人",
projectScope: "项目1",
phone: "13800000000",
permissionDesc: "对项目1的工作票进行签发",
status: "1"
},
{
accountName: 'SZDL_WX',
realName: '王霞',
unit: '苏州市供电公司 - 城南片区 - B办公楼',
phone: '137****9876',
role: '物业管理员',
status: '已禁用'
}
])
id: 2,
needBallheadNum: "1002",
projectName: "项目2",
returnStatus: "待审核",
returnUserName: "用户2",
submitEndTime: "2023-08-02 10:00:00",
userName: "李四",
departmentName: "安全监督部",
roleName: "危险作业票签发人",
projectScope: "项目2",
phone: "13900000000",
permissionDesc: "对项目2的危险作业票进行签发",
status: "2"
},
//
const handleTreeChange = (data) => {
console.log('Current tree node:', data)
]);
const dialogShow = ref(false); // -
const pageNum = ref(1); //
const pageSize = ref(5); //
const total = ref(20); //
//
const handleAdd = (row) => {
dialogShow.value = true
}
//
const getStatusTagType = (status) => {
if (status === '已生效') return 'success'
if (status === '待审核') return 'warning'
if (status === '已禁用') return 'danger'
return 'info'
//
const handleEdit = (row) => {
dialogShow.value = true
}
//
const handleDetail = (row) => {
dialogShow.value = true
}
//
const handleCancel = () => {
console.log('关闭弹窗')
dialogShow.value = false
}
</script>
<style scoped lang="scss">
.MainBox {
padding: 20px;
@ -222,46 +174,41 @@ const getStatusTagType = (status) => {
background: #F9FAFB;
}
.CardBox {
height: 570px;
display: flex;
padding: 0px 26px;
background: #fff;
overflow: auto;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
flex-direction: column;
border-radius: 8px;
.LeftTitle {
font-size: 16px;
color: #303133;
line-height: 40px;
border-bottom: 1px solid #E4E7ED;
}
.TreeBox {
margin-top: 20px;
border: 1px solid #E4E7ED;
}
}
.tree-actions {
margin-top: 20px;
.FlexBox {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 10px;
}
.detail-card {
margin-bottom: 20px;
.TitleBox {
font-size: 16px;
font-weight: bold;
color: #303133;
}
.account-card {
position: relative;
.mt-2 {
margin-top: 20px;
}
.float-right {
position: absolute;
right: 20px;
top: 10px;
.card-box {
background: #fff;
padding: 16px;
border-radius: 4px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
</style>
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
}
.total-count {
color: #606266;
}
</style>

View File

@ -0,0 +1,92 @@
<!-- 后勤模块- 项目全流程查询- 详情弹窗- 审批记录 -->
<template>
<div class="MainBox">
<el-table :data="tableData" stripe>
<el-table-column prop="name" label="审批节点" width="200" />
<el-table-column prop="approver" label="审批人" width="120" />
<el-table-column prop="time" label="审批时间" width="180" />
<el-table-column prop="remark" label="审批意见" min-width="200" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === '已通过' ? 'success' : 'danger'">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
</el-table>
<div class="pageBox">
<el-pagination :current-page="pageNum" :page-size="pageSize" :total="total"
layout="total, prev, pager, next" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const tableData = ref([
{
key: 1,
name: '项目立项审批',
time: '2023-01-01 10:00:00',
status: '已通过',
remark: '同意立项,按计划执行',
approver: '张三'
},
{
key: 2,
name: '预算审批',
time: '2023-01-02 14:30:00',
status: '已拒绝',
remark: '预算超标,需重新调整预算方案',
approver: '李四'
},
{
key: 3,
name: '施工方案审批',
time: '2023-01-03 09:15:00',
status: '已通过',
remark: '施工方案合理,同意实施',
approver: '王五'
},
{
key: 4,
name: '安全检查审批',
time: '2023-01-04 15:20:00',
status: '已通过',
remark: '安全措施到位,符合要求',
approver: '赵六'
},
{
key: 5,
name: '竣工验收审批',
time: '2023-01-05 11:45:00',
status: '已通过',
remark: '项目验收合格,交付使用',
approver: '孙七'
}
])
const pageNum = ref(1) //
const pageSize = ref(5) //
const total = ref(10) //
//
const handleCurrentChange = (val) => {
pageNum.value = val
}
</script>
<style scoped lang="scss">
.MainBox {
width: 100%;
height: 100%;
}
.pageBox {
margin-top: 10px;
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -0,0 +1,100 @@
<!-- 后勤模块- 项目全流程查询- 详情弹窗- 附件列表 -->
<template>
<div class="MainBox">
<div class="CardBox">
<div class="CarrBoxItem" v-for="item in dataSource" :key="item.id">
<img v-if="item.type === 'pdf'" src="@/assets/icons/pdf_icon.png" alt="">
<img v-else-if="item.type === 'excel'" src="@/assets/icons/excel_icon.png" alt="">
<img v-else-if="item.type === 'img'" src="@/assets/icons/img_icon.png" alt="">
<div class="ItemName">{{ item.name }}</div>
<div class="ItemSize">{{ item.size }}MB</div>
<div class="BtnList">
<el-button type="primary" plain size="small">预览</el-button>
<el-button type="primary" plain size="small">下载</el-button>
</div>
</div>
</div>
<div class="pageBox">
<el-pagination :current-page="pageNum" :page-size="pageSize" :total="total"
layout="total, prev, pager, next" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const dataSource = ref([
{
id: 1,
name: '工作计划书.pdf',
type: 'pdf',
size: '1.1',
FileUrl: '',
},
{
id: 1,
name: '工作计划书.xlsx',
type: 'excel',
size: '23',
FileUrl: '',
},
{
id: 1,
name: '工作计划书.xlsx',
type: 'img',
size: '23',
FileUrl: '',
},
])
const pageNum = ref(1) //
const pageSize = ref(5) //
const total = ref(10) //
//
const handleCurrentChange = (val) => {
pageNum.value = val
}
</script>
<style scoped lang="scss">
.MainBox {
width: 100%;
height: 100%;
}
.CardBox {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 20px;
}
.CarrBoxItem {
width: 200px;
height: 160px;
background-color: #fff;
border-radius: 5px;
border: 1px solid #eee;
padding: 10px;
gap: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.pageBox {
margin-top: 10px;
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -0,0 +1,67 @@
<!-- 后勤模块- 项目全流程查询- 详情弹窗- 操作日志 -->
<template>
<div class="MainBox">
<el-table :data="tableData" stripe>
<el-table-column prop="operationTime" label="操作时间" />
<el-table-column prop="operationUser" label="操作人" />
<el-table-column prop="operationType" label="操作类型" />
<el-table-column prop="operationDetail" label="操作详情" />
</el-table>
<div class="pageBox">
<el-pagination :current-page="pageNum" :page-size="pageSize" :total="total"
layout="total, prev, pager, next" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const tableData = ref([
{
id: 1,
operationTime: '2023-08-01 10:00:00',
operationUser: '张三',
operationType: '项目风险审批',
operationDetail: '项目风险审批通过'
},
{
id: 2,
operationTime: '2023-08-02 15:30:00',
operationUser: '李四',
operationType: '项目风险审批',
operationDetail: '项目风险审批拒绝'
},
{
id: 3,
operationTime: '2023-08-03 09:15:00',
operationUser: '王五',
operationType: '项目风险审批',
operationDetail: '项目风险审批通过'
},
])
const pageNum = ref(1) //
const pageSize = ref(5) //
const total = ref(10) //
//
const handleCurrentChange = (val) => {
pageNum.value = val
}
</script>
<style scoped lang="scss">
.MainBox {
width: 100%;
height: 100%;
}
.pageBox {
margin-top: 10px;
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -0,0 +1,48 @@
<!-- 后勤模块- 项目全流程查询- 详情弹窗- 总览 -->
<template>
<div class="MainBox">
<el-descriptions :column="3" border size="small">
<el-descriptions-item label="项目编号">PRJ202304001</el-descriptions-item>
<el-descriptions-item label="项目名称">行政楼电力改造工程</el-descriptions-item>
<el-descriptions-item label="所属单位">后勤保障部</el-descriptions-item>
<el-descriptions-item label="负责人">李建国</el-descriptions-item>
<el-descriptions-item label="联系电话">138****8888</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag type="success">已生效</el-tag>
</el-descriptions-item>
<el-descriptions-item label="创建时间">2023-04-15 09:30:00</el-descriptions-item>
<el-descriptions-item label="预计完成时间">2023-06-30</el-descriptions-item>
<el-descriptions-item label="实际完成时间">-</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<div class="description-section">
<h3>项目描述</h3>
<p>本项目旨在对行政楼进行全面的电力系统改造包括更换老旧配电箱升级电缆线路安装智能电表等以提高用电安全性与能效管理水平项目覆盖行政楼A座1-5涉及办公室会议室公共区域等共计约2000平方米
</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
</script>
<style scoped lang="scss">
.MainBox {
width: 100%;
height: 100%;
}
.description-section h3 {
margin-top: 0px;
margin-bottom: 10px;
font-size: 16px;
font-weight: bold;
}
description-section p {
line-height: 1.6;
color: #666;
}
</style>

View File

@ -0,0 +1,69 @@
<!-- 后勤模块- 项目全流程查询- 详情弹窗- 风险管控明细 -->
<template>
<div class="MainBox">
<el-table :data="tableData" stripe>
<el-table-column prop="name" label="风险管控项" />
<el-table-column prop="type" label="类型" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === '已通过' ? 'success' : 'danger'">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="feedback" label="AI/监理反馈" />
<el-table-column prop="rectification" label="整改记录" />
</el-table>
<div class="pageBox">
<el-pagination :current-page="pageNum" :page-size="pageSize" :total="total"
layout="total, prev, pager, next" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const tableData = ref([
{
key: 1,
name: '项目立项审批',
type: '项目风险',
status: '已通过',
feedback: 'AI/监理反馈内容',
rectification: '整改记录内容'
},
{
key: 2,
name: '项目风险审批',
type: '项目风险',
status: '已拒绝',
feedback: 'AI/监理反馈内容',
rectification: '整改记录内容'
},
])
const pageNum = ref(1) //
const pageSize = ref(5) //
const total = ref(10) //
//
const handleCurrentChange = (val) => {
pageNum.value = val
}
</script>
<style scoped lang="scss">
.MainBox {
width: 100%;
height: 100%;
}
.pageBox {
margin-top: 10px;
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -0,0 +1,70 @@
<!-- 后勤模块- 项目全流程查询- 详情弹窗 -->
<template>
<el-dialog title="全流程详情" v-model="props.show" @close="handleClose" width="900px">
<div>
<el-tabs v-model="activeTab" class="demo-tabs">
<el-tab-pane label="总览" name="overview"></el-tab-pane>
<el-tab-pane label="审批记录" name="approval"></el-tab-pane>
<el-tab-pane label="风险管控明细" name="risk"></el-tab-pane>
<el-tab-pane label="附件档案" name="attachment"></el-tab-pane>
<el-tab-pane label="操作日志" name="operation"></el-tab-pane>
</el-tabs>
</div>
<div class="CenterBox">
<Overview v-if="activeTab === 'overview'"/>
<Approval v-if="activeTab === 'approval'"/>
<Risk v-if="activeTab === 'risk'"/>
<Attachment v-if="activeTab === 'attachment'"/>
<Operation v-if="activeTab === 'operation'"/>
</div>
<!-- 底部按钮 -->
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" plain @click="handlePrint">打印详情</el-button>
<el-button type="primary" @click="handleExport">导出详情</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
import Overview from './Overview.vue'
import Approval from './Approval.vue'
import Risk from './Risk.vue'
import Attachment from './Attachment.vue'
import Operation from './Operation.vue'
const props = defineProps({
show: {
type: Boolean,
default: false
},
CloseDialog: {
type: Function,
default: () => { }
}
})
const activeTab = ref('overview') // tabs
//
const handleClose = () => {
props.CloseDialog()
}
//
const handlePrint = () => { }
//
const handleExport = () => { }
</script>
<style scoped lang="scss">
.CenterBox {
padding: 10px;
width: 100%;
max-height: 400px;
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,335 @@
<!-- 后勤模块 - 项目总览查询 - 列表 -->
<template>
<div class="MainBox">
<el-form :model="queryForm" class="card-box mb-4" label-position="top">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="项目名称/编号">
<el-input v-model="queryForm.projectName" placeholder="请输入项目名称/编号"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所属层级节点">
<el-select v-model="queryForm.nodeType" placeholder="请选择节点类型" clearable>
<el-option v-for="item in nodeTypeList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="施工单位">
<el-input v-model="queryForm.constructionUnit" placeholder="请输入施工单位"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="监理单位">
<el-input v-model="queryForm.supervisionUnit" placeholder="请输入监理单位"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="作业类型">
<el-select v-model="queryForm.workType" placeholder="请选择作业类型" clearable>
<el-option v-for="item in workTypeList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="风险类型">
<el-select v-model="queryForm.riskType" placeholder="请选择风险类型" clearable>
<el-option v-for="item in riskTypeList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目状态">
<el-select v-model="queryForm.projectStatus" placeholder="请选择项目状态" clearable>
<el-option v-for="item in projectStatusList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="作业时间">
<el-date-picker v-model="queryForm.dateRange" type="daterange" value-format="yyyy-MM-dd"
range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<el-button size="default">重置</el-button>
<el-button type="primary" size="default">查询</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="card-box">
<el-table :data="tableData" class="mt-2">
<el-table-column prop="projectNo" label="项目编号" fixed="left" width="200"></el-table-column>
<el-table-column prop="projectName" label="项目名称" fixed="left" width="200"></el-table-column>
<el-table-column prop="nodeName" label="所属节点" width="200"></el-table-column>
<el-table-column prop="constructionUnit" label="单位施工" width="200"></el-table-column>
<el-table-column prop="supervisionUnit" label="监理单位" width="200"></el-table-column>
<el-table-column prop="operationLocation" label="作业地点" width="200"></el-table-column>
<el-table-column prop="operationTime" label="作业起止时间" width="200"></el-table-column>
<el-table-column prop="workPlanStatus" label="工作计划审批状态" width="200"></el-table-column>
<el-table-column prop="entrancePermitStatus" label="出入证申请状态" width="200"></el-table-column>
<el-table-column prop="workTicketStatus" label="工作票状态" width="200"></el-table-column>
<el-table-column prop="highRiskWorkTicketStatus" label="高风险工作票状态" width="200"></el-table-column>
<el-table-column prop="allotStatus" label="球机分配状态" width="200"></el-table-column>
<el-table-column prop="ReviewStatusA" label="监理复核状态" width="200"></el-table-column>
<el-table-column prop="ReviewStatusB" label="物业复核状态" width="200"></el-table-column>
<el-table-column prop="TemplateType" label="风险卡模版类型" width="200"></el-table-column>
<el-table-column prop="progress" label="必选项完成度" width="200">
<template #default="scope">
<el-progress :percentage="scope.row.progress"
:format="(percentage) => `${percentage}%`"></el-progress>
</template>
</el-table-column>
<el-table-column prop="problemNumAI" label="AI反馈问题数" width="200"></el-table-column>
<el-table-column prop="problemNumSupervision" label="监理反馈问题数" width="200"></el-table-column>
<el-table-column prop="rectifyNum" label="整改完成数" width="200"></el-table-column>
<el-table-column prop="permissionDesc" label="整改通过率" width="200"></el-table-column>
<el-table-column prop="projectStatus" label="项目状态" width="200">
<template #default="scope">
<el-tag :type="scope.row.projectStatus === '已完成' ? 'success' : 'danger'">{{
scope.row.projectStatus }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleDetailClick(scope.row)">查看详情</el-button>
<el-button size="small" type="primary" link @click="handleExportClick(scope.row)">导出档案</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<span class="total-count">显示第 {{ (pageNum - 1) * pageSize + 1 }} {{ pageNum * pageSize }} 条记录 {{
total }}</span>
<el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[5, 10, 20]"
:total="total" layout="prev, pager, next"></el-pagination>
</div>
</div>
<!-- 节点审核弹窗 -->
<DialogBox v-if="dialogShow" ref="dialogRef" :show="dialogShow" :CloseDialog="handleCancel" />
</div>
</template>
<script setup name="Index">
import { ref } from "vue";
import DialogBox from "./DialogBox/index.vue";
//
const queryForm = ref({
projectName: "",
nodeType: "",
constructionUnit: "",
dateRange: [],
});
//
const workTypeList = ref([
{
label: "正常作业",
value: "1"
},
{
label: "异常作业",
value: "2"
},
]);
//
const riskTypeList = ref([
{
label: "高风险",
value: "1"
},
{
label: "中风险",
value: "2"
},
{
label: "低风险",
value: "3"
},
]);
//
const projectStatusList = ref([
{
label: "实施中",
value: "1"
},
{
label: "待审批",
value: "2"
},
{
label: "已完成",
value: "3"
},
]);
//
const nodeTypeList = ref([
{
label: "部门",
value: "1"
},
{
label: "楼层",
value: "2"
},
{
label: "班组",
value: "3"
},
]);
//
const tableData = ref([
{
id: 1,
projectNo: "PM20240610001",
projectName: "XX输变电工程",
nodeName: "城东片区",
constructionUnit: "XX建设有限公司",
supervisionUnit: "XX监理有限公司",
operationLocation: "XX市XX区",
operationTime: "2024-06-10 09:00至2024-06-15 17:00",
workPlanStatus: "已审批",
entrancePermitStatus: "已申请",
workTicketStatus: "已签发",
highRiskWorkTicketStatus: "已签发",
allotStatus: "已分配",
ReviewStatusA: "已复核",
ReviewStatusB: "已复核",
TemplateType: "高空作业",
progress: 75,
problemNumAI: 2,
problemNumSupervision: 1,
rectifyNum: 3,
permissionDesc: "100%",
projectStatus: "已完成"
},
{
id: 2,
projectNo: "PM20240609001",
projectName: "XX变电站工程",
nodeName: "城西片区",
constructionUnit: "XX建设有限公司",
supervisionUnit: "XX监理有限公司",
operationLocation: "XX市XX区",
operationTime: "2024-06-09 10:00至2024-06-14 18:00",
workPlanStatus: "已审批",
entrancePermitStatus: "已申请",
workTicketStatus: "已签发",
highRiskWorkTicketStatus: "已签发",
allotStatus: "已分配",
ReviewStatusA: "已复核",
ReviewStatusB: "已复核",
TemplateType: "高空作业",
progress: 80,
problemNumAI: 1,
problemNumSupervision: 0,
rectifyNum: 1,
permissionDesc: "100%",
projectStatus: "已完成"
},
{
id: 3,
projectNo: "PM20240608001",
projectName: "XX线路工程",
nodeName: "城南片区",
constructionUnit: "XX建设有限公司",
supervisionUnit: "XX监理有限公司",
operationLocation: "XX市XX区",
operationTime: "2024-06-08 08:00至2024-06-13 16:00",
workPlanStatus: "已审批",
entrancePermitStatus: "已申请",
workTicketStatus: "已签发",
highRiskWorkTicketStatus: "已签发",
allotStatus: "已分配",
ReviewStatusA: "已复核",
ReviewStatusB: "已复核",
TemplateType: "高空作业",
progress: 90,
problemNumAI: 0,
problemNumSupervision: 0,
rectifyNum: 0,
permissionDesc: "100%",
projectStatus: "已完成"
}
]);
const dialogShow = ref(false); // -
const pageNum = ref(1); //
const pageSize = ref(5); //
const total = ref(20); //
//
const handleDetailClick = (row) => {
dialogShow.value = true
}
//
const handleCancel = () => {
console.log('关闭弹窗')
dialogShow.value = false
}
</script>
<style scoped lang="scss">
.MainBox {
padding: 20px;
height: 100%;
background: #F9FAFB;
}
.FlexBox {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 10px;
}
.TitleBox {
font-size: 16px;
font-weight: bold;
color: #303133;
}
.mt-2 {
margin-top: 20px;
}
.card-box {
background: #fff;
padding: 16px;
border-radius: 4px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
}
.total-count {
color: #606266;
}
</style>

View File

@ -0,0 +1,217 @@
<!-- 后勤模块-风险管控卡详情 -->
<template>
<div class="MainBox">
<!-- 模板基础信息 -->
<el-card class="info-card" shadow="hover">
<template #header>
<span>模板基础信息</span>
</template>
<el-descriptions :column="3" border>
<el-descriptions-item label="模板名称">
高处危险作业风险管控卡
</el-descriptions-item>
<el-descriptions-item label="风险类型">
高空作业
</el-descriptions-item>
<el-descriptions-item label="备注">
适用于建筑施工中高度超过2米的作业场景
</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 检查项配置 -->
<el-card class="config-card" shadow="hover" style="margin-top: 20px;">
<template #header>
<span>检查项配置</span>
</template>
<el-tabs v-model="activeTab" >
<el-tab-pane label="必选项" name="required">
<div class="tab-content">
<p class="tab-description">以下检查项为红头文件规定必选项不可删除仅可编辑描述:</p>
<el-list>
<el-list-item v-for="(item, index) in requiredItems" :key="index" class="list-item">
<div class="item-content">
<span class="item-number">{{ index + 1 }}</span>
<el-input v-model="item.content" class="item-input" />
<el-button type="primary" size="small">人工检查</el-button>
<div class="item-actions">
<el-button type="text" icon="ArrowUp" @click="moveItemUp(index)" />
<el-button type="text" icon="ArrowDown" @click="moveItemDown(index)" />
</div>
</div>
</el-list-item>
</el-list>
</div>
</el-tab-pane>
<el-tab-pane label="可选项" name="optional">
<div class="tab-content">
<p class="tab-description">以下检查项为可选项支持新增删除和编辑:</p>
<div class="add-section">
<el-input v-model="newItemContent" placeholder="请输入新的检查项描述..." class="add-input" />
<el-button type="primary" icon="Plus" @click="addOptionalItem">+添加检查项</el-button>
</div>
<el-list>
<el-list-item v-for="(item, index) in optionalItems" :key="index" class="list-item">
<div class="item-content">
<span class="item-number">{{ index + 1 }}</span>
<el-input v-model="item.content" class="item-input" />
<el-button type="primary" size="small">人工检查</el-button>
<div class="item-actions">
<el-button type="text" icon="Close" @click="deleteOptionalItem(index)" />
<el-button type="text" icon="ArrowUp" @click="moveOptionalItemUp(index)" />
<el-button type="text" icon="ArrowDown" @click="moveOptionalItemDown(index)" />
</div>
</div>
</el-list-item>
</el-list>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
<!-- 底部操作按钮 -->
<div class="bottom-actions">
<el-button type="default" icon="ArrowLeft" @click="router.back()">返回</el-button>
<el-button type="primary" plain style="margin-left: 10px;">保存配置</el-button>
<el-button type="primary" style="margin-left: 10px;">保存并发布</el-button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const activeTab = ref('required')
const requiredItems = ref([
{ content: '从事登高架设作业人员是否持《特种作业操作证》' },
{ content: '作业人员是否无高血压/心脏病等不宜高处作业的病症,是否未饮酒' },
{ content: '安全带/梯子等登高设施是否符合安全要求' },
{ content: '遇5级及以上大风/大雪/大雾暴雨停止室外高处作业' },
{ content: '高处作业是否设置围栏/警戒线,夜间作业照明是否充足' }
])
//
const optionalItems = ref([
{ content: '施工现场是否有足够的照明设备' },
{ content: '是否设有专人监护' }
])
const newItemContent = ref('')
//
const addOptionalItem = () => {
if (newItemContent.value.trim()) {
optionalItems.value.push({ content: newItemContent.value })
newItemContent.value = ''
}
}
//
const deleteOptionalItem = (index) => {
optionalItems.value.splice(index, 1)
}
//
const moveOptionalItemUp = (index) => {
if (index > 0) {
const temp = optionalItems.value[index]
optionalItems.value[index] = optionalItems.value[index - 1]
optionalItems.value[index - 1] = temp
}
}
//
const moveOptionalItemDown = (index) => {
if (index < optionalItems.value.length - 1) {
const temp = optionalItems.value[index]
optionalItems.value[index] = optionalItems.value[index + 1]
optionalItems.value[index + 1] = temp
}
}
const moveItemUp = (index) => {
if (index > 0) {
const temp = requiredItems.value[index]
requiredItems.value[index] = requiredItems.value[index - 1]
requiredItems.value[index - 1] = temp
}
}
const moveItemDown = (index) => {
if (index < requiredItems.value.length - 1) {
const temp = requiredItems.value[index]
requiredItems.value[index] = requiredItems.value[index + 1]
requiredItems.value[index + 1] = temp
}
}
</script>
<style scoped lang="scss">
.MainBox {
padding: 20px;
height: 100%;
background: #F9FAFB;
}
.info-card {
margin-bottom: 20px;
}
.config-card {
margin-bottom: 20px;
}
.tab-content {
padding: 20px 0;
}
.tab-description {
margin-bottom: 20px;
color: #666;
}
.list-item {
margin-bottom: 15px;
border-radius: 4px;
padding: 10px;
}
.item-content {
display: flex;
align-items: center;
gap: 10px;
}
.item-number {
width: 30px;
text-align: center;
font-weight: 600;
}
.item-input {
flex: 1;
}
.item-actions {
display: flex;
gap: 5px;
}
.add-section {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.add-input {
flex: 1;
}
.bottom-actions {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,181 @@
<!-- 后勤模块 - 建档审核中心 - 单位建档 -->
<template>
<div class="MainBox">
<div class="card-box" style="display: flex; justify-content: space-between; align-items: center; ">
<div class="FlexBox">
<el-button type="primary" size="default">新增模版</el-button>
<el-button type="primary" plain size="default">批量导入检查项</el-button>
<el-button type="primary" plain size="default">导出模版</el-button>
<el-button type="primary" plain size="default">发布模版</el-button>
</div>
<div class="FlexBox">
<el-select v-model="queryForm.selectedRiskType" placeholder="请选择风险类型" style="width: 120px;">
<el-option label="全部风险" value="0"></el-option>
<el-option label="高空作业" value="1"></el-option>
<el-option label="电气安全" value="2"></el-option>
<el-option label="机械设备" value="3"></el-option>
<el-option label="特殊环境作业" value="4"></el-option>
</el-select>
<el-select v-model="queryForm.selectedRiskStatus" placeholder="请选择审核状态" style="width: 100px;">
<el-option label="全部状态" value="0"></el-option>
<el-option label="未发布" value="1"></el-option>
<el-option label="已发布" value="2"></el-option>
<el-option label="已作废" value="3"></el-option>
</el-select>
</div>
</div>
<div class="card-box">
<el-table :data="tableData" class="mt-2">
<el-table-column prop="riskTemplateName" label="模版名称"></el-table-column>
<el-table-column prop="riskTypeName" label="风险类型"></el-table-column>
<el-table-column prop="checkItemNum" label="检查项数量"></el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-tag :type="scope.row.status === '已发布' ? 'success' : 'info'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column label="操作" width="220">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="primary" link @click="handleDetail(scope.row)">查看详情</el-button>
<el-button size="small" type="success" link @click="handlePublish(scope.row)">发布</el-button>
<el-button size="small" type="danger" link @click="handleInvalidate(scope.row)">作废</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<span class="total-count">显示第 {{ (pageNum - 1) * pageSize + 1 }} {{ pageNum * pageSize }} 条记录 {{
total }}
</span>
<el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[5, 10, 20]"
:total="total" layout="prev, pager, next"></el-pagination>
</div>
</div>
</div>
</template>
<script setup name="Index">
import { ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
//
const queryForm = ref({
selectedRiskType: "0",
selectedRiskStatus: "0"
});
//
const tableData = ref([
{
id: 1,
riskTemplateName: "高空作业风险模版",
riskTypeName: "高空作业",
checkItemNum: "10",
status: "已发布",
createTime: "2023-08-01 10:00:00"
},
{
id: 2,
riskTemplateName: "电气安全风险模版",
riskTypeName: "电气安全",
checkItemNum: "15",
status: "未发布",
createTime: "2023-08-02 10:00:00"
},
]);
const pageNum = ref(1); //
const pageSize = ref(5); //
const total = ref(20); //
//
const handleEdit = (row) => {
console.log('编辑风险模版', row)
router.push({
path: "/logistics/RiskManagement/Details",
query: {
id: row.id,
operate: "edit"
}
})
}
//
const handleDetail = (row) => {
console.log('查看详情', row)
router.push({
path: "/logistics/RiskManagement/Details",
query: {
id: row.id,
operate: "detail"
}
})
}
//
const handlePublish = (row) => {
console.log('发布风险模版', row)
}
//
const handleInvalidate = (row) => {
console.log('作废风险模版', row)
}
</script>
<style scoped lang="scss">
.MainBox {
padding: 20px;
height: 100%;
background: #F9FAFB;
}
.FlexBox {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 10px;
}
.TitleBox {
font-size: 16px;
font-weight: bold;
color: #303133;
}
.mt-2 {
margin-top: 20px;
}
.card-box {
background: #fff;
padding: 16px;
border-radius: 4px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
}
.total-count {
color: #606266;
}
</style>

View File

@ -0,0 +1,166 @@
<!-- 后勤模块-账户管理-账户新增/编辑/查看弹窗详情 -->
<template>
<el-dialog title="新建账户" v-model="props.show" @close="handleClose" width="600px">
<div class="Cneter-box">
<el-form :model="formData" label-width="120px" class="user-form" label-position="top">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="账户名">
<el-input v-model="formData.account" placeholder="如: NJGD_ZH" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="姓名">
<el-input v-model="formData.realName" placeholder="请输入姓名" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="初始密码">
<el-input v-model="formData.password" type="password" placeholder="请输入密码" show-password />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系电话">
<el-input v-model="formData.phone" placeholder="请输入手机号" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="邮箱">
<el-input v-model="formData.email" placeholder="请输入邮箱地址" />
</el-form-item>
<div class="section-title">关联信息</div>
<el-form-item label="所属单位">
<div class="TreeBox">
<el-tree :data="unitTreeData" :props="treeProps" show-line highlight-current default-expand-all
@current-change="handleUnitChange" class="unit-tree" />
</div>
</el-form-item>
<el-form-item label="角色">
<el-select v-model="formData.role" placeholder="请选择角色">
<el-option label="项目经理" value="projectManager" />
<el-option label="审核人" value="auditor" />
<el-option label="物业管理员" value="propertyManager" />
</el-select>
</el-form-item>
<el-form-item label="权限有效期">
<el-input v-model="formData.expiry" placeholder="永久有效" disabled />
</el-form-item>
</el-form>
</div>
<!-- 底部按钮 -->
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">确认提交</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
show: {
type: Boolean,
default: false
},
CloseDialog: {
type: Function,
default: () => { }
}
})
//
const formData = ref({
account: '',
realName: '',
password: '',
phone: '',
email: '',
role: '',
expiry: '永久有效'
})
//
const unitTreeData = ref([
{
label: '江苏省电力公司',
children: [
{
label: '南京市供电公司',
children: [
{
label: '城东片区',
children: [
{ label: 'A办公楼' }
]
}
]
}
]
}
])
//
const treeProps = {
children: 'children',
label: 'label'
}
//
const handleUnitChange = (data) => {
console.log('Selected unit:', data)
}
//
const handleClose = () => {
props.CloseDialog()
}
const handleSubmit = () => {
//
props.CloseDialog()
}
</script>
<style scoped lang="scss">
.Cneter-box {
padding: 20px;
}
.TreeBox {
width: 100%;
height: 200px;
overflow: auto;
border: 1px solid #e4e7ed;
padding: 10px;
box-sizing: border-box;
}
.user-form {
max-width: 800px;
}
.section-title {
font-size: 16px;
font-weight: 600;
margin: 20px 0 10px 0;
color: #303133;
}
.unit-tree {
--el-tree-node-current-bg-color: #1890ff;
--el-tree-node-current-text-color: #ffffff;
.el-tree-node {
padding: 2px 0;
}
}
</style>

View File

@ -0,0 +1,268 @@
<!-- 后勤模块 - 账户管理 -->
<template>
<div class="MainBox">
<el-row :gutter="20">
<!-- 左侧树形结构 -->
<el-col :span="8">
<div class="CardBox">
<div class="LeftTitle">组织架构树</div>
<div class="TreeBox">
<el-tree :data="treeData" :props="treeProps" show-line default-expand-all highlight-current
@current-change="handleTreeChange" />
</div>
<div class="tree-actions">
<el-button type="primary" icon="Plus">新增节点</el-button>
<el-button icon="Edit">编辑节点</el-button>
<el-button icon="Delete">删除节点</el-button>
</div>
</div>
</el-col>
<!-- 右侧节点详情 -->
<el-col :span="16">
<el-card class="detail-card" shadow="hover">
<template #header>
<span>节点详情</span>
</template>
<el-form :model="formData" label-width="120px" label-position="top">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="节点名称">
<el-input v-model="formData.nodeName" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="节点编码">
<el-input v-model="formData.nodeCode" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="上级节点">
<el-input v-model="formData.parentNode" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="创建时间">
<el-input v-model="formData.createTime" disabled />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 关联账户 -->
<el-card class="account-card" shadow="hover" style="margin-top: 20px;">
<template #header>
<span>关联账户</span>
</template>
<el-table :data="accountList" height="230px">
<el-table-column prop="accountName" label="账户名" />
<el-table-column prop="realName" label="姓名" />
<el-table-column prop="unit" label="所属单位" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="getStatusTagType(row.status)">{{ row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
<el-pagination layout="prev, pager, next" :total="25" :page-size="10" :current-page="1"
class="float-right" />
</el-card>
</el-col>
</el-row>
<!-- 账户列表 -->
<el-row :gutter="20">
<el-col :span="24">
<el-card class="account-card" shadow="hover" style="margin-top: 20px;">
<template #header>
<span>账户列表</span>
<el-button type="primary" icon="Plus" class="float-right" size="small" @click="handleAddUserClick">新建账户</el-button>
</template>
<el-table :data="accountList" border height="300px">
<el-table-column prop="accountName" label="账户名" />
<el-table-column prop="realName" label="姓名" />
<el-table-column prop="unit" label="所属单位" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="role" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="getStatusTagType(row.status)">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button type="text" icon="Edit">编辑</el-button>
<el-button type="text" icon="Delete">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination layout="prev, pager, next" :total="25" :page-size="10" :current-page="1"
class="float-right" />
</el-card>
</el-col>
</el-row>
<!-- 账户新增/编辑/查看弹窗 -->
<UserDialog v-if="dialogShow" :show="dialogShow" :CloseDialog="handleCancel"/>
</div>
</template>
<script setup>
import UserDialog from "./UserDialog.vue";
import { ref } from 'vue'
const dialogShow = ref(false); //
//
const treeData = ref([
{
label: '江苏省电力公司',
children: [
{
label: '南京市供电公司',
children: [
{
label: '城东片区',
children: [
{ label: 'A办公楼' },
{ label: 'B办公楼' }
]
},
{
label: '城西片区',
children: [
{ label: 'C办公楼' }
]
}
]
}
]
}
])
//
const treeProps = {
children: 'children',
label: 'label'
}
//
const formData = ref({
nodeName: '江苏省电力公司',
nodeCode: 'JS001',
parentNode: '-',
createTime: '2023-05-15'
})
//
const accountList = ref([
{
accountName: 'NJGD_ZH',
realName: '赵华',
unit: '南京市供电公司 - 城东片区 - A办公楼',
phone: '138****1234',
role: '项目经理',
status: '已生效'
},
{
accountName: 'NJGD_LW',
realName: '李伟',
unit: '南京市供电公司 - 城西片区 - C办公楼',
phone: '139****5678',
role: '审核人',
status: '待审核'
},
{
accountName: 'SZDL_WX',
realName: '王霞',
unit: '苏州市供电公司 - 城南片区 - B办公楼',
phone: '137****9876',
role: '物业管理员',
status: '已禁用'
}
])
//
const handleTreeChange = (data) => {
console.log('Current tree node:', data)
}
//
const getStatusTagType = (status) => {
if (status === '已生效') return 'success'
if (status === '待审核') return 'warning'
if (status === '已禁用') return 'danger'
return 'info'
}
//
const handleAddUserClick = () => {
dialogShow.value = true;
}
//
const handleCancel = () => {
dialogShow.value = false;
}
</script>
<style scoped lang="scss">
.MainBox {
padding: 20px;
height: 100%;
background: #F9FAFB;
}
.CardBox {
height: 570px;
display: flex;
padding: 0px 26px;
background: #fff;
overflow: auto;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
flex-direction: column;
border-radius: 8px;
.LeftTitle {
font-size: 16px;
color: #303133;
line-height: 40px;
border-bottom: 1px solid #E4E7ED;
}
.TreeBox {
margin-top: 20px;
border: 1px solid #E4E7ED;
}
}
.tree-actions {
margin-top: 20px;
display: flex;
gap: 10px;
}
.detail-card {
margin-bottom: 20px;
}
.account-card {
position: relative;
}
.float-right {
position: absolute;
right: 20px;
top: 14px;
}
</style>

View File

@ -0,0 +1,226 @@
<!-- 后勤模块 - 个人中心 -->
<template>
<div class="MainBox">
<!-- 基本信息 -->
<el-card class="info-card" shadow="hover">
<div class="info-header">
<h2 class="card-title">基本信息</h2>
<el-button type="text" class="edit-btn">编辑</el-button>
</div>
<el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="label">姓名</span>
<span class="value">张三</span>
</div>
<div class="info-item">
<span class="label">所属部门</span>
<span class="value">后勤部</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<span class="label">账号</span>
<span class="value">zhangsan</span>
</div>
<div class="info-item">
<span class="label">联系方式</span>
<span class="value">138****1234</span>
</div>
</el-col>
</el-row>
</el-card>
<!-- 密码安全 -->
<el-card class="password-card" shadow="hover">
<h2 class="card-title">密码安全</h2>
<el-form :model="passwordForm" label-width="100px">
<el-form-item label="原密码">
<el-input v-model="passwordForm.oldPassword" type="password" placeholder="请输入原密码" />
</el-form-item>
<el-form-item label="新密码">
<el-input v-model="passwordForm.newPassword" type="password" placeholder="请输入新密码" />
<div class="password-tip">密码长度至少8位包含大小写字母数字及特殊符号中至少三种</div>
</el-form-item>
<el-form-item label="确认新密码">
<el-input v-model="passwordForm.confirmPassword" type="password" placeholder="请再次输入新密码" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleChangePassword">确定修改</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 最近操作日志 -->
<el-card class="log-card" shadow="hover">
<h2 class="card-title">最近操作日志</h2>
<div class="log-list">
<el-timeline>
<el-timeline-item v-for="(item, index) in logList" :key="index" placement="left">
<template #dot>
<el-tag type="primary" size="small" class="log-dot"></el-tag>
</template>
<el-card shadow="hover" class="log-card-item">
<div class="log-content">
<div class="log-time">{{ item.time }}</div>
<div class="log-desc">{{ item.content }}</div>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-card>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const passwordForm = ref({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
const logList = ref([
{
time: '2024-07-15',
content: '审核 XX 项目建档'
},
{
time: '2024-07-10',
content: '新增高空作业风险卡模板'
},
{
time: '2024-06-29',
content: '修改个人信息联系方式'
},
{
time: '2024-06-25',
content: '审核过新项目资料'
},
{
time: '2024-06-20',
content: '下载项目进度报表'
},
{
time: '2024-06-15',
content: '更新系统权限配置'
},
{
time: '2024-06-10',
content: '提交月度安全报告'
},
{
time: '2024-06-05',
content: '创建新用户账户'
},
])
const handleChangePassword = () => {
ElMessage.success('密码修改成功')
}
</script>
<style scoped lang="scss">
.MainBox {
padding: 20px;
height: 100%;
background: #F9FAFB;
}
.info-card,
.password-card,
.log-card {
margin-bottom: 20px;
transition: all 0.3s ease;
}
.info-card:hover,
.password-card:hover,
.log-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.info-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.edit-btn {
color: #409EFF;
}
.info-item {
margin-bottom: 15px;
}
.label {
display: inline-block;
width: 80px;
color: #606266;
font-weight: 500;
}
.value {
color: #303133;
}
.password-tip {
margin-top: 8px;
font-size: 12px;
color: #909399;
}
.log-list {
padding: 10px;
height: 200px;
overflow-y: auto;
}
.log-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #409EFF;
border: none;
padding: 0;
margin: 0;
}
.log-card-item {
border: none;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.log-card-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
}
.log-content {
color: #303133;
font-size: 14px;
.log-time {
font-size: 14px;
font-weight: 500;
color: #999;
}
.log-desc {
margin-top: 4px;
}
}
</style>

View File

@ -11,7 +11,7 @@
</div>
<el-form-item label="姓名">
<el-input v-model="queryForm.projectName" placeholder="请输入项目名称"></el-input>
<el-input v-model="queryForm.userName" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="所属部门">
<el-select v-model="queryForm.departmentId" placeholder="请选择部门" clearable>
@ -83,8 +83,10 @@ import DialogBox from "./DialogBox.vue";
//
const queryForm = ref({
dateRange: [],
projectName: "",
returnStatus: ""
userName: "",
departmentId: "",
roleId: "",
status: ""
});
//