feat:add employee work_region (#634)

* feat:add employee work_region

* env
This commit is contained in:
dagongren 2024-11-07 11:52:55 +08:00 committed by GitHub
parent 28dca7f086
commit 5e7c6199bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 700 additions and 582 deletions

View File

@ -860,20 +860,30 @@ body {
.vue-treeselect__control { .vue-treeselect__control {
border-radius: 2px !important; border-radius: 2px !important;
height: 32px; height: 32px;
border-color: #e4e7ed;
.vue-treeselect__value-container{
height: 30px;
}
.vue-treeselect__input-container{
display: flex;
align-items: center;
height: 30px;
}
} }
.vue-treeselect__placeholder, .vue-treeselect__placeholder,
.vue-treeselect__single-value { .vue-treeselect__single-value {
line-height: 32px !important; line-height: 28px !important;
} }
.vue-treeselect__input { .vue-treeselect__input {
height: 32px !important; height: 28px !important;
line-height: 32px !important; line-height: 28px !important;
} }
} }
// vue-treeselect 多选样式 // vue-treeselect 多选样式
.ops-setting-treeselect.vue-treeselect--multi { .ops-setting-treeselect.vue-treeselect--multi {
.vue-treeselect__control { .vue-treeselect__control {
border-radius: 2px !important; border-radius: 2px !important;
border-color: #e4e7ed;
} }
} }

View File

@ -1,393 +0,0 @@
<template>
<a-modal
:visible="visible"
:title="$t('cs.companyStructure.batchImport')"
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" />{{ $t('cs.companyStructure.selectFile') }}</a-button>
</a-upload>
<p><a @click="download">{{ $t('cs.companyStructure.clickDownloadImportTemplate') }}</a></p>
</template>
<div
:style="{
height: '60px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
whiteSpace: 'pre-wrap',
}"
v-if="currentStep === 3"
>
{{ $t('cs.companyStructure.importSuccess', { allCount: allCount })
}}<span :style="{ color: '#2362FB' }"> {{ allCount - errorCount }} </span>{{ $t('cs.companyStructure.count') }},
{{ $t('cs.companyStructure.importFailed') }}<span :style="{ color: '#D81E06' }"> {{ errorCount }} </span
>{{ $t('cs.companyStructure.count') }}
</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="$t('cs.companyStructure.email')" min-width="120" fixed="left"></vxe-column>
<vxe-column field="username" :title="$t('cs.companyStructure.username')" min-width="80" ></vxe-column>
<vxe-column field="nickname" :title="$t('cs.companyStructure.nickname')" min-width="80"></vxe-column>
<vxe-column field="password" :title="$t('cs.companyStructure.password')" min-width="80"></vxe-column>
<vxe-column field="sex" :title="$t('cs.companyStructure.sex')" min-width="60"></vxe-column>
<vxe-column field="mobile" :title="$t('cs.companyStructure.mobile')" min-width="80"></vxe-column>
<vxe-column field="position_name" :title="$t('cs.companyStructure.positionName')" min-width="80"></vxe-column>
<vxe-column field="department_name" :title="$t('cs.companyStructure.departmentName')" min-width="80"></vxe-column>
<vxe-column v-if="has_error" field="err" :title="$t('cs.companyStructure.importFailedReason')" 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">{{ $t('cancel') }}</a-button>
<a-button v-if="currentStep !== 1" size="small" type="primary" ghost @click="goPre">{{ $t('cs.companyStructure.prevStep') }}</a-button>
<a-button v-if="currentStep !== 3" size="small" type="primary" @click="goNext">{{ $t('cs.companyStructure.nextStep') }}</a-button>
<a-button v-else size="small" type="primary" @click="close">{{ $t('cs.companyStructure.done') }}</a-button>
</a-space>
</a-modal>
</template>
<script>
import { downloadExcel, excel2Array } from '@/utils/download'
import { importEmployee } from '@/api/employee'
export default {
name: 'BatchUpload',
data() {
const stepList = [
{
value: 1,
label: this.$t('cs.companyStructure.uploadFile'),
icon: 'icon-shidi-tianjia',
},
{
value: 2,
label: this.$t('cs.companyStructure.confirmData'),
icon: 'icon-shidi-yunshangchuan',
},
{
value: 3,
label: this.$t('cs.companyStructure.uploadDone'),
icon: 'icon-shidi-queren',
},
]
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,
common_importParamsList,
visible: false,
currentStep: 1,
importData: [],
has_error: false,
allCount: 0,
errorCount: 0,
}
},
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(this.$t('cs.companyStructure.dataErr'))
} else {
this.currentStep += 1
this.$message.success(this.$t('cs.companyStructure.opSuccess'))
}
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',
},
],
]
data[1] = data[1].filter((item) => item['v'] !== '目前所属主体')
data[1] = data[1].filter((item) => item['v'] !== '初始入职日期')
downloadExcel(data, this.$t('cs.companyStructure.downloadTemplateName'))
},
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 = {}
// 格式化日期字段
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>

View File

@ -10,10 +10,22 @@
:body-style="{ height: `${windowHeight - 320}px`, overflow: 'hidden', overflowY: 'scroll' }" :body-style="{ height: `${windowHeight - 320}px`, overflow: 'hidden', overflowY: 'scroll' }"
> >
<a-form-model ref="employeeFormData" :model="employeeFormData" :rules="rules" :colon="false"> <a-form-model ref="employeeFormData" :model="employeeFormData" :rules="rules" :colon="false">
<a-form-model-item ref="email" :label="$t('cs.companyStructure.email')" prop="email" :style="formModalItemStyle" v-if="attributes.findIndex(v=>v=='email')!==-1"> <a-form-model-item
<a-input v-model="employeeFormData.email" :placeholder="$t('cs.companyStructure.emailPlaceholder')"/> ref="email"
:label="$t('cs.companyStructure.email')"
prop="email"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'email') !== -1"
>
<a-input v-model="employeeFormData.email" :placeholder="$t('cs.companyStructure.emailPlaceholder')" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item ref="username" :label="$t('cs.companyStructure.username')" prop="username" :style="formModalItemStyle" v-if="attributes.findIndex(v=>v=='username')!==-1"> <a-form-model-item
ref="username"
:label="$t('cs.companyStructure.username')"
prop="username"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'username') !== -1"
>
<a-input v-model="employeeFormData.username" :placeholder="$t('cs.companyStructure.usernamePlaceholder')" /> <a-input v-model="employeeFormData.username" :placeholder="$t('cs.companyStructure.usernamePlaceholder')" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item
@ -23,31 +35,82 @@
prop="password" prop="password"
:style="formModalItemStyle" :style="formModalItemStyle"
> >
<a-input-password v-model="employeeFormData.password" :placeholder="$t('cs.companyStructure.passwordPlaceholder')" /> <a-input-password
v-model="employeeFormData.password"
:placeholder="$t('cs.companyStructure.passwordPlaceholder')"
/>
</a-form-model-item> </a-form-model-item>
<a-form-model-item ref="nickname" :label="$t('cs.companyStructure.nickname')" prop="nickname" :style="formModalItemStyle" v-if="attributes.findIndex(v=>v=='nickname')!==-1"> <a-form-model-item
ref="nickname"
:label="$t('cs.companyStructure.nickname')"
prop="nickname"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'nickname') !== -1"
>
<a-input v-model="employeeFormData.nickname" :placeholder="$t('cs.companyStructure.nicknamePlaceholder')" /> <a-input v-model="employeeFormData.nickname" :placeholder="$t('cs.companyStructure.nicknamePlaceholder')" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item :label="$t('cs.companyStructure.sex')" prop="sex" :style="formModalItemStyle" v-if="attributes.findIndex(v=>v=='sex')!==-1"> <a-form-model-item
:label="$t('cs.companyStructure.sex')"
prop="sex"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'sex') !== -1"
>
<a-select v-model="employeeFormData.sex" :placeholder="$t('cs.companyStructure.sexPlaceholder')"> <a-select v-model="employeeFormData.sex" :placeholder="$t('cs.companyStructure.sexPlaceholder')">
<a-select-option value=""> {{ $t('cs.companyStructure.male') }} </a-select-option> <a-select-option value=""> {{ $t('cs.companyStructure.male') }} </a-select-option>
<a-select-option value=""> {{ $t('cs.companyStructure.female') }} </a-select-option> <a-select-option value=""> {{ $t('cs.companyStructure.female') }} </a-select-option>
</a-select> </a-select>
</a-form-model-item> </a-form-model-item>
<a-form-model-item ref="mobile" :label="$t('cs.companyStructure.mobile')" prop="mobile" :style="formModalItemStyle" v-if="attributes.findIndex(v=>v=='mobile')!==-1"> <a-form-model-item
ref="mobile"
:label="$t('cs.companyStructure.mobile')"
prop="mobile"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'mobile') !== -1"
>
<a-input v-model="employeeFormData.mobile" :placeholder="$t('cs.companyStructure.mobilePlaceholder')" /> <a-input v-model="employeeFormData.mobile" :placeholder="$t('cs.companyStructure.mobilePlaceholder')" />
</a-form-model-item> </a-form-model-item>
<div :style="{ width: '361px', display: 'inline-block', margin: '0 7px' }" v-if="attributes.findIndex(v=>v=='department_id')!==-1"> <a-form-model-item
<div :style="{ height: '41px', lineHeight: '40px' }">{{ $t('cs.companyStructure.departmentName') }}</div> ref="department_id"
<DepartmentTreeSelect v-model="employeeFormData.department_id" /> :label="$t('cs.companyStructure.departmentName')"
</div> prop="department_id"
<a-form-model-item ref="position_name" :label="$t('cs.companyStructure.positionName')" prop="position_name" :style="formModalItemStyle" v-if="attributes.findIndex(v=>v=='position_name')!==-1"> :style="formModalItemStyle"
<a-input v-model="employeeFormData.position_name" :placeholder="$t('cs.companyStructure.positionNamePlaceholder')" /> v-if="attributes.findIndex((v) => v == 'department_id') !== -1"
>
<DepartmentTreeSelect style="margin-top: 4px" v-model="employeeFormData.department_id" />
</a-form-model-item>
<a-form-model-item
ref="position_name"
:label="$t('cs.companyStructure.positionName')"
prop="position_name"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'position_name') !== -1"
>
<a-input
v-model="employeeFormData.position_name"
:placeholder="$t('cs.companyStructure.positionNamePlaceholder')"
/>
</a-form-model-item>
<a-form-model-item
ref="direct_supervisor_id"
:label="$t('cs.companyStructure.selectDirectSupervisor')"
prop="direct_supervisor_id"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'direct_supervisor_id') !== -1"
>
<EmployeeTreeSelect style="margin-top: 4px" v-model="employeeFormData.direct_supervisor_id" />
</a-form-model-item>
<a-form-model-item
ref="work_region"
:label="$t('cs.companyStructure.work_region')"
prop="work_region"
:style="formModalItemStyle"
v-if="attributes.findIndex((v) => v == 'work_region') !== -1"
>
<a-select v-model="employeeFormData.work_region" :placeholder="$t('cs.companyStructure.workRegionPlaceholder')">
<a-select-option value="china_mainland"> {{ $t('cs.companyStructure.china_mainland') }} </a-select-option>
<a-select-option value="china_hk"> {{ $t('cs.companyStructure.china_hk') }} </a-select-option>
</a-select>
</a-form-model-item> </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' }">{{ $t('cs.companyStructure.selectDirectSupervisor') }}</div>
<EmployeeTreeSelect v-model="employeeFormData.direct_supervisor_id" />
</div>
</a-form-model> </a-form-model>
<template slot="footer"> <template slot="footer">
<a-button key="back" @click="close"> {{ $t('cancel') }} </a-button> <a-button key="back" @click="close"> {{ $t('cancel') }} </a-button>
@ -75,7 +138,7 @@ export default {
educational_experience: [], educational_experience: [],
children_information: [], children_information: [],
file_is_show: true, file_is_show: true,
attributes: [] attributes: [],
} }
}, },
created() { created() {
@ -103,7 +166,12 @@ export default {
rules() { rules() {
return { return {
email: [ email: [
{ required: true, whitespace: true, message: this.$t('cs.companyStructure.emailPlaceholder'), trigger: 'blur' }, {
required: true,
whitespace: true,
message: this.$t('cs.companyStructure.emailPlaceholder'),
trigger: 'blur',
},
{ {
pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/, pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
message: this.$t('cs.companyStructure.emailFormatErr'), message: this.$t('cs.companyStructure.emailFormatErr'),
@ -112,12 +180,29 @@ export default {
{ max: 50, message: this.$t('cs.person.inputStrCountLimit', { limit: 50 }) }, { max: 50, message: this.$t('cs.person.inputStrCountLimit', { limit: 50 }) },
], ],
username: [ username: [
{ required: true, whitespace: true, message: this.$t('cs.companyStructure.usernamePlaceholder'), trigger: 'blur' }, {
required: true,
whitespace: true,
message: this.$t('cs.companyStructure.usernamePlaceholder'),
trigger: 'blur',
},
{ max: 20, message: this.$t('cs.person.inputStrCountLimit', { limit: 20 }) }, { max: 20, message: this.$t('cs.person.inputStrCountLimit', { limit: 20 }) },
], ],
password: [{ required: true, whitespace: true, message: this.$t('cs.companyStructure.passwordPlaceholder'), trigger: 'blur' }], password: [
{
required: true,
whitespace: true,
message: this.$t('cs.companyStructure.passwordPlaceholder'),
trigger: 'blur',
},
],
nickname: [ nickname: [
{ required: true, whitespace: true, message: this.$t('cs.companyStructure.nicknamePlaceholder'), trigger: 'blur' }, {
required: true,
whitespace: true,
message: this.$t('cs.companyStructure.nicknamePlaceholder'),
trigger: 'blur',
},
{ max: 20, message: this.$t('cs.person.inputStrCountLimit', { limit: 20 }) }, { max: 20, message: this.$t('cs.person.inputStrCountLimit', { limit: 20 }) },
], ],
mobile: [ mobile: [
@ -128,7 +213,7 @@ export default {
}, },
], ],
} }
} },
}, },
beforeDestroy() { beforeDestroy() {
Bus.$off('getAttributes') Bus.$off('getAttributes')
@ -136,7 +221,8 @@ export default {
methods: { methods: {
async open(getData, type) { async open(getData, type) {
// 提交时去掉school, major, education, graduation_year, name, gender, birthday, parental_leave_left // 提交时去掉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 { school, major, education, graduation_year, name, gender, birthday, parental_leave_left, ...newGetData } =
getData
const _getData = _.cloneDeep(newGetData) const _getData = _.cloneDeep(newGetData)
const { direct_supervisor_id } = newGetData const { direct_supervisor_id } = newGetData
if (direct_supervisor_id) { if (direct_supervisor_id) {
@ -149,46 +235,54 @@ export default {
// if (type !== 'add' && this.employeeFormData.educational_experience.length !== 0) { // if (type !== 'add' && this.employeeFormData.educational_experience.length !== 0) {
// this.educational_experience = this.employeeFormData.educational_experience // this.educational_experience = this.employeeFormData.educational_experience
// } // }
this.children_information = this.formatChildrenInformationList() || [{ this.children_information = this.formatChildrenInformationList() || [
{
id: uuidv4(), id: uuidv4(),
name: '', name: '',
gender: undefined, gender: undefined,
birthday: null, birthday: null,
parental_leave_left: 0 parental_leave_left: 0,
}] },
this.educational_experience = this.formatEducationalExperienceList() || [{ ]
this.educational_experience = this.formatEducationalExperienceList() || [
{
id: uuidv4(), id: uuidv4(),
school: '', school: '',
major: '', major: '',
education: undefined, education: undefined,
graduation_year: null graduation_year: null,
}] },
]
this.type = type this.type = type
this.visible = true this.visible = true
}, },
close() { close() {
this.$refs.employeeFormData.resetFields() this.$refs.employeeFormData.resetFields()
this.educational_experience = [{ this.educational_experience = [
{
school: '', school: '',
major: '', major: '',
education: undefined, education: undefined,
graduation_year: null graduation_year: null,
}] },
this.children_information = [{ ]
this.children_information = [
{
id: uuidv4(), id: uuidv4(),
name: '', name: '',
gender: undefined, gender: undefined,
birthday: null, birthday: null,
parental_leave_left: 0 parental_leave_left: 0,
}] },
]
this.visible = false this.visible = false
}, },
formatChildrenInformationList() { formatChildrenInformationList() {
let arr = [] let arr = []
arr = this.employeeFormData.children_information ? this.employeeFormData.children_information : undefined arr = this.employeeFormData.children_information ? this.employeeFormData.children_information : undefined
if (arr && arr.length) { if (arr && arr.length) {
arr.forEach(item => { arr.forEach((item) => {
item.id = uuidv4() item.id = uuidv4()
}) })
return arr return arr
@ -199,7 +293,7 @@ export default {
let arr = [] let arr = []
arr = this.employeeFormData.educational_experience ? this.employeeFormData.educational_experience : undefined arr = this.employeeFormData.educational_experience ? this.employeeFormData.educational_experience : undefined
if (arr && arr.length) { if (arr && arr.length) {
arr.forEach(item => { arr.forEach((item) => {
item.id = uuidv4() item.id = uuidv4()
}) })
return arr return arr
@ -212,12 +306,12 @@ export default {
school: '', school: '',
major: '', major: '',
education: undefined, education: undefined,
graduation_year: null graduation_year: null,
} }
this.educational_experience.push(newEducational_experience) this.educational_experience.push(newEducational_experience)
}, },
removeEducation(removeId) { removeEducation(removeId) {
const _idx = this.educational_experience.findIndex(item => item.id === removeId) const _idx = this.educational_experience.findIndex((item) => item.id === removeId)
if (_idx !== -1) { if (_idx !== -1) {
this.educational_experience.splice(_idx, 1) this.educational_experience.splice(_idx, 1)
} }
@ -228,12 +322,12 @@ export default {
name: '', name: '',
gender: undefined, gender: undefined,
birthday: null, birthday: null,
parental_leave_left: 0 parental_leave_left: 0,
} }
this.children_information.push(newChildrenInfo) this.children_information.push(newChildrenInfo)
}, },
removeChildren(removeId) { removeChildren(removeId) {
const _idx = this.children_information.findIndex(item => item.id === removeId) const _idx = this.children_information.findIndex((item) => item.id === removeId)
if (_idx !== -1) { if (_idx !== -1) {
this.children_information.splice(_idx, 1) this.children_information.splice(_idx, 1)
} }
@ -254,10 +348,10 @@ export default {
// } // }
if (date !== null) { if (date !== null) {
if (param === 'graduation_year') { if (param === 'graduation_year') {
const _idx = this.educational_experience.findIndex(item => item.id === id) const _idx = this.educational_experience.findIndex((item) => item.id === id)
this.educational_experience[_idx].graduation_year = moment(date).format('YYYY-MM') this.educational_experience[_idx].graduation_year = moment(date).format('YYYY-MM')
} else if (param === 'birthday') { } else if (param === 'birthday') {
const _idx = this.children_information.findIndex(item => item.id === id) const _idx = this.children_information.findIndex((item) => item.id === id)
this.children_information[_idx].birthday = moment(date).format('YYYY-MM-DD') this.children_information[_idx].birthday = moment(date).format('YYYY-MM-DD')
} else { } else {
this.employeeFormData[param] = moment(date).format('YYYY-MM-DD') this.employeeFormData[param] = moment(date).format('YYYY-MM-DD')

View File

@ -16,17 +16,17 @@
:key="group.id" :key="group.id"
> >
<div <div
class="ops-setting-structure-sidebar-group-header" :class="{
:class="{ 'group-selected': groupIndex === activeGroupIndex }" 'ops-setting-structure-sidebar-group-header': true,
'group-selected': groupIndex === activeGroupIndex,
}"
> >
<div class="ops-setting-structure-sidebar-group-header-avatar"> <div class="ops-setting-structure-sidebar-group-header-avatar">
<a-icon :type="group.icon"/> <a-icon :type="group.icon" />
</div> </div>
<span <span
class="ops-setting-structure-sidebar-group-header-title" class="ops-setting-structure-sidebar-group-header-title"
@click=" @click="clickSelectGroup(groupIndex)"
clickSelectGroup(groupIndex)
"
:id="[group.id === 0 ? 'employee' : 'department']" :id="[group.id === 0 ? 'employee' : 'department']"
> >
{{ group.title }} {{ group.title }}
@ -100,7 +100,7 @@
/> />
</div> </div>
<!-- 筛选框 --> <!-- 筛选框 -->
<div class="Screening-box" v-if="activeGroupIndex === 1" style="background-color: rgb(240, 245, 255) ;"> <div class="Screening-box" v-if="activeGroupIndex === 1" style="background-color: rgb(240, 245, 255)">
<a-popover <a-popover
@visibleChange="visibleChange" @visibleChange="visibleChange"
trigger="click" trigger="click"
@ -122,16 +122,17 @@
</div> </div>
</template> </template>
<span :style="{ whiteSpace: 'nowrap' }"> <span :style="{ whiteSpace: 'nowrap' }">
<a-icon class="screening-box-scene-icon" type="filter"/> <a-icon class="screening-box-scene-icon" type="filter" />
{{ getCurrentSceneLabel() }} {{ getCurrentSceneLabel() }}
<a-icon class="screening-box-scene-icon" :type="displayTimeIcon"/> <a-icon class="screening-box-scene-icon" :type="displayTimeIcon" />
</span> </span>
</a-popover> </a-popover>
</div> </div>
<SearchForm <SearchForm
ref="search" ref="search"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList" :canSearchPreferenceAttrList="canSearchPreferenceAttrList"
@refresh="handleSearch"/> @refresh="handleSearch"
/>
</div> </div>
<div> <div>
<a-space v-if="isEditable"> <a-space v-if="isEditable">
@ -168,23 +169,24 @@
<div> <div>
<div :style="{ marginTop: '8px' }" class="ops-list-batch-action" v-show="!!selectedRowKeys.length"> <div :style="{ marginTop: '8px' }" class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
<span @click="downloadEmployeeAll">{{ $t('cs.companyStructure.downloadAll') }}</span> <span @click="downloadEmployeeAll">{{ $t('cs.companyStructure.downloadAll') }}</span>
<a-divider type="vertical"/> <a-divider type="vertical" />
<span @click="exportSelectEvent">{{ $t('cs.companyStructure.downloadSelected') }}</span> <span @click="exportSelectEvent">{{ $t('cs.companyStructure.downloadSelected') }}</span>
<a-divider type="vertical"/> <a-divider type="vertical" />
<span @click="openBatchModal('department_id')">{{ $t('cs.companyStructure.editDepartment') }}</span> <span @click="openBatchModal('department_id')">{{ $t('cs.companyStructure.editDepartment') }}</span>
<a-divider type="vertical"/> <a-divider type="vertical" />
<span <span @click="openBatchModal('direct_supervisor_id')">{{
@click="openBatchModal('direct_supervisor_id')">{{ $t('cs.companyStructure.editDirectSupervisor') }}</span> $t('cs.companyStructure.editDirectSupervisor')
<a-divider type="vertical"/> }}</span>
<a-divider type="vertical" />
<span @click="openBatchModal('position_name')">{{ $t('cs.companyStructure.editPosition') }}</span> <span @click="openBatchModal('position_name')">{{ $t('cs.companyStructure.editPosition') }}</span>
<a-divider type="vertical"/> <a-divider type="vertical" />
<span @click="openBatchModal('password')">{{ $t('cs.companyStructure.resetPassword') }}</span> <span @click="openBatchModal('password')">{{ $t('cs.companyStructure.resetPassword') }}</span>
<a-divider type="vertical"/> <a-divider type="vertical" />
<span @click="openBatchModal('block', null, 1)">{{ $t('cs.companyStructure.block') }}</span> <span @click="openBatchModal('block', null, 1)">{{ $t('cs.companyStructure.block') }}</span>
<a-divider type="vertical"/> <a-divider type="vertical" />
<span @click="openBatchModal('block', null, 0)">{{ $t('cs.companyStructure.recover') }}</span> <span @click="openBatchModal('block', null, 0)">{{ $t('cs.companyStructure.recover') }}</span>
<a-divider type="vertical"/> <a-divider type="vertical" />
<span>{{ $t('selectRows', {rows: selectedRowKeys.length}) }}</span> <span>{{ $t('selectRows', { rows: selectedRowKeys.length }) }}</span>
</div> </div>
</div> </div>
<!-- <div> <!-- <div>
@ -195,11 +197,11 @@
</div> </div>
</div> </div>
<!-- 批量操作对话框 --> <!-- 批量操作对话框 -->
<BatchModal ref="BatchModal" @refresh="updateAll"/> <BatchModal ref="BatchModal" @refresh="updateAll" />
<!-- 部门表单对话框 --> <!-- 部门表单对话框 -->
<DepartmentModal ref="DepartmentModal" @refresh="clickSelectGroup(1)"/> <DepartmentModal ref="DepartmentModal" @refresh="clickSelectGroup(1)" />
<!-- 员工表单对话框 --> <!-- 员工表单对话框 -->
<EmployeeModal ref="EmployeeModal" @refresh="updateAll"/> <EmployeeModal ref="EmployeeModal" @refresh="updateAll" />
<!-- 表格展示 --> <!-- 表格展示 -->
<EmployeeTable <EmployeeTable
@ -211,7 +213,7 @@
@onSelectChange="onSelectChange" @onSelectChange="onSelectChange"
@openEmployeeModal="openEmployeeModal" @openEmployeeModal="openEmployeeModal"
@openBatchModal="openBatchModal" @openBatchModal="openBatchModal"
@tranferAttributes="getAttributes" @transferAttributes="getAttributes"
:isEditable="isEditable" :isEditable="isEditable"
:loading="loading" :loading="loading"
> >
@ -225,7 +227,9 @@
:page-size-options="pageSizeOptions" :page-size-options="pageSizeOptions"
:current="tablePage.currentPage" :current="tablePage.currentPage"
:total="tablePage.totalResult" :total="tablePage.totalResult"
:show-total="(total, range) => $t('pagination.total', { range0: range[0], range1: range[1], total:total })" :show-total="
(total, range) => $t('pagination.total', { range0: range[0], range1: range[1], total: total })
"
:page-size="tablePage.pageSize" :page-size="tablePage.pageSize"
:default-current="1" :default-current="1"
@change="pageOrSizeChange" @change="pageOrSizeChange"
@ -252,6 +256,9 @@
clickSelectGroup(1) clickSelectGroup(1)
} }
" "
@downloadTemplate="downloadTemplate"
@customRequest="customRequest"
@import="importEmployee"
/> />
</div> </div>
</template> </template>
@ -262,15 +269,22 @@ import SplitPane from '@/components/SplitPane'
import CollapseTransition from '@/components/CollapseTransition' import CollapseTransition from '@/components/CollapseTransition'
import Bus from './eventBus/bus' import Bus from './eventBus/bus'
import CategroyTree from './CategoryTree' import CategroyTree from './CategoryTree'
import BatchUpload from './BatchUpload' import BatchUpload from '../components/BatchUpload.vue'
import BatchModal from './BatchModal.vue' import BatchModal from './BatchModal.vue'
import EmployeeModal from './EmployeeModal.vue' import EmployeeModal from './EmployeeModal.vue'
import DepartmentModal from './DepartmentModal.vue' import DepartmentModal from './DepartmentModal.vue'
import EmployeeTable from '../components/employeeTable.vue' import EmployeeTable from '../components/employeeTable.vue'
import { getDepartmentList, deleteDepartmentById, getAllDepartmentList, getAllDepAndEmployee } from '@/api/company' import { getDepartmentList, deleteDepartmentById, getAllDepartmentList, getAllDepAndEmployee } from '@/api/company'
import { getEmployeeList, getEmployeeCount, downloadAllEmployee, getEmployeeListByFilter } from '@/api/employee' import {
getEmployeeList,
getEmployeeCount,
downloadAllEmployee,
getEmployeeListByFilter,
importEmployee,
} from '@/api/employee'
import { mixinPermissions } from '@/utils/mixin' import { mixinPermissions } from '@/utils/mixin'
import SearchForm from '../components/SearchForm.vue' import SearchForm from '../components/SearchForm.vue'
import { downloadExcel, excel2Array } from '@/utils/download'
export default { export default {
name: 'CompanyStructure', name: 'CompanyStructure',
@ -284,10 +298,22 @@ export default {
EmployeeModal, EmployeeModal,
DepartmentModal, DepartmentModal,
EmployeeTable, EmployeeTable,
SearchForm SearchForm,
}, },
data() { data() {
const common_importParamsList = [
'email',
'username',
'nickname',
'password',
'sex',
'mobile',
'position_name',
'department_name',
'work_region',
]
return { return {
common_importParamsList,
isActive: '', isActive: '',
visible: true, visible: true,
localStorageKey: 'itsm-company-strcutre', localStorageKey: 'itsm-company-strcutre',
@ -322,7 +348,7 @@ export default {
attributes: [], attributes: [],
pageSizeOptions: ['50', '100', '200', '9999'], pageSizeOptions: ['50', '100', '200', '9999'],
expression: [], expression: [],
loading: false loading: false,
} }
}, },
// created() { // created() {
@ -364,15 +390,25 @@ export default {
is_choice: true, is_choice: true,
choice_value: [ choice_value: [
{ label: this.$t('cs.companyStructure.male'), value: '' }, { label: this.$t('cs.companyStructure.male'), value: '' },
{ label: this.$t('cs.companyStructure.female'), value: '' }] { label: this.$t('cs.companyStructure.female'), value: '' },
],
}, },
{ label: this.$t('cs.companyStructure.mobile'), value: 'mobile' }, { label: this.$t('cs.companyStructure.mobile'), value: 'mobile' },
{ label: this.$t('cs.companyStructure.departmentName'), value: 'department_name' }, { label: this.$t('cs.companyStructure.departmentName'), value: 'department_name' },
{ label: this.$t('cs.companyStructure.positionName'), value: 'position_name' }, { label: this.$t('cs.companyStructure.positionName'), value: 'position_name' },
{ label: this.$t('cs.companyStructure.supervisor'), value: 'direct_supervisor_id' }, { label: this.$t('cs.companyStructure.supervisor'), value: 'direct_supervisor_id' },
{
label: this.$t('cs.companyStructure.work_region'),
value: 'work_region',
is_choice: true,
choice_value: [
{ label: this.$t('cs.companyStructure.china_mainland'), value: 'china_mainland' },
{ label: this.$t('cs.companyStructure.china_hk'), value: 'china_hk' },
],
},
] ]
}, },
sceneList () { sceneList() {
return [ return [
{ {
label: this.$t('all'), label: this.$t('all'),
@ -388,7 +424,7 @@ export default {
}, },
] ]
}, },
groupData () { groupData() {
return [ return [
{ {
id: 0, id: 0,
@ -396,7 +432,7 @@ export default {
icon: 'user', icon: 'user',
}, },
] ]
} },
}, },
provide() { provide() {
return { return {
@ -426,11 +462,8 @@ export default {
} else { } else {
this.currentScene = 0 this.currentScene = 0
} }
// console.log(this.currentScene) this.updateCount()
// this.init()
this.clickSelectGroup(0).then(val => {
this.clickSelectItem(0) this.clickSelectItem(0)
})
Bus.$on('updataAllIncludeEmployees', () => { Bus.$on('updataAllIncludeEmployees', () => {
this.getAllFlatEmployees() this.getAllFlatEmployees()
this.getAllDepAndEmployee() this.getAllDepAndEmployee()
@ -492,7 +525,7 @@ export default {
setSearchPreferenceAttrList() { setSearchPreferenceAttrList() {
this.canSearchPreferenceAttrList.forEach((item) => { this.canSearchPreferenceAttrList.forEach((item) => {
if (!this.attributes.includes(item.value)) { if (!this.attributes.includes(item.value)) {
this.canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter(v => v.value !== item.value) this.canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((v) => v.value !== item.value)
} }
}) })
}, },
@ -504,9 +537,6 @@ export default {
this.$refs.ScreeningBoxScenePopover.$refs.tooltip.onVisibleChange(false) this.$refs.ScreeningBoxScenePopover.$refs.tooltip.onVisibleChange(false)
} }
document.getElementById('department').click() document.getElementById('department').click()
// this.currentPage = 1
// this.updateTableData(1)
// this.departmentList = this.reqDepartmentList(-1)
}, },
clickHandler(event) { clickHandler(event) {
this.isActive = event.target.innerText this.isActive = event.target.innerText
@ -544,37 +574,6 @@ export default {
this.activeEmployeeCount = res1.employee_count this.activeEmployeeCount = res1.employee_count
this.deactiveEmployeeCount = res2.employee_count this.deactiveEmployeeCount = res2.employee_count
}, },
async updateTableData(currentPage = 1, pageSize = this.tablePage.pageSize) {
this.selectedRowKeys = []
let reqEmployeeData = null
if (this.activeGroupIndex === 0) {
reqEmployeeData = await getEmployeeList({
...this.tableFilterData,
block_status: this.block_status,
page: currentPage,
page_size: pageSize,
search: this.filterName,
order: this.tableSortData || 'direct_supervisor_id',
})
} else if (this.activeGroupIndex === 1) {
reqEmployeeData = await getEmployeeList({
...this.tableFilterData,
block_status: this.currentScene,
department_id: this.selectDepartment.id,
page: currentPage,
page_size: pageSize,
search: this.filterName,
order: this.tableSortData || 'direct_supervisor_id',
})
}
this.tableData = this.FilterTableData(reqEmployeeData)
this.tablePage = {
...this.tablePage,
currentPage: reqEmployeeData.page,
pageSize: reqEmployeeData.page_size,
totalResult: reqEmployeeData.total,
}
},
async updateTableDataByFilter(currentPage = 1, pageSize = this.tablePage.pageSize) { async updateTableDataByFilter(currentPage = 1, pageSize = this.tablePage.pageSize) {
this.loading = true this.loading = true
this.selectedRowKeys = [] this.selectedRowKeys = []
@ -623,7 +622,8 @@ export default {
let max_index = 0 let max_index = 0
educational_experience.forEach((item, index) => { educational_experience.forEach((item, index) => {
if (index < educational_experience.length - 1) { if (index < educational_experience.length - 1) {
max_index = item.graduation_year > educational_experience[index + 1].graduation_year ? index : index + 1 max_index =
item.graduation_year > educational_experience[index + 1].graduation_year ? index : index + 1
} }
}) })
tableData[index].school = educational_experience[max_index].school tableData[index].school = educational_experience[max_index].school
@ -708,7 +708,7 @@ export default {
} else { } else {
block_status = this.currentScene block_status = this.currentScene
} }
downloadAllEmployee({ block_status: block_status }).then(res => { downloadAllEmployee({ block_status: block_status }).then((res) => {
const content = res const content = res
const blob = new Blob([content], { type: 'application/vnd.ms-excel' }) const blob = new Blob([content], { type: 'application/vnd.ms-excel' })
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
@ -787,7 +787,8 @@ export default {
}, },
// 请求部门数据 // 请求部门数据
async reqDepartmentList(departmentId) { async reqDepartmentList(departmentId) {
const res = (await getDepartmentList({ department_parent_id: departmentId, block: this.currentScene })).departments const res = (await getDepartmentList({ department_parent_id: departmentId, block: this.currentScene }))
.departments
return this.transformDepartmentData(res) return this.transformDepartmentData(res)
}, },
openDepartmentModal(type) { openDepartmentModal(type) {
@ -852,6 +853,137 @@ export default {
exportSelectEvent() { exportSelectEvent() {
Bus.$emit('reqExportSelectEvent') Bus.$emit('reqExportSelectEvent')
}, },
downloadTemplate() {
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',
},
],
]
downloadExcel(data, this.$t('cs.companyStructure.downloadTemplateName'))
},
customRequest({ data }, callback) {
excel2Array(data.file).then((res) => {
res = res.filter((item) => item.length)
callback(
res.slice(2).map((item) => {
const obj = {}
item.forEach((ele, index) => {
obj[this.common_importParamsList[index]] = ele
})
return obj
})
)
})
},
async importEmployee({ importData }, callback) {
this.allCount = importData.length
const _importData = 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
const regionMap = {
中国大陆: 'china_mainland',
中国香港: 'china_hk',
'Chinese Mainland': 'china_mainland',
'HK China': 'china_hk',
}
rest.work_region = regionMap[rest.work_region] ?? res.work_region
return rest
})
const res = await importEmployee({ employee_list: _importData })
callback(res)
},
}, },
} }
</script> </script>
@ -1051,7 +1183,6 @@ export default {
color: @primary-color; color: @primary-color;
font-size: 12px; font-size: 12px;
} }
} }
} }
} }
@ -1068,7 +1199,6 @@ export default {
} }
} }
} }
} }
.ops-setting-structure-main-pagination { .ops-setting-structure-main-pagination {
width: 100%; width: 100%;

View File

@ -0,0 +1,243 @@
<template>
<a-modal
:visible="visible"
:title="$t('cs.companyStructure.batchImport')"
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" />{{ $t('cs.companyStructure.selectFile') }}</a-button
>
</a-upload>
<p>
<a @click="download">
<slot name="downloadTemplateText">{{ $t('cs.companyStructure.clickDownloadImportTemplate') }}</slot>
</a>
</p>
</template>
<div
:style="{
height: '60px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
whiteSpace: 'pre-wrap',
}"
v-if="currentStep === 3"
>
{{ $t('cs.companyStructure.importSuccess', { allCount: allCount })
}}<span :style="{ color: '#2362FB' }"> {{ allCount - errorCount }} </span>{{ $t('cs.companyStructure.count') }},
{{ $t('cs.companyStructure.importFailed') }}<span :style="{ color: '#D81E06' }"> {{ errorCount }} </span
>{{ $t('cs.companyStructure.count') }}
</div>
<slot>
<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="$t('cs.companyStructure.email')" min-width="120" fixed="left"></vxe-column>
<vxe-column field="username" :title="$t('cs.companyStructure.username')" min-width="80"></vxe-column>
<vxe-column field="nickname" :title="$t('cs.companyStructure.nickname')" min-width="80"></vxe-column>
<vxe-column field="password" :title="$t('cs.companyStructure.password')" min-width="80"></vxe-column>
<vxe-column field="sex" :title="$t('cs.companyStructure.sex')" min-width="60"></vxe-column>
<vxe-column field="mobile" :title="$t('cs.companyStructure.mobile')" min-width="80"></vxe-column>
<vxe-column
field="department_name"
:title="$t('cs.companyStructure.departmentName')"
min-width="80"
></vxe-column>
<vxe-column field="position_name" :title="$t('cs.companyStructure.positionName')" min-width="80"></vxe-column>
<vxe-column field="work_region" :title="$t('cs.companyStructure.work_region')" min-width="80"></vxe-column>
<vxe-column
v-if="has_error"
field="err"
:title="$t('cs.companyStructure.importFailedReason')"
min-width="120"
fixed="right"
>
<template #default="{ row }">
<span :style="{ color: '#D81E06' }">{{ row.err }}</span>
</template>
</vxe-column>
</vxe-table>
</slot>
<a-space slot="footer">
<a-button size="small" type="primary" ghost @click="close">{{ $t('cancel') }}</a-button>
<a-button v-if="currentStep !== 1" size="small" type="primary" ghost @click="goPre">{{
$t('cs.companyStructure.prevStep')
}}</a-button>
<a-button v-if="currentStep !== 3" size="small" type="primary" @click="goNext">{{
$t('cs.companyStructure.nextStep')
}}</a-button>
<a-button v-else size="small" type="primary" @click="close">{{ $t('cs.companyStructure.done') }}</a-button>
</a-space>
</a-modal>
</template>
<script>
export default {
name: 'BatchUpload',
data() {
const stepList = [
{
value: 1,
label: this.$t('cs.companyStructure.uploadFile'),
icon: 'icon-shidi-tianjia',
},
{
value: 2,
label: this.$t('cs.companyStructure.confirmData'),
icon: 'icon-shidi-yunshangchuan',
},
{
value: 3,
label: this.$t('cs.companyStructure.uploadDone'),
icon: 'icon-shidi-queren',
},
]
return {
stepList,
visible: false,
currentStep: 1,
importData: [],
has_error: false,
allCount: 0,
errorCount: 0,
}
},
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
this.$emit('import', { importData: this.importData }, (res) => {
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(this.$t('cs.companyStructure.dataErr'))
} else {
this.currentStep += 1
this.$message.success(this.$t('cs.companyStructure.opSuccess'))
}
this.$emit('refresh')
})
}
},
goPre() {
this.has_error = false
this.errorCount = 0
this.currentStep -= 1
},
download() {
this.$emit('downloadTemplate')
},
customRequest(data) {
this.fileList = [data.file]
this.$emit('customRequest', { data }, (importData) => {
this.importData = importData
this.currentStep = 2
})
},
},
}
</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>

View File

@ -92,7 +92,7 @@
<span>{{ $t('cs.companyStructure.sex') }}</span> <span>{{ $t('cs.companyStructure.sex') }}</span>
</span> </span>
</template> </template>
<template #default="{row}"> <template #default="{ row }">
<span v-if="row.sex === ''">{{ $t('cs.companyStructure.male') }}</span> <span v-if="row.sex === ''">{{ $t('cs.companyStructure.male') }}</span>
<span v-if="row.sex === ''">{{ $t('cs.companyStructure.female') }}</span> <span v-if="row.sex === ''">{{ $t('cs.companyStructure.female') }}</span>
</template> </template>
@ -175,6 +175,26 @@
}}</span> }}</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column
field="work_region"
v-if="
checkedCols.findIndex((v) => v == 'work_region') !== -1 &&
attributes.findIndex((v) => v == 'work_region') !== -1
"
:title="$t('cs.companyStructure.work_region')"
min-width="120px"
key="work_region"
>
<template #header>
<span class="vxe-handle">
<OpsMoveIcon class="move-icon" />
<span>{{ $t('cs.companyStructure.work_region') }}</span>
</span>
</template>
<template #default="{ row }">
{{ $t(`cs.companyStructure.${row.work_region}`) }}
</template>
</vxe-column>
<vxe-column <vxe-column
field="control" field="control"
width="100px" width="100px"
@ -197,7 +217,7 @@
> >
<template slot="content"> <template slot="content">
<div :style="{ maxHeight: `${windowHeight - 320}px`, overflowY: 'auto', width: '160px' }"> <div :style="{ maxHeight: `${windowHeight - 320}px`, overflowY: 'auto', width: '160px' }">
<a-checkbox-group v-model="unsbmitCheckedCols" :options="options" style="display: grid;"> <a-checkbox-group v-model="unsbmitCheckedCols" :options="options" style="display: grid">
</a-checkbox-group> </a-checkbox-group>
</div> </div>
<div <div
@ -209,22 +229,24 @@
justifyContent: 'flex-end', justifyContent: 'flex-end',
}" }"
> >
<a-button :style="{ marginRight: '10px' }" size="small" @click="handleCancel">{{ $t('cancel') }}</a-button> <a-button :style="{ marginRight: '10px' }" size="small" @click="handleCancel">{{
$t('cancel')
}}</a-button>
<a-button size="small" @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button> <a-button size="small" @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div> </div>
</template> </template>
<a-icon type="control" style="cursor: pointer;" /> <a-icon type="control" style="cursor: pointer" />
</a-popover> </a-popover>
</template> </template>
</template> </template>
<template #default="{ row }"> <template #default="{ row }">
<a-space v-if="tableType === 'structure'"> <a-space v-if="tableType === 'structure'">
<a><a-icon type="edit" @click="openEmployeeModal(row, 'edit')"/></a> <a><a-icon type="edit" @click="openEmployeeModal(row, 'edit')" /></a>
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
{{ $t('cs.companyStructure.resetPassword') }} {{ $t('cs.companyStructure.resetPassword') }}
</template> </template>
<a><a-icon type="reload" @click="openBatchModal('password', row)"/></a> <a><a-icon type="reload" @click="openBatchModal('password', row)" /></a>
</a-tooltip> </a-tooltip>
<a-tooltip v-if="!row.block"> <a-tooltip v-if="!row.block">
<template slot="title"> <template slot="title">
@ -305,6 +327,7 @@ export default {
{ label: this.$t('cs.companyStructure.departmentName'), value: 'department_name' }, { label: this.$t('cs.companyStructure.departmentName'), value: 'department_name' },
{ label: this.$t('cs.companyStructure.positionName'), value: 'position_name' }, { label: this.$t('cs.companyStructure.positionName'), value: 'position_name' },
{ label: this.$t('cs.companyStructure.supervisor'), value: 'direct_supervisor_id' }, { label: this.$t('cs.companyStructure.supervisor'), value: 'direct_supervisor_id' },
{ label: this.$t('cs.companyStructure.work_region'), value: 'work_region' },
] ]
const checkedCols = JSON.parse(localStorage.getItem('setting-table-CheckedCols')) || [ const checkedCols = JSON.parse(localStorage.getItem('setting-table-CheckedCols')) || [
'nickname', 'nickname',
@ -315,6 +338,7 @@ export default {
'department_name', 'department_name',
'position_name', 'position_name',
'direct_supervisor_id', 'direct_supervisor_id',
'work_region',
] ]
return { return {
filterRoleList: [], filterRoleList: [],
@ -442,7 +466,7 @@ export default {
} }
} }
Bus.$emit('getAttributes', this.attributes) Bus.$emit('getAttributes', this.attributes)
this.$emit('tranferAttributes', this.attributes) this.$emit('transferAttributes', this.attributes)
}, },
getIsInterInship(is_internship) { getIsInterInship(is_internship) {
return this.internMap.filter((item) => item.id === is_internship)[0]['label'] return this.internMap.filter((item) => item.id === is_internship)[0]['label']
@ -541,7 +565,7 @@ export default {
useStyle: true, // 是否导出样式 useStyle: true, // 是否导出样式
isFooter: false, // 是否导出表尾比如合计 isFooter: false, // 是否导出表尾比如合计
// 过滤那个字段导出 // 过滤那个字段导出
columnFilterMethod: function(column, $columnIndex) { columnFilterMethod: function (column, $columnIndex) {
return !(column.$columnIndex === 0) return !(column.$columnIndex === 0)
// 0是复选框 不导出 // 0是复选框 不导出
}, },

View File

@ -19,7 +19,7 @@ const cs_en = {
app: 'APP Authority', app: 'APP Authority',
basic: 'Basic Settings', basic: 'Basic Settings',
theme: 'Theme Settings', theme: 'Theme Settings',
security: 'Security Settings' security: 'Security Settings',
}, },
companyInfo: { companyInfo: {
spanCompany: 'Description', spanCompany: 'Description',
@ -201,7 +201,11 @@ const cs_en = {
createEmployee: 'Create Employee', createEmployee: 'Create Employee',
editEmployee: 'Edit Employee', editEmployee: 'Edit Employee',
role: 'Role', role: 'Role',
selectDisplayColumn: 'Please select columns to display' selectDisplayColumn: 'Please select columns to display',
work_region: 'Work Region',
workRegionPlaceholder: 'Please select work region',
china_mainland: 'Chinese Mainland',
china_hk: 'HK China',
}, },
auth: { auth: {
basic: 'Basic', basic: 'Basic',
@ -220,7 +224,7 @@ const cs_en = {
user: 'User', user: 'User',
username: 'Username', username: 'Username',
userPlaceholder: 'Please enter username', userPlaceholder: 'Please enter username',
userHelp: 'User DN: cn={},ou=users,dc=xxx,dc=com {} will be replaced by username' userHelp: 'User DN: cn={},ou=users,dc=xxx,dc=com {} will be replaced by username',
}, },
cas: { cas: {
server: 'Server Address', server: 'Server Address',
@ -237,10 +241,11 @@ const cs_en = {
validateRoutePlaceholder: 'Please enter validate route', validateRoutePlaceholder: 'Please enter validate route',
afterLoginRoute: 'Redirect Route', afterLoginRoute: 'Redirect Route',
afterLoginRoutePlaceholder: 'Please enter redirect route', afterLoginRoutePlaceholder: 'Please enter redirect route',
userMap: 'User Attribute Mapping' userMap: 'User Attribute Mapping',
}, },
autoRedirectLogin: 'Auto Redirect to Third-party Login Page', autoRedirectLogin: 'Auto Redirect to Third-party Login Page',
autoRedirectLoginHelp: 'If disabled, a confirmation will be displayed to redirect to third-party login page. Click the Cancel button will go to the built-in login page', autoRedirectLoginHelp:
'If disabled, a confirmation will be displayed to redirect to third-party login page. Click the Cancel button will go to the built-in login page',
usernameOrEmail: 'Username/Email', usernameOrEmail: 'Username/Email',
usernameOrEmailPlaceholder: 'Please enter username/email', usernameOrEmailPlaceholder: 'Please enter username/email',
password: 'Password', password: 'Password',
@ -257,7 +262,7 @@ const cs_en = {
userInfo: 'User Info', userInfo: 'User Info',
scopes: 'Scopes', scopes: 'Scopes',
scopesPlaceholder: 'Please enter scopes', scopesPlaceholder: 'Please enter scopes',
} },
}, },
duty: { duty: {
basicSetting: 'Basic Settings', basicSetting: 'Basic Settings',
@ -274,13 +279,13 @@ const cs_en = {
mainDutyPeople: 'Main Duty Person', mainDutyPeople: 'Main Duty Person',
deputyDutyPeople: 'Deputy Duty Person', deputyDutyPeople: 'Deputy Duty Person',
dutyRule: 'Duty Rule', dutyRule: 'Duty Rule',
'一': 'Mon', : 'Mon',
'二': 'Tue', : 'Tue',
'三': 'Wed', : 'Wed',
'四': 'Thu', : 'Thu',
'五': 'Fri', : 'Fri',
'六': 'Sat', : 'Sat',
'日': 'Sun', : 'Sun',
searchPlaceholder: 'Please search', searchPlaceholder: 'Please search',
dutyTable: 'Duty Schedule', dutyTable: 'Duty Schedule',
dutyMember: 'Duty Member', dutyMember: 'Duty Member',
@ -304,7 +309,7 @@ const cs_en = {
offDutyReceiverPlaceholder: 'Please select off-duty receiver', offDutyReceiverPlaceholder: 'Please select off-duty receiver',
titleLimit: 'Please enter title (20 characters)', titleLimit: 'Please enter title (20 characters)',
remarkLimit: 'Remark 150 characters max', remarkLimit: 'Remark 150 characters max',
frequencyLimit: 'Please enter duty frequency (positive integer)' frequencyLimit: 'Please enter duty frequency (positive integer)',
}, },
group: { group: {
groupName: 'User Group', groupName: 'User Group',
@ -329,7 +334,7 @@ const cs_en = {
moreThan: 'More Than', moreThan: 'More Than',
lessThan: 'Less Than', lessThan: 'Less Than',
operatorInPlaceholder: 'Separate by ;', operatorInPlaceholder: 'Separate by ;',
selectEmployee: 'Select Employee' selectEmployee: 'Select Employee',
}, },
notice: { notice: {
corpid: 'Corp ID', corpid: 'Corp ID',
@ -368,7 +373,8 @@ const cs_en = {
disableCreationOfRequestsViaEmail: 'Disable Creation of Requests Via Email', disableCreationOfRequestsViaEmail: 'Disable Creation of Requests Via Email',
specifyAllowedEmails: 'Specify Allowed Emails/Domains, Separate Multiple Values By Comma', specifyAllowedEmails: 'Specify Allowed Emails/Domains, Separate Multiple Values By Comma',
specifyAllowedEmailsExample: 'E.g. user@domain.com,*@domain.com', specifyAllowedEmailsExample: 'E.g. user@domain.com,*@domain.com',
specifyAllowedEmailsLimit: 'Limit cannot apply to requests already in sessions, it will aggregate to its parent ticket', specifyAllowedEmailsLimit:
'Limit cannot apply to requests already in sessions, it will aggregate to its parent ticket',
messageConfig: 'Message Settings', messageConfig: 'Message Settings',
moveWrongMessagesToFolder: 'Move Messages to Wrong Folder', moveWrongMessagesToFolder: 'Move Messages to Wrong Folder',
knowMore: 'Learn More', knowMore: 'Learn More',
@ -438,7 +444,7 @@ const cs_en = {
myDepartmentAndSubordinateDepartments: 'My Department And Subordinate Departments', myDepartmentAndSubordinateDepartments: 'My Department And Subordinate Departments',
test: 'Test', test: 'Test',
selectApp: 'Select App', selectApp: 'Select App',
} },
} }
export default cs_en export default cs_en

View File

@ -19,7 +19,7 @@ const cs_zh = {
app: '应用权限', app: '应用权限',
basic: '基础设置', basic: '基础设置',
theme: '主题配置', theme: '主题配置',
security: '安全配置' security: '安全配置',
}, },
companyInfo: { companyInfo: {
spanCompany: '公司描述', spanCompany: '公司描述',
@ -201,7 +201,11 @@ const cs_zh = {
createEmployee: '新建员工', createEmployee: '新建员工',
editEmployee: '编辑员工', editEmployee: '编辑员工',
role: '角色', role: '角色',
selectDisplayColumn: '请选择需要展示的列' selectDisplayColumn: '请选择需要展示的列',
work_region: '工作地区',
workRegionPlaceholder: '请选择工作地区',
china_mainland: '中国大陆',
china_hk: '中国香港',
}, },
auth: { auth: {
basic: '基本', basic: '基本',
@ -220,7 +224,7 @@ const cs_zh = {
user: '用户', user: '用户',
username: '用户名称', username: '用户名称',
userPlaceholder: '请输入用户名称', userPlaceholder: '请输入用户名称',
userHelp: '用户dn: cn={},ou=users,dc=xxx,dc=com {}会替换成用户名' userHelp: '用户dn: cn={},ou=users,dc=xxx,dc=com {}会替换成用户名',
}, },
cas: { cas: {
server: '服务端地址', server: '服务端地址',
@ -237,7 +241,7 @@ const cs_zh = {
validateRoutePlaceholder: '请输入验证路由', validateRoutePlaceholder: '请输入验证路由',
afterLoginRoute: '重定向路由', afterLoginRoute: '重定向路由',
afterLoginRoutePlaceholder: '请输入重定向路由', afterLoginRoutePlaceholder: '请输入重定向路由',
userMap: '用户属性映射' userMap: '用户属性映射',
}, },
autoRedirectLogin: '自动跳转到第三方登录页', autoRedirectLogin: '自动跳转到第三方登录页',
autoRedirectLoginHelp: '如果关闭,则会弹出跳转到第三方登录页的确认,点取消按钮会进入系统内置的登录页', autoRedirectLoginHelp: '如果关闭,则会弹出跳转到第三方登录页的确认,点取消按钮会进入系统内置的登录页',
@ -257,7 +261,7 @@ const cs_zh = {
userInfo: '用户信息', userInfo: '用户信息',
scopes: '授权范围', scopes: '授权范围',
scopesPlaceholder: '请输入授权范围', scopesPlaceholder: '请输入授权范围',
} },
}, },
duty: { duty: {
basicSetting: '基础设置', basicSetting: '基础设置',
@ -274,13 +278,13 @@ const cs_zh = {
mainDutyPeople: '主值班人', mainDutyPeople: '主值班人',
deputyDutyPeople: '副值班人', deputyDutyPeople: '副值班人',
dutyRule: '排班规则', dutyRule: '排班规则',
'一': '一', : '一',
'二': '二', : '二',
'三': '三', : '三',
'四': '四', : '四',
'五': '五', : '五',
'六': '六', : '六',
'日': '日', : '日',
searchPlaceholder: '请查找', searchPlaceholder: '请查找',
dutyTable: '值班表', dutyTable: '值班表',
dutyMember: '值班人员', dutyMember: '值班人员',
@ -304,7 +308,7 @@ const cs_zh = {
offDutyReceiverPlaceholder: '请选择非值班时间接收人', offDutyReceiverPlaceholder: '请选择非值班时间接收人',
titleLimit: '请输入标题20个字符', titleLimit: '请输入标题20个字符',
remarkLimit: '备注150个字符以内', remarkLimit: '备注150个字符以内',
frequencyLimit: '请输入值班频次(正整数)' frequencyLimit: '请输入值班频次(正整数)',
}, },
group: { group: {
groupName: '用户分组', groupName: '用户分组',
@ -329,7 +333,7 @@ const cs_zh = {
moreThan: '大于', moreThan: '大于',
lessThan: '小于', lessThan: '小于',
operatorInPlaceholder: '以 ; 分隔', operatorInPlaceholder: '以 ; 分隔',
selectEmployee: '选择员工' selectEmployee: '选择员工',
}, },
notice: { notice: {
corpid: '企业ID', corpid: '企业ID',
@ -438,6 +442,6 @@ const cs_zh = {
myDepartmentAndSubordinateDepartments: '本部门及下属部门', myDepartmentAndSubordinateDepartments: '本部门及下属部门',
test: '测试', test: '测试',
selectApp: '选择应用', selectApp: '选择应用',
} },
} }
export default cs_zh export default cs_zh