初始化项目
This commit is contained in:
parent
848be42621
commit
f7b923539a
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
node_modules/
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
.env
|
||||
.env.local
|
||||
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
10
pc/.gitignore
vendored
Normal file
10
pc/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
node_modules/
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
.env
|
||||
.env.local
|
||||
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
77
pc/package.json
Normal file
77
pc/package.json
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "yoaf-web",
|
||||
"version": "1.1.2",
|
||||
"description": "智慧教育平台",
|
||||
"author": "front-end",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"preview": "node build/index.js --preview"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"admin",
|
||||
"dashboard",
|
||||
"element-ui",
|
||||
"boilerplate",
|
||||
"admin-template",
|
||||
"management-system"
|
||||
],
|
||||
"dependencies": {
|
||||
"@riophae/vue-treeselect": "^0.4.0",
|
||||
"axios": "0.24.0",
|
||||
"clipboard": "2.0.8",
|
||||
"core-js": "3.19.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "4.9.0",
|
||||
"element-ui": "^2.15.6",
|
||||
"file-saver": "2.0.5",
|
||||
"fuse.js": "6.4.3",
|
||||
"highlight.js": "9.18.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-beautify": "1.13.0",
|
||||
"js-cookie": "3.0.1",
|
||||
"jszip": "^3.10.1",
|
||||
"moment": "^2.30.1",
|
||||
"nprogress": "0.2.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"quill": "1.3.7",
|
||||
"screenfull": "5.0.2",
|
||||
"sm-crypto": "^0.3.12",
|
||||
"sortablejs": "1.10.2",
|
||||
"tui-image-editor": "^3.15.3",
|
||||
"vue": "^2.7.14",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-cropper": "0.5.5",
|
||||
"vue-meta": "2.4.0",
|
||||
"vue-router": "3.4.9",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "3.6.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "4.4.6",
|
||||
"@vue/cli-service": "4.4.6",
|
||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||
"chalk": "4.1.0",
|
||||
"compression-webpack-plugin": "5.0.2",
|
||||
"connect": "3.6.6",
|
||||
"mockjs": "^1.0.1-beta3",
|
||||
"runjs": "4.4.2",
|
||||
"sass": "1.32.13",
|
||||
"sass-loader": "10.1.1",
|
||||
"script-ext-html-webpack-plugin": "2.1.5",
|
||||
"svg-sprite-loader": "5.1.1",
|
||||
"vue-template-compiler": "2.6.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
">1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
}
|
17
pc/public/index.html
Normal file
17
pc/public/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>智慧教育平台</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
20
pc/src/App.vue
Normal file
20
pc/src/App.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
96
pc/src/api/asset/classification.js
Normal file
96
pc/src/api/asset/classification.js
Normal file
@ -0,0 +1,96 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询资产分类列表
|
||||
export function listClassification(query) {
|
||||
return request({
|
||||
url: '/asset/classification/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询资产分类详情
|
||||
export function getClassification(id) {
|
||||
return request({
|
||||
url: '/asset/classification/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增资产分类
|
||||
export function addClassification(data) {
|
||||
return request({
|
||||
url: '/asset/classification',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改资产分类
|
||||
export function updateClassification(data) {
|
||||
return request({
|
||||
url: '/asset/classification',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除资产分类
|
||||
export function delClassification(id) {
|
||||
return request({
|
||||
url: '/asset/classification/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 禁用资产分类
|
||||
export function disableClassification(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/classification/disable/' + id,
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
})
|
||||
}
|
||||
|
||||
// 启用资产分类
|
||||
export function enableClassification(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/classification/enable/' + id,
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除资产分类
|
||||
export function delClassificationBatch(ids, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/classification/batch/' + ids,
|
||||
method: 'delete',
|
||||
params: { lastModUserId }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取资产分类树形结构
|
||||
export function getClassificationTree() {
|
||||
return request({
|
||||
url: '/asset/classification/tree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 检查分类是否被使用
|
||||
export function checkClassificationInUse(id) {
|
||||
return request({
|
||||
url: '/asset/classification/' + id + '/check',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出资产分类
|
||||
export function exportClassification(query) {
|
||||
return request({
|
||||
url: '/asset/classification/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
96
pc/src/api/asset/location.js
Normal file
96
pc/src/api/asset/location.js
Normal file
@ -0,0 +1,96 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询资产位置列表
|
||||
export function listLocation(query) {
|
||||
return request({
|
||||
url: '/asset/location/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询资产位置详情
|
||||
export function getLocation(id) {
|
||||
return request({
|
||||
url: '/asset/location/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增资产位置
|
||||
export function addLocation(data) {
|
||||
return request({
|
||||
url: '/asset/location',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改资产位置
|
||||
export function updateLocation(data) {
|
||||
return request({
|
||||
url: '/asset/location',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除资产位置
|
||||
export function delLocation(id) {
|
||||
return request({
|
||||
url: '/asset/location/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 禁用资产位置
|
||||
export function disableLocation(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/location/disable/' + id,
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
})
|
||||
}
|
||||
|
||||
// 启用资产位置
|
||||
export function enableLocation(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/location/enable/' + id,
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除资产位置
|
||||
export function delLocationBatch(ids, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/location/batch/' + ids,
|
||||
method: 'delete',
|
||||
params: { lastModUserId }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取资产位置树形结构
|
||||
export function getLocationTree() {
|
||||
return request({
|
||||
url: '/asset/location/tree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 检查位置是否被使用
|
||||
export function checkLocationInUse(id) {
|
||||
return request({
|
||||
url: '/asset/location/' + id + '/check',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出资产位置
|
||||
export function exportLocation(query) {
|
||||
return request({
|
||||
url: '/asset/location/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
133
pc/src/api/building.js
Normal file
133
pc/src/api/building.js
Normal file
@ -0,0 +1,133 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询楼宇列表
|
||||
export function getBuildingList(query) {
|
||||
return request({
|
||||
url: '/room/building/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询楼宇详细
|
||||
export function getBuildingDetail(id) {
|
||||
return request({
|
||||
url: `/room/building/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增楼宇
|
||||
export function addBuilding(data) {
|
||||
// 处理字段名映射,确保前端驼峰命名与后端下划线命名正确对应
|
||||
const processedData = {
|
||||
...data,
|
||||
buildingTags: data.buildingTags,
|
||||
isHot: data.isHot,
|
||||
imageUrl: data.buildingImage,
|
||||
}
|
||||
|
||||
// 删除undefined的属性
|
||||
Object.keys(processedData).forEach(key => {
|
||||
if (processedData[key] === undefined) {
|
||||
delete processedData[key]
|
||||
}
|
||||
})
|
||||
|
||||
return request({
|
||||
url: '/room/building',
|
||||
method: 'post',
|
||||
data: processedData
|
||||
})
|
||||
}
|
||||
|
||||
// 修改楼宇
|
||||
export function updateBuilding(data) {
|
||||
// 处理字段名映射,确保前端驼峰命名与后端下划线命名正确对应
|
||||
const processedData = {
|
||||
...data,
|
||||
buildingTags: data.buildingTags,
|
||||
isHot: data.isHot,
|
||||
imageUrl: data.buildingImage
|
||||
}
|
||||
|
||||
// 删除undefined的属性
|
||||
Object.keys(processedData).forEach(key => {
|
||||
if (processedData[key] === undefined) {
|
||||
delete processedData[key]
|
||||
}
|
||||
})
|
||||
|
||||
return request({
|
||||
url: '/room/building',
|
||||
method: 'put',
|
||||
data: processedData
|
||||
})
|
||||
}
|
||||
|
||||
// 删除楼宇
|
||||
export function deleteBuilding(ids) {
|
||||
return request({
|
||||
url: `/room/building/${ids}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取楼宇统计信息
|
||||
export function getBuildingStatistics(id) {
|
||||
return request({
|
||||
url: `/room/building/${id}/statistics`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询楼层列表
|
||||
export function getFloorList(query) {
|
||||
return request({
|
||||
url: '/room/floor/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取楼层详情
|
||||
export function getFloorDetail(id) {
|
||||
return request({
|
||||
url: `/room/floor/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增楼层
|
||||
export function addFloor(data) {
|
||||
return request({
|
||||
url: '/room/floor',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改楼层
|
||||
export function updateFloor(data) {
|
||||
return request({
|
||||
url: '/room/floor',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除楼层
|
||||
export function deleteFloor(ids) {
|
||||
return request({
|
||||
url: `/room/floor/${ids}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据楼宇ID获取楼层列表
|
||||
export function getFloorListByBuilding(buildingId) {
|
||||
return request({
|
||||
url: `/room/floor/building/${buildingId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
61
pc/src/api/project.js
Normal file
61
pc/src/api/project.js
Normal file
@ -0,0 +1,61 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取项目列表
|
||||
export function getProjectList(query) {
|
||||
return request({
|
||||
url: '/room/project/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取项目详情
|
||||
export function getProjectDetail(id) {
|
||||
return request({
|
||||
url: `/room/project/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增项目
|
||||
export function addProject(data) {
|
||||
return request({
|
||||
url: '/room/project',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改项目
|
||||
export function updateProject(id, data) {
|
||||
const updateData = { ...data, id }
|
||||
return request({
|
||||
url: '/room/project',
|
||||
method: 'put',
|
||||
data: updateData
|
||||
})
|
||||
}
|
||||
|
||||
// 删除项目
|
||||
export function deleteProject(id) {
|
||||
return request({
|
||||
url: `/room/project/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取项目统计数据
|
||||
export function getProjectStatistics(id) {
|
||||
return request({
|
||||
url: `/room/project/${id}/statistics`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取所有项目统计数据(整体概况)
|
||||
export function getAllProjectStatistics() {
|
||||
return request({
|
||||
url: '/room/project/statistics',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
24
pc/src/assets/styles/index.scss
Normal file
24
pc/src/assets/styles/index.scss
Normal file
@ -0,0 +1,24 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.el-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.el-header {
|
||||
padding: 0;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
background-color: #f0f2f5;
|
||||
padding: 20px;
|
||||
}
|
112
pc/src/layout/index.vue
Normal file
112
pc/src/layout/index.vue
Normal file
@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<el-container class="app-wrapper">
|
||||
<el-aside width="200px">
|
||||
<el-menu
|
||||
:default-active="$route.path"
|
||||
class="el-menu-vertical"
|
||||
background-color="#304156"
|
||||
text-color="#bfcbd9"
|
||||
active-text-color="#409EFF"
|
||||
router
|
||||
>
|
||||
<template v-for="route in routes">
|
||||
<el-submenu
|
||||
v-if="route.children && route.children.length > 1"
|
||||
:key="route.path"
|
||||
:index="route.path"
|
||||
>
|
||||
<template slot="title">
|
||||
<i :class="route.meta && route.meta.icon"></i>
|
||||
<span>{{ route.meta && route.meta.title }}</span>
|
||||
</template>
|
||||
<el-menu-item
|
||||
v-for="child in route.children"
|
||||
:key="child.path"
|
||||
:index="route.path + '/' + child.path"
|
||||
>
|
||||
<i :class="child.meta && child.meta.icon"></i>
|
||||
<span slot="title">{{ child.meta && child.meta.title }}</span>
|
||||
</el-menu-item>
|
||||
</el-submenu>
|
||||
<el-menu-item
|
||||
v-else-if="!route.hidden"
|
||||
:key="route.path"
|
||||
:index="route.path"
|
||||
>
|
||||
<i :class="route.meta && route.meta.icon"></i>
|
||||
<span slot="title">{{ route.meta && route.meta.title }}</span>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<div class="header-right">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
管理员<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item>个人信息</el-dropdown-item>
|
||||
<el-dropdown-item>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<router-view />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Layout',
|
||||
computed: {
|
||||
routes() {
|
||||
return this.$router.options.routes.filter(route => {
|
||||
return route.path !== '/' && !route.hidden
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-wrapper {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.el-aside {
|
||||
background-color: #304156;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.el-header {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
line-height: 60px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
background-color: #f0f2f5;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
16
pc/src/main.js
Normal file
16
pc/src/main.js
Normal file
@ -0,0 +1,16 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import './assets/styles/index.scss'
|
||||
|
||||
Vue.use(ElementUI)
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
59
pc/src/router/index.js
Normal file
59
pc/src/router/index.js
Normal file
@ -0,0 +1,59 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Layout from '@/layout/index'
|
||||
import systemRoutes from './modules/system'
|
||||
import projectRoutes from './modules/project'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('@/layout/index'),
|
||||
redirect: '/home',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
component: () => import('@/layout/index'),
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'Home',
|
||||
component: () => import('../views/Home.vue'),
|
||||
meta: { title: '首页', icon: 'el-icon-s-home' }
|
||||
}
|
||||
],
|
||||
meta: { title: '首页', icon: 'el-icon-s-home' }
|
||||
},
|
||||
systemRoutes,
|
||||
projectRoutes,
|
||||
{
|
||||
path: '/asset',
|
||||
component: Layout,
|
||||
name: 'Asset',
|
||||
meta: { title: '资产管理', icon: 'el-icon-s-management' },
|
||||
children: [
|
||||
{
|
||||
path: 'location',
|
||||
component: () => import('@/views/asset/location/index'),
|
||||
name: 'AssetLocation',
|
||||
meta: { title: '资产位置设置', icon: 'el-icon-location' }
|
||||
},
|
||||
{
|
||||
path: 'classification',
|
||||
component: () => import('@/views/asset/classification/index'),
|
||||
name: 'AssetClassification',
|
||||
meta: { title: '资产分类设置', icon: 'el-icon-folder' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
21
pc/src/router/modules/project.js
Normal file
21
pc/src/router/modules/project.js
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
path: '/project',
|
||||
component: () => import('@/layout/index'),
|
||||
redirect: '/project/list',
|
||||
name: 'Project',
|
||||
meta: { title: '项目管理', icon: 'el-icon-office-building' },
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
name: 'ProjectList',
|
||||
component: () => import('@/views/project/index'),
|
||||
meta: { title: '项目列表' }
|
||||
},
|
||||
{
|
||||
path: 'building',
|
||||
name: 'BuildingList',
|
||||
component: () => import('@/views/project/building/index'),
|
||||
meta: { title: '楼宇列表' }
|
||||
}
|
||||
]
|
||||
}
|
15
pc/src/router/modules/system.js
Normal file
15
pc/src/router/modules/system.js
Normal file
@ -0,0 +1,15 @@
|
||||
export default {
|
||||
path: '/system',
|
||||
component: () => import('@/layout/index'),
|
||||
redirect: '/system/department-member',
|
||||
name: 'System',
|
||||
meta: { title: '系统管理', icon: 'el-icon-s-tools' },
|
||||
children: [
|
||||
{
|
||||
path: 'department-member',
|
||||
component: () => import('@/views/system/department-member/index'),
|
||||
name: 'DepartmentMember',
|
||||
meta: { title: '部门成员管理', icon: 'el-icon-office-building' }
|
||||
}
|
||||
]
|
||||
}
|
15
pc/src/store/index.js
Normal file
15
pc/src/store/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
},
|
||||
mutations: {
|
||||
},
|
||||
actions: {
|
||||
},
|
||||
modules: {
|
||||
}
|
||||
})
|
51
pc/src/utils/request.js
Normal file
51
pc/src/utils/request.js
Normal file
@ -0,0 +1,51 @@
|
||||
import axios from 'axios'
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
// baseURL: '/api', // 修改为相对路径,使用代理
|
||||
baseURL: 'http://192.168.137.38:8080/api', // 接口地址
|
||||
|
||||
timeout: 10000 // 请求超时时间
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
// 可以在这里添加请求头等信息
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
console.log(error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
const res = response.data
|
||||
// 如果返回的状态码不是200,则判断为错误
|
||||
if (res.code !== '000000') {
|
||||
Message({
|
||||
message: res.message || '系统错误',
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
return Promise.reject(new Error(res.message || '系统错误'))
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('err' + error)
|
||||
Message({
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
84
pc/src/views/Home.vue
Normal file
84
pc/src/views/Home.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div class="card-header">
|
||||
<i class="el-icon-user"></i>
|
||||
<span>总成员数</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="number">100</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div class="card-header">
|
||||
<i class="el-icon-office-building"></i>
|
||||
<span>部门数量</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="number">10</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div class="card-header">
|
||||
<i class="el-icon-user-solid"></i>
|
||||
<span>在职人数</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="number">95</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div class="card-header">
|
||||
<i class="el-icon-user"></i>
|
||||
<span>离职人数</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="number">5</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Home'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.home {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header i {
|
||||
font-size: 20px;
|
||||
margin-right: 10px;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
</style>
|
416
pc/src/views/asset/classification/index.vue
Normal file
416
pc/src/views/asset/classification/index.vue
Normal file
@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧树形结构 -->
|
||||
<el-col :span="6">
|
||||
<div class="tree-container">
|
||||
<el-tree
|
||||
ref="classTree"
|
||||
:data="classTree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
@node-click="handleNodeClick"
|
||||
:default-expanded-keys="defaultExpandedKeys">
|
||||
<span class="custom-tree-node" slot-scope="{ node, data }">
|
||||
<span>{{ node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧列表 -->
|
||||
<el-col :span="18">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>分类列表</span>
|
||||
<el-button
|
||||
style="float: right;"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd">新增分类</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||||
<el-form-item label="分类编码">
|
||||
<el-input v-model="queryParams.classificationCode" placeholder="请输入分类编码" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类名称">
|
||||
<el-input v-model="queryParams.classificationName" placeholder="请输入分类名称" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="禁用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="classList" border stripe style="width: 100%">
|
||||
<el-table-column prop="classificationCode" label="分类编码" width="150" />
|
||||
<el-table-column prop="classificationName" label="分类名称" width="200" />
|
||||
<el-table-column prop="parentName" label="上级分类" width="200" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'">
|
||||
{{ scope.row.status === '1' ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" />
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="handleEdit(scope.row)">修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleStatusChange(scope.row)">
|
||||
{{ scope.row.status === '1' ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
style="color: #F56C6C"
|
||||
@click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="queryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
class="pagination">
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="分类编码" prop="classificationCode">
|
||||
<el-input v-model="form.classificationCode" placeholder="请输入分类编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类名称" prop="classificationName">
|
||||
<el-input v-model="form.classificationName" placeholder="请输入分类名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上级分类" prop="parentId">
|
||||
<el-tree-select
|
||||
v-model="form.parentId"
|
||||
:data="classTree"
|
||||
:props="defaultProps"
|
||||
placeholder="请选择上级分类"
|
||||
clearable
|
||||
filterable
|
||||
check-strictly
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:render-after-expand="false"
|
||||
style="width: 100%">
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio label="1">启用</el-radio>
|
||||
<el-radio label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listClassification, getClassification, addClassification, updateClassification, delClassification, enableClassification, disableClassification, getClassificationTree, checkClassificationInUse } from '@/api/asset/classification'
|
||||
|
||||
export default {
|
||||
name: 'AssetClassification',
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 分类列表
|
||||
classList: [],
|
||||
// 分类树形数据
|
||||
classTree: [],
|
||||
// 默认展开的节点
|
||||
defaultExpandedKeys: [],
|
||||
// 树形配置
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'classificationName'
|
||||
},
|
||||
// 弹出层标题
|
||||
dialogTitle: '',
|
||||
// 是否显示弹出层
|
||||
dialogVisible: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
classificationCode: undefined,
|
||||
classificationName: undefined,
|
||||
status: undefined,
|
||||
parentId: undefined
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
classificationCode: [
|
||||
{ required: true, message: '请输入分类编码', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
classificationName: [
|
||||
{ required: true, message: '请输入分类名称', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||
],
|
||||
parentId: [
|
||||
{ required: false, message: '请选择上级分类', trigger: 'change' }
|
||||
],
|
||||
remark: [
|
||||
{ max: 200, message: '长度不能超过200个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTree()
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 获取分类树形结构 */
|
||||
getTree() {
|
||||
getClassificationTree().then(res => {
|
||||
if (res.code === 200) {
|
||||
this.classTree = res.data
|
||||
// 默认展开第一级节点
|
||||
if (this.classTree.length > 0) {
|
||||
this.defaultExpandedKeys = [this.classTree[0].id]
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 查询分类列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listClassification(this.queryParams).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.classList = res.data.rows
|
||||
this.total = res.data.total
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 节点单击事件 */
|
||||
handleNodeClick(data) {
|
||||
this.queryParams.parentId = data.id
|
||||
this.getList()
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.queryParams.parentId = undefined
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
classificationCode: undefined,
|
||||
classificationName: undefined,
|
||||
status: undefined,
|
||||
parentId: undefined
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.dialogTitle = '新增分类'
|
||||
this.form = {
|
||||
classificationCode: '',
|
||||
classificationName: '',
|
||||
parentId: this.queryParams.parentId || '', // 如果当前有选中节点,则默认为父节点
|
||||
status: '1',
|
||||
remark: ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleEdit(row) {
|
||||
const id = row.id
|
||||
getClassification(id).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.form = res.data
|
||||
// 确保parentId是字符串或数字类型,而不是对象
|
||||
if (this.form.parentId && typeof this.form.parentId === 'object') {
|
||||
this.form.parentId = this.form.parentId.id || ''
|
||||
}
|
||||
this.dialogTitle = '编辑分类'
|
||||
this.dialogVisible = true
|
||||
// 重置表单验证
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
const method = this.form.id ? updateClassification : addClassification
|
||||
method(this.form).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success('操作成功')
|
||||
this.dialogVisible = false
|
||||
this.getTree()
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$confirm('是否确认删除该分类?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 先检查分类是否被使用
|
||||
checkClassificationInUse(row.id).then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.$message.error('该分类已被使用,无法删除')
|
||||
return
|
||||
}
|
||||
// 未被使用,执行删除操作
|
||||
delClassification(row.id).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success('删除成功')
|
||||
this.getTree()
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 状态修改按钮操作 */
|
||||
handleStatusChange(row) {
|
||||
const action = row.status === '1' ? '禁用' : '启用'
|
||||
this.$confirm(`是否确认${action}该分类?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 获取当前用户ID
|
||||
const lastModUserId = this.$store.getters.userId || '1'
|
||||
const method = row.status === '1' ? disableClassification : enableClassification
|
||||
method(row.id, lastModUserId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success(`${action}成功`)
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 分页大小改变 */
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
/** 分页页码改变 */
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
.tree-container {
|
||||
background-color: #fff;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
height: calc(100vh - 140px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 让所有输入框和下拉框保持一致宽度 */
|
||||
.el-input, .el-select, .el-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 确保所有表单元素的高度和边距一致 */
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.el-form-item__content {
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保el-select和普通输入框宽度一致 */
|
||||
::v-deep .el-select, ::v-deep .el-tree-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 树形选择器样式优化 */
|
||||
::v-deep .el-tree-select .el-select-dropdown__item {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
416
pc/src/views/asset/location/index.vue
Normal file
416
pc/src/views/asset/location/index.vue
Normal file
@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧树形结构 -->
|
||||
<el-col :span="6">
|
||||
<div class="tree-container">
|
||||
<el-tree
|
||||
ref="locationTree"
|
||||
:data="locationTree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
@node-click="handleNodeClick"
|
||||
:default-expanded-keys="defaultExpandedKeys">
|
||||
<span class="custom-tree-node" slot-scope="{ node, data }">
|
||||
<span>{{ node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧列表 -->
|
||||
<el-col :span="18">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>位置列表</span>
|
||||
<el-button
|
||||
style="float: right;"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd">新增位置</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||||
<el-form-item label="位置编码">
|
||||
<el-input v-model="queryParams.locationCode" placeholder="请输入位置编码" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="位置名称">
|
||||
<el-input v-model="queryParams.locationName" placeholder="请输入位置名称" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="禁用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="locationList" border stripe style="width: 100%">
|
||||
<el-table-column prop="locationCode" label="位置编码" width="150" />
|
||||
<el-table-column prop="locationName" label="位置名称" width="200" />
|
||||
<el-table-column prop="parentName" label="上级位置" width="200" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'">
|
||||
{{ scope.row.status === '1' ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" />
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="handleEdit(scope.row)">修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleStatusChange(scope.row)">
|
||||
{{ scope.row.status === '1' ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
style="color: #F56C6C"
|
||||
@click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="queryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
class="pagination">
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="位置编码" prop="locationCode">
|
||||
<el-input v-model="form.locationCode" placeholder="请输入位置编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="位置名称" prop="locationName">
|
||||
<el-input v-model="form.locationName" placeholder="请输入位置名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上级位置" prop="parentId">
|
||||
<el-tree-select
|
||||
v-model="form.parentId"
|
||||
:data="locationTree"
|
||||
:props="defaultProps"
|
||||
placeholder="请选择上级位置"
|
||||
clearable
|
||||
filterable
|
||||
check-strictly
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:render-after-expand="false"
|
||||
style="width: 100%">
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio label="1">启用</el-radio>
|
||||
<el-radio label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listLocation, getLocation, addLocation, updateLocation, delLocation, enableLocation, disableLocation, getLocationTree, checkLocationInUse } from '@/api/asset/location'
|
||||
|
||||
export default {
|
||||
name: 'AssetLocation',
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 位置列表
|
||||
locationList: [],
|
||||
// 位置树形数据
|
||||
locationTree: [],
|
||||
// 默认展开的节点
|
||||
defaultExpandedKeys: [],
|
||||
// 树形配置
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'locationName'
|
||||
},
|
||||
// 弹出层标题
|
||||
dialogTitle: '',
|
||||
// 是否显示弹出层
|
||||
dialogVisible: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
locationCode: undefined,
|
||||
locationName: undefined,
|
||||
status: undefined,
|
||||
parentId: undefined
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
locationCode: [
|
||||
{ required: true, message: '请输入位置编码', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
locationName: [
|
||||
{ required: true, message: '请输入位置名称', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||
],
|
||||
parentId: [
|
||||
{ required: false, message: '请选择上级位置', trigger: 'change' }
|
||||
],
|
||||
remark: [
|
||||
{ max: 200, message: '长度不能超过200个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTree()
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 获取位置树形结构 */
|
||||
getTree() {
|
||||
getLocationTree().then(res => {
|
||||
if (res.code === 200) {
|
||||
this.locationTree = res.data
|
||||
// 默认展开第一级节点
|
||||
if (this.locationTree.length > 0) {
|
||||
this.defaultExpandedKeys = [this.locationTree[0].id]
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 查询位置列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listLocation(this.queryParams).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.locationList = res.data.rows
|
||||
this.total = res.data.total
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 节点单击事件 */
|
||||
handleNodeClick(data) {
|
||||
this.queryParams.parentId = data.id
|
||||
this.getList()
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.queryParams.parentId = undefined
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
locationCode: undefined,
|
||||
locationName: undefined,
|
||||
status: undefined,
|
||||
parentId: undefined
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.dialogTitle = '新增位置'
|
||||
this.form = {
|
||||
locationCode: '',
|
||||
locationName: '',
|
||||
parentId: this.queryParams.parentId || '', // 如果当前有选中节点,则默认为父节点
|
||||
status: '1',
|
||||
remark: ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleEdit(row) {
|
||||
const id = row.id
|
||||
getLocation(id).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.form = res.data
|
||||
// 确保parentId是字符串或数字类型,而不是对象
|
||||
if (this.form.parentId && typeof this.form.parentId === 'object') {
|
||||
this.form.parentId = this.form.parentId.id || ''
|
||||
}
|
||||
this.dialogTitle = '编辑位置'
|
||||
this.dialogVisible = true
|
||||
// 重置表单验证
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
const method = this.form.id ? updateLocation : addLocation
|
||||
method(this.form).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success('操作成功')
|
||||
this.dialogVisible = false
|
||||
this.getTree()
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$confirm('是否确认删除该位置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 先检查位置是否被使用
|
||||
checkLocationInUse(row.id).then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.$message.error('该位置已被使用,无法删除')
|
||||
return
|
||||
}
|
||||
// 未被使用,执行删除操作
|
||||
delLocation(row.id).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success('删除成功')
|
||||
this.getTree()
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 状态修改按钮操作 */
|
||||
handleStatusChange(row) {
|
||||
const action = row.status === '1' ? '禁用' : '启用'
|
||||
this.$confirm(`是否确认${action}该位置?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 获取当前用户ID
|
||||
const lastModUserId = this.$store.getters.userId || '1'
|
||||
const method = row.status === '1' ? disableLocation : enableLocation
|
||||
method(row.id, lastModUserId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success(`${action}成功`)
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 分页大小改变 */
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
/** 分页页码改变 */
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
.tree-container {
|
||||
background-color: #fff;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
height: calc(100vh - 140px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 让所有输入框和下拉框保持一致宽度 */
|
||||
.el-input, .el-select, .el-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 确保所有表单元素的高度和边距一致 */
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.el-form-item__content {
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保el-select和普通输入框宽度一致 */
|
||||
::v-deep .el-select, ::v-deep .el-tree-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 树形选择器样式优化 */
|
||||
::v-deep .el-tree-select .el-select-dropdown__item {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
1802
pc/src/views/project/building/index.vue
Normal file
1802
pc/src/views/project/building/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
537
pc/src/views/project/index.vue
Normal file
537
pc/src/views/project/index.vue
Normal file
@ -0,0 +1,537 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 统计数据展示区域 -->
|
||||
<!-- <el-row :gutter="20" class="statistics-row">
|
||||
<el-col :span="4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">管理面积</div>
|
||||
<div class="stat-value">{{ statistics.managementArea }} m²</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">可招商面积</div>
|
||||
<div class="stat-value">{{ statistics.availableArea }} m²</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">可招商占比</div>
|
||||
<div class="stat-value">{{ statistics.availableRatio }}%</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">总房源数量</div>
|
||||
<div class="stat-value">{{ statistics.totalRooms }} 间</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">可招商房源数量</div>
|
||||
<div class="stat-value">{{ statistics.availableRooms }} 间</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row> ..>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>项目列表</span>
|
||||
<el-button
|
||||
style="float: right;"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd">新增项目</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||||
<el-form-item label="项目名称">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目类型">
|
||||
<el-select v-model="queryParams.projectType" placeholder="请选择项目类型" clearable size="small">
|
||||
<el-option label="写字楼" value="写字楼" />
|
||||
<el-option label="产业园区" value="产业园区" />
|
||||
<el-option label="商场" value="商场" />
|
||||
<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-input v-model="queryParams.region" placeholder="请输入所属地区" clearable size="small" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<el-table v-loading="loading" :data="projectList" border style="width: 100%">
|
||||
<el-table-column prop="projectName" label="项目名称" />
|
||||
<el-table-column prop="projectType" label="项目类型" />
|
||||
<el-table-column prop="region" label="所属地区" />
|
||||
<el-table-column prop="totalArea" label="管理面积(㎡)" />
|
||||
<el-table-column prop="availableArea" label="可招商面积(㎡)" />
|
||||
<el-table-column prop="totalBuildings" label="总房源数量" />
|
||||
<el-table-column prop="availableBuildings" label="可招商房源数量" />
|
||||
<el-table-column label="操作" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="handleDetail(scope.row)">详情</el-button>
|
||||
<el-button size="mini" type="text" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="text" style="color: #F56C6C" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="queryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total">
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="50%" @open="onDialogOpen" @close="onDialogClose">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目类型" prop="projectType">
|
||||
<el-select v-model="form.projectType" placeholder="请选择项目类型" :disabled="form.id !== undefined">
|
||||
<el-option label="写字楼" value="写字楼" />
|
||||
<el-option label="产业园区" value="产业园区" />
|
||||
<el-option label="商场" value="商场" />
|
||||
<el-option label="联合办公" value="联合办公" />
|
||||
<el-option label="公寓" value="公寓" />
|
||||
<el-option label="小区" value="小区" />
|
||||
<el-option label="社区养老" value="社区养老" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="form.projectName" placeholder="请输入项目名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目简称" prop="projectShortName">
|
||||
<el-input v-model="form.projectShortName" placeholder="请输入项目简称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属地区" prop="region">
|
||||
<el-input v-model="form.region" placeholder="请输入所属地区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="详细地址" prop="address">
|
||||
<el-input v-model="form.address" type="textarea" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="项目标签" prop="projectTags">
|
||||
<el-select v-model="form.projectTags" multiple placeholder="请选择项目标签">
|
||||
<el-option
|
||||
v-for="tag in tagOptions"
|
||||
:key="tag.value"
|
||||
:label="tag.label"
|
||||
:value="tag.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="项目简介" prop="projectDesc">
|
||||
<el-input v-model="form.projectDesc" type="textarea" :rows="4" placeholder="请输入项目简介" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="项目图片" prop="projectImage">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="/api/upload"
|
||||
:show-file-list="false"
|
||||
:on-success="handleImageSuccess"
|
||||
:before-upload="beforeImageUpload">
|
||||
<img v-if="form.projectImage" :src="form.projectImage" class="avatar">
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="项目详情" :visible.sync="detailVisible" width="50%">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="项目名称">{{ detail.projectName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目简称">{{ detail.projectShortName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目类型">{{ detail.projectType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属地区">{{ detail.region }}</el-descriptions-item>
|
||||
<el-descriptions-item label="详细地址">{{ detail.address }}</el-descriptions-item>
|
||||
<el-descriptions-item label="管理面积">{{ detail.totalArea }}㎡</el-descriptions-item>
|
||||
<el-descriptions-item label="可招商面积">{{ detail.availableArea }}㎡</el-descriptions-item>
|
||||
<el-descriptions-item label="总房源数量">{{ detail.totalBuildings }}</el-descriptions-item>
|
||||
<el-descriptions-item label="可招商房源数量">{{ detail.availableBuildings }}</el-descriptions-item>
|
||||
<el-descriptions-item label="排序值">{{ detail.sort }}</el-descriptions-item>
|
||||
<el-descriptions-item label="竣工时间">{{ detail.completionTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="标准层高">{{ detail.standardHeight }}m</el-descriptions-item>
|
||||
<el-descriptions-item label="物业">{{ detail.property }}</el-descriptions-item>
|
||||
<el-descriptions-item label="物业费">{{ detail.propertyFee }}元/㎡/月</el-descriptions-item>
|
||||
<el-descriptions-item label="车位数量">{{ detail.parkingSpaces }}</el-descriptions-item>
|
||||
<el-descriptions-item label="车位月租金">{{ detail.parkingFee }}元/月</el-descriptions-item>
|
||||
<el-descriptions-item label="空调">{{ detail.airConditioning }}</el-descriptions-item>
|
||||
<el-descriptions-item label="空调费">{{ detail.airConditioningFee }}元/㎡/月</el-descriptions-item>
|
||||
<el-descriptions-item label="空调开放时间">{{ detail.airConditioningTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="电梯">{{ detail.elevator }}</el-descriptions-item>
|
||||
<el-descriptions-item label="网络">{{ detail.network }}</el-descriptions-item>
|
||||
<el-descriptions-item label="入驻企业">{{ detail.enterprises }}</el-descriptions-item>
|
||||
<el-descriptions-item label="招商岗位">{{ detail.recruitmentPosition }}</el-descriptions-item>
|
||||
<el-descriptions-item label="招商部门">{{ detail.recruitmentDepartment }}</el-descriptions-item>
|
||||
<el-descriptions-item label="VR链接">{{ detail.vrLink }}</el-descriptions-item>
|
||||
<el-descriptions-item label="招商时间">{{ detail.recruitmentTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目图片" :span="2">
|
||||
<el-image
|
||||
v-if="detail.projectImage"
|
||||
:src="detail.projectImage"
|
||||
:preview-src-list="[detail.projectImage]"
|
||||
fit="cover"
|
||||
style="width: 200px; height: 200px">
|
||||
</el-image>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailVisible = false">关 闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProjectList, getProjectDetail, addProject, updateProject, deleteProject, getAllProjectStatistics } from '@/api/project'
|
||||
|
||||
export default {
|
||||
name: 'ProjectList',
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目列表
|
||||
projectList: [],
|
||||
// 弹出层标题
|
||||
dialogTitle: '',
|
||||
// 是否显示弹出层
|
||||
dialogVisible: false,
|
||||
// 是否显示详情弹出层
|
||||
detailVisible: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectName: undefined,
|
||||
projectType: undefined,
|
||||
region: undefined
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 详情数据
|
||||
detail: {},
|
||||
// 标签选项
|
||||
tagOptions: [],
|
||||
// 表单校验
|
||||
rules: {
|
||||
projectType: [
|
||||
{ required: true, message: '请选择项目类型', trigger: 'change' }
|
||||
],
|
||||
projectName: [
|
||||
{ required: true, message: '请输入项目名称', trigger: 'blur' }
|
||||
],
|
||||
projectShortName: [
|
||||
{ required: true, message: '请输入项目简称', trigger: 'blur' }
|
||||
],
|
||||
region: [
|
||||
{ required: true, message: '请输入所属地区', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请输入详细地址', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
// 统计数据
|
||||
statistics: {
|
||||
managementArea: '37,234.52',
|
||||
availableArea: '20,916.2',
|
||||
availableRatio: '56.17',
|
||||
totalRooms: '637',
|
||||
availableRooms: '51'
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getTagOptions()
|
||||
this.fetchStatistics()
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
const params = {
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize,
|
||||
projectName: this.queryParams.projectName,
|
||||
projectType: this.queryParams.projectType,
|
||||
region: this.queryParams.region
|
||||
}
|
||||
|
||||
getProjectList(params).then(res => {
|
||||
this.projectList = res.data.list
|
||||
this.total = res.data.total
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 获取标签选项 */
|
||||
getTagOptions() {
|
||||
// TODO: 调用后端接口获取标签列表
|
||||
this.tagOptions = [
|
||||
{ value: '1', label: '标签1' },
|
||||
{ value: '2', label: '标签2' }
|
||||
]
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectName: undefined,
|
||||
projectType: undefined,
|
||||
region: undefined
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.dialogTitle = '新增项目'
|
||||
// 完全重置表单数据
|
||||
this.form = {}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleEdit(row) {
|
||||
this.dialogTitle = '编辑项目'
|
||||
getProjectDetail(row.id).then(res => {
|
||||
// 处理从后端返回的项目标签,将逗号分隔的字符串转为数组
|
||||
const projectData = { ...res.data };
|
||||
|
||||
if (typeof projectData.projectTags === 'string' && projectData.projectTags) {
|
||||
projectData.projectTags = projectData.projectTags.split(',');
|
||||
} else if (!projectData.projectTags) {
|
||||
projectData.projectTags = [];
|
||||
}
|
||||
|
||||
this.form = projectData;
|
||||
this.dialogVisible = true;
|
||||
})
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
getProjectDetail(row.id).then(res => {
|
||||
// 处理项目详情数据,确保标签显示友好
|
||||
const detailData = { ...res.data };
|
||||
|
||||
// 如果标签是字符串,则保持原样,用于显示
|
||||
if (typeof detailData.projectTags === 'string' && detailData.projectTags) {
|
||||
// 可以在这里处理标签的显示格式,如果需要的话
|
||||
} else if (Array.isArray(detailData.projectTags)) {
|
||||
// 如果是数组,转为逗号分隔的字符串
|
||||
detailData.projectTags = detailData.projectTags.join(',');
|
||||
}
|
||||
|
||||
this.detail = detailData;
|
||||
this.detailVisible = true;
|
||||
})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$confirm('是否确认删除该项目?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
deleteProject(row.id).then(() => {
|
||||
this.getList()
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 表单提交 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
// 处理项目标签,将数组转为逗号分隔的字符串
|
||||
const formData = { ...this.form };
|
||||
if (Array.isArray(formData.projectTags)) {
|
||||
formData.projectTags = formData.projectTags.join(',');
|
||||
}
|
||||
|
||||
// 如果表单有ID则为编辑操作,否则为新增操作
|
||||
const isEdit = formData.id !== undefined;
|
||||
const method = isEdit ? updateProject(formData.id, formData) : addProject(formData);
|
||||
|
||||
method.then(() => {
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 图片上传成功处理 */
|
||||
handleImageSuccess(res, file) {
|
||||
// 假设后端返回了图片URL
|
||||
this.form.projectImage = res.data
|
||||
},
|
||||
/** 图片上传前处理 */
|
||||
beforeImageUpload(file) {
|
||||
const isJPG = file.type === 'image/jpeg'
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
|
||||
if (!isJPG) {
|
||||
this.$message.error('上传图片只能是 JPG 格式!')
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传图片大小不能超过 2MB!')
|
||||
}
|
||||
return isJPG && isLt2M
|
||||
},
|
||||
/** 分页大小改变 */
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
/** 分页页码改变 */
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
},
|
||||
/** 获取统计数据 */
|
||||
async fetchStatistics() {
|
||||
try {
|
||||
const res = await getAllProjectStatistics()
|
||||
this.statistics = {
|
||||
managementArea: res.data.totalArea || '0',
|
||||
availableArea: res.data.availableArea || '0',
|
||||
availableRatio: res.data.availableArea && res.data.totalArea
|
||||
? ((res.data.availableArea / res.data.totalArea) * 100).toFixed(2)
|
||||
: '0',
|
||||
totalRooms: res.data.totalBuildings || '0',
|
||||
availableRooms: res.data.availableBuildings || '0'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
// 使用默认数据
|
||||
this.statistics = {
|
||||
managementArea: '37,234.52',
|
||||
availableArea: '20,916.2',
|
||||
availableRatio: '56.17',
|
||||
totalRooms: '637',
|
||||
availableRooms: '51'
|
||||
}
|
||||
}
|
||||
},
|
||||
/** 对话框打开时处理 */
|
||||
onDialogOpen() {
|
||||
// 只重置校验状态,不清空表单数据
|
||||
this.$nextTick(() => {
|
||||
this.$refs['form'] && this.$refs['form'].clearValidate()
|
||||
})
|
||||
},
|
||||
/** 对话框关闭时处理 */
|
||||
onDialogClose() {
|
||||
// 重置表单和校验状态
|
||||
this.form = {}
|
||||
this.$refs['form'].clearValidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.statistics-row {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.stat-card {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
text-align: center;
|
||||
|
||||
.stat-label {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: #303133;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保下拉框和输入框宽度一致 */
|
||||
::v-deep .el-form-item {
|
||||
.el-select {
|
||||
width: 100%;
|
||||
}
|
||||
.el-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
line-height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
946
pc/src/views/system/department-member/index.vue
Normal file
946
pc/src/views/system/department-member/index.vue
Normal file
@ -0,0 +1,946 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-tabs v-model="activeTab" type="border-card">
|
||||
<!-- 部门管理 tab -->
|
||||
<el-tab-pane label="部门管理" name="department">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>部门管理</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text" @click="handleAddDepartment">新增部门</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="departmentLoading"
|
||||
:data="departmentList"
|
||||
row-key="id"
|
||||
border
|
||||
default-expand-all
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" label="部门名称" />
|
||||
<el-table-column prop="level" label="部门层级" />
|
||||
<el-table-column prop="memberCount" label="部门人数" sortable />
|
||||
<el-table-column prop="leader" label="部门负责人" />
|
||||
<el-table-column label="操作" width="220">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" @click="handleEditDepartment(scope.row)">编辑</el-button>
|
||||
<el-button type="text" @click="handleAddSubDepartment(scope.row)">添加子部门</el-button>
|
||||
<el-button type="text" class="delete-btn" @click="handleDeleteDepartment(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 成员管理 tab -->
|
||||
<el-tab-pane label="成员管理" name="member">
|
||||
<el-card class="box-card">
|
||||
<div class="member-container">
|
||||
<!-- 左侧部门树 -->
|
||||
<div class="department-tree">
|
||||
<div class="tree-header">
|
||||
<el-input
|
||||
v-model="deptSearchValue"
|
||||
placeholder="搜索部门"
|
||||
size="small"
|
||||
prefix-icon="el-icon-search"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="tree-content">
|
||||
<el-tree
|
||||
ref="deptTree"
|
||||
:data="departmentList"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<span slot-scope="{ node, data }" class="custom-tree-node">
|
||||
<span>{{ node.label }}</span>
|
||||
<span class="member-count">({{ data.memberCount || 0 }})</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧成员列表 -->
|
||||
<div class="member-list">
|
||||
<div class="member-header">
|
||||
<div class="header-title">
|
||||
<span>{{ selectedDeptName || '全部' }}</span>
|
||||
<span class="member-count">{{ total }}人</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" size="small" @click="handleAddMember">添加成员</el-button>
|
||||
<el-button type="primary" plain size="small" @click="handleImportMember">申请入列表</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="text" size="small">
|
||||
更多<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item>导出Excel</el-dropdown-item>
|
||||
<el-dropdown-item>打印</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="member-filter">
|
||||
<el-radio-group v-model="queryParams.status" size="small" @change="handleStatusChange">
|
||||
<el-radio-button label="">全部</el-radio-button>
|
||||
<el-radio-button label="1">正常</el-radio-button>
|
||||
<el-radio-button label="0">未激活</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="memberLoading"
|
||||
:data="memberList"
|
||||
row-key="id"
|
||||
border
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" label="姓名" width="120" />
|
||||
<el-table-column prop="status" label="账号状态" width="100">
|
||||
<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 prop="employeeId" label="工号" width="100" />
|
||||
<el-table-column prop="position" label="职位" width="120" />
|
||||
<el-table-column prop="phone" label="手机" width="120" />
|
||||
<el-table-column prop="deptName" label="部门" width="120" />
|
||||
<el-table-column prop="gender" label="性别" width="80">
|
||||
<template #default="scope">
|
||||
{{ scope.row.gender === '1' ? '男' : '女' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="entryDate" label="入职时间" width="120" />
|
||||
<el-table-column label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" @click="handleEditMember(scope.row)">编辑</el-button>
|
||||
<el-button type="danger" size="small" @click="handleDeleteMember(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="queryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 部门对话框 -->
|
||||
<el-dialog :title="departmentTitle" :visible.sync="departmentOpen" width="500px" append-to-body>
|
||||
<el-form ref="departmentForm" :model="departmentForm" :rules="departmentRules" label-width="80px">
|
||||
<el-form-item label="上级部门" prop="parentId">
|
||||
<treeselect
|
||||
v-model="departmentForm.parentId"
|
||||
:options="departmentOptions"
|
||||
:normalizer="normalizer"
|
||||
placeholder="选择上级部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="departmentForm.name" placeholder="请输入部门名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门层级" prop="level">
|
||||
<el-input v-model="departmentForm.level" placeholder="请输入部门层级" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="leader">
|
||||
<el-input v-model="departmentForm.leader" placeholder="请输入负责人" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitDepartmentForm">确 定</el-button>
|
||||
<el-button @click="cancelDepartment">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 成员对话框 -->
|
||||
<el-dialog :title="memberTitle" :visible.sync="memberOpen" width="500px" append-to-body>
|
||||
<el-form ref="memberForm" :model="memberForm" :rules="memberRules" label-width="80px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="memberForm.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<treeselect
|
||||
v-model="memberForm.deptId"
|
||||
:options="departmentOptions"
|
||||
:normalizer="normalizer"
|
||||
placeholder="选择部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="职位" prop="position">
|
||||
<el-input v-model="memberForm.position" placeholder="请输入职位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="phone">
|
||||
<el-input v-model="memberForm.phone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="memberForm.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="memberForm.status">
|
||||
<el-radio label="1">在职</el-radio>
|
||||
<el-radio label="0">离职</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitMemberForm">确 定</el-button>
|
||||
<el-button @click="cancelMember">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
|
||||
export default {
|
||||
name: 'DepartmentMember',
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
// 当前激活的 tab
|
||||
activeTab: 'department',
|
||||
// 部门相关数据
|
||||
departmentLoading: true,
|
||||
departmentList: [],
|
||||
departmentOptions: [],
|
||||
departmentTitle: '',
|
||||
departmentOpen: false,
|
||||
departmentForm: {},
|
||||
departmentRules: {
|
||||
name: [
|
||||
{ required: true, message: '部门名称不能为空', trigger: 'blur' }
|
||||
],
|
||||
leader: [
|
||||
{ required: true, message: '负责人不能为空', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
// 成员相关数据
|
||||
memberLoading: true,
|
||||
memberList: [],
|
||||
total: 0,
|
||||
memberTitle: '',
|
||||
memberOpen: false,
|
||||
memberForm: {},
|
||||
memberRules: {
|
||||
name: [
|
||||
{ required: true, message: '姓名不能为空', trigger: 'blur' }
|
||||
],
|
||||
deptId: [
|
||||
{ required: true, message: '所属部门不能为空', trigger: 'change' }
|
||||
]
|
||||
},
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
deptId: undefined
|
||||
},
|
||||
deptSearchValue: '',
|
||||
selectedDeptName: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getDepartmentList()
|
||||
this.getMemberList()
|
||||
},
|
||||
methods: {
|
||||
/** 获取部门列表 */
|
||||
getDepartmentList() {
|
||||
this.departmentLoading = true
|
||||
// 这里添加获取部门列表的API调用
|
||||
setTimeout(() => {
|
||||
this.departmentList = [
|
||||
{
|
||||
id: 1,
|
||||
name: '小红马演示园区',
|
||||
level: '分公司',
|
||||
memberCount: 19,
|
||||
leader: '张三',
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
name: '技术部',
|
||||
level: '分公司',
|
||||
memberCount: 8,
|
||||
leader: '李四',
|
||||
children: [
|
||||
{
|
||||
id: 21,
|
||||
name: '技术1组',
|
||||
level: '分公司',
|
||||
memberCount: 4,
|
||||
leader: '王五'
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
name: '技术2组',
|
||||
level: '分公司',
|
||||
memberCount: 4,
|
||||
leader: '赵六'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '成都分公司',
|
||||
level: '分公司',
|
||||
memberCount: 5,
|
||||
leader: '王七',
|
||||
children: [
|
||||
{
|
||||
id: 31,
|
||||
name: '管理部门',
|
||||
level: '分公司',
|
||||
memberCount: 5,
|
||||
leader: '刘八'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '安防部',
|
||||
level: '分公司',
|
||||
memberCount: 3,
|
||||
leader: '钱九',
|
||||
children: [
|
||||
{
|
||||
id: 41,
|
||||
name: '保安',
|
||||
level: '分公司',
|
||||
memberCount: 3,
|
||||
leader: '孙十'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '保洁部',
|
||||
level: '分公司',
|
||||
memberCount: 2,
|
||||
leader: '周十一',
|
||||
children: [
|
||||
{
|
||||
id: 51,
|
||||
name: '电梯保洁',
|
||||
level: '分公司',
|
||||
memberCount: 1,
|
||||
leader: '吴十二'
|
||||
},
|
||||
{
|
||||
id: 52,
|
||||
name: '楼道保洁',
|
||||
level: '分公司',
|
||||
memberCount: 1,
|
||||
leader: '郑十三'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '巡检部门',
|
||||
level: '分公司',
|
||||
memberCount: 1,
|
||||
leader: '冯十四'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '客服部',
|
||||
level: '分公司',
|
||||
memberCount: 1,
|
||||
leader: '陈十五'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '维修部',
|
||||
level: '分公司',
|
||||
memberCount: 1,
|
||||
leader: '褚十六'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
this.departmentOptions = this.departmentList
|
||||
this.departmentLoading = false
|
||||
}, 500)
|
||||
},
|
||||
/** 获取成员列表 */
|
||||
getMemberList() {
|
||||
this.memberLoading = true
|
||||
// 这里添加获取成员列表的API调用
|
||||
setTimeout(() => {
|
||||
// 根据部门ID筛选成员
|
||||
let filteredList = [
|
||||
{
|
||||
id: 1,
|
||||
name: '小马',
|
||||
deptId: 21,
|
||||
deptName: '技术1组',
|
||||
status: '1',
|
||||
phone: '13800138001',
|
||||
gender: '1',
|
||||
employeeId: 'EMP001',
|
||||
position: '高级工程师',
|
||||
entryDate: '2023-01-15'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '罗卜',
|
||||
deptId: 21,
|
||||
deptName: '技术1组',
|
||||
status: '1',
|
||||
phone: '13800138002',
|
||||
gender: '2',
|
||||
employeeId: 'EMP002',
|
||||
position: '前端工程师',
|
||||
entryDate: '2023-02-20'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '车轮',
|
||||
deptId: 21,
|
||||
deptName: '技术1组',
|
||||
status: '1',
|
||||
phone: '13800138003',
|
||||
gender: '1',
|
||||
employeeId: 'EMP003',
|
||||
position: '后端工程师',
|
||||
entryDate: '2023-03-10'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '刘斌',
|
||||
deptId: 21,
|
||||
deptName: '技术1组',
|
||||
status: '1',
|
||||
phone: '13800138004',
|
||||
gender: '1',
|
||||
employeeId: 'EMP004',
|
||||
position: '测试工程师',
|
||||
entryDate: '2023-04-05'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '王师傅',
|
||||
deptId: 22,
|
||||
deptName: '技术2组',
|
||||
status: '1',
|
||||
phone: '13800138005',
|
||||
gender: '1',
|
||||
employeeId: 'EMP005',
|
||||
position: '技术主管',
|
||||
entryDate: '2023-01-01'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '王鹏飞',
|
||||
deptId: 22,
|
||||
deptName: '技术2组',
|
||||
status: '1',
|
||||
phone: '13800138006',
|
||||
gender: '1',
|
||||
employeeId: 'EMP006',
|
||||
position: '高级工程师',
|
||||
entryDate: '2023-02-15'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '正容',
|
||||
deptId: 22,
|
||||
deptName: '技术2组',
|
||||
status: '1',
|
||||
phone: '13800138007',
|
||||
gender: '2',
|
||||
employeeId: 'EMP007',
|
||||
position: 'UI设计师',
|
||||
entryDate: '2023-03-20'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '王小二',
|
||||
deptId: 22,
|
||||
deptName: '技术2组',
|
||||
status: '1',
|
||||
phone: '13800138008',
|
||||
gender: '1',
|
||||
employeeId: 'EMP008',
|
||||
position: '运维工程师',
|
||||
entryDate: '2023-04-10'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: '张三',
|
||||
deptId: 31,
|
||||
deptName: '管理部门',
|
||||
status: '1',
|
||||
phone: '13800138009',
|
||||
gender: '1',
|
||||
employeeId: 'EMP009',
|
||||
position: '部门经理',
|
||||
entryDate: '2023-01-05'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: '李四',
|
||||
deptId: 31,
|
||||
deptName: '管理部门',
|
||||
status: '1',
|
||||
phone: '13800138010',
|
||||
gender: '1',
|
||||
employeeId: 'EMP010',
|
||||
position: '行政主管',
|
||||
entryDate: '2023-02-01'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: '王五',
|
||||
deptId: 31,
|
||||
deptName: '管理部门',
|
||||
status: '1',
|
||||
phone: '13800138011',
|
||||
gender: '1',
|
||||
employeeId: 'EMP011',
|
||||
position: '人力资源',
|
||||
entryDate: '2023-03-15'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: '赵六',
|
||||
deptId: 31,
|
||||
deptName: '管理部门',
|
||||
status: '1',
|
||||
phone: '13800138012',
|
||||
gender: '2',
|
||||
employeeId: 'EMP012',
|
||||
position: '财务主管',
|
||||
entryDate: '2023-04-01'
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: '钱七',
|
||||
deptId: 31,
|
||||
deptName: '管理部门',
|
||||
status: '1',
|
||||
phone: '13800138013',
|
||||
gender: '2',
|
||||
employeeId: 'EMP013',
|
||||
position: '会计',
|
||||
entryDate: '2023-05-10'
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: '孙八',
|
||||
deptId: 41,
|
||||
deptName: '保安',
|
||||
status: '1',
|
||||
phone: '13800138014',
|
||||
gender: '1',
|
||||
employeeId: 'EMP014',
|
||||
position: '保安队长',
|
||||
entryDate: '2023-01-10'
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: '周九',
|
||||
deptId: 41,
|
||||
deptName: '保安',
|
||||
status: '1',
|
||||
phone: '13800138015',
|
||||
gender: '1',
|
||||
employeeId: 'EMP015',
|
||||
position: '保安',
|
||||
entryDate: '2023-02-15'
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: '吴十',
|
||||
deptId: 41,
|
||||
deptName: '保安',
|
||||
status: '1',
|
||||
phone: '13800138016',
|
||||
gender: '1',
|
||||
employeeId: 'EMP016',
|
||||
position: '保安',
|
||||
entryDate: '2023-03-20'
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
name: '郑十一',
|
||||
deptId: 51,
|
||||
deptName: '电梯保洁',
|
||||
status: '1',
|
||||
phone: '13800138017',
|
||||
gender: '2',
|
||||
employeeId: 'EMP017',
|
||||
position: '保洁员',
|
||||
entryDate: '2023-01-20'
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: '王十二',
|
||||
deptId: 52,
|
||||
deptName: '楼道保洁',
|
||||
status: '1',
|
||||
phone: '13800138018',
|
||||
gender: '2',
|
||||
employeeId: 'EMP018',
|
||||
position: '保洁员',
|
||||
entryDate: '2023-02-25'
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: '冯十三',
|
||||
deptId: 6,
|
||||
deptName: '巡检部门',
|
||||
status: '0',
|
||||
phone: '13800138019',
|
||||
gender: '1',
|
||||
employeeId: 'EMP019',
|
||||
position: '巡检员',
|
||||
entryDate: '2023-03-30'
|
||||
}
|
||||
]
|
||||
|
||||
// 如果有部门ID筛选,则过滤数据(包含子部门)
|
||||
if (this.queryParams.deptId) {
|
||||
// 获取所选部门及其所有子部门的ID
|
||||
const deptIds = this.getChildDeptIds(this.queryParams.deptId)
|
||||
filteredList = filteredList.filter(item => deptIds.includes(item.deptId))
|
||||
}
|
||||
|
||||
// 如果有状态筛选,则过滤数据
|
||||
if (this.queryParams.status) {
|
||||
filteredList = filteredList.filter(item => item.status === this.queryParams.status)
|
||||
}
|
||||
|
||||
this.memberList = filteredList
|
||||
this.total = filteredList.length
|
||||
this.memberLoading = false
|
||||
}, 500)
|
||||
},
|
||||
/** 获取部门及其所有子部门的ID */
|
||||
getChildDeptIds(deptId) {
|
||||
const ids = []
|
||||
|
||||
const collectIds = (dept) => {
|
||||
ids.push(dept.id)
|
||||
if (dept.children) {
|
||||
dept.children.forEach(child => {
|
||||
collectIds(child)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const findDepartment = (deptList) => {
|
||||
for (const dept of deptList) {
|
||||
if (dept.id === deptId) {
|
||||
collectIds(dept)
|
||||
return true
|
||||
}
|
||||
if (dept.children && findDepartment(dept.children)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
findDepartment(this.departmentList)
|
||||
return ids
|
||||
},
|
||||
/** 转换部门数据结构 */
|
||||
normalizer(node) {
|
||||
if (node.children && !node.children.length) {
|
||||
delete node.children
|
||||
}
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children
|
||||
}
|
||||
},
|
||||
// 部门相关方法
|
||||
handleAddDepartment() {
|
||||
this.departmentForm = {
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
name: undefined,
|
||||
level: undefined,
|
||||
leader: undefined
|
||||
}
|
||||
this.departmentTitle = '添加部门'
|
||||
this.departmentOpen = true
|
||||
},
|
||||
handleEditDepartment(row) {
|
||||
this.departmentForm = row
|
||||
this.departmentTitle = '修改部门'
|
||||
this.departmentOpen = true
|
||||
},
|
||||
handleDeleteDepartment(row) {
|
||||
this.$confirm('是否确认删除名称为"' + row.name + '"的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 这里添加删除部门的API调用
|
||||
this.$message.success('删除成功')
|
||||
this.getDepartmentList()
|
||||
})
|
||||
},
|
||||
submitDepartmentForm() {
|
||||
this.$refs['departmentForm'].validate(valid => {
|
||||
if (valid) {
|
||||
// 这里添加新增或修改部门的API调用
|
||||
this.$message.success('操作成功')
|
||||
this.departmentOpen = false
|
||||
this.getDepartmentList()
|
||||
}
|
||||
})
|
||||
},
|
||||
cancelDepartment() {
|
||||
this.departmentOpen = false
|
||||
this.$refs['departmentForm'].resetFields()
|
||||
},
|
||||
handleAddSubDepartment(row) {
|
||||
this.departmentForm = {
|
||||
id: undefined,
|
||||
parentId: row.id,
|
||||
name: undefined,
|
||||
level: row.level + 1,
|
||||
leader: undefined
|
||||
}
|
||||
this.departmentTitle = '添加子部门'
|
||||
this.departmentOpen = true
|
||||
},
|
||||
// 成员相关方法
|
||||
handleAddMember() {
|
||||
this.memberForm = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
deptId: undefined,
|
||||
position: undefined,
|
||||
phone: undefined,
|
||||
email: undefined,
|
||||
status: '1'
|
||||
}
|
||||
this.memberTitle = '添加成员'
|
||||
this.memberOpen = true
|
||||
},
|
||||
handleEditMember(row) {
|
||||
this.memberForm = row
|
||||
this.memberTitle = '修改成员'
|
||||
this.memberOpen = true
|
||||
},
|
||||
handleDeleteMember(row) {
|
||||
this.$confirm('是否确认删除姓名为"' + row.name + '"的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 这里添加删除成员的API调用
|
||||
this.$message.success('删除成功')
|
||||
this.getMemberList()
|
||||
})
|
||||
},
|
||||
submitMemberForm() {
|
||||
this.$refs['memberForm'].validate(valid => {
|
||||
if (valid) {
|
||||
// 这里添加新增或修改成员的API调用
|
||||
this.$message.success('操作成功')
|
||||
this.memberOpen = false
|
||||
this.getMemberList()
|
||||
}
|
||||
})
|
||||
},
|
||||
cancelMember() {
|
||||
this.memberOpen = false
|
||||
this.$refs['memberForm'].resetFields()
|
||||
},
|
||||
// 查询相关方法
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getMemberList()
|
||||
},
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
deptId: undefined
|
||||
}
|
||||
this.getMemberList()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getMemberList()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getMemberList()
|
||||
},
|
||||
filterNode(value, data) {
|
||||
if (!value) return true;
|
||||
return data.name.indexOf(value) !== -1;
|
||||
},
|
||||
handleNodeClick(node) {
|
||||
this.selectedDeptName = node.label;
|
||||
this.queryParams.deptId = node.id;
|
||||
this.getMemberList();
|
||||
},
|
||||
handleStatusChange() {
|
||||
this.getMemberList();
|
||||
},
|
||||
handleDetail(row) {
|
||||
// 实现详情功能
|
||||
},
|
||||
handleImportMember() {
|
||||
// 实现申请入列表功能
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.el-tabs {
|
||||
margin-bottom: 20px;
|
||||
height: calc(100vh - 60px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.el-tabs ::v-deep .el-tabs__content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-tabs ::v-deep .el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
.el-button + .el-button {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.member-container {
|
||||
display: flex;
|
||||
height: calc(100vh - 180px);
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.department-tree {
|
||||
width: 280px;
|
||||
border-right: 1px solid #e6e6e6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tree-header {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.tree-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.member-list {
|
||||
flex: 1;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.member-header {
|
||||
padding: 16px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-title .member-count {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.member-filter {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.custom-tree-node .member-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-table ::v-deep .el-table__body-wrapper {
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
margin-top: 20px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
203
pc/src/views/system/department/index.vue
Normal file
203
pc/src/views/system/department/index.vue
Normal file
@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>部门管理</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text" @click="handleAdd">新增部门</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="departmentList"
|
||||
row-key="id"
|
||||
border
|
||||
default-expand-all
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
>
|
||||
<el-table-column prop="name" label="部门名称" />
|
||||
<el-table-column prop="code" label="部门编码" />
|
||||
<el-table-column prop="leader" label="负责人" />
|
||||
<el-table-column prop="phone" label="联系电话" />
|
||||
<el-table-column prop="email" label="邮箱" />
|
||||
<el-table-column label="操作" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改部门对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="上级部门" prop="parentId">
|
||||
<treeselect
|
||||
v-model="form.parentId"
|
||||
:options="departmentOptions"
|
||||
:normalizer="normalizer"
|
||||
placeholder="选择上级部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入部门名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门编码" prop="code">
|
||||
<el-input v-model="form.code" placeholder="请输入部门编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="leader">
|
||||
<el-input v-model="form.leader" placeholder="请输入负责人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
|
||||
export default {
|
||||
name: 'Department',
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 部门表格数据
|
||||
departmentList: [],
|
||||
// 部门树选项
|
||||
departmentOptions: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '部门名称不能为空', trigger: 'blur' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '部门编码不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 查询部门列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
// 这里添加获取部门列表的API调用
|
||||
setTimeout(() => {
|
||||
this.departmentList = [
|
||||
{
|
||||
id: 1,
|
||||
name: '总公司',
|
||||
code: 'HQ',
|
||||
leader: '张三',
|
||||
phone: '13800138000',
|
||||
email: 'zhangsan@example.com',
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
name: '技术部',
|
||||
code: 'TECH',
|
||||
leader: '李四',
|
||||
phone: '13800138001',
|
||||
email: 'lisi@example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
this.loading = false
|
||||
}, 500)
|
||||
},
|
||||
/** 转换部门数据结构 */
|
||||
normalizer(node) {
|
||||
if (node.children && !node.children.length) {
|
||||
delete node.children
|
||||
}
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children
|
||||
}
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false
|
||||
this.reset()
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
leader: undefined,
|
||||
phone: undefined,
|
||||
email: undefined
|
||||
}
|
||||
this.resetForm('form')
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.open = true
|
||||
this.title = '添加部门'
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleEdit(row) {
|
||||
this.reset()
|
||||
this.form = row
|
||||
this.open = true
|
||||
this.title = '修改部门'
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$confirm('是否确认删除名称为"' + row.name + '"的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 这里添加删除部门的API调用
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
// 这里添加新增或修改部门的API调用
|
||||
this.$message.success('操作成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
1
pc/src/views/system/index.vue
Normal file
1
pc/src/views/system/index.vue
Normal file
@ -0,0 +1 @@
|
||||
|
285
pc/src/views/system/member/index.vue
Normal file
285
pc/src/views/system/member/index.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>成员管理</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text" @click="handleAdd">新增成员</el-button>
|
||||
</div>
|
||||
|
||||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||||
<el-form-item label="部门">
|
||||
<treeselect
|
||||
v-model="queryParams.deptId"
|
||||
:options="departmentOptions"
|
||||
:normalizer="normalizer"
|
||||
placeholder="选择部门"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="mini" @click="handleQuery">查询</el-button>
|
||||
<el-button size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="memberList"
|
||||
row-key="id"
|
||||
border
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="50" />
|
||||
<el-table-column prop="name" label="姓名" />
|
||||
<el-table-column prop="deptName" label="所属部门" />
|
||||
<el-table-column prop="position" label="职位" />
|
||||
<el-table-column prop="phone" label="联系电话" />
|
||||
<el-table-column prop="email" label="邮箱" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'">
|
||||
{{ scope.row.status === '1' ? '在职' : '离职' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="queryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改成员对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<treeselect
|
||||
v-model="form.deptId"
|
||||
:options="departmentOptions"
|
||||
:normalizer="normalizer"
|
||||
placeholder="选择部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="职位" prop="position">
|
||||
<el-input v-model="form.position" placeholder="请输入职位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio label="1">在职</el-radio>
|
||||
<el-radio label="0">离职</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
|
||||
export default {
|
||||
name: 'Member',
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 成员表格数据
|
||||
memberList: [],
|
||||
// 部门树选项
|
||||
departmentOptions: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
deptId: undefined
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '姓名不能为空', trigger: 'blur' }
|
||||
],
|
||||
deptId: [
|
||||
{ required: true, message: '所属部门不能为空', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
this.getDepartmentOptions()
|
||||
},
|
||||
methods: {
|
||||
/** 查询成员列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
// 这里添加获取成员列表的API调用
|
||||
setTimeout(() => {
|
||||
this.memberList = [
|
||||
{
|
||||
id: 1,
|
||||
name: '张三',
|
||||
deptName: '技术部',
|
||||
position: '工程师',
|
||||
phone: '13800138000',
|
||||
email: 'zhangsan@example.com',
|
||||
status: '1'
|
||||
}
|
||||
]
|
||||
this.total = 1
|
||||
this.loading = false
|
||||
}, 500)
|
||||
},
|
||||
/** 获取部门选项 */
|
||||
getDepartmentOptions() {
|
||||
// 这里添加获取部门选项的API调用
|
||||
this.departmentOptions = [
|
||||
{
|
||||
id: 1,
|
||||
name: '总公司',
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
name: '技术部'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
/** 转换部门数据结构 */
|
||||
normalizer(node) {
|
||||
if (node.children && !node.children.length) {
|
||||
delete node.children
|
||||
}
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children
|
||||
}
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false
|
||||
this.reset()
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
deptId: undefined,
|
||||
position: undefined,
|
||||
phone: undefined,
|
||||
email: undefined,
|
||||
status: '1'
|
||||
}
|
||||
this.resetForm('form')
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.open = true
|
||||
this.title = '添加成员'
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleEdit(row) {
|
||||
this.reset()
|
||||
this.form = row
|
||||
this.open = true
|
||||
this.title = '修改成员'
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$confirm('是否确认删除姓名为"' + row.name + '"的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 这里添加删除成员的API调用
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
// 这里添加新增或修改成员的API调用
|
||||
this.$message.success('操作成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 分页大小改变 */
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
/** 分页页码改变 */
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
23
pc/vue.config.js
Normal file
23
pc/vue.config.js
Normal file
@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
publicPath: '/',
|
||||
outputDir: 'dist',
|
||||
assetsDir: 'static',
|
||||
productionSourceMap: false,
|
||||
devServer: {
|
||||
port: 8080,
|
||||
open: true,
|
||||
overlay: {
|
||||
warnings: false,
|
||||
errors: true
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api': '/api'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user