mirror of
https://github.com/veops/cmdb.git
synced 2025-08-09 22:38:53 +08:00
前后端全面升级
This commit is contained in:
371
cmdb-ui/src/views/setting/companyInfo/index.vue
Normal file
371
cmdb-ui/src/views/setting/companyInfo/index.vue
Normal file
@@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<div class="ops-setting-companyinfo" :style="{ height: `${windowHeight - 64}px` }">
|
||||
<a-form-model ref="infoData" :model="infoData" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rule">
|
||||
<SpanTitle>公司描述</SpanTitle>
|
||||
<a-form-model-item label="名称" prop="name">
|
||||
<a-input v-model="infoData.name" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="描述">
|
||||
<a-input v-model="infoData.description" type="textarea" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<SpanTitle>公司地址</SpanTitle>
|
||||
<a-form-model-item label="国家/地区">
|
||||
<a-input v-model="infoData.country" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="城市">
|
||||
<a-input v-model="infoData.city" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="地址">
|
||||
<a-input v-model="infoData.address" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="邮编">
|
||||
<a-input v-model="infoData.postCode" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<SpanTitle>联系方式</SpanTitle>
|
||||
<a-form-model-item label="网站">
|
||||
<a-input v-model="infoData.website" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="电话号码" prop="phone">
|
||||
<a-input v-model="infoData.phone" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="传真号码" prop="faxCode">
|
||||
<a-input v-model="infoData.faxCode" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="电子邮箱" prop="email">
|
||||
<a-input v-model="infoData.email" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<SpanTitle>公司标识</SpanTitle>
|
||||
<a-form-model-item label="部署域名" prop="domainName">
|
||||
<a-input v-model="infoData.domainName" :disabled="!isEditable" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="公司logo">
|
||||
<a-space>
|
||||
<a-upload
|
||||
:disabled="!isEditable"
|
||||
name="avatar"
|
||||
list-type="picture-card"
|
||||
class="avatar-uploader"
|
||||
:show-upload-list="false"
|
||||
:customRequest="customRequest"
|
||||
:before-upload="beforeUpload"
|
||||
:style="{ width: '400px', height: '80px' }"
|
||||
accept=".png,.jpg,.jpeg"
|
||||
>
|
||||
<div
|
||||
class="ops-setting-companyinfo-upload-show"
|
||||
v-if="infoData.logoName"
|
||||
:style="{ width: '400px', height: '80px' }"
|
||||
@click="eidtImageOption.type = 'Logo'"
|
||||
>
|
||||
<img :src="`/api/common-setting/v1/file/${infoData.logoName}`" alt="avatar" />
|
||||
<a-icon
|
||||
v-if="isEditable"
|
||||
type="minus-circle"
|
||||
theme="filled"
|
||||
class="delete-icon"
|
||||
@click.stop="deleteLogo"
|
||||
/>
|
||||
</div>
|
||||
<div v-else @click="eidtImageOption.type = 'Logo'">
|
||||
<a-icon type="plus" />
|
||||
<div class="ant-upload-text">上传</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
|
||||
<a-upload
|
||||
:disabled="!isEditable"
|
||||
name="avatar"
|
||||
list-type="picture-card"
|
||||
class="avatar-uploader"
|
||||
:show-upload-list="false"
|
||||
:customRequest="customRequest"
|
||||
:before-upload="beforeUpload"
|
||||
:style="{ width: '82px', height: '82px' }"
|
||||
accept=".png,.jpg,.jpeg"
|
||||
>
|
||||
<div
|
||||
class="ops-setting-companyinfo-upload-show"
|
||||
v-if="infoData.smallLogoName"
|
||||
:style="{ width: '82px', height: '82px' }"
|
||||
@click="eidtImageOption.type = 'SmallLogo'"
|
||||
>
|
||||
<img :src="`/api/common-setting/v1/file/${infoData.smallLogoName}`" alt="avatar" />
|
||||
<a-icon
|
||||
v-if="isEditable"
|
||||
type="minus-circle"
|
||||
theme="filled"
|
||||
class="delete-icon"
|
||||
@click.stop="deleteSmallLogo"
|
||||
/>
|
||||
</div>
|
||||
<div v-else @click="eidtImageOption.type = 'SmallLogo'">
|
||||
<a-icon type="plus" />
|
||||
<div class="ant-upload-text">上传</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</a-space>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :wrapper-col="{ span: 14, offset: 3 }" v-if="isEditable">
|
||||
<a-button type="primary" @click="onSubmit"> 保存 </a-button>
|
||||
<a-button ghost type="primary" style="margin-left: 28px" @click="resetForm"> 重置 </a-button>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<edit-image
|
||||
v-if="showEditImage"
|
||||
:show="showEditImage"
|
||||
:image="editImage"
|
||||
:title="eidtImageOption.title"
|
||||
:eidtImageOption="eidtImageOption"
|
||||
@save="submitImage"
|
||||
@close="showEditImage = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCompanyInfo, postCompanyInfo, putCompanyInfo, postImageFile } from '@/api/company'
|
||||
import { mapMutations, mapState } from 'vuex'
|
||||
import SpanTitle from '../components/spanTitle.vue'
|
||||
import EditImage from '../components/EditImage.vue'
|
||||
import { mixinPermissions } from '@/utils/mixin'
|
||||
export default {
|
||||
name: 'CompanyInfo',
|
||||
mixins: [mixinPermissions],
|
||||
components: { SpanTitle, EditImage },
|
||||
data() {
|
||||
return {
|
||||
labelCol: { span: 3 },
|
||||
wrapperCol: { span: 10 },
|
||||
infoData: {
|
||||
name: '',
|
||||
description: '',
|
||||
address: '',
|
||||
city: '',
|
||||
postCode: '',
|
||||
country: '',
|
||||
website: '',
|
||||
phone: '',
|
||||
faxCode: '',
|
||||
email: '',
|
||||
logoName: '',
|
||||
smallLogoName: '',
|
||||
},
|
||||
rule: {
|
||||
name: [{ required: true, whitespace: true, message: '请输入名称', trigger: 'blur' }],
|
||||
phone: [
|
||||
{
|
||||
required: false,
|
||||
whitespace: true,
|
||||
pattern: new RegExp('^([0-9]|-)+$', 'g'),
|
||||
message: '请输入正确的电话号码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
faxCode: [
|
||||
{
|
||||
required: false,
|
||||
whitespace: true,
|
||||
pattern: new RegExp('^([0-9]|-)+$', 'g'),
|
||||
message: '请输入正确的传真号码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
email: [
|
||||
{
|
||||
required: false,
|
||||
whitespace: true,
|
||||
pattern: new RegExp('^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(.[a-zA-Z0-9-]+)*.[a-zA-Z0-9]{2,6}$', 'g'),
|
||||
message: '请输入正确的邮箱地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
getId: -1,
|
||||
showEditImage: false,
|
||||
editImage: null,
|
||||
eidtImageOption: {
|
||||
type: 'Logo',
|
||||
fixedNumber: [15, 4],
|
||||
title: '编辑企业logo',
|
||||
previewWidth: '200px',
|
||||
previewHeight: '40px',
|
||||
autoCropWidth: 200,
|
||||
autoCropHeight: 40,
|
||||
},
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const res = await getCompanyInfo()
|
||||
if (!res.id) {
|
||||
this.getId = -1
|
||||
} else {
|
||||
this.infoData = res.info
|
||||
this.getId = res.id
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
isEditable() {
|
||||
return this.hasDetailPermission('backend', '公司信息', ['update'])
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['SET_FILENAME', 'SET_SMALL_FILENAME']),
|
||||
deleteLogo() {
|
||||
this.infoData.logoName = ''
|
||||
},
|
||||
deleteSmallLogo() {
|
||||
this.infoData.smallLogoName = ''
|
||||
},
|
||||
async onSubmit() {
|
||||
this.$refs.infoData.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (this.getId === -1) {
|
||||
await postCompanyInfo(this.infoData)
|
||||
} else {
|
||||
await putCompanyInfo(this.getId, this.infoData)
|
||||
}
|
||||
this.SET_FILENAME(this.infoData.logoName)
|
||||
this.SET_SMALL_FILENAME(this.infoData.smallFileName)
|
||||
this.$message.success('保存成功')
|
||||
} else {
|
||||
this.$message.warning('检查您的输入是否正确!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.infoData = {
|
||||
name: '',
|
||||
description: '',
|
||||
address: '',
|
||||
city: '',
|
||||
postCode: '',
|
||||
country: '',
|
||||
website: '',
|
||||
phone: '',
|
||||
faxCode: '',
|
||||
email: '',
|
||||
logoName: '',
|
||||
smallLogoName: '',
|
||||
}
|
||||
},
|
||||
customRequest(file) {
|
||||
const reader = new FileReader()
|
||||
var self = this
|
||||
if (this.eidtImageOption.type === 'Logo') {
|
||||
this.eidtImageOption = {
|
||||
type: 'Logo',
|
||||
fixedNumber: [20, 4],
|
||||
title: '编辑企业logo',
|
||||
previewWidth: '200px',
|
||||
previewHeight: '40px',
|
||||
autoCropWidth: 200,
|
||||
autoCropHeight: 40,
|
||||
}
|
||||
} else if (this.eidtImageOption.type === 'SmallLogo') {
|
||||
this.eidtImageOption = {
|
||||
type: 'SmallLogo',
|
||||
fixedNumber: [4, 4],
|
||||
title: '编辑企业logo缩略图',
|
||||
previewWidth: '80px',
|
||||
previewHeight: '80px',
|
||||
autoCropWidth: 250,
|
||||
autoCropHeight: 250,
|
||||
}
|
||||
}
|
||||
reader.onload = function(e) {
|
||||
let result
|
||||
if (typeof e.target.result === 'object') {
|
||||
// 把Array Buffer转化为blob 如果是base64不需要
|
||||
result = window.URL.createObjectURL(new Blob([e.target.result]))
|
||||
} else {
|
||||
result = e.target.result
|
||||
}
|
||||
|
||||
self.editImage = result
|
||||
self.showEditImage = true
|
||||
}
|
||||
reader.readAsDataURL(file.file)
|
||||
},
|
||||
submitImage(file) {
|
||||
postImageFile(file).then((res) => {
|
||||
if (res.file_name) {
|
||||
if (this.eidtImageOption.type === 'Logo') {
|
||||
this.infoData.logoName = res.file_name
|
||||
} else if (this.eidtImageOption.type === 'SmallLogo') {
|
||||
this.infoData.smallLogoName = res.file_name
|
||||
}
|
||||
} else {
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
beforeUpload(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
if (!isLt2M) {
|
||||
this.$message.error('图片大小不可超过2MB!')
|
||||
}
|
||||
return isLt2M
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.ops-setting-companyinfo {
|
||||
padding-top: 15px;
|
||||
background-color: #fff;
|
||||
border-radius: 15px;
|
||||
overflow: auto;
|
||||
margin-bottom: -24px;
|
||||
.ops-setting-companyinfo-upload-show {
|
||||
position: relative;
|
||||
width: 290px;
|
||||
height: 100px;
|
||||
max-height: 100px;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.ant-upload:hover .delete-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
color: rgb(247, 85, 85);
|
||||
}
|
||||
.ant-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ant-form-item label {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.avatar-uploader > .ant-upload {
|
||||
// max-width: 100px;
|
||||
max-height: 100px;
|
||||
}
|
||||
// .ant-upload.ant-upload-select-picture-card {
|
||||
// width: 100%;
|
||||
// > .ant-upload {
|
||||
// padding: 0px;
|
||||
.ant-upload-picture-card-wrapper {
|
||||
height: 100px;
|
||||
.ant-upload.ant-upload-select-picture-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
> .ant-upload {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
190
cmdb-ui/src/views/setting/companyStructure/BatchModal.vue
Normal file
190
cmdb-ui/src/views/setting/companyStructure/BatchModal.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
dialogClass="ops-modal"
|
||||
width="500px"
|
||||
v-model="visible"
|
||||
:title="title"
|
||||
layout="inline"
|
||||
@cancel="close"
|
||||
>
|
||||
<a-form-model v-if="visible && batchProps.type === 'department_id'">
|
||||
<div :style="{ width: '420px', display: 'inline-block', margin: '0 7px' }">
|
||||
<div :style="{ height: '40px', lineHeight: '40px' }">选择部门:</div>
|
||||
<DepartmentTreeSelect v-model="batchForm.value"> </DepartmentTreeSelect>
|
||||
</div>
|
||||
</a-form-model>
|
||||
<a-form-model v-else-if="batchProps.type === 'direct_supervisor_id'" ref="ruleForm">
|
||||
<div :style="{ width: '420px', display: 'inline-block', margin: '0 7px' }">
|
||||
<div :style="{ height: '40px', lineHeight: '40px' }">选择上级:</div>
|
||||
<EmployeeTreeSelect v-model="batchForm.value" />
|
||||
</div>
|
||||
</a-form-model>
|
||||
<a-form-model v-else-if="batchProps.type === 'position_name'">
|
||||
<a-form-model-item label="编辑岗位">
|
||||
<a-input v-model="batchForm.value" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<a-form-model v-else-if="batchProps.type === 'annual_leave'">
|
||||
<a-form-model-item label="编辑年假">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:step="1"
|
||||
:style="{ width: '100%' }"
|
||||
v-model="batchForm.value"
|
||||
placeholder="请输入年假"
|
||||
:formatter="(value) => `${value} 天`"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<a-form-model v-else-if="batchProps.type === 'password'" ref="batchForm" :model="batchForm" :rules="rules">
|
||||
<a-form-model-item label="重置密码" prop="password">
|
||||
<a-input-password v-model="batchForm.value" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="确认密码" prop="repeatPassword">
|
||||
<a-input-password v-model="batchForm.confirmValue" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<a-form-model v-else-if="batchProps.type === 'block' && batchProps.state === 1">
|
||||
<a-icon type="info-circle" :style="{ color: '#FF9E58', fontSize: '16px', marginRight: '10px' }" />
|
||||
<span v-if="batchProps.selectedRowKeys.length > 1">这些用户将会被禁用,是否继续?</span>
|
||||
<span v-else>该用户将会被禁用,是否继续?</span>
|
||||
</a-form-model>
|
||||
<a-form-model v-else-if="batchProps.type === 'block' && batchProps.state === 0">
|
||||
<a-icon type="info-circle" :style="{ color: '#FF9E58', fontSize: '16px', marginRight: '10px' }" />
|
||||
<span v-if="batchProps.selectedRowKeys.length > 1">这些用户将会被恢复,是否继续?</span>
|
||||
<span v-else>该用户将会被恢复,是否继续?</span>
|
||||
</a-form-model>
|
||||
<template slot="footer">
|
||||
<a-button key="back" @click="close"> 取消 </a-button>
|
||||
<a-button key="submit" type="primary" @click="batchModalHandleOk"> 确定 </a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import { batchEditEmployee } from '@/api/employee'
|
||||
import EmployeeTreeSelect from '../components/employeeTreeSelect.vue'
|
||||
import DepartmentTreeSelect from '../components/departmentTreeSelect.vue'
|
||||
import { getDirectorName } from '@/utils/util'
|
||||
import Bus from './eventBus/bus'
|
||||
export default {
|
||||
components: { Treeselect, DepartmentTreeSelect, EmployeeTreeSelect },
|
||||
inject: ['provide_allFlatEmployees'],
|
||||
data() {
|
||||
const validatePass = (rule, value, callback) => {
|
||||
console.log(this.batchForm)
|
||||
if (this.batchForm.value === '') {
|
||||
callback(new Error('请输入密码'))
|
||||
} else {
|
||||
this.$refs.batchForm.validateField('repeatPassword')
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validatePass2 = (rule, value, callback) => {
|
||||
console.log(this.batchForm)
|
||||
if (this.batchForm.confirmValue === '') {
|
||||
callback(new Error('请输入密码'))
|
||||
} else if (this.batchForm.confirmValue !== this.batchForm.value) {
|
||||
callback(new Error('两次密码不一致'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
visible: false,
|
||||
batchProps: {},
|
||||
batchForm: {
|
||||
value: '',
|
||||
confirmValue: '',
|
||||
},
|
||||
title: '',
|
||||
rules: {
|
||||
password: [{ required: true, validator: validatePass, trigger: 'blur' }],
|
||||
repeatPassword: [{ required: true, validator: validatePass2, trigger: 'blur' }],
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
allFlatEmployees() {
|
||||
return this.provide_allFlatEmployees()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open(batchProps) {
|
||||
this.visible = true
|
||||
this.batchProps = batchProps
|
||||
const { type, selectedRowKeys, state } = batchProps
|
||||
this.title = '批量编辑'
|
||||
if (type === 'department_id') {
|
||||
this.batchForm.value = null
|
||||
} else if (type === 'direct_supervisor_id') {
|
||||
this.batchForm.value = undefined
|
||||
} else if (type === 'password') {
|
||||
if (selectedRowKeys.length <= 1) {
|
||||
this.title = '重置密码'
|
||||
}
|
||||
} else if (type === 'block') {
|
||||
this.batchForm.value = state
|
||||
if (selectedRowKeys.length <= 1) {
|
||||
this.title = state ? '禁用' : '恢复'
|
||||
}
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.batchForm.value = ''
|
||||
this.batchForm.confirmValue = ''
|
||||
this.visible = false
|
||||
},
|
||||
async batchModalHandleOk() {
|
||||
if (this.batchProps.type === 'direct_supervisor_id') {
|
||||
this.batchForm.value = this.batchForm.value
|
||||
? this.batchForm.value.includes('-')
|
||||
? Number(this.batchForm.value.split('-')[1])
|
||||
: Number(this.batchForm.value)
|
||||
: 0
|
||||
}
|
||||
if (this.batchProps.type === 'password') {
|
||||
this.$refs.batchForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.sendReq()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.sendReq()
|
||||
}
|
||||
},
|
||||
async sendReq() {
|
||||
const employeeIdList = this.batchProps.selectedRowKeys.map((item) => item.employee_id)
|
||||
const res = await batchEditEmployee({
|
||||
column_name: this.batchProps.type,
|
||||
column_value: this.batchForm.value,
|
||||
employee_id_list: employeeIdList,
|
||||
})
|
||||
if (res.length) {
|
||||
this.$notification.error({
|
||||
message: '操作失败',
|
||||
description: res
|
||||
.map((item) => `${getDirectorName(this.allFlatEmployees, item.employee_id)}:${item.err}`)
|
||||
.join('\n'),
|
||||
duration: null,
|
||||
style: {
|
||||
width: '600px',
|
||||
marginLeft: `${335 - 600}px`,
|
||||
whiteSpace: 'pre-line',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
this.$message.success('操作成功')
|
||||
}
|
||||
if (this.batchProps.type === 'department_id') {
|
||||
Bus.$emit('clickSelectGroup', 1)
|
||||
} else {
|
||||
this.$emit('refresh')
|
||||
}
|
||||
Bus.$emit('updataAllIncludeEmployees')
|
||||
this.close()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
550
cmdb-ui/src/views/setting/companyStructure/BatchUpload.vue
Normal file
550
cmdb-ui/src/views/setting/companyStructure/BatchUpload.vue
Normal file
@@ -0,0 +1,550 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
title="批量导入"
|
||||
dialogClass="ops-modal setting-structure-upload"
|
||||
:width="800"
|
||||
@cancel="close"
|
||||
>
|
||||
<div class="setting-structure-upload-steps">
|
||||
<div
|
||||
:class="{ 'setting-structure-upload-step': true, selected: index + 1 <= currentStep }"
|
||||
v-for="(step, index) in stepList"
|
||||
:key="step.value"
|
||||
>
|
||||
<div :class="{ 'setting-structure-upload-step-icon': true }">
|
||||
<ops-icon :type="step.icon" />
|
||||
</div>
|
||||
<span>{{ step.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="currentStep === 1">
|
||||
<a-upload :multiple="false" :customRequest="customRequest" accept=".xlsx" :showUploadList="false">
|
||||
<a-button :style="{ marginBottom: '20px' }" type="primary"> <a-icon type="upload" />选择文件</a-button>
|
||||
</a-upload>
|
||||
<p><a @click="download">点击下载《员工导入模板》</a></p>
|
||||
</template>
|
||||
<div
|
||||
:style="{ height: '60px', display: 'flex', justifyContent: 'center', alignItems: 'center' }"
|
||||
v-if="currentStep === 3"
|
||||
>
|
||||
导入总数据{{ allCount }}条, 导入成功 <span :style="{ color: '#2362FB' }">{{ allCount - errorCount }}</span> 条,
|
||||
导入失败<span :style="{ color: '#D81E06' }">{{ errorCount }}</span
|
||||
>条
|
||||
</div>
|
||||
<vxe-table
|
||||
v-if="currentStep === 2 || has_error"
|
||||
ref="employeeTable"
|
||||
stripe
|
||||
:data="importData"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
highlight-hover-row
|
||||
size="small"
|
||||
class="ops-stripe-table"
|
||||
:max-height="400"
|
||||
:column-config="{ resizable: true }"
|
||||
>
|
||||
<vxe-column field="email" title="邮箱" min-width="120" fixed="left"></vxe-column>
|
||||
<vxe-column field="username" title="用户名" min-width="80" ></vxe-column>
|
||||
<vxe-column field="nickname" title="姓名" min-width="80"></vxe-column>
|
||||
<vxe-column field="password" title="密码" min-width="80"></vxe-column>
|
||||
<vxe-column field="sex" title="性别" min-width="60"></vxe-column>
|
||||
<vxe-column field="mobile" title="手机号" min-width="80"></vxe-column>
|
||||
<vxe-column field="position_name" title="岗位" min-width="80"></vxe-column>
|
||||
<vxe-column field="department_name" title="部门" min-width="80"></vxe-column>
|
||||
<vxe-column field="current_company" v-if="useDFC" title="目前所属主体" min-width="120"></vxe-column>
|
||||
<vxe-column field="dfc_entry_date" v-if="useDFC" title="初始入职日期" min-width="120"></vxe-column>
|
||||
<vxe-column field="entry_date" title="目前主体入职日期" min-width="120"></vxe-column>
|
||||
<vxe-column field="is_internship" title="正式/实习生" min-width="120"></vxe-column>
|
||||
<vxe-column field="leave_date" title="离职日期" min-width="120"></vxe-column>
|
||||
<vxe-column field="id_card" title="身份证号码" min-width="120"></vxe-column>
|
||||
<vxe-column field="nation" title="民族" min-width="80"></vxe-column>
|
||||
<vxe-column field="id_place" title="籍贯" min-width="80"></vxe-column>
|
||||
<vxe-column field="party" title="组织关系" min-width="80"></vxe-column>
|
||||
<vxe-column field="household_registration_type" title="户籍类型" min-width="80"></vxe-column>
|
||||
<vxe-column field="hometown" title="户口所在地" min-width="80"></vxe-column>
|
||||
<vxe-column field="marry" title="婚姻情况" min-width="80"></vxe-column>
|
||||
<vxe-column field="max_degree" title="最高学历" min-width="80"></vxe-column>
|
||||
<vxe-column field="emergency_person" title="紧急联系人" min-width="120"></vxe-column>
|
||||
<vxe-column field="emergency_phone" title="紧急联系电话" min-width="120"></vxe-column>
|
||||
<vxe-column field="bank_card_number" title="卡号" min-width="120"></vxe-column>
|
||||
<vxe-column field="bank_card_name" title="银行" min-width="80"></vxe-column>
|
||||
<vxe-column field="opening_bank" title="开户行" min-width="80"></vxe-column>
|
||||
<vxe-column field="account_opening_location" title="开户地" min-width="120"></vxe-column>
|
||||
<vxe-column field="school" title="学校" min-width="80"></vxe-column>
|
||||
<vxe-column field="major" title="专业" min-width="80"></vxe-column>
|
||||
<vxe-column field="education" title="学历" min-width="80"></vxe-column>
|
||||
<vxe-column field="graduation_year" title="毕业年份" min-width="120"></vxe-column>
|
||||
<vxe-column v-if="has_error" field="err" title="失败原因" min-width="120" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<span :style="{ color: '#D81E06' }">{{ row.err }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<a-space slot="footer">
|
||||
<a-button size="small" type="primary" ghost @click="close">取消</a-button>
|
||||
<a-button v-if="currentStep !== 1" size="small" type="primary" ghost @click="goPre">上一步</a-button>
|
||||
<a-button v-if="currentStep !== 3" size="small" type="primary" @click="goNext">下一步</a-button>
|
||||
<a-button v-else size="small" type="primary" @click="close">完成</a-button>
|
||||
</a-space>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { downloadExcel, excel2Array } from '@/utils/download'
|
||||
import { importEmployee } from '@/api/employee'
|
||||
import appConfig from '@/config/app'
|
||||
export default {
|
||||
name: 'BatchUpload',
|
||||
data() {
|
||||
const stepList = [
|
||||
{
|
||||
value: 1,
|
||||
label: '上传文件',
|
||||
icon: 'icon-shidi-tianjia',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '确认数据',
|
||||
icon: 'icon-shidi-yunshangchuan',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '上传完成',
|
||||
icon: 'icon-shidi-queren',
|
||||
},
|
||||
]
|
||||
const dfc_importParamsList = [
|
||||
'email',
|
||||
'username',
|
||||
'nickname',
|
||||
'password',
|
||||
'sex',
|
||||
'mobile',
|
||||
'position_name',
|
||||
'department_name',
|
||||
'current_company',
|
||||
'dfc_entry_date',
|
||||
'entry_date',
|
||||
'is_internship',
|
||||
'leave_date',
|
||||
'id_card',
|
||||
'nation',
|
||||
'id_place',
|
||||
'party',
|
||||
'household_registration_type',
|
||||
'hometown',
|
||||
'marry',
|
||||
'max_degree',
|
||||
'emergency_person',
|
||||
'emergency_phone',
|
||||
'bank_card_number',
|
||||
'bank_card_name',
|
||||
'opening_bank',
|
||||
'account_opening_location',
|
||||
'school',
|
||||
'major',
|
||||
'education',
|
||||
'graduation_year',
|
||||
]
|
||||
const common_importParamsList = [
|
||||
'email',
|
||||
'username',
|
||||
'nickname',
|
||||
'password',
|
||||
'sex',
|
||||
'mobile',
|
||||
'position_name',
|
||||
'department_name',
|
||||
'entry_date',
|
||||
'is_internship',
|
||||
'leave_date',
|
||||
'id_card',
|
||||
'nation',
|
||||
'id_place',
|
||||
'party',
|
||||
'household_registration_type',
|
||||
'hometown',
|
||||
'marry',
|
||||
'max_degree',
|
||||
'emergency_person',
|
||||
'emergency_phone',
|
||||
'bank_card_number',
|
||||
'bank_card_name',
|
||||
'opening_bank',
|
||||
'account_opening_location',
|
||||
'school',
|
||||
'major',
|
||||
'education',
|
||||
'graduation_year',
|
||||
]
|
||||
return {
|
||||
stepList,
|
||||
dfc_importParamsList,
|
||||
common_importParamsList,
|
||||
visible: false,
|
||||
currentStep: 1,
|
||||
importData: [],
|
||||
has_error: false,
|
||||
allCount: 0,
|
||||
errorCount: 0,
|
||||
useDFC: appConfig.useDFC,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.importData = []
|
||||
this.has_error = false
|
||||
this.errorCount = 0
|
||||
this.visible = true
|
||||
},
|
||||
close() {
|
||||
this.currentStep = 1
|
||||
this.visible = false
|
||||
},
|
||||
async goNext() {
|
||||
if (this.currentStep === 2) {
|
||||
// 此处调用后端接口
|
||||
this.allCount = this.importData.length
|
||||
const importData = this.importData.map((item) => {
|
||||
const { _X_ROW_KEY, ...rest } = item
|
||||
const keyArr = Object.keys(rest)
|
||||
keyArr.forEach((key) => {
|
||||
if (rest[key]) {
|
||||
rest[key] = rest[key] + ''
|
||||
}
|
||||
})
|
||||
rest.educational_experience = [{
|
||||
'school': rest.school,
|
||||
'major': rest.major,
|
||||
'education': rest.education,
|
||||
'graduation_year': rest.graduation_year
|
||||
}]
|
||||
delete rest.school
|
||||
delete rest.major
|
||||
delete rest.education
|
||||
delete rest.graduation_year
|
||||
return rest
|
||||
})
|
||||
const res = await importEmployee({ employee_list: importData })
|
||||
if (res.length) {
|
||||
const errData = res.filter((item) => {
|
||||
return item.err.length
|
||||
})
|
||||
console.log('err', errData)
|
||||
this.has_error = true
|
||||
this.errorCount = errData.length
|
||||
this.currentStep += 1
|
||||
this.importData = errData
|
||||
this.$message.error('数据存在错误')
|
||||
} else {
|
||||
this.currentStep += 1
|
||||
this.$message.success('操作成功')
|
||||
}
|
||||
this.$emit('refresh')
|
||||
}
|
||||
},
|
||||
goPre() {
|
||||
this.has_error = false
|
||||
this.errorCount = 0
|
||||
this.currentStep -= 1
|
||||
},
|
||||
download() {
|
||||
const data = [
|
||||
[
|
||||
{
|
||||
v:
|
||||
'1、表头标“*”的红色字体为必填项\n2、邮箱、用户名不允许重复\n3、登录密码:密码由6-20位字母、数字组成\n4、部门:上下级部门间用"/"隔开,且从最上级部门开始,例如“深圳分公司/IT部/IT二部”。如出现相同的部门,则默认导入组织架构中顺序靠前的部门',
|
||||
t: 's',
|
||||
s: {
|
||||
alignment: {
|
||||
wrapText: true,
|
||||
vertical: 'center',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
v: '*邮箱',
|
||||
t: 's',
|
||||
s: {
|
||||
font: {
|
||||
color: {
|
||||
rgb: 'FF0000',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
v: '*用户名',
|
||||
t: 's',
|
||||
s: {
|
||||
font: {
|
||||
color: {
|
||||
rgb: 'FF0000',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
v: '*姓名',
|
||||
t: 's',
|
||||
s: {
|
||||
font: {
|
||||
color: {
|
||||
rgb: 'FF0000',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
v: '*密码',
|
||||
t: 's',
|
||||
s: {
|
||||
font: {
|
||||
color: {
|
||||
rgb: 'FF0000',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
v: '性别',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '手机号',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '岗位',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '部门',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '目前所属主体',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '初始入职日期',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '目前主体入职日期',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '正式/实习生',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '离职日期',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '身份证号码',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '民族',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '籍贯',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '组织关系',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '户籍类型',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '户口所在地',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '婚姻情况',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '最高学历',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '紧急联系人',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '紧急联系电话',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '卡号',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '银行',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '开户行',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '开户地',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '学校',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '专业',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '学历',
|
||||
t: 's',
|
||||
},
|
||||
{
|
||||
v: '毕业年份',
|
||||
t: 's',
|
||||
},
|
||||
],
|
||||
]
|
||||
if (this.useDFC) {
|
||||
downloadExcel(data, '员工导入模板')
|
||||
} else {
|
||||
data[1] = data[1].filter(item => item['v'] !== '目前所属主体')
|
||||
data[1] = data[1].filter(item => item['v'] !== '初始入职日期')
|
||||
downloadExcel(data, '员工导入模板')
|
||||
}
|
||||
},
|
||||
customRequest(data) {
|
||||
this.fileList = [data.file]
|
||||
excel2Array(data.file).then((res) => {
|
||||
res = res.filter((item) => item.length)
|
||||
this.importData = res.slice(2).map((item) => {
|
||||
const obj = {}
|
||||
// 格式化日期字段
|
||||
if (this.useDFC) {
|
||||
item[9] = this.formatDate(item[9]) // 初始入职日期日期
|
||||
item[10] = this.formatDate(item[10]) // 目前主体入职日期
|
||||
item[12] = this.formatDate(item[12]) // 离职日期
|
||||
item[30] = this.formatDate(item[30]) // 毕业年份
|
||||
item.forEach((ele, index) => {
|
||||
obj[this.dfc_importParamsList[index]] = ele
|
||||
})
|
||||
} else {
|
||||
item[8] = this.formatDate(item[8]) // 目前主体入职日期
|
||||
item[10] = this.formatDate(item[10]) // 离职日期
|
||||
item[28] = this.formatDate(item[28]) // 毕业年份
|
||||
item.forEach((ele, index) => {
|
||||
obj[this.common_importParamsList[index]] = ele
|
||||
})
|
||||
}
|
||||
return obj
|
||||
})
|
||||
this.currentStep = 2
|
||||
})
|
||||
},
|
||||
formatDate(numb) {
|
||||
if (numb) {
|
||||
const time = new Date((numb - 1) * 24 * 3600000 + 1)
|
||||
time.setYear(time.getFullYear() - 70)
|
||||
time.setMonth(time.getMonth())
|
||||
time.setHours(time.getHours() - 8)
|
||||
time.setMinutes(time.getMinutes())
|
||||
time.setMilliseconds(time.getMilliseconds())
|
||||
// return time.valueOf()
|
||||
// 日期格式
|
||||
const format = 'Y-m-d'
|
||||
const year = time.getFullYear()
|
||||
// 由于 getMonth 返回值会比正常月份小 1
|
||||
let month = time.getMonth() + 1
|
||||
let day = time.getDate()
|
||||
month = month > 9 ? month : `0${month}`
|
||||
day = day > 9 ? day : `0${day}`
|
||||
const hash = {
|
||||
'Y': year,
|
||||
'm': month,
|
||||
'd': day,
|
||||
}
|
||||
return format.replace(/\w/g, o => {
|
||||
return hash[o]
|
||||
})
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.setting-structure-upload {
|
||||
.ant-modal-body {
|
||||
padding: 24px 48px;
|
||||
}
|
||||
.setting-structure-upload-steps {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
.setting-structure-upload-step {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
.setting-structure-upload-step-icon {
|
||||
width: 86px;
|
||||
height: 86px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-image: url('../../../assets/icon-bg.png');
|
||||
margin-bottom: 20px;
|
||||
> i {
|
||||
font-size: 40px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
> span {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
.setting-structure-upload-step:not(:first-child)::before {
|
||||
content: '';
|
||||
height: 2px;
|
||||
width: 223px;
|
||||
position: absolute;
|
||||
background-color: #e7ecf3;
|
||||
left: -223px;
|
||||
top: 43px;
|
||||
z-index: 0;
|
||||
}
|
||||
.selected.setting-structure-upload-step {
|
||||
&:not(:first-child)::before {
|
||||
background-color: #7eb0ff;
|
||||
}
|
||||
}
|
||||
.selected {
|
||||
.setting-structure-upload-step-icon {
|
||||
background-image: url('../../../assets/icon-bg-selected.png');
|
||||
}
|
||||
> span {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
321
cmdb-ui/src/views/setting/companyStructure/CategoryTree.vue
Normal file
321
cmdb-ui/src/views/setting/companyStructure/CategoryTree.vue
Normal file
@@ -0,0 +1,321 @@
|
||||
<template>
|
||||
<li class="ops-setting-companystructure-sidebar-tree">
|
||||
<div
|
||||
:class="{
|
||||
'ops-setting-companystructure-sidebar-group-tree-item': true,
|
||||
'ops-setting-companystructure-sidebar-group-tree-line': showLine,
|
||||
isSelected: activeId === TreeData.id || asFatherSelected,
|
||||
}"
|
||||
>
|
||||
<div class="ops-setting-companystructure-sidebar-group-tree-info" @click.stop="selectItem(TreeData)">
|
||||
<!-- <div class="info-title"> -->
|
||||
<span :title="TreeData.title">
|
||||
<ops-icon :style="{ marginRight: '8px' }" :type="icon" />
|
||||
{{ TreeData.title }}
|
||||
</span>
|
||||
<!-- </div> -->
|
||||
<!-- <span class="item-title"
|
||||
:title="TreeData.title"
|
||||
><ops-icon :style="{ marginRight: '8px' }" :type="icon" />{{ TreeData.title }}{{ TreeData.count }}</span
|
||||
> -->
|
||||
<div class="ops-setting-companystructure-sidebar-group-tree-info-count-toggle">
|
||||
<div class="item-count-before">{{ TreeData.count }}</div>
|
||||
<!-- 显示折叠展开的图标,如果没有下级目录的话,则不显示 -->
|
||||
<div class="item-folder">
|
||||
<span v-if="isFolder" @click.stop="toggle">
|
||||
<a-icon :style="{ color: '#a1bcfb' }" :type="open ? 'up-circle' : 'down-circle'" theme="filled" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul v-if="isFolder && open" :style="{ marginLeft: '12px' }">
|
||||
<draggable v-model="TreeData.children" @end="handleEndDrag(TreeData.children)" :disabled="!isEditable">
|
||||
<CategroyTree
|
||||
v-for="(SubTree, SubIndex) in TreeData.children"
|
||||
:id="SubTree.id"
|
||||
:key="SubTree.id"
|
||||
:TreeData="SubTree"
|
||||
:showLine="SubIndex !== TreeData.children.length - 1"
|
||||
icon="setting-structure-depart2"
|
||||
/>
|
||||
</draggable>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
import Bus from '@/views/setting/companyStructure/eventBus/bus'
|
||||
import { updateDepartmentsSort } from '@/api/company'
|
||||
import { mixinPermissions } from '@/utils/mixin'
|
||||
export default {
|
||||
name: 'CategroyTree',
|
||||
mixins: [mixinPermissions],
|
||||
components: { draggable },
|
||||
props: {
|
||||
TreeData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
showLine: {
|
||||
type: Boolean,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'setting-structure-depart2',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 默认不显示下级目录
|
||||
open: false,
|
||||
activeId: null,
|
||||
asFatherSelected: false,
|
||||
// isClick: 'item-count-before',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 控制是否有下级目录和显示下级目录
|
||||
isFolder() {
|
||||
return this.TreeData.hasSub
|
||||
},
|
||||
isEditable() {
|
||||
return this.hasDetailPermission('backend', '公司架构', ['update'])
|
||||
},
|
||||
},
|
||||
created() {
|
||||
Bus.$on('changeActiveId', (cid) => {
|
||||
this.activeId = cid
|
||||
})
|
||||
Bus.$on('asFatherSelected', (cid) => {
|
||||
this.fatherSelected(cid)
|
||||
})
|
||||
Bus.$on('resettoggle', (isToggle) => {
|
||||
this.open = isToggle
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
Bus.$off('changeActiveId')
|
||||
Bus.$off('asFatherSelected')
|
||||
},
|
||||
methods: {
|
||||
// 点击折叠展开的方法
|
||||
toggle() {
|
||||
if (this.isFolder) {
|
||||
this.selectItem(this.TreeData)
|
||||
if (!this.open) {
|
||||
Bus.$emit('reqChildren')
|
||||
}
|
||||
this.open = !this.open
|
||||
}
|
||||
},
|
||||
selectItem(selectDepartment) {
|
||||
Bus.$emit('selectDepartment', selectDepartment)
|
||||
this.activeId = selectDepartment.id
|
||||
Bus.$emit('changeActiveId', selectDepartment.id)
|
||||
Bus.$emit('asFatherSelected', this.TreeData.id)
|
||||
},
|
||||
fatherSelected(childId) {
|
||||
this.asFatherSelected = this.ownIdInChildren(childId, this.TreeData, false)
|
||||
},
|
||||
ownIdInChildren(cid, TreeData, flag = false) {
|
||||
if (TreeData.children) {
|
||||
if (TreeData.children.map((item) => item.id).includes(cid)) {
|
||||
flag = true
|
||||
return true
|
||||
} else {
|
||||
return TreeData.children
|
||||
.map((item) => {
|
||||
return this.ownIdInChildren(cid, item, flag)
|
||||
})
|
||||
.includes(true)
|
||||
}
|
||||
} else {
|
||||
return flag
|
||||
}
|
||||
},
|
||||
handleEndDrag(data) {
|
||||
updateDepartmentsSort({
|
||||
department_list: data.map((item, index) => {
|
||||
return { id: item.id, sort_value: index }
|
||||
}),
|
||||
}).then(() => {
|
||||
Bus.$emit('updateAllIncludeDepartment')
|
||||
})
|
||||
},
|
||||
// mouseOver: function() {
|
||||
// this.isClick = 'item-count-after'
|
||||
// },
|
||||
// mouseLeave: function() {
|
||||
// this.isClick = 'item-count-before'
|
||||
// },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import '~@/style/static.less';
|
||||
ul,
|
||||
li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ops-setting-companystructure-sidebar-tree {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
position: relative;
|
||||
// padding: 7px 0 7px 10px;
|
||||
padding-left: 10px;
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
font-size: 14px;
|
||||
.ops-setting-companystructure-sidebar-group-tree-info:hover {
|
||||
color: #custom_colors[color_1];
|
||||
> .ops-setting-companystructure-sidebar-group-tree-info::before {
|
||||
background-color: #custom_colors[color_1];
|
||||
}
|
||||
}
|
||||
// .ops-setting-companystructure-sidebar-group-tree-info:first-child::before {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// top: 50%;
|
||||
// left: 0;
|
||||
// transform: translateY(-50%);
|
||||
// display: inline-block;
|
||||
// width: 5px;
|
||||
// height: 5px;
|
||||
// background-color: #cacaca;
|
||||
// border-radius: 50%;
|
||||
// }
|
||||
.ops-setting-companystructure-sidebar-group-tree-item {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
.ops-setting-companystructure-sidebar-group-tree-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
> span:first-child {
|
||||
font-size: 15px;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: calc(100% - 14px - 15px);
|
||||
// margin-bottom: 10px;
|
||||
// line-height: 10px;
|
||||
// height: 5%;
|
||||
}
|
||||
.info-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// > span:first-child {
|
||||
// font-size: 15px;
|
||||
// display: inline-block;
|
||||
// white-space: nowrap;
|
||||
// overflow: hidden;
|
||||
// text-overflow: ellipsis;
|
||||
// width: calc(100% - 14px - 15px);
|
||||
// margin-bottom: 10px;
|
||||
// }
|
||||
}
|
||||
//flex-wrap: wrap;
|
||||
// align-items: center;
|
||||
// .item-title{
|
||||
// display: inline-block;
|
||||
// white-space: nowrap;
|
||||
// overflow: hidden;
|
||||
// text-overflow: ellipsis;
|
||||
// }
|
||||
.item-count-after {
|
||||
//position: absolute;
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
width: 27px;
|
||||
height: 15px;
|
||||
background: #ffffff;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
color: #2f54eb;
|
||||
}
|
||||
.ops-setting-companystructure-sidebar-group-tree-info-count-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.item-count-before {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// display: inline-block;
|
||||
margin: 0 auto;
|
||||
width: 27px;
|
||||
height: 15px;
|
||||
background: #e1efff;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
color: #9094a6;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.item-folder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
// // display: inline-block;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
}
|
||||
}
|
||||
// > span:nth-child(2) {
|
||||
// color: #a1bcfb!important;
|
||||
// }
|
||||
}
|
||||
}
|
||||
// .ops-setting-companystructure-sidebar-group-tree-line::after {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// width: 1px;
|
||||
// height: 100%;
|
||||
// background-color: rgba(0, 0, 0, 0.1);
|
||||
// top: 12px;
|
||||
// left: 12px;
|
||||
// }
|
||||
.isSelected {
|
||||
color: #2f54eb;
|
||||
> .ops-setting-companystructure-sidebar-group-tree-info {
|
||||
> span:nth-child(2) > i {
|
||||
color: #a1bcfb !important;
|
||||
}
|
||||
}
|
||||
> .ops-setting-companystructure-sidebar-group-tree-info::before {
|
||||
background-color: #custom_colors[color_1];
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
143
cmdb-ui/src/views/setting/companyStructure/DepartmentModal.vue
Normal file
143
cmdb-ui/src/views/setting/companyStructure/DepartmentModal.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
width="500px"
|
||||
v-model="visible"
|
||||
:title="type === 'add' ? '创建子部门' : '编辑部门'"
|
||||
layout="inline"
|
||||
@cancel="close"
|
||||
>
|
||||
<a-form-model
|
||||
ref="departmentFormData"
|
||||
:model="departmentFormData"
|
||||
:rules="rules"
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
>
|
||||
<a-form-model-item ref="title" label="部门名称" prop="department_name">
|
||||
<a-input v-model="departmentFormData.department_name" placeholder="请输入部门名称" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="上级部门" prop="department_parent_id">
|
||||
<DepartmentTreeSelect v-model="departmentFormData.department_parent_id" />
|
||||
<!-- <Treeselect
|
||||
v-else
|
||||
:multiple="false"
|
||||
:options="currentDepartmentParentList"
|
||||
v-model="departmentFormData.department_parent_id"
|
||||
class="ops-setting-treeselect"
|
||||
placeholder="请选择上级部门"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.department_id,
|
||||
label: node.department_name,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
/> -->
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="部门负责人" prop="department_director_id">
|
||||
<EmployeeTreeSelect v-model="departmentFormData.department_director_id" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<template slot="footer">
|
||||
<a-button key="back" @click="close"> 取消 </a-button>
|
||||
<a-button key="submit" type="primary" @click="departmentModalHandleOk"> 确定 </a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import { getParentDepartmentList, putDepartmentById, postDepartment } from '@/api/company'
|
||||
import EmployeeTreeSelect from '../components/employeeTreeSelect.vue'
|
||||
import DepartmentTreeSelect from '../components/departmentTreeSelect.vue'
|
||||
import Bus from './eventBus/bus'
|
||||
export default {
|
||||
name: 'DepartmentModal',
|
||||
components: { Treeselect, EmployeeTreeSelect, DepartmentTreeSelect },
|
||||
data() {
|
||||
return {
|
||||
labelCol: { span: 5 },
|
||||
wrapperCol: { span: 19 },
|
||||
visible: false,
|
||||
departmentFormData: {
|
||||
department_name: '',
|
||||
department_parent_id: '',
|
||||
department_director_id: undefined,
|
||||
},
|
||||
rules: {
|
||||
department_name: [{ required: true, message: '请输入部门名称' }],
|
||||
department_parent_id: [{ required: true, message: '请选择上级部门' }],
|
||||
},
|
||||
currentDepartmentParentList: [],
|
||||
selectDepartment: {},
|
||||
type: 'add',
|
||||
}
|
||||
},
|
||||
inject: ['provide_allFlatEmployees'],
|
||||
computed: {
|
||||
allFlatEmployees() {
|
||||
return this.provide_allFlatEmployees()
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
async open({ type, selectDepartment }) {
|
||||
this.selectDepartment = selectDepartment
|
||||
this.type = type
|
||||
const { title, parentId, leaderId, id } = selectDepartment
|
||||
let department_director_id
|
||||
if (type === 'add') {
|
||||
this.departmentFormData = {
|
||||
department_name: '',
|
||||
department_parent_id: id,
|
||||
department_director_id,
|
||||
}
|
||||
} else if (type === 'edit') {
|
||||
const res = await getParentDepartmentList({ department_id: id })
|
||||
this.currentDepartmentParentList = res
|
||||
if (leaderId) {
|
||||
const _find = this.allFlatEmployees.find((item) => item.employee_id === leaderId)
|
||||
department_director_id = `${_find.department_id}-${leaderId}`
|
||||
}
|
||||
this.departmentFormData = {
|
||||
department_name: title,
|
||||
department_parent_id: parentId,
|
||||
department_director_id,
|
||||
}
|
||||
}
|
||||
this.visible = true
|
||||
},
|
||||
close() {
|
||||
this.selectDepartment = {}
|
||||
this.visible = false
|
||||
},
|
||||
async departmentModalHandleOk() {
|
||||
this.$refs.departmentFormData.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const { department_director_id } = this.departmentFormData
|
||||
const params = {
|
||||
...this.departmentFormData,
|
||||
department_director_id: department_director_id
|
||||
? String(department_director_id).split('-')[String(department_director_id).split('-').length - 1]
|
||||
: undefined,
|
||||
}
|
||||
if (this.type === 'edit') {
|
||||
await putDepartmentById(this.selectDepartment.id, params)
|
||||
} else if (this.type === 'add') {
|
||||
await postDepartment(params)
|
||||
}
|
||||
this.$message.success('操作成功')
|
||||
this.$emit('refresh')
|
||||
Bus.$emit('updateAllIncludeDepartment')
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
695
cmdb-ui/src/views/setting/companyStructure/EmployeeModal.vue
Normal file
695
cmdb-ui/src/views/setting/companyStructure/EmployeeModal.vue
Normal file
@@ -0,0 +1,695 @@
|
||||
<template>
|
||||
<a-modal
|
||||
dialogClass="ops-modal"
|
||||
destroyOnClose
|
||||
width="810px"
|
||||
v-model="visible"
|
||||
:title="title"
|
||||
layout="inline"
|
||||
@cancel="close"
|
||||
:body-style="{ height: `${windowHeight - 320}px`, overflow: 'hidden', overflowY: 'scroll' }"
|
||||
>
|
||||
<a-form-model ref="employeeFormData" :model="employeeFormData" :rules="rules" :colon="false">
|
||||
<a-form-model-item
|
||||
ref="email"
|
||||
label="邮箱"
|
||||
prop="email"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'email') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.email" placeholder="请输入邮箱" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="username"
|
||||
label="用户名"
|
||||
prop="username"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'username') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.username" placeholder="请输入用户名" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
v-if="type === 'add'"
|
||||
ref="password"
|
||||
label="登录密码"
|
||||
prop="password"
|
||||
:style="formModalItemStyle"
|
||||
>
|
||||
<a-input-password v-model="employeeFormData.password" placeholder="请输入登录密码" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="nickname"
|
||||
label="姓名"
|
||||
prop="nickname"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'nickname') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.nickname" placeholder="请输入姓名" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
label="性别"
|
||||
prop="sex"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'sex') !== -1"
|
||||
>
|
||||
<a-select v-model="employeeFormData.sex" placeholder="请选择性别">
|
||||
<a-select-option value="男"> 男 </a-select-option>
|
||||
<a-select-option value="女"> 女 </a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="mobile"
|
||||
label="手机号"
|
||||
prop="mobile"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'mobile') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.mobile" placeholder="请输入手机号" />
|
||||
</a-form-model-item>
|
||||
<div
|
||||
:style="{ width: '361px', display: 'inline-block', margin: '0 7px' }"
|
||||
v-if="attributes.findIndex((v) => v == 'department_id') !== -1"
|
||||
>
|
||||
<div :style="{ height: '41px', lineHeight: '40px' }">部门</div>
|
||||
<DepartmentTreeSelect v-model="employeeFormData.department_id" />
|
||||
</div>
|
||||
<a-form-model-item
|
||||
ref="position_name"
|
||||
label="岗位"
|
||||
prop="position_name"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'position_name') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.position_name" placeholder="请输入岗位" />
|
||||
</a-form-model-item>
|
||||
<div
|
||||
:style="{ width: '361px', display: 'inline-block', margin: '0 7px' }"
|
||||
v-if="attributes.findIndex((v) => v == 'direct_supervisor_id') !== -1"
|
||||
>
|
||||
<div :style="{ height: '41px', lineHeight: '40px' }">上级</div>
|
||||
<EmployeeTreeSelect v-model="employeeFormData.direct_supervisor_id" />
|
||||
</div>
|
||||
<a-form-model-item
|
||||
ref="annual_leave"
|
||||
label="年假"
|
||||
prop="annual_leave"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'annual_leave') !== -1"
|
||||
>
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:step="1"
|
||||
:style="{ width: '100%' }"
|
||||
v-model="employeeFormData.annual_leave"
|
||||
placeholder="请输入年假"
|
||||
:formatter="(value) => `${value} 天`"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="virtual_annual_leave"
|
||||
label="虚拟年假"
|
||||
prop="virtual_annual_leave"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'virtual_annual_leave') !== -1"
|
||||
>
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:step="1"
|
||||
:style="{ width: '100%' }"
|
||||
v-model="employeeFormData.virtual_annual_leave"
|
||||
placeholder="请输入虚拟年假"
|
||||
:formatter="(value) => `${value} 天`"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="parenting_leave"
|
||||
label="育儿假"
|
||||
prop="parenting_leave"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'parenting_leave') !== -1"
|
||||
>
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:step="1"
|
||||
:style="{ width: '100%' }"
|
||||
v-model="employeeFormData.parenting_leave"
|
||||
placeholder="请输入育儿假"
|
||||
:formatter="(value) => `${value} 天`"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
v-if="useDFC && attributes.findIndex((v) => v == 'current_company') !== -1"
|
||||
ref="current_company"
|
||||
label="目前所属主体"
|
||||
prop="current_company"
|
||||
:style="formModalItemStyle"
|
||||
>
|
||||
<a-input v-model="employeeFormData.current_company" placeholder="请输入目前所属主体" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
v-if="useDFC && attributes.findIndex((v) => v == 'dfc_entry_date') !== -1"
|
||||
ref="dfc_entry_date"
|
||||
label="初始入职日期"
|
||||
prop="dfc_entry_date"
|
||||
:style="formModalItemStyle"
|
||||
>
|
||||
<a-date-picker
|
||||
placeholder="请选择初始入职日期"
|
||||
v-model="employeeFormData.dfc_entry_date"
|
||||
:style="{ width: '100%' }"
|
||||
@change="onChange($event, 'dfc_entry_date')"
|
||||
></a-date-picker>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="entry_date"
|
||||
label="目前主体入职日期"
|
||||
prop="entry_date"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'entry_date') !== -1"
|
||||
>
|
||||
<a-date-picker
|
||||
placeholder="请选择目前主体入职日期"
|
||||
v-model="employeeFormData.entry_date"
|
||||
:style="{ width: '100%' }"
|
||||
@change="onChange($event, 'entry_date')"
|
||||
></a-date-picker>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="is_internship"
|
||||
label="正式/实习生"
|
||||
prop="is_internship"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'is_internship') !== -1"
|
||||
>
|
||||
<a-select v-model="employeeFormData.is_internship" placeholder="请选择是否正式/实习生">
|
||||
<a-select-option :value="0"> 正式 </a-select-option>
|
||||
<a-select-option :value="1"> 实习生 </a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="leave_date"
|
||||
label="离职日期"
|
||||
prop="leave_date"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'leave_date') !== -1"
|
||||
>
|
||||
<a-date-picker
|
||||
v-model="employeeFormData.leave_date"
|
||||
placeholder="请选择离职日期"
|
||||
:style="{ width: '100%' }"
|
||||
@change="onChange($event, 'leave_date')"
|
||||
></a-date-picker>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="id_card"
|
||||
label="身份证号码"
|
||||
prop="id_card"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'id_card') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.id_card" placeholder="请输入身份证号码" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="nation"
|
||||
label="民族"
|
||||
prop="nation"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'nation') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.nation" placeholder="请输入民族" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="id_place"
|
||||
label="籍贯"
|
||||
prop="id_place"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'id_place') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.id_place" placeholder="请输入籍贯" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="party"
|
||||
label="组织关系"
|
||||
prop="party"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'party') !== -1"
|
||||
>
|
||||
<a-select v-model="employeeFormData.party" placeholder="请选择组织关系">
|
||||
<a-select-option value="党员"> 党员 </a-select-option>
|
||||
<a-select-option value="团员"> 团员 </a-select-option>
|
||||
<a-select-option value="群众"> 群众 </a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="household_registration_type"
|
||||
label="户籍类型"
|
||||
prop="household_registration_type"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'household_registration_type') !== -1"
|
||||
>
|
||||
<a-select v-model="employeeFormData.household_registration_type" placeholder="请选择户籍类型">
|
||||
<a-select-option value="城镇"> 城镇 </a-select-option>
|
||||
<a-select-option value="农业"> 农业 </a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="hometown"
|
||||
label="户口所在地"
|
||||
prop="hometown"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'hometown') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.hometown" placeholder="请输入户口所在地" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="marry"
|
||||
label="婚姻情况"
|
||||
prop="marry"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'marry') !== -1"
|
||||
>
|
||||
<a-select v-model="employeeFormData.marry" placeholder="请选择婚姻情况">
|
||||
<a-select-option value="未婚"> 未婚 </a-select-option>
|
||||
<a-select-option value="已婚"> 已婚 </a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="max_degree"
|
||||
label="最高学历"
|
||||
prop="max_degree"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'max_degree') !== -1"
|
||||
>
|
||||
<a-select v-model="employeeFormData.max_degree" placeholder="请选择最高学历">
|
||||
<a-select-option value="博士"> 博士 </a-select-option>
|
||||
<a-select-option value="硕士"> 硕士 </a-select-option>
|
||||
<a-select-option value="本科"> 本科 </a-select-option>
|
||||
<a-select-option value="专科"> 专科 </a-select-option>
|
||||
<a-select-option value="高中"> 高中 </a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="emergency_person"
|
||||
label="紧急联系人"
|
||||
prop="emergency_person"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'emergency_person') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.emergency_person" placeholder="请输入紧急联系人" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
ref="emergency_phone"
|
||||
label="紧急联系电话"
|
||||
prop="emergency_phone"
|
||||
:style="formModalItemStyle"
|
||||
v-if="attributes.findIndex((v) => v == 'emergency_phone') !== -1"
|
||||
>
|
||||
<a-input v-model="employeeFormData.emergency_phone" placeholder="请输入紧急联系电话" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
label="教育经历"
|
||||
prop="employeeFormData"
|
||||
:style="{ display: 'inline-block', width: '100%', margin: '0 7px' }"
|
||||
v-if="attributes.findIndex((v) => v == 'educational_experience') !== -1"
|
||||
>
|
||||
<a-row :gutter="[8, { xs: 8 }]" v-for="item in educational_experience" :key="item.id">
|
||||
<a-col :span="5">
|
||||
<a-input v-model="item.school" placeholder="学校" allowClear></a-input>
|
||||
</a-col>
|
||||
<a-col :span="5">
|
||||
<a-input v-model="item.major" placeholder="专业" allowClear></a-input>
|
||||
</a-col>
|
||||
<a-col :span="5">
|
||||
<a-select v-model="item.education" placeholder="学历" allowClear>
|
||||
<a-select-option value="小学"> 小学 </a-select-option>
|
||||
<a-select-option value="初中"> 初中 </a-select-option>
|
||||
<a-select-option value="中专/高中"> 中专/高中 </a-select-option>
|
||||
<a-select-option value="专科"> 专科 </a-select-option>
|
||||
<a-select-option value="本科"> 本科 </a-select-option>
|
||||
<a-select-option value="硕士"> 硕士 </a-select-option>
|
||||
<a-select-option value="博士"> 博士 </a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="5">
|
||||
<a-month-picker
|
||||
v-model="item.graduation_year"
|
||||
placeholder="毕业年份"
|
||||
@change="onChange($event, 'graduation_year', item.id)"
|
||||
></a-month-picker>
|
||||
</a-col>
|
||||
<a-col :span="1">
|
||||
<a @click="addEducation">
|
||||
<a-icon type="plus-circle" />
|
||||
</a>
|
||||
</a-col>
|
||||
<a-col :span="1" v-if="educational_experience.length > 1">
|
||||
<a @click="() => removeEducation(item.id)" :style="{ color: 'red' }">
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
label="子女信息"
|
||||
prop="employeeFormData"
|
||||
:style="{ display: 'inline-block', width: '100%', margin: '0 7px' }"
|
||||
v-if="attributes.findIndex((v) => v == 'children_information') !== -1"
|
||||
>
|
||||
<!-- <a-space
|
||||
v-for="(item,index) in educational_experience"
|
||||
:key="index"
|
||||
align="baseline"
|
||||
> -->
|
||||
<a-row :gutter="[8, { xs: 8 }]" v-for="item in children_information" :key="item.id">
|
||||
<a-col :span="5">
|
||||
<a-input v-model="item.name" placeholder="姓名" allowClear></a-input>
|
||||
</a-col>
|
||||
<a-col :span="5">
|
||||
<a-select v-model="item.gender" placeholder="请选择性别" allowClear>
|
||||
<a-select-option value="男"> 男 </a-select-option>
|
||||
<a-select-option value="女"> 女 </a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="5">
|
||||
<a-date-picker
|
||||
v-model="item.birthday"
|
||||
placeholder="出生日期"
|
||||
@change="onChange($event, 'birth_date', item.id)"
|
||||
></a-date-picker>
|
||||
</a-col>
|
||||
<a-col :span="5">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:step="1"
|
||||
:style="{ width: '100%' }"
|
||||
v-model="item.parental_leave_left"
|
||||
placeholder="请输入剩余育儿假"
|
||||
:formatter="(value) => `${value} 天`"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="1">
|
||||
<a @click="addChildren">
|
||||
<a-icon type="plus-circle" />
|
||||
</a>
|
||||
</a-col>
|
||||
<a-col :span="1" v-if="children_information.length > 1">
|
||||
<a @click="() => removeChildren(item.id)" :style="{ color: 'red' }">
|
||||
<a-icon type="delete" />
|
||||
</a>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
label="银行卡"
|
||||
prop="bank_card"
|
||||
:style="{ display: 'inline-block', width: '98%', margin: '0 7px 24px' }"
|
||||
v-if="
|
||||
attributes.findIndex((v) => v == 'bank_card_number') !== -1 ||
|
||||
attributes.findIndex((v) => v == 'bank_card_name') !== -1 ||
|
||||
attributes.findIndex((v) => v == 'opening_bank') !== -1 ||
|
||||
attributes.findIndex((v) => v == 'account_opening_location') !== -1
|
||||
"
|
||||
>
|
||||
<a-row :gutter="[8, { xs: 8 }]">
|
||||
<a-col :span="6" v-if="attributes.findIndex((v) => v == 'bank_card_number') !== -1">
|
||||
<a-input v-model="employeeFormData.bank_card_number" placeholder="卡号" allowClear></a-input>
|
||||
</a-col>
|
||||
<a-col :span="6" v-if="attributes.findIndex((v) => v == 'bank_card_name') !== -1">
|
||||
<a-input v-model="employeeFormData.bank_card_name" placeholder="银行" allowClear></a-input>
|
||||
</a-col>
|
||||
<a-col :span="6" v-if="attributes.findIndex((v) => v == 'opening_bank') !== -1">
|
||||
<a-input v-model="employeeFormData.opening_bank" placeholder="开户行" allowClear></a-input>
|
||||
</a-col>
|
||||
<a-col :span="6" v-if="attributes.findIndex((v) => v == 'account_opening_location') !== -1">
|
||||
<a-input v-model="employeeFormData.account_opening_location" placeholder="开户地" allowClear></a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<template slot="footer">
|
||||
<a-button key="back" @click="close"> 取消 </a-button>
|
||||
<a-button type="primary" @click="employeeModalHandleOk"> 确定 </a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import _ from 'lodash'
|
||||
import { postEmployee, putEmployee } from '@/api/employee'
|
||||
import Bus from './eventBus/bus'
|
||||
import EmployeeTreeSelect from '../components/employeeTreeSelect.vue'
|
||||
import DepartmentTreeSelect from '../components/departmentTreeSelect.vue'
|
||||
import appConfig from '@/config/app'
|
||||
import moment from 'moment'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
export default {
|
||||
components: { EmployeeTreeSelect, DepartmentTreeSelect },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
employeeFormData: {},
|
||||
formModalItemStyle: { display: 'inline-block', width: '48%', margin: '0 7px 24px', overflow: 'hidden' },
|
||||
rules: {
|
||||
email: [
|
||||
{ required: true, whitespace: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
|
||||
message: '邮箱格式错误',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ max: 50, message: '字符数须小于50' },
|
||||
],
|
||||
username: [
|
||||
{ required: true, whitespace: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ max: 20, message: '字符数须小于20' },
|
||||
],
|
||||
password: [{ required: true, whitespace: true, message: '请输入密码', trigger: 'blur' }],
|
||||
nickname: [
|
||||
{ required: true, whitespace: true, message: '请输入姓名', trigger: 'blur' },
|
||||
{ max: 20, message: '字符数须小于20' },
|
||||
],
|
||||
mobile: [
|
||||
{
|
||||
pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
||||
message: '请输入正确的手机号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'add',
|
||||
useDFC: appConfig.useDFC,
|
||||
educational_experience: [],
|
||||
children_information: [],
|
||||
file_is_show: true,
|
||||
attributes: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
Bus.$on('getAttributes', (attributes) => {
|
||||
this.attributes = attributes
|
||||
})
|
||||
},
|
||||
inject: ['provide_allTreeDepartment', 'provide_allFlatEmployees'],
|
||||
computed: {
|
||||
...mapState({
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
departemntTreeSelectOption() {
|
||||
return this.provide_allTreeDepartment()
|
||||
},
|
||||
allFlatEmployees() {
|
||||
return this.provide_allFlatEmployees()
|
||||
},
|
||||
title() {
|
||||
if (this.type === 'add') {
|
||||
return '新建员工'
|
||||
}
|
||||
return '编辑员工'
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
Bus.$off('getAttributes')
|
||||
},
|
||||
methods: {
|
||||
async open(getData, type) {
|
||||
// 提交时去掉school, major, education, graduation_year, name, gender, birthday, parental_leave_left
|
||||
const { school, major, education, graduation_year, name, gender, birthday, parental_leave_left, ...newGetData } =
|
||||
getData
|
||||
const _getData = _.cloneDeep(newGetData)
|
||||
const { direct_supervisor_id } = newGetData
|
||||
if (direct_supervisor_id) {
|
||||
const _find = this.allFlatEmployees.find((item) => item.employee_id === direct_supervisor_id)
|
||||
_getData.direct_supervisor_id = `${_find.department_id}-${direct_supervisor_id}`
|
||||
} else {
|
||||
_getData.direct_supervisor_id = undefined
|
||||
}
|
||||
this.employeeFormData = _.cloneDeep(_getData)
|
||||
// if (type !== 'add' && this.employeeFormData.educational_experience.length !== 0) {
|
||||
// this.educational_experience = this.employeeFormData.educational_experience
|
||||
// }
|
||||
this.children_information = this.formatChildrenInformationList() || [
|
||||
{
|
||||
id: uuidv4(),
|
||||
name: '',
|
||||
gender: undefined,
|
||||
birthday: null,
|
||||
parental_leave_left: 0,
|
||||
},
|
||||
]
|
||||
this.educational_experience = this.formatEducationalExperienceList() || [
|
||||
{
|
||||
id: uuidv4(),
|
||||
school: '',
|
||||
major: '',
|
||||
education: undefined,
|
||||
graduation_year: null,
|
||||
},
|
||||
]
|
||||
|
||||
this.type = type
|
||||
this.visible = true
|
||||
},
|
||||
close() {
|
||||
this.$refs.employeeFormData.resetFields()
|
||||
this.educational_experience = [
|
||||
{
|
||||
school: '',
|
||||
major: '',
|
||||
education: undefined,
|
||||
graduation_year: null,
|
||||
},
|
||||
]
|
||||
this.children_information = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
name: '',
|
||||
gender: undefined,
|
||||
birthday: null,
|
||||
parental_leave_left: 0,
|
||||
},
|
||||
]
|
||||
this.visible = false
|
||||
},
|
||||
formatChildrenInformationList() {
|
||||
let arr = []
|
||||
arr = this.employeeFormData.children_information ? this.employeeFormData.children_information : undefined
|
||||
if (arr && arr.length) {
|
||||
arr.forEach((item) => {
|
||||
item.id = uuidv4()
|
||||
})
|
||||
return arr
|
||||
}
|
||||
return null
|
||||
},
|
||||
formatEducationalExperienceList() {
|
||||
let arr = []
|
||||
arr = this.employeeFormData.educational_experience ? this.employeeFormData.educational_experience : undefined
|
||||
if (arr && arr.length) {
|
||||
arr.forEach((item) => {
|
||||
item.id = uuidv4()
|
||||
})
|
||||
return arr
|
||||
}
|
||||
return null
|
||||
},
|
||||
addEducation() {
|
||||
const newEducational_experience = {
|
||||
id: uuidv4(),
|
||||
school: '',
|
||||
major: '',
|
||||
education: undefined,
|
||||
graduation_year: null,
|
||||
}
|
||||
this.educational_experience.push(newEducational_experience)
|
||||
},
|
||||
removeEducation(removeId) {
|
||||
const _idx = this.educational_experience.findIndex((item) => item.id === removeId)
|
||||
if (_idx !== -1) {
|
||||
this.educational_experience.splice(_idx, 1)
|
||||
}
|
||||
},
|
||||
addChildren() {
|
||||
const newChildrenInfo = {
|
||||
id: uuidv4(),
|
||||
name: '',
|
||||
gender: undefined,
|
||||
birthday: null,
|
||||
parental_leave_left: 0,
|
||||
}
|
||||
this.children_information.push(newChildrenInfo)
|
||||
},
|
||||
removeChildren(removeId) {
|
||||
const _idx = this.children_information.findIndex((item) => item.id === removeId)
|
||||
if (_idx !== -1) {
|
||||
this.children_information.splice(_idx, 1)
|
||||
}
|
||||
},
|
||||
onChange(date, param, id) {
|
||||
// if (param === 'graduation_year') {
|
||||
// if (date === null) {
|
||||
// this.educational_experience[index].graduation_year = null
|
||||
// } else {
|
||||
// this.educational_experience[index].graduation_year = moment(date).format('YYYY-MM-DD')
|
||||
// }
|
||||
// } else {
|
||||
// if (date === null) {
|
||||
// this.employeeFormData[param] = null
|
||||
// } else {
|
||||
// this.employeeFormData[param] = moment(date).format('YYYY-MM-DD')
|
||||
// }
|
||||
// }
|
||||
if (date !== null) {
|
||||
if (param === 'graduation_year') {
|
||||
const _idx = this.educational_experience.findIndex((item) => item.id === id)
|
||||
this.educational_experience[_idx].graduation_year = moment(date).format('YYYY-MM')
|
||||
} else if (param === 'birth_date') {
|
||||
const _idx = this.children_information.findIndex((item) => item.id === id)
|
||||
this.children_information[_idx].birthday = moment(date).format('YYYY-MM-DD')
|
||||
} else {
|
||||
this.employeeFormData[param] = moment(date).format('YYYY-MM-DD')
|
||||
}
|
||||
}
|
||||
},
|
||||
async employeeModalHandleOk() {
|
||||
if (this.attributes.includes('educational_experience')) {
|
||||
this.employeeFormData.educational_experience = this.educational_experience
|
||||
}
|
||||
if (this.attributes.includes('children_information')) {
|
||||
this.employeeFormData.children_information = this.children_information
|
||||
}
|
||||
// if (!this.employeeFormData.annual_leave) {
|
||||
// this.employeeFormData.annual_leave = 0
|
||||
// }
|
||||
const getFormData = this.employeeFormData
|
||||
getFormData.direct_supervisor_id = getFormData.direct_supervisor_id
|
||||
? (getFormData.direct_supervisor_id + '').includes('-')
|
||||
? +getFormData.direct_supervisor_id.split('-')[1]
|
||||
: +getFormData.direct_supervisor_id
|
||||
: 0
|
||||
this.$refs.employeeFormData.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (this.type === 'add') {
|
||||
await postEmployee(getFormData)
|
||||
}
|
||||
if (this.type === 'edit') {
|
||||
await putEmployee(getFormData.employee_id, getFormData)
|
||||
}
|
||||
this.$message.success('操作成功')
|
||||
this.$emit('refresh')
|
||||
Bus.$emit('updataAllIncludeEmployees')
|
||||
this.close()
|
||||
} else {
|
||||
this.$message.warning('检查您的输入是否正确!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.el-date-picker {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,3 @@
|
||||
// 用于树状递归组件的通信
|
||||
import Vue from 'vue'
|
||||
export default new Vue()
|
1143
cmdb-ui/src/views/setting/companyStructure/index.vue
Normal file
1143
cmdb-ui/src/views/setting/companyStructure/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
140
cmdb-ui/src/views/setting/components/EditImage.vue
Normal file
140
cmdb-ui/src/views/setting/components/EditImage.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<a-modal
|
||||
class="ops-modal"
|
||||
v-loading="loading"
|
||||
:title="title"
|
||||
width="600px"
|
||||
:append-to-body="true"
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="showDialog"
|
||||
@cancel="hiddenView"
|
||||
>
|
||||
<div class="ops-modal-cropper-box">
|
||||
<vueCropper
|
||||
ref="cropper"
|
||||
:can-move="true"
|
||||
:auto-crop="true"
|
||||
:fixed="true"
|
||||
:img="cropperImg"
|
||||
output-type="png"
|
||||
@realTime="realTime"
|
||||
v-bind="eidtImageOption"
|
||||
/>
|
||||
<div class="ops-modal-preview">
|
||||
<div class="ops-modal-preview-name">预览</div>
|
||||
<img
|
||||
:style="{
|
||||
width: eidtImageOption.previewWidth,
|
||||
height: eidtImageOption.previewHeight,
|
||||
border: '1px solid #f2f2f2',
|
||||
}"
|
||||
:src="previewImg"
|
||||
class="ops-modal-preview-img"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="ops-modal-dialog-footer">
|
||||
<a-button type="primary" @click="submitImage()">确定</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script type="text/javascript">
|
||||
import { VueCropper } from 'vue-cropper'
|
||||
|
||||
export default {
|
||||
name: 'EditImage', // 处理头像
|
||||
components: {
|
||||
VueCropper,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '编辑头像',
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
image: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
eidtImageOption: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
showDialog: false,
|
||||
cropperImg: '',
|
||||
previewImg: '',
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
show: {
|
||||
handler(val) {
|
||||
this.showDialog = val
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
image: function(val) {
|
||||
this.cropperImg = val
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.cropperImg = this.image
|
||||
},
|
||||
methods: {
|
||||
realTime(data) {
|
||||
this.$refs.cropper.getCropData((cropperData) => {
|
||||
this.previewImg = cropperData
|
||||
})
|
||||
},
|
||||
submitImage() {
|
||||
// 获取截图的blob数据
|
||||
this.$refs.cropper.getCropBlob((data) => {
|
||||
const form = new FormData()
|
||||
form.append('file', data)
|
||||
this.$emit('save', form)
|
||||
this.hiddenView()
|
||||
})
|
||||
},
|
||||
hiddenView() {
|
||||
this.$emit('close')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ops-modal {
|
||||
.ops-modal-cropper-box {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.ops-modal-preview {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 325px;
|
||||
.ops-modal-preview-name {
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
.ops-modal-preview-img {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.ops-modal-content {
|
||||
position: relative;
|
||||
padding: 0 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
80
cmdb-ui/src/views/setting/components/SearchForm.vue
Normal file
80
cmdb-ui/src/views/setting/components/SearchForm.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :style="{ marginLeft: '10px'}">
|
||||
<FilterComp
|
||||
ref="filterComp"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList"
|
||||
:placement="placement"
|
||||
@setExpFromFilter="setExpFromFilter"
|
||||
>
|
||||
<div slot="popover_item" class="search-form-bar-filter">
|
||||
<a-icon :class="filterExp.length ? 'search-form-bar-filter-icon' : 'search-form-bar-filter-icon_selected'" type="filter"/>
|
||||
条件过滤
|
||||
<a-icon :class="filterExp.length ? 'search-form-bar-filter-icon' : 'search-form-bar-filter-icon_selected'" type="down"/>
|
||||
</div>
|
||||
</FilterComp>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FilterComp from './settingFilterComp'
|
||||
export default {
|
||||
name: 'SearchForm',
|
||||
components: {
|
||||
FilterComp,
|
||||
},
|
||||
props: {
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottomLeft'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filterExp: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setExpFromFilter(filterExp) {
|
||||
// const regSort = /(?<=sort=).+/g
|
||||
// const expSort = this.expression.match(regSort) ? this.expression.match(regSort)[0] : undefined
|
||||
// let expression = ''
|
||||
// if (filterExp) {
|
||||
// expression = `q=${filterExp}`
|
||||
// }
|
||||
// if (expSort) {
|
||||
// expression += `&sort=${expSort}`
|
||||
// }
|
||||
this.filterExp = filterExp
|
||||
this.emitRefresh(filterExp)
|
||||
},
|
||||
emitRefresh(filterExp) {
|
||||
this.$nextTick(() => {
|
||||
this.$emit('refresh', filterExp)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '~@/style/static.less';
|
||||
.search-form-bar-filter {
|
||||
background-color: rgb(240, 245, 255);
|
||||
.ops_display_wrapper();
|
||||
.search-form-bar-filter-icon {
|
||||
color: #custom_colors[color_1];
|
||||
font-size: 12px;
|
||||
}
|
||||
.search-form-bar-filter-icon_selected{
|
||||
color:#606266;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<treeselect
|
||||
:multiple="false"
|
||||
:options="departemntTreeSelectOption"
|
||||
placeholder="请选择部门"
|
||||
v-model="treeValue"
|
||||
:normalizer="normalizer"
|
||||
noChildrenText="空"
|
||||
noOptionsText="空"
|
||||
class="ops-setting-treeselect"
|
||||
v-bind="$attrs"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
export default {
|
||||
name: 'DepartmentTreeSelect',
|
||||
components: { Treeselect },
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Array, Number, null],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
normalizer: (node) => {
|
||||
if (node.sub_departments && node.sub_departments.length) {
|
||||
return {
|
||||
id: node.department_id,
|
||||
label: node.department_name,
|
||||
children: node.sub_departments,
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: node.department_id,
|
||||
label: node.department_name,
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
inject: ['provide_allTreeDepartment'],
|
||||
computed: {
|
||||
departemntTreeSelectOption() {
|
||||
return this.provide_allTreeDepartment()
|
||||
},
|
||||
treeValue: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('change', val)
|
||||
return val
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
1841
cmdb-ui/src/views/setting/components/employeeTable.vue
Normal file
1841
cmdb-ui/src/views/setting/components/employeeTable.vue
Normal file
File diff suppressed because it is too large
Load Diff
74
cmdb-ui/src/views/setting/components/employeeTreeSelect.vue
Normal file
74
cmdb-ui/src/views/setting/components/employeeTreeSelect.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<treeselect
|
||||
:disable-branch-nodes="multiple ? false : true"
|
||||
:multiple="multiple"
|
||||
:options="employeeTreeSelectOption"
|
||||
placeholder="请选择员工"
|
||||
v-model="treeValue"
|
||||
:max-height="200"
|
||||
noChildrenText="空"
|
||||
noOptionsText="空"
|
||||
:class="className ? className: 'ops-setting-treeselect'"
|
||||
value-consists-of="LEAF_PRIORITY"
|
||||
:limit="20"
|
||||
:limitText="(count) => `+ ${count}`"
|
||||
v-bind="$attrs"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
</treeselect>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import { formatOption } from '@/utils/util'
|
||||
export default {
|
||||
name: 'EmployeeTreeSelect',
|
||||
components: {
|
||||
Treeselect,
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Array, Number, null],
|
||||
default: null,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: 'ops-setting-treeselect'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
inject: ['provide_allTreeDepAndEmp'],
|
||||
computed: {
|
||||
treeValue: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('change', val)
|
||||
return val
|
||||
},
|
||||
},
|
||||
allTreeDepAndEmp() {
|
||||
return this.provide_allTreeDepAndEmp()
|
||||
},
|
||||
employeeTreeSelectOption() {
|
||||
return formatOption(this.allTreeDepAndEmp)
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
37
cmdb-ui/src/views/setting/components/relateEmployee.vue
Normal file
37
cmdb-ui/src/views/setting/components/relateEmployee.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<a-modal title="关联员工" :visible="visible" @cancel="handleCancel" @ok="handleOK">
|
||||
<EmployeeTreeSelect v-model="values" :multiple="true" />
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EmployeeTreeSelect from './employeeTreeSelect.vue'
|
||||
export default {
|
||||
name: 'RelateEmployee',
|
||||
components: { EmployeeTreeSelect },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
values: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.visible = true
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false
|
||||
this.values = []
|
||||
},
|
||||
handleOK() {
|
||||
this.$emit('relate', {
|
||||
action: 'add',
|
||||
employee_id_list: this.values.filter((item) => String(item).includes('-')).map((item) => item.split('-')[1]),
|
||||
})
|
||||
this.handleCancel()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
@@ -0,0 +1,33 @@
|
||||
export const ruleTypeList = [
|
||||
{ value: '&', label: '与' },
|
||||
{ value: '|', label: '或' },
|
||||
// { value: 'not', label: '非' },
|
||||
]
|
||||
|
||||
export const expList = [
|
||||
{ value: 1, label: '等于' },
|
||||
{ value: 2, label: '不等于' },
|
||||
// { value: 'contain', label: '包含' },
|
||||
// { value: '~contain', label: '不包含' },
|
||||
// { value: 'start_with', label: '以...开始' },
|
||||
// { value: '~start_with', label: '不以...开始' },
|
||||
// { value: 'end_with', label: '以...结束' },
|
||||
// { value: '~end_with', label: '不以...结束' },
|
||||
{ value: 7, label: '为空' }, // 为空的定义有点绕
|
||||
{ value: 8, label: '不为空' },
|
||||
]
|
||||
|
||||
export const advancedExpList = [
|
||||
// { value: 'in', label: 'in查询' },
|
||||
// { value: '~in', label: '非in查询' },
|
||||
// { value: 'range', label: '范围' },
|
||||
// { value: '~range', label: '范围外' },
|
||||
{ value: 'compare', label: '比较' },
|
||||
]
|
||||
|
||||
export const compareTypeList = [
|
||||
{ value: 5, label: '大于' },
|
||||
// { value: '2', label: '>=' },
|
||||
{ value: 6, label: '小于' },
|
||||
// { value: '4', label: '<=' },
|
||||
]
|
380
cmdb-ui/src/views/setting/components/settingFilterComp/index.vue
Normal file
380
cmdb-ui/src/views/setting/components/settingFilterComp/index.vue
Normal file
@@ -0,0 +1,380 @@
|
||||
<template>
|
||||
<a-popover
|
||||
v-model="visible"
|
||||
trigger="click"
|
||||
:placement="placement"
|
||||
overlayClassName="table-filter"
|
||||
@visibleChange="visibleChange"
|
||||
>
|
||||
<slot name="popover_item">
|
||||
<a-button type="primary" ghost>条件过滤<a-icon type="filter"/></a-button>
|
||||
</slot>
|
||||
<template slot="content">
|
||||
<svg
|
||||
:style="{ position: 'absolute', top: '0', left: '0', width: '110px', height: '100%', zIndex: '-1' }"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
height="100%"
|
||||
width="100%"
|
||||
id="svgDom"
|
||||
></svg>
|
||||
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
|
||||
<div :style="{ width: '50px', height: '24px', position: 'relative' }">
|
||||
<treeselect
|
||||
v-if="index"
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '50px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
|
||||
v-model="item.relation"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="ruleTypeList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
</treeselect>
|
||||
</div>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '130px', '--custom-height': '24px' }"
|
||||
v-model="item.column"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="canSearchPreferenceAttrList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
>value
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
<!-- <ValueTypeMapIcon :attr="node.raw" /> -->
|
||||
{{ node.label }}
|
||||
</div>
|
||||
<div
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
slot="value-label"
|
||||
slot-scope="{ node }"
|
||||
>
|
||||
<!-- <ValueTypeMapIcon :attr="node.raw" /> -->
|
||||
{{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '100px', '--custom-height': '24px' }"
|
||||
v-model="item.operator"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="[...expList, ...compareTypeList]"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
@select="(value) => handleChangeExp(value, item, index)"
|
||||
>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '175px', '--custom-height': '24px' }"
|
||||
v-model="item.value"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
v-if="isChoiceByProperty(item.column) && (item.operator === 1 || item.operator === 2)"
|
||||
:options="getChoiceValueByProperty(item.column)"
|
||||
placeholder="请选择"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
{{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
<div v-else-if="item.column === 'direct_supervisor_id' && (item.operator === 1 || item.operator === 2)" style="width: 175px">
|
||||
<EmployeeTreeSelect v-model="item.value" className="custom-treeselect"/>
|
||||
</div>
|
||||
<a-input
|
||||
v-else-if="item.operator !== 7 && item.operator !== 8"
|
||||
size="small"
|
||||
v-model="item.value"
|
||||
:placeholder="item.exp === 'in' || item.exp === '~in' ? '以 ; 分隔' : ''"
|
||||
class="ops-input"
|
||||
></a-input>
|
||||
<!-- <div v-else :style="{ width: '175px' }"></div> -->
|
||||
<a-tooltip title="复制">
|
||||
<a class="operation" @click="handleCopyRule(item)"><a-icon type="copy"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="删除">
|
||||
<a class="operation" @click="handleDeleteRule(item)" :style="{ color: 'red' }"><a-icon type="delete"/></a>
|
||||
</a-tooltip>
|
||||
|
||||
</a-space>
|
||||
<div class="table-filter-add">
|
||||
<a @click="handleAddRule">+ 新增</a>
|
||||
</div>
|
||||
<a-divider :style="{ margin: '10px 0' }" />
|
||||
<div style="width:534px">
|
||||
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
|
||||
<a-button type="primary" size="small" @click="handleSubmit">确定</a-button>
|
||||
<a-button size="small" @click="handleClear">清空</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants'
|
||||
import DepartmentTreeSelect from '../../components/departmentTreeSelect.vue'
|
||||
import EmployeeTreeSelect from '../../components/employeeTreeSelect.vue'
|
||||
|
||||
export default {
|
||||
name: 'FilterComp',
|
||||
components: {
|
||||
// ValueTypeMapIcon,
|
||||
Treeselect,
|
||||
DepartmentTreeSelect,
|
||||
EmployeeTreeSelect },
|
||||
props: {
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
regQ: {
|
||||
type: String,
|
||||
default: '(?<=q=).+(?=&)|(?<=q=).+$',
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottomRight',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ruleTypeList,
|
||||
expList,
|
||||
advancedExpList,
|
||||
compareTypeList,
|
||||
visible: false,
|
||||
ruleList: [],
|
||||
filterExp: '',
|
||||
}
|
||||
},
|
||||
inject: ['provide_allFlatEmployees'],
|
||||
computed: {
|
||||
allFlatEmployees() {
|
||||
return this.provide_allFlatEmployees()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
visibleChange(open) {
|
||||
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||
const _exp = this.ruleList.length
|
||||
? this.ruleList
|
||||
: null
|
||||
if (open && _exp) {
|
||||
_exp.forEach((item, index) => {
|
||||
if (item.column === 'direct_supervisor_id' && item.value) {
|
||||
if (!(item.value + '').includes('-')) {
|
||||
const _find = this.allFlatEmployees.find((v) => v.employee_id === item.value)
|
||||
_exp[index].value = `${_find.department_id}-${item.value}`
|
||||
}
|
||||
}
|
||||
})
|
||||
this.ruleList = _exp
|
||||
} else if (open) {
|
||||
this.ruleList = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
relation: '&',
|
||||
column: this.canSearchPreferenceAttrList[0].value,
|
||||
operator: 1,
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
handleAddRule() {
|
||||
this.ruleList.push({
|
||||
id: uuidv4(),
|
||||
relation: '&',
|
||||
column: this.canSearchPreferenceAttrList[0].value,
|
||||
operator: 1,
|
||||
value: null,
|
||||
})
|
||||
},
|
||||
handleCopyRule(item) {
|
||||
this.ruleList.push({ ...item, id: uuidv4() })
|
||||
},
|
||||
handleDeleteRule(item) {
|
||||
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||
if (idx > -1) {
|
||||
this.ruleList.splice(idx, 1)
|
||||
}
|
||||
},
|
||||
handleClear() {
|
||||
this.ruleList = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
relation: '&',
|
||||
column: this.canSearchPreferenceAttrList[0].value,
|
||||
operator: 1,
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
this.filterExp = []
|
||||
this.visible = false
|
||||
this.$emit('setExpFromFilter', this.filterExp)
|
||||
},
|
||||
handleSubmit() {
|
||||
if (this.ruleList && this.ruleList.length) {
|
||||
const getDataFromRuleList = this.ruleList
|
||||
getDataFromRuleList.forEach((item, index) => {
|
||||
if (item.column === 'direct_supervisor_id') {
|
||||
getDataFromRuleList[index].value = item.value ? (item.value + '').includes('-') ? +item.value.split('-')[1] : +item.value : 0
|
||||
}
|
||||
})
|
||||
getDataFromRuleList[0].relation = '&' // 增删后,以防万一第一个不是and
|
||||
this.$emit('setExpFromFilter', getDataFromRuleList)
|
||||
} else {
|
||||
this.$emit('setExpFromFilter', '')
|
||||
}
|
||||
this.visible = false
|
||||
},
|
||||
handleChangeExp({ value }, item, index) {
|
||||
const _ruleList = _.cloneDeep(this.ruleList)
|
||||
if (value === 7 || value === 8) {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
value: null,
|
||||
operator: value
|
||||
}
|
||||
} else {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
operator: value,
|
||||
}
|
||||
}
|
||||
this.ruleList = _ruleList
|
||||
},
|
||||
filterOption(input, option) {
|
||||
return option.componentOptions.children[1].children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
},
|
||||
// getExpListByProperty(column) {
|
||||
// if (column) {
|
||||
// const _find = this.canSearchPreferenceAttrList.find((item) => item.value === column)
|
||||
// if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
|
||||
// return [
|
||||
// { value: 'is', label: '等于' },
|
||||
// { value: '~is', label: '不等于' },
|
||||
// { value: '~value', label: '为空' }, // 为空的定义有点绕
|
||||
// { value: 'value', label: '不为空' },
|
||||
// ]
|
||||
// }
|
||||
// return this.expList
|
||||
// }
|
||||
// return this.expList
|
||||
// },
|
||||
isChoiceByProperty(column) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.value === column)
|
||||
if (_find) {
|
||||
return _find.is_choice
|
||||
}
|
||||
return false
|
||||
},
|
||||
getChoiceValueByProperty(column) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.value === column)
|
||||
if (_find) {
|
||||
return _find.choice_value
|
||||
}
|
||||
return []
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table-filter {
|
||||
.table-filter-add {
|
||||
margin-top: 10px;
|
||||
& > a {
|
||||
padding: 2px 8px;
|
||||
&:hover {
|
||||
background-color: #f0faff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.table-filter-extra-icon {
|
||||
padding: 0px 2px;
|
||||
&:hover {
|
||||
display: inline-block;
|
||||
border-radius: 5px;
|
||||
background-color: #f0faff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table-filter-extra-operation {
|
||||
.ant-popover-inner-content {
|
||||
padding: 3px 4px;
|
||||
.operation {
|
||||
cursor: pointer;
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 3px 4px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s;
|
||||
&:hover {
|
||||
background-color: #f0faff;
|
||||
}
|
||||
> .anticon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
32
cmdb-ui/src/views/setting/components/spanTitle.vue
Normal file
32
cmdb-ui/src/views/setting/components/spanTitle.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<a-row>
|
||||
<a-col :span="span">
|
||||
<div class="ops-setting-spantitle"><slot></slot></div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SpanTitle',
|
||||
props: {
|
||||
span: {
|
||||
type: Number,
|
||||
default: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ops-setting-spantitle {
|
||||
height: 28px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 28px;
|
||||
padding-left: 24px;
|
||||
border-radius: 0px 20px 20px 0px;
|
||||
font-weight: 700;
|
||||
color: #0637bf;
|
||||
background-color: #e0e9ff;
|
||||
}
|
||||
</style>
|
367
cmdb-ui/src/views/setting/person/index.vue
Normal file
367
cmdb-ui/src/views/setting/person/index.vue
Normal file
@@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<div class="setting-person">
|
||||
<div class="setting-person-left">
|
||||
<div
|
||||
@click="
|
||||
() => {
|
||||
$refs.personForm.clearValidate()
|
||||
$nextTick(() => {
|
||||
current = '1'
|
||||
})
|
||||
}
|
||||
"
|
||||
:class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '1' }"
|
||||
>
|
||||
<ops-icon type="icon-shidi-yonghu" />个人信息
|
||||
</div>
|
||||
<div
|
||||
@click="
|
||||
() => {
|
||||
$refs.personForm.clearValidate()
|
||||
$nextTick(() => {
|
||||
current = '2'
|
||||
})
|
||||
}
|
||||
"
|
||||
:class="{ 'setting-person-left-item': true, 'setting-person-left-item-selected': current === '2' }"
|
||||
>
|
||||
<a-icon type="unlock" theme="filled" />账号密码
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-person-right">
|
||||
<a-form-model
|
||||
ref="personForm"
|
||||
:model="form"
|
||||
:rules="current === '1' ? rules1 : rules2"
|
||||
:colon="false"
|
||||
labelAlign="left"
|
||||
:labelCol="{ span: 4 }"
|
||||
:wrapperCol="{ span: 10 }"
|
||||
>
|
||||
<div v-show="current === '1'">
|
||||
<a-form-model-item label="头像" :style="{ display: 'flex', alignItems: 'center' }">
|
||||
<a-space>
|
||||
<a-avatar v-if="form.avatar" :src="`/api/common-setting/v1/file/${form.avatar}`" :size="64"> </a-avatar>
|
||||
<a-avatar v-else style="backgroundColor:#F0F5FF" :size="64">
|
||||
<ops-icon type="icon-shidi-yonghu" :style="{ color: '#2F54EB' }" />
|
||||
</a-avatar>
|
||||
<a-upload
|
||||
name="avatar"
|
||||
:show-upload-list="false"
|
||||
:customRequest="customRequest"
|
||||
:before-upload="beforeUpload"
|
||||
:style="{ width: '310px', height: '100px' }"
|
||||
accept="png,jpg,jpeg"
|
||||
>
|
||||
<a-button type="primary" ghost size="small">更换头像</a-button>
|
||||
</a-upload>
|
||||
</a-space>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="姓名" prop="nickname">
|
||||
<a-input v-model="form.nickname" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="用户名">
|
||||
<div class="setting-person-right-disabled">{{ form.username }}</div>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="邮箱">
|
||||
<div class="setting-person-right-disabled">{{ form.email }}</div>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="直属上级">
|
||||
<div class="setting-person-right-disabled">
|
||||
{{ getDirectorName(allFlatEmployees, form.direct_supervisor_id) }}
|
||||
</div>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="性别">
|
||||
<a-select v-model="form.sex">
|
||||
<a-select-option value="男">男</a-select-option>
|
||||
<a-select-option value="女">女</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="手机号" prop="mobile">
|
||||
<a-input v-model="form.mobile" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="部门">
|
||||
<div class="setting-person-right-disabled">
|
||||
{{ getDepartmentName(allFlatDepartments, form.department_id) }}
|
||||
</div>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="岗位">
|
||||
<div class="setting-person-right-disabled">{{ form.position_name }}</div>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="绑定信息">
|
||||
<a-space>
|
||||
<div :class="{ 'setting-person-bind': true, 'setting-person-bind-existed': form.wx_id }">
|
||||
<ops-icon type="ops-setting-notice-wx" />
|
||||
</div>
|
||||
<div @click="handleBindWx" class="setting-person-bind-button">
|
||||
{{ form.wx_id ? '重新绑定' : '绑定' }}
|
||||
</div>
|
||||
</a-space>
|
||||
</a-form-model-item>
|
||||
</div>
|
||||
<div v-show="current === '2'">
|
||||
<a-form-model-item label="新密码" prop="password1">
|
||||
<a-input v-model="form.password1" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="确认密码" prop="password2">
|
||||
<a-input v-model="form.password2" />
|
||||
</a-form-model-item>
|
||||
</div>
|
||||
<div style="margin-right: 120px">
|
||||
<a-form-model-item label=" ">
|
||||
<a-button type="primary" @click="handleSave" :style="{ width: '100%' }">保存</a-button>
|
||||
</a-form-model-item>
|
||||
</div>
|
||||
</a-form-model>
|
||||
</div>
|
||||
<EditImage
|
||||
v-if="showEditImage"
|
||||
:fixed-number="eidtImageOption.fixedNumber"
|
||||
:show="showEditImage"
|
||||
:image="editImage"
|
||||
:title="eidtImageOption.title"
|
||||
:preview-width="eidtImageOption.previewWidth"
|
||||
:preview-height="eidtImageOption.previewHeight"
|
||||
preview-radius="0"
|
||||
width="550px"
|
||||
save-button-title="确定"
|
||||
@save="submitImage"
|
||||
@close="showEditImage = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import { getAllDepartmentList, postImageFile } from '@/api/company'
|
||||
import {
|
||||
getEmployeeList,
|
||||
getEmployeeByUid,
|
||||
updateEmployeeByUid,
|
||||
updatePasswordByUid,
|
||||
bindWxByUid,
|
||||
} from '@/api/employee'
|
||||
import { getDepartmentName, getDirectorName } from '@/utils/util'
|
||||
import EditImage from '../components/EditImage.vue'
|
||||
export default {
|
||||
name: 'Person',
|
||||
components: { EditImage },
|
||||
data() {
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请二次确认新密码'))
|
||||
}
|
||||
if (value !== this.form.password1) {
|
||||
callback(new Error('两次输入密码不一致'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
return {
|
||||
current: '1',
|
||||
form: {},
|
||||
rules1: {
|
||||
nickname: [
|
||||
{ required: true, whitespace: true, message: '请输入姓名', trigger: 'blur' },
|
||||
{ max: 20, message: '字符数须小于20' },
|
||||
],
|
||||
mobile: [
|
||||
{
|
||||
pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
||||
message: '请输入正确的手机号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
rules2: {
|
||||
password1: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
|
||||
password2: [{ required: true, message: '两次输入密码不一致', trigger: 'blur', validator: validatePassword }],
|
||||
},
|
||||
allFlatEmployees: [],
|
||||
allFlatDepartments: [],
|
||||
showEditImage: false,
|
||||
eidtImageOption: {
|
||||
type: 'avatar',
|
||||
fixedNumber: [4, 4],
|
||||
title: '编辑头像',
|
||||
previewWidth: '60px',
|
||||
previewHeight: '60px',
|
||||
},
|
||||
editImage: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['uid']),
|
||||
},
|
||||
mounted() {
|
||||
this.getAllFlatEmployees()
|
||||
this.getAllFlatDepartment()
|
||||
this.getEmployeeByUid()
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['GetInfo']),
|
||||
getDepartmentName,
|
||||
getDirectorName,
|
||||
getEmployeeByUid() {
|
||||
getEmployeeByUid(this.uid).then((res) => {
|
||||
this.form = { ...res }
|
||||
})
|
||||
},
|
||||
getAllFlatEmployees() {
|
||||
getEmployeeList({ block_status: 0, page_size: 99999 }).then((res) => {
|
||||
this.allFlatEmployees = res.data_list
|
||||
})
|
||||
},
|
||||
getAllFlatDepartment() {
|
||||
getAllDepartmentList({ is_tree: 0 }).then((res) => {
|
||||
this.allFlatDepartments = res
|
||||
})
|
||||
},
|
||||
async handleSave() {
|
||||
await this.$refs.personForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const { nickname, mobile, sex, avatar, password1 } = this.form
|
||||
const params = { nickname, mobile, sex, avatar }
|
||||
if (this.current === '1') {
|
||||
await updateEmployeeByUid(this.uid, params).then((res) => {
|
||||
this.$message.success('保存成功!')
|
||||
this.getEmployeeByUid()
|
||||
this.GetInfo()
|
||||
})
|
||||
} else {
|
||||
await updatePasswordByUid(this.uid, { password: password1 }).then((res) => {
|
||||
this.$message.success('保存成功!')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
customRequest(file) {
|
||||
const reader = new FileReader()
|
||||
var self = this
|
||||
reader.onload = function(e) {
|
||||
let result
|
||||
if (typeof e.target.result === 'object') {
|
||||
// 把Array Buffer转化为blob 如果是base64不需要
|
||||
result = window.URL.createObjectURL(new Blob([e.target.result]))
|
||||
} else {
|
||||
result = e.target.result
|
||||
}
|
||||
|
||||
self.editImage = result
|
||||
self.showEditImage = true
|
||||
}
|
||||
reader.readAsDataURL(file.file)
|
||||
},
|
||||
beforeUpload(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
if (!isLt2M) {
|
||||
this.$message.error('图片大小不可超过2MB!')
|
||||
}
|
||||
return isLt2M
|
||||
},
|
||||
submitImage(file) {
|
||||
postImageFile(file).then((res) => {
|
||||
if (res.file_name) {
|
||||
this.form.avatar = res.file_name
|
||||
}
|
||||
})
|
||||
},
|
||||
async handleBindWx() {
|
||||
await this.$refs.personForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const { nickname, mobile, sex, avatar } = this.form
|
||||
const params = { nickname, mobile, sex, avatar }
|
||||
await updateEmployeeByUid(this.uid, params)
|
||||
bindWxByUid(this.uid)
|
||||
.then(() => {
|
||||
this.$message.success('绑定成功!')
|
||||
})
|
||||
.finally(() => {
|
||||
this.getEmployeeByUid()
|
||||
this.GetInfo()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '~@/style/static.less';
|
||||
.setting-person {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
.setting-person-left {
|
||||
width: 200px;
|
||||
height: 400px;
|
||||
margin-right: 24px;
|
||||
background-color: #fff;
|
||||
border-radius: 15px;
|
||||
padding-top: 15px;
|
||||
.setting-person-left-item {
|
||||
cursor: pointer;
|
||||
padding: 10px 20px;
|
||||
color: #a5a9bc;
|
||||
border-left: 4px solid #fff;
|
||||
margin-bottom: 5px;
|
||||
&:hover {
|
||||
.ops_popover_item_selected();
|
||||
border-color: #custom_colors[color_1];
|
||||
}
|
||||
> i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.setting-person-left-item-selected {
|
||||
.ops_popover_item_selected();
|
||||
border-color: #custom_colors[color_1];
|
||||
}
|
||||
}
|
||||
.setting-person-right {
|
||||
width: 800px;
|
||||
height: 700px;
|
||||
background-color: #fff;
|
||||
border-radius: 15px;
|
||||
padding: 24px 48px;
|
||||
.setting-person-right-disabled {
|
||||
background-color: #custom_colors[color_2];
|
||||
border-radius: 4px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-top: 4px;
|
||||
padding: 0 10px;
|
||||
color: #a5a9bc;
|
||||
}
|
||||
.setting-person-bind {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #a5a9bc;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.setting-person-bind-existed {
|
||||
background: #008cee;
|
||||
}
|
||||
.setting-person-bind-button {
|
||||
height: 40px;
|
||||
width: 72px;
|
||||
background: #f0f5ff;
|
||||
border-radius: 4px;
|
||||
padding: 0 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.setting-person-right .ant-form-item {
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user