新增飞行员管理,修复用户管理、区域管理bug,修复404返回首页路径bug,修复查看个人中心bug

This commit is contained in:
Double-_-Z 2025-07-19 23:59:08 +08:00
parent 1bce9382fb
commit 1098f19aaf
6 changed files with 436 additions and 129 deletions

45
src/api/system/pilot.js Normal file
View File

@ -0,0 +1,45 @@
import request from '@/utils/request'
import { encrypt } from '@/utils/rsaEncrypt'
export function add(data) {
data.password = encrypt(data.password)
console.log(data);
return request({
url: 'api/emEmployees',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/emEmployees',
method: 'delete',
data: ids
})
}
export function edit(data) {
if(data.password) data.password = encrypt(data.password)
return request({
url: 'api/emEmployees',
method: 'put',
data
})
}
export function updatePass(user) {
const data = {
oldPass: encrypt(user.oldPass),
newPass: encrypt(user.newPass)
}
return request({
url: 'api/emEmployees/updatePass/',
method: 'post',
data
})
}
export default { add, edit, del }

View File

@ -11,7 +11,7 @@
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确请点击以下按钮返回主页或者发送错误报告</div>
<a href="/" class="bullshit__return-home">返回首页</a>
<a :href="backUrl" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
@ -21,6 +21,11 @@
export default {
name: 'Page404',
data(){
return {
backUrl: process.env.VUE_APP_BASE_PATH
}
},
computed: {
message() {
return '网管说这个页面你不能进......'

View File

@ -11,7 +11,7 @@
:props="{ children: 'sons', label: 'name' }" @node-click="handleNodeClick">
<template slot-scope="{node,data}">
<div style="width:100%;" class="treeData"
:class="{ activeBlue: active === data.id, activeIndent: node.level === 3 || node.level === 4 }"
:class="{ activeBlue: active === data.id && form.type === data.type, activeIndent: node.level === 3 || node.level === 4 }"
@click="handleTool(node, data)">
<div v-if="node.level !== 1" class="dashedBox">
<div class="firstBox" />
@ -121,6 +121,7 @@ export default {
this.lamLoading = true;
await crudArea.tree().then(async(res) => {
res.id = 0;
res.type = 'area';
this.list[0] = res;
await this.getTree();
})
@ -141,9 +142,9 @@ export default {
parentId: data.parentId,
parentName: data.parentName
}])
const type = node.level === 1
const type = node.level === 1 || node.level === 2
this.crud.title = type ? '区域' : '景区'
this.form.type = type ? 'area' : 'spot'
this.form.type = type ? 'area' : 'scenic'
this.getList(id,data)
},
handleSourceClick(){

View File

@ -0,0 +1,369 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!--飞行员数据-->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<!--工具栏-->
<div class="head-container">
<div>
<!-- 搜索 -->
<el-input
v-model="query.name"
clearable
size="small"
placeholder="输入飞行员名称"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<rrOperation />
<el-button
v-permission="permission.add"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-plus"
@click="crud.toAdd"
>
新增飞行员
</el-button>
</div>
</div>
<!--表单渲染-->
<el-dialog append-to-body :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title"
@close="uploadDisabled = false;">
<el-form ref="form" v-loading="loading" :model="form" :rules="rules" size="small" label-width="80px">
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" @keydown.native="keydown($event)" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="账户" prop="username">
<el-input v-model="form.username" placeholder="请输入账户" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="手机号" prop="phone">
<el-input v-model.number="form.phone" type="tel" placeholder="请输入手机号" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="区域" prop="areaId">
<el-select v-model="form.areaId" placeholder="请选择区域" @change="changeArea">
<el-option v-for="item in areas" :key="item.name" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="景区" prop="scenicId">
<el-select v-model="form.scenicId" placeholder="请选择景区">
<el-option v-for="item in scenics" :key="item.name"
:label="item.name" :value="Number(item.id)"/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="飞行资质" prop="qualification">
<el-select v-model="form.qualification" placeholder="请选择飞行资质" clearable>
<el-option v-for="item in dict.user_qualification" :key="item.id" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="飞行资质附件" prop="files" label-width="120px">
<el-upload
ref="uploadRef"
action="#"
list-type="picture-card"
:class="{ disabled: uploadDisabled }"
:limit="1"
:file-list="form.files"
:on-change="handleChange"
:auto-upload="false"
>
<div style="display: flex;flex-direction: column;align-items: center;justify-content: center;margin-top: -20px;">
<i class="el-icon-plus" style="font-size: 30px;" />
<span style="margin: 5px 0 -20px;line-height: 20px">上传</span>
</div>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="">
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</div>
</template>
</el-upload>
<el-dialog append-to-body :visible.sync="dialogVisible" top="20vh">
<div>
<img :src="dialogImageUrl" style="width: 100%" alt="Preview Image">
</div>
</el-dialog>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer" style="margin-top: -30px;">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column :show-overflow-tooltip="true" prop="name" label="姓名" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="username" label="账户" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="手机号" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="areaName" label="区域" :formatter="commonFormatter" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="scenicName" label="景区" :formatter="commonFormatter" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期" align="center"/>
<el-table-column
v-if="checkPer(['admin','user:edit','user:del'])"
label="操作"
width="115"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
:disabled-dle="scope.row.id === user.id"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</el-col>
</el-row>
</div>
</template>
<script>
import crudPilot from '@/api/system/pilot'
import { isvalidPhone } from '@/utils/validate'
import { allAreas } from '@/api/system/area'
import { allScenic } from '@/api/system/scenic'
import { getAll } from '@/api/system/role'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import { mapGetters } from 'vuex'
import { upload } from '@/utils/upload'
const defaultForm = { id: null, username: null, name: null,
phone: null, areaId: null, scenicId: null, roles: null, qualification: null,
qualificationAttachment: null, files: [], userType: null, enabled: 1 }
export default {
name: 'Pilot',
components: { rrOperation, udOperation, pagination },
cruds() {
return CRUD({ title: '飞行员', url: 'api/emEmployees/findByPage', crudMethod: { ...crudPilot }, optShow: { add: true, reset: true }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
//
dicts: ['user_qualification'],
data() {
//
const validPhone = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入电话号码'))
} else if (!isvalidPhone(value)) {
callback(new Error('请输入正确的11位手机号码'))
} else {
callback()
}
}
return {
height: document.documentElement.clientHeight - 180 + 'px;',
roles: [],
defaultProps: { children: 'children', label: 'name', isLeaf: 'leaf' },
permission: {
add: ['admin', 'user:add'],
edit: ['admin', 'user:edit'],
del: ['admin', 'user:del']
},
areas: [],
scenics: [],
uploadDisabled: false,
dialogVisible: false,
loading: false,
dialogImageUrl: '',
rules: {
username: [
{ required: true, message: '请输入账户', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
],
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
],
phone: [
{ required: true, trigger: 'blur', validator: validPhone }
],
areaId: [
{ required: true, message: '区域不能为空', trigger: 'blur' }
],
scenicId: [
{ required: true, message: '景区不能为空', trigger: 'blur' }
],
qualification: [
{ required: true, message: '飞行资质不能为空', trigger: 'blur' }
],
files: [
{ required: false, message: '请上传资质文件', trigger: 'change' }
],
}
}
},
computed: {
...mapGetters([
'user',
'imagesUploadApi',
'baseApi'
])
},
created() {
this.crud.msg.add = '新增成功'
},
mounted: function() {
const that = this
window.onresize = function temp() {
that.height = document.documentElement.clientHeight - 180 + 'px;'
}
},
methods: {
//
keydown(e) {
if (e.keyCode === 32) {
e.returnValue = false
}
},
//
handlePictureCardPreview(file, uploadFiles) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
//
handleRemove(file, uploadFiles) {
this.$refs['uploadRef'].clearFiles()
this.form.qualificationAttachment = ''
this.form.files = [];
this.uploadDisabled = false
},
commonFormatter(row, column){
return row[column.property] || '暂无';
},
//
handleChange(file, uploadFiles) {
if (file.raw.type === 'image/png' || file.raw.type === 'image/jpeg' || file.raw.type === 'image/jpg' || file.raw.type === 'image/gif') {
this.loading = true
this.form.files = uploadFiles
this.uploadDisabled = true
try {
upload(this.imagesUploadApi, file.raw).then(res => {
if(res.status === 200){
const data = res.data;
const url = `/file/图片/` + data.newFileName;
this.form.qualificationAttachment = url;
this.loading = false;
}else{
this.$message.error(`图片上传失败!`);
this.loading = false;
return;
}
})
} catch (error) {
this.loading = false
this.$message.error('图片上传失败!')
}
} else {
this.$confirm('图片的格式不正确,请重新选择!', '提示', {
confirmButtonText: '确定',
type: 'warning'
})
uploadFiles.pop()
this.uploadDisabled = false
}
},
//
changeArea(value){
this.form.scenicId = '';
allScenic({areaId: value}).then(res=>{
this.scenics = res || [];
})
},
//
[CRUD.HOOK.afterToCU](crud, form) {
this.getRoles()
this.getAreas()
if(form.scenicId&&form.areaId){
allScenic({areaId: form.areaId}).then(res=>{
this.scenics = res || [];
})
}
},
//
[CRUD.HOOK.beforeToAdd]() {
this.roleDatas = []
},
//
[CRUD.HOOK.beforeToEdit](crud, form) {
this.roleDatas = []
if(form.scenicId) form.scenicId = Number(form.scenicId);
if(form.qualificationAttachment){
this.form.files = [{url: this.baseApi + form.qualificationAttachment,}]
this.uploadDisabled = true;
}
},
//
[CRUD.HOOK.afterValidateCU](crud) {
const rolesIds = this.roles.find(item=>item.name === '飞行员').id;
crud.form.userType = 1;
crud.form.roles = [{id:rolesIds}];
return true;
},
//
getAreas(){
allAreas().then(res => {
this.areas = res;
})
},
//
getRoles() {
getAll().then(res => {
this.roles = res
}).catch(() => { })
},
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-upload--picture-card,::v-deep .el-upload-list__item{
width: 110px !important;
height: 110px !important;
display: flex;
align-items: center;
justify-content: center;
}
::v-deep .disabled{
.el-upload--picture-card{
display: none;
}
}
</style>

View File

@ -21,7 +21,6 @@
<ul class="user-info">
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
<li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
<li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
<li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
<li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
<li>

View File

@ -30,8 +30,7 @@
</div>
</div>
<!--表单渲染-->
<el-dialog append-to-body :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title"
@close="uploadDisabled = false;">
<el-dialog append-to-body :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title">
<el-form ref="form" v-loading="loading" :model="form" :rules="rules" size="small" label-width="80px">
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
@ -76,56 +75,6 @@
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="飞行资质" prop="qualification">
<el-select v-model="form.qualification" placeholder="请选择飞行资质" clearable>
<el-option v-for="item in dict.user_qualification" :key="item.id" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8" :xl="6" class="mb20">
<el-form-item label="飞行资质附件" prop="files" label-width="120px">
<el-upload
ref="uploadRef"
action="#"
list-type="picture-card"
:class="{ disabled: uploadDisabled }"
:limit="1"
:file-list="form.files"
:on-change="handleChange"
:auto-upload="false"
>
<div style="display: flex;flex-direction: column;align-items: center;justify-content: center;margin-top: -20px;">
<i class="el-icon-plus" style="font-size: 30px;" />
<span style="margin: 5px 0 -20px;line-height: 20px">上传</span>
</div>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="">
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</div>
</template>
</el-upload>
<el-dialog append-to-body :visible.sync="dialogVisible" top="20vh">
<div>
<img :src="dialogImageUrl" style="width: 100%" alt="Preview Image">
</div>
</el-dialog>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer" style="margin-top: -30px;">
@ -136,13 +85,13 @@
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="nickName" label="姓名" />
<el-table-column :show-overflow-tooltip="true" prop="username" label="账户" />
<el-table-column show-overflow-tooltip prop="roles[0].name" label="角色" />
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="手机号" />
<el-table-column :show-overflow-tooltip="true" prop="areaName" label="区域" :formatter="commonFormatter" />
<el-table-column :show-overflow-tooltip="true" prop="scenicName" label="景区" :formatter="commonFormatter" />
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期" />
<el-table-column :show-overflow-tooltip="true" prop="nickName" label="姓名" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="username" label="账户" align="center"/>
<el-table-column show-overflow-tooltip prop="roles[0].name" label="角色" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="手机号" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="areaName" label="区域" :formatter="commonFormatter" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="scenicName" label="景区" :formatter="commonFormatter" align="center"/>
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期" align="center"/>
<el-table-column
v-if="checkPer(['admin','user:edit','user:del'])"
label="操作"
@ -177,10 +126,8 @@ import rrOperation from '@crud/RR.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import { mapGetters } from 'vuex'
import { upload } from '@/utils/upload'
const defaultForm = { id: null, username: null, nickName: null, password: null,
phone: null, areaId: null, scenicId: null, roles: null, qualification: null,
qualificationAttachment: null, files: [], userType: null, enabled: 1 }
phone: null, areaId: null, scenicId: null, roles: null, userType: null, enabled: 1 }
export default {
name: 'User',
components: { rrOperation, udOperation, pagination },
@ -189,7 +136,7 @@ export default {
},
mixins: [presenter(), header(), form(defaultForm), crud()],
//
dicts: ['user_status','user_qualification'],
dicts: ['user_status'],
data() {
//
const validPhone = (rule, value, callback) => {
@ -212,10 +159,7 @@ export default {
},
areas: [],
scenics: [],
uploadDisabled: false,
dialogVisible: false,
loading: false,
dialogImageUrl: '',
rules: {
username: [
{ required: true, message: '请输入账户', trigger: 'blur' },
@ -234,12 +178,6 @@ export default {
areaId: [
{ required: false, message: '区域不能为空', trigger: 'blur' }
],
qualification: [
{ required: true, message: '飞行资质不能为空', trigger: 'blur' }
],
files: [
{ required: false, message: '请上传资质文件', trigger: 'change' }
],
roles: [
{ required: true, message: '角色不能为空', trigger: 'blur' }
]
@ -269,53 +207,9 @@ export default {
e.returnValue = false
}
},
//
handlePictureCardPreview(file, uploadFiles) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
//
handleRemove(file, uploadFiles) {
this.$refs['uploadRef'].clearFiles()
this.form.qualificationAttachment = ''
this.form.files = [];
this.uploadDisabled = false
},
commonFormatter(row, column){
return row[column.property] || '暂无';
},
//
handleChange(file, uploadFiles) {
if (file.raw.type === 'image/png' || file.raw.type === 'image/jpeg' || file.raw.type === 'image/jpg' || file.raw.type === 'image/gif') {
this.loading = true
this.form.files = uploadFiles
this.uploadDisabled = true
try {
upload(this.imagesUploadApi, file.raw).then(res => {
if(res.status === 200){
const data = res.data;
const url = `/file/图片/` + data.newFileName;
this.form.qualificationAttachment = url;
this.loading = false;
}else{
this.$message.error(`图片上传失败!`);
this.loading = false;
return;
}
})
} catch (error) {
this.loading = false
this.$message.error('图片上传失败!')
}
} else {
this.$confirm('图片的格式不正确,请重新选择!', '提示', {
confirmButtonText: '确定',
type: 'warning'
})
uploadFiles.pop()
this.uploadDisabled = false
}
},
//
changeArea(value){
this.form.scenicId = '';
@ -343,19 +237,13 @@ export default {
form.password = '$123456$';
form.roles = form.roles[0].id;
if(form.scenicId) form.scenicId = Number(form.scenicId);
if(form.qualificationAttachment){
this.form.files = [{url: this.baseApi + form.qualificationAttachment,}]
this.uploadDisabled = true;
}
},
//
[CRUD.HOOK.afterValidateCU](crud) {
const rolesIds = JSON.parse(JSON.stringify(crud.form.roles));
const roleItem = this.roles.find(item=>item.id === crud.form.roles);
crud.form.userType = roleItem.name === '飞行员' ? 1 : 0;
crud.form.userType = 0;
crud.form.roles = [{id:rolesIds}];
console.log(crud.form.password);
if(crud.form.password === '$123456$')
delete crud.form.password;
return true;
@ -379,7 +267,7 @@ export default {
//
getRoles() {
getAll().then(res => {
this.roles = res
this.roles = res.filter(item => item.name !== '飞行员')
}).catch(() => { })
},
checkboxT(row, rowIndex) {