From 5e7c6199bfb5e0944e235ba85dc8246de8e9a91c Mon Sep 17 00:00:00 2001 From: dagongren <53748875+wang-liang0615@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:52:55 +0800 Subject: [PATCH] feat:add employee work_region (#634) * feat:add employee work_region * env --- cmdb-ui/src/style/global.less | 16 +- .../setting/companyStructure/BatchUpload.vue | 393 ------------------ .../companyStructure/EmployeeModal.vue | 218 +++++++--- .../views/setting/companyStructure/index.vue | 298 +++++++++---- .../views/setting/components/BatchUpload.vue | 243 +++++++++++ .../setting/components/employeeTable.vue | 40 +- cmdb-ui/src/views/setting/lang/en.js | 40 +- cmdb-ui/src/views/setting/lang/zh.js | 34 +- 8 files changed, 700 insertions(+), 582 deletions(-) delete mode 100644 cmdb-ui/src/views/setting/companyStructure/BatchUpload.vue create mode 100644 cmdb-ui/src/views/setting/components/BatchUpload.vue diff --git a/cmdb-ui/src/style/global.less b/cmdb-ui/src/style/global.less index 7d86eeb..729bcf2 100644 --- a/cmdb-ui/src/style/global.less +++ b/cmdb-ui/src/style/global.less @@ -860,20 +860,30 @@ body { .vue-treeselect__control { border-radius: 2px !important; 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__single-value { - line-height: 32px !important; + line-height: 28px !important; } .vue-treeselect__input { - height: 32px !important; - line-height: 32px !important; + height: 28px !important; + line-height: 28px !important; } } // vue-treeselect 多选样式 .ops-setting-treeselect.vue-treeselect--multi { .vue-treeselect__control { border-radius: 2px !important; + border-color: #e4e7ed; } } diff --git a/cmdb-ui/src/views/setting/companyStructure/BatchUpload.vue b/cmdb-ui/src/views/setting/companyStructure/BatchUpload.vue deleted file mode 100644 index 93c163e..0000000 --- a/cmdb-ui/src/views/setting/companyStructure/BatchUpload.vue +++ /dev/null @@ -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> diff --git a/cmdb-ui/src/views/setting/companyStructure/EmployeeModal.vue b/cmdb-ui/src/views/setting/companyStructure/EmployeeModal.vue index 1ebea1a..afd9b10 100644 --- a/cmdb-ui/src/views/setting/companyStructure/EmployeeModal.vue +++ b/cmdb-ui/src/views/setting/companyStructure/EmployeeModal.vue @@ -10,10 +10,22 @@ :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="$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 + 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 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-form-model-item> <a-form-model-item @@ -23,31 +35,82 @@ prop="password" :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 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-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-option value="男"> {{ $t('cs.companyStructure.male') }} </a-select-option> <a-select-option value="女"> {{ $t('cs.companyStructure.female') }} </a-select-option> </a-select> </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-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' }">{{ $t('cs.companyStructure.departmentName') }}</div> - <DepartmentTreeSelect v-model="employeeFormData.department_id" /> - </div> - <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 + ref="department_id" + :label="$t('cs.companyStructure.departmentName')" + prop="department_id" + :style="formModalItemStyle" + 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> - <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> <template slot="footer"> <a-button key="back" @click="close"> {{ $t('cancel') }} </a-button> @@ -75,7 +138,7 @@ export default { educational_experience: [], children_information: [], file_is_show: true, - attributes: [] + attributes: [], } }, created() { @@ -85,7 +148,7 @@ export default { }, inject: ['provide_allTreeDepartment', 'provide_allFlatEmployees'], computed: { - ...mapState({ + ...mapState({ windowHeight: (state) => state.windowHeight, }), departemntTreeSelectOption() { @@ -103,7 +166,12 @@ export default { rules() { return { 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}$/, message: this.$t('cs.companyStructure.emailFormatErr'), @@ -112,12 +180,29 @@ export default { { max: 50, message: this.$t('cs.person.inputStrCountLimit', { limit: 50 }) }, ], 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 }) }, ], - 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: [ - { 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 }) }, ], mobile: [ @@ -128,7 +213,7 @@ export default { }, ], } - } + }, }, beforeDestroy() { Bus.$off('getAttributes') @@ -136,7 +221,8 @@ export default { 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 { 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) { @@ -149,46 +235,54 @@ export default { // 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.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.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 => { + arr.forEach((item) => { item.id = uuidv4() }) return arr @@ -199,7 +293,7 @@ export default { let arr = [] arr = this.employeeFormData.educational_experience ? this.employeeFormData.educational_experience : undefined if (arr && arr.length) { - arr.forEach(item => { + arr.forEach((item) => { item.id = uuidv4() }) return arr @@ -212,12 +306,12 @@ export default { school: '', major: '', education: undefined, - graduation_year: null + graduation_year: null, } this.educational_experience.push(newEducational_experience) }, 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) { this.educational_experience.splice(_idx, 1) } @@ -228,12 +322,12 @@ export default { name: '', gender: undefined, birthday: null, - parental_leave_left: 0 + parental_leave_left: 0, } this.children_information.push(newChildrenInfo) }, 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) { this.children_information.splice(_idx, 1) } @@ -254,10 +348,10 @@ export default { // } if (date !== null) { 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') } 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') } else { this.employeeFormData[param] = moment(date).format('YYYY-MM-DD') @@ -303,7 +397,7 @@ export default { </script> <style lang="less" scoped> .el-date-picker { - width: 100%; - height: 36px; + width: 100%; + height: 36px; } </style> diff --git a/cmdb-ui/src/views/setting/companyStructure/index.vue b/cmdb-ui/src/views/setting/companyStructure/index.vue index 290e40a..b154142 100644 --- a/cmdb-ui/src/views/setting/companyStructure/index.vue +++ b/cmdb-ui/src/views/setting/companyStructure/index.vue @@ -16,17 +16,17 @@ :key="group.id" > <div - class="ops-setting-structure-sidebar-group-header" - :class="{ 'group-selected': groupIndex === activeGroupIndex }" + :class="{ + 'ops-setting-structure-sidebar-group-header': true, + 'group-selected': groupIndex === activeGroupIndex, + }" > <div class="ops-setting-structure-sidebar-group-header-avatar"> - <a-icon :type="group.icon"/> + <a-icon :type="group.icon" /> </div> <span class="ops-setting-structure-sidebar-group-header-title" - @click=" - clickSelectGroup(groupIndex) - " + @click="clickSelectGroup(groupIndex)" :id="[group.id === 0 ? 'employee' : 'department']" > {{ group.title }} @@ -100,7 +100,7 @@ /> </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 @visibleChange="visibleChange" trigger="click" @@ -122,16 +122,17 @@ </div> </template> <span :style="{ whiteSpace: 'nowrap' }"> - <a-icon class="screening-box-scene-icon" type="filter"/> + <a-icon class="screening-box-scene-icon" type="filter" /> {{ getCurrentSceneLabel() }} - <a-icon class="screening-box-scene-icon" :type="displayTimeIcon"/> + <a-icon class="screening-box-scene-icon" :type="displayTimeIcon" /> </span> </a-popover> </div> <SearchForm ref="search" :canSearchPreferenceAttrList="canSearchPreferenceAttrList" - @refresh="handleSearch"/> + @refresh="handleSearch" + /> </div> <div> <a-space v-if="isEditable"> @@ -168,23 +169,24 @@ <div> <div :style="{ marginTop: '8px' }" class="ops-list-batch-action" v-show="!!selectedRowKeys.length"> <span @click="downloadEmployeeAll">{{ $t('cs.companyStructure.downloadAll') }}</span> - <a-divider type="vertical"/> + <a-divider type="vertical" /> <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> - <a-divider type="vertical"/> - <span - @click="openBatchModal('direct_supervisor_id')">{{ $t('cs.companyStructure.editDirectSupervisor') }}</span> - <a-divider type="vertical"/> + <a-divider type="vertical" /> + <span @click="openBatchModal('direct_supervisor_id')">{{ + $t('cs.companyStructure.editDirectSupervisor') + }}</span> + <a-divider type="vertical" /> <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> - <a-divider type="vertical"/> + <a-divider type="vertical" /> <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> - <a-divider type="vertical"/> - <span>{{ $t('selectRows', {rows: selectedRowKeys.length}) }}</span> + <a-divider type="vertical" /> + <span>{{ $t('selectRows', { rows: selectedRowKeys.length }) }}</span> </div> </div> <!-- <div> @@ -195,11 +197,11 @@ </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 @@ -211,7 +213,7 @@ @onSelectChange="onSelectChange" @openEmployeeModal="openEmployeeModal" @openBatchModal="openBatchModal" - @tranferAttributes="getAttributes" + @transferAttributes="getAttributes" :isEditable="isEditable" :loading="loading" > @@ -225,7 +227,9 @@ :page-size-options="pageSizeOptions" :current="tablePage.currentPage" :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" :default-current="1" @change="pageOrSizeChange" @@ -252,6 +256,9 @@ clickSelectGroup(1) } " + @downloadTemplate="downloadTemplate" + @customRequest="customRequest" + @import="importEmployee" /> </div> </template> @@ -262,15 +269,22 @@ import SplitPane from '@/components/SplitPane' import CollapseTransition from '@/components/CollapseTransition' import Bus from './eventBus/bus' import CategroyTree from './CategoryTree' -import BatchUpload from './BatchUpload' +import BatchUpload from '../components/BatchUpload.vue' import BatchModal from './BatchModal.vue' import EmployeeModal from './EmployeeModal.vue' import DepartmentModal from './DepartmentModal.vue' import EmployeeTable from '../components/employeeTable.vue' 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 SearchForm from '../components/SearchForm.vue' +import { downloadExcel, excel2Array } from '@/utils/download' export default { name: 'CompanyStructure', @@ -284,10 +298,22 @@ export default { EmployeeModal, DepartmentModal, EmployeeTable, - SearchForm + SearchForm, }, data() { + const common_importParamsList = [ + 'email', + 'username', + 'nickname', + 'password', + 'sex', + 'mobile', + 'position_name', + 'department_name', + 'work_region', + ] return { + common_importParamsList, isActive: '', visible: true, localStorageKey: 'itsm-company-strcutre', @@ -322,7 +348,7 @@ export default { attributes: [], pageSizeOptions: ['50', '100', '200', '9999'], expression: [], - loading: false + loading: false, } }, // created() { @@ -363,16 +389,26 @@ export default { value: 'sex', is_choice: true, choice_value: [ - { label: this.$t('cs.companyStructure.male'), value: '男' }, - { label: this.$t('cs.companyStructure.female'), value: '女' }] + { label: this.$t('cs.companyStructure.male'), value: '男' }, + { label: this.$t('cs.companyStructure.female'), value: '女' }, + ], }, { label: this.$t('cs.companyStructure.mobile'), value: 'mobile' }, { label: this.$t('cs.companyStructure.departmentName'), value: 'department_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.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 [ { label: this.$t('all'), @@ -388,7 +424,7 @@ export default { }, ] }, - groupData () { + groupData() { return [ { id: 0, @@ -396,7 +432,7 @@ export default { icon: 'user', }, ] - } + }, }, provide() { return { @@ -426,11 +462,8 @@ export default { } else { this.currentScene = 0 } - // console.log(this.currentScene) - // this.init() - this.clickSelectGroup(0).then(val => { - this.clickSelectItem(0) - }) + this.updateCount() + this.clickSelectItem(0) Bus.$on('updataAllIncludeEmployees', () => { this.getAllFlatEmployees() this.getAllDepAndEmployee() @@ -492,7 +525,7 @@ export default { setSearchPreferenceAttrList() { this.canSearchPreferenceAttrList.forEach((item) => { 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) } document.getElementById('department').click() - // this.currentPage = 1 - // this.updateTableData(1) - // this.departmentList = this.reqDepartmentList(-1) }, clickHandler(event) { this.isActive = event.target.innerText @@ -544,37 +574,6 @@ export default { this.activeEmployeeCount = res1.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) { this.loading = true this.selectedRowKeys = [] @@ -623,7 +622,8 @@ export default { let max_index = 0 educational_experience.forEach((item, index) => { 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 @@ -708,7 +708,7 @@ export default { } else { block_status = this.currentScene } - downloadAllEmployee({ block_status: block_status }).then(res => { + downloadAllEmployee({ block_status: block_status }).then((res) => { const content = res const blob = new Blob([content], { type: 'application/vnd.ms-excel' }) const url = window.URL.createObjectURL(blob) @@ -787,7 +787,8 @@ export default { }, // 请求部门数据 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) }, openDepartmentModal(type) { @@ -828,10 +829,10 @@ export default { }, sortChangeEvent({ sortList }) { this.tableSortData = sortList - .map((item) => { - return `${item.order === 'asc' ? '' : '-'}${item.property}` - }) - .join(',') + .map((item) => { + return `${item.order === 'asc' ? '' : '-'}${item.property}` + }) + .join(',') this.updateTableDataByFilter() }, filterChangeEvent({ column, property, values, datas, filterList, $event }) { @@ -852,6 +853,137 @@ export default { exportSelectEvent() { 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> @@ -1051,7 +1183,6 @@ export default { color: @primary-color; font-size: 12px; } - } } } @@ -1068,7 +1199,6 @@ export default { } } } - } .ops-setting-structure-main-pagination { width: 100%; diff --git a/cmdb-ui/src/views/setting/components/BatchUpload.vue b/cmdb-ui/src/views/setting/components/BatchUpload.vue new file mode 100644 index 0000000..809e1f9 --- /dev/null +++ b/cmdb-ui/src/views/setting/components/BatchUpload.vue @@ -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> diff --git a/cmdb-ui/src/views/setting/components/employeeTable.vue b/cmdb-ui/src/views/setting/components/employeeTable.vue index ad40758..184b21b 100644 --- a/cmdb-ui/src/views/setting/components/employeeTable.vue +++ b/cmdb-ui/src/views/setting/components/employeeTable.vue @@ -92,7 +92,7 @@ <span>{{ $t('cs.companyStructure.sex') }}</span> </span> </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.female') }}</span> </template> @@ -175,6 +175,26 @@ }}</span> </template> </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 field="control" width="100px" @@ -197,7 +217,7 @@ > <template slot="content"> <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> </div> <div @@ -209,22 +229,24 @@ 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> </div> </template> - <a-icon type="control" style="cursor: pointer;" /> + <a-icon type="control" style="cursor: pointer" /> </a-popover> </template> </template> <template #default="{ row }"> <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> <template slot="title"> {{ $t('cs.companyStructure.resetPassword') }} </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 v-if="!row.block"> <template slot="title"> @@ -305,6 +327,7 @@ export default { { label: this.$t('cs.companyStructure.departmentName'), value: 'department_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.work_region'), value: 'work_region' }, ] const checkedCols = JSON.parse(localStorage.getItem('setting-table-CheckedCols')) || [ 'nickname', @@ -315,6 +338,7 @@ export default { 'department_name', 'position_name', 'direct_supervisor_id', + 'work_region', ] return { filterRoleList: [], @@ -442,7 +466,7 @@ export default { } } Bus.$emit('getAttributes', this.attributes) - this.$emit('tranferAttributes', this.attributes) + this.$emit('transferAttributes', this.attributes) }, getIsInterInship(is_internship) { return this.internMap.filter((item) => item.id === is_internship)[0]['label'] @@ -541,7 +565,7 @@ export default { useStyle: true, // 是否导出样式 isFooter: false, // 是否导出表尾(比如合计) // 过滤那个字段导出 - columnFilterMethod: function(column, $columnIndex) { + columnFilterMethod: function (column, $columnIndex) { return !(column.$columnIndex === 0) // 0是复选框 不导出 }, diff --git a/cmdb-ui/src/views/setting/lang/en.js b/cmdb-ui/src/views/setting/lang/en.js index c19a020..0785318 100644 --- a/cmdb-ui/src/views/setting/lang/en.js +++ b/cmdb-ui/src/views/setting/lang/en.js @@ -19,7 +19,7 @@ const cs_en = { app: 'APP Authority', basic: 'Basic Settings', theme: 'Theme Settings', - security: 'Security Settings' + security: 'Security Settings', }, companyInfo: { spanCompany: 'Description', @@ -201,7 +201,11 @@ const cs_en = { createEmployee: 'Create Employee', editEmployee: 'Edit Employee', 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: { basic: 'Basic', @@ -220,7 +224,7 @@ const cs_en = { user: 'User', username: '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: { server: 'Server Address', @@ -237,10 +241,11 @@ const cs_en = { validateRoutePlaceholder: 'Please enter validate route', afterLoginRoute: 'Redirect Route', afterLoginRoutePlaceholder: 'Please enter redirect route', - userMap: 'User Attribute Mapping' + userMap: 'User Attribute Mapping', }, 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', usernameOrEmailPlaceholder: 'Please enter username/email', password: 'Password', @@ -257,7 +262,7 @@ const cs_en = { userInfo: 'User Info', scopes: 'Scopes', scopesPlaceholder: 'Please enter scopes', - } + }, }, duty: { basicSetting: 'Basic Settings', @@ -274,13 +279,13 @@ const cs_en = { mainDutyPeople: 'Main Duty Person', deputyDutyPeople: 'Deputy Duty Person', dutyRule: 'Duty Rule', - '一': 'Mon', - '二': 'Tue', - '三': 'Wed', - '四': 'Thu', - '五': 'Fri', - '六': 'Sat', - '日': 'Sun', + 一: 'Mon', + 二: 'Tue', + 三: 'Wed', + 四: 'Thu', + 五: 'Fri', + 六: 'Sat', + 日: 'Sun', searchPlaceholder: 'Please search', dutyTable: 'Duty Schedule', dutyMember: 'Duty Member', @@ -304,7 +309,7 @@ const cs_en = { offDutyReceiverPlaceholder: 'Please select off-duty receiver', titleLimit: 'Please enter title (20 characters)', remarkLimit: 'Remark 150 characters max', - frequencyLimit: 'Please enter duty frequency (positive integer)' + frequencyLimit: 'Please enter duty frequency (positive integer)', }, group: { groupName: 'User Group', @@ -329,7 +334,7 @@ const cs_en = { moreThan: 'More Than', lessThan: 'Less Than', operatorInPlaceholder: 'Separate by ;', - selectEmployee: 'Select Employee' + selectEmployee: 'Select Employee', }, notice: { corpid: 'Corp ID', @@ -368,7 +373,8 @@ const cs_en = { disableCreationOfRequestsViaEmail: 'Disable Creation of Requests Via Email', specifyAllowedEmails: 'Specify Allowed Emails/Domains, Separate Multiple Values By Comma', 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', moveWrongMessagesToFolder: 'Move Messages to Wrong Folder', knowMore: 'Learn More', @@ -438,7 +444,7 @@ const cs_en = { myDepartmentAndSubordinateDepartments: 'My Department And Subordinate Departments', test: 'Test', selectApp: 'Select App', - } + }, } export default cs_en diff --git a/cmdb-ui/src/views/setting/lang/zh.js b/cmdb-ui/src/views/setting/lang/zh.js index def1e92..e406848 100644 --- a/cmdb-ui/src/views/setting/lang/zh.js +++ b/cmdb-ui/src/views/setting/lang/zh.js @@ -19,7 +19,7 @@ const cs_zh = { app: '应用权限', basic: '基础设置', theme: '主题配置', - security: '安全配置' + security: '安全配置', }, companyInfo: { spanCompany: '公司描述', @@ -201,7 +201,11 @@ const cs_zh = { createEmployee: '新建员工', editEmployee: '编辑员工', role: '角色', - selectDisplayColumn: '请选择需要展示的列' + selectDisplayColumn: '请选择需要展示的列', + work_region: '工作地区', + workRegionPlaceholder: '请选择工作地区', + china_mainland: '中国大陆', + china_hk: '中国香港', }, auth: { basic: '基本', @@ -220,7 +224,7 @@ const cs_zh = { user: '用户', username: '用户名称', userPlaceholder: '请输入用户名称', - userHelp: '用户dn: cn={},ou=users,dc=xxx,dc=com {}会替换成用户名' + userHelp: '用户dn: cn={},ou=users,dc=xxx,dc=com {}会替换成用户名', }, cas: { server: '服务端地址', @@ -237,7 +241,7 @@ const cs_zh = { validateRoutePlaceholder: '请输入验证路由', afterLoginRoute: '重定向路由', afterLoginRoutePlaceholder: '请输入重定向路由', - userMap: '用户属性映射' + userMap: '用户属性映射', }, autoRedirectLogin: '自动跳转到第三方登录页', autoRedirectLoginHelp: '如果关闭,则会弹出跳转到第三方登录页的确认,点取消按钮会进入系统内置的登录页', @@ -257,7 +261,7 @@ const cs_zh = { userInfo: '用户信息', scopes: '授权范围', scopesPlaceholder: '请输入授权范围', - } + }, }, duty: { basicSetting: '基础设置', @@ -274,13 +278,13 @@ const cs_zh = { mainDutyPeople: '主值班人', deputyDutyPeople: '副值班人', dutyRule: '排班规则', - '一': '一', - '二': '二', - '三': '三', - '四': '四', - '五': '五', - '六': '六', - '日': '日', + 一: '一', + 二: '二', + 三: '三', + 四: '四', + 五: '五', + 六: '六', + 日: '日', searchPlaceholder: '请查找', dutyTable: '值班表', dutyMember: '值班人员', @@ -304,7 +308,7 @@ const cs_zh = { offDutyReceiverPlaceholder: '请选择非值班时间接收人', titleLimit: '请输入标题(20个字符)', remarkLimit: '备注150个字符以内', - frequencyLimit: '请输入值班频次(正整数)' + frequencyLimit: '请输入值班频次(正整数)', }, group: { groupName: '用户分组', @@ -329,7 +333,7 @@ const cs_zh = { moreThan: '大于', lessThan: '小于', operatorInPlaceholder: '以 ; 分隔', - selectEmployee: '选择员工' + selectEmployee: '选择员工', }, notice: { corpid: '企业ID', @@ -438,6 +442,6 @@ const cs_zh = { myDepartmentAndSubordinateDepartments: '本部门及下属部门', test: '测试', selectApp: '选择应用', - } + }, } export default cs_zh