[add] 内部工作组选择组件

This commit is contained in:
danshihao
2023-10-30 10:03:43 +08:00
parent cf44116037
commit 0a8c11776e
4 changed files with 541 additions and 0 deletions
+6
View File
@@ -541,6 +541,12 @@ module.exports = {
selectedPersonnel: '已选人员',
workGroup: '内部工作组'
},
// 内部工作组选择器
workGroupSelect: {
select: '选择',
selectWorkGroup: '选择内部工作组',
enterWorkGroupPressEnter: '请输入工作组名称按回车进行搜索'
},
// 弹框上传附件
uploadFile: {
clickUpload: 'Click To Upload',
+6
View File
@@ -562,6 +562,12 @@ module.exports = {
selectedPersonnel: '已选人员',
workGroup: '内部工作组'
},
// 内部工作组选择器
workGroupSelect: {
select: '选择',
selectWorkGroup: '选择内部工作组',
enterWorkGroupPressEnter: '请输入工作组名称按回车进行搜索'
},
// 弹框上传附件
uploadFile: {
clickUpload: '点击上传',
@@ -0,0 +1,213 @@
<template>
<div class="selection-wrapper">
<div class="box-title-text">
<a-input class="box-input" :value="textVals" :title="textVals"
:disabled="disabledInput" readonly :placeholder="placeholder">
<a-icon v-if="storeVals" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<a-button v-if="!disabled" type="primary" class="button-box" @click="workGroupClick">
{{this.$t('workGroupSelect.select')}}
</a-button>
</div>
<work-group-selection-modal ref="workGroupSelectionModal" v-bind="$attrs"
:modal-width="modalWidth"
:multi="multi"
:rootOpened="rootOpened"
:depart-id="value"
:store="storeField"
:text="textField"
:treeOpera="treeOpera"
@ok="handleOK"
@initComp="initComp"/>
</div>
</template>
<script>
import WorkGroupSelectionModal from '@comp/selection/WorkGroupSelectionModal'
import { underLinetoHump } from '@comp/_util/StringUtil'
export default {
name: 'WorkGroupSelection',
components: { WorkGroupSelectionModal },
props: {
value: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
disabled: {
type: Boolean,
required: false,
default: false
},
// 只禁用输入框
disabledInput: {
type: Boolean,
default: false
},
modalWidth: {
type: Number,
default: 500,
required: false
},
multi: {
type: Boolean,
default: false,
required: false
},
rootOpened: {
type: Boolean,
default: true,
required: false
},
// 自定义返回字段,默认返回 id
customReturnField: {
type: String,
default: ''
},
backDepart: {
type: Boolean,
default: false,
required: false
},
// 存储字段 [key field]
store: {
type: String,
default: 'id',
required: false
},
// 显示字段 [label field]
text: {
type: String,
default: 'name',
required: false
},
treeOpera: {
type: Boolean,
default: false,
required: false
}
},
data () {
return {
visible: false,
confirmLoading: false,
storeVals: '', // [key values]
textVals: '' // [label values]
}
},
computed: {
storeField () {
let field = this.customReturnField
if (!field) {
field = this.store
}
return underLinetoHump(field)
},
textField () {
return underLinetoHump(this.text)
}
},
mounted () {
this.storeVals = this.value
},
watch: {
value (val) {
this.storeVals = val
}
},
methods: {
initComp (textVals) {
this.textVals = textVals
},
// 返回选中的部门信息
backDepartInfo () {
if (this.backDepart === true) {
if (this.storeVals && this.storeVals.length > 0) {
const arr1 = this.storeVals.split(',')
const arr2 = this.textVals.split(',')
const info = []
for (let i = 0; i < arr1.length; i++) {
info.push({
value: arr1[i],
text: arr2[i]
})
}
this.$emit('back', info)
}
}
},
handleOK (rows) {
if (!rows && rows.length <= 0) {
this.textVals = ''
this.storeVals = ''
} else {
const arr1 = []
const arr2 = []
for (const dep of rows) {
arr1.push(dep[this.storeField])
arr2.push(dep[this.textField])
}
this.storeVals = arr1.join(',')
this.textVals = arr2.join(',')
}
this.$emit('change', this.storeVals)
this.backDepartInfo()
},
getDepartNames () {
return this.departNames
},
handleEmpty () {
this.handleOK('')
},
// 点击选择按钮
workGroupClick () {
this.$refs.workGroupSelectionModal.show()
},
// 输入框输入监听
indexClick (event) {
this.$emit('change', event.target.value)
},
modalInput (value) {
this.$emit('change', value)
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.selection-wrapper {
height: 40px;
line-height: 40px;
}
.box-title-text {
height: 100%;
display: flex;
align-items: center;
.box-input {
width: 100%;
}
.button-box {
margin-left: 5px;
}
}
.selection-wrapper .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.selection-wrapper .anticon-close-circle:hover {
color: #f5222d;
}
.selection-wrapper .anticon-close-circle:active {
color: #666;
}
</style>
@@ -0,0 +1,316 @@
<template>
<j-modal
:title="$t('workGroupSelect.selectWorkGroup')"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel"
@update:fullscreen="isFullscreen"
wrapClassName="j-depart-select-modal"
switchFullscreen
:cancelText="$t('close')">
<a-spin tip="Loading..." :spinning="false">
<a-input-search style="margin-bottom: 1px" :placeholder="$t('workGroupSelect.enterWorkGroupPressEnter')" @search="onSearch" />
<a-tree
:checkable="true"
:class="treeScreenClass"
:treeData="treeData"
:checkStrictly="checkStrictly"
@check="onCheck"
@select="onSelect"
@expand="onExpand"
:autoExpandParent="autoExpandParent"
:expandedKeys="expandedKeys"
:checkedKeys="checkedKeys">
<template slot="title" slot-scope="{title}">
<span v-if="title.indexOf(searchValue) > -1">
{{title.substr(0, title.indexOf(searchValue))}}
<span style="color: #f50">{{searchValue}}</span>
{{title.substr(title.indexOf(searchValue) + searchValue.length)}}
</span>
<span v-else>{{title}}</span>
</template>
</a-tree>
</a-spin>
<!--底部父子关联操作和确认取消按钮-->
<template slot="footer" v-if="treeOpera && multi">
<div class="drawer-bootom-button">
<a-dropdown style="float: left" :trigger="['click']" placement="topCenter">
<a-menu slot="overlay">
<a-menu-item key="1" @click="switchCheckStrictly(1)">{{ $t('parentChildConnection') }}</a-menu-item>
<a-menu-item key="2" @click="switchCheckStrictly(2)">{{ $t('cancelConnection') }}</a-menu-item>
</a-menu>
<a-button>
{{ $t('treeOperation') }} <a-icon type="up" />
</a-button>
</a-dropdown>
<a-button @click="handleCancel" type="primary" style="margin-right: 0.8rem">{{ $t('close') }}</a-button>
<a-button @click="handleSubmit" type="primary" >{{ $t('confirm') }}</a-button>
</div>
</template>
</j-modal>
</template>
<script>
import { getWorkGroupTreeList } from '@api/api'
export default {
name: 'WorkGroupSelectionModal',
props: ['modalWidth', 'multi', 'rootOpened', 'departId', 'store', 'text', 'treeOpera'],
data () {
return {
visible: false,
confirmLoading: false,
treeData: [],
autoExpandParent: true,
expandedKeys: [],
dataList: [],
checkedKeys: [],
checkedRows: [],
searchValue: '',
checkStrictly: true,
fullscreen: false
}
},
created () {
this.loadWorkGroup()
},
watch: {
departId () {
this.initDepartComponent()
},
visible: {
handler () {
this.initDepartComponent(true)
}
}
},
computed: {
treeScreenClass () {
return {
'my-dept-select-tree': true,
fullscreen: this.fullscreen
}
}
},
methods: {
show () {
this.visible = true
this.checkedRows = []
this.checkedKeys = []
},
// 处理工作组树数据结构
dealTreeData (treeData) {
return treeData.map(item => {
item.title = item.name
item.value = item.id
item.key = item.id
item.children = this.dealTreeData(item.subset)
return item
})
},
loadWorkGroup () {
getWorkGroupTreeList().then(res => {
if (res.success) {
console.log(res.result)
const arr = this.dealTreeData(res.result)
this.reWriterWithSlot(arr)
this.treeData = arr
this.initDepartComponent()
if (this.rootOpened) {
this.initExpandedKeys(res.result)
}
}
})
},
initDepartComponent (flag) {
const arr = []
// 该方法两个地方用 1.visible改变事件重新设置选中项 2.组件编辑页面回显
const fieldName = flag === true ? 'key' : this.text
if (this.departId) {
const arr2 = this.departId.split(',')
for (const item of this.dataList) {
if (arr2.indexOf(item[this.store]) >= 0) {
arr.push(item[fieldName])
}
}
}
if (flag === true) {
this.checkedKeys = [...arr]
} else {
this.$emit('initComp', arr.join(','))
}
},
reWriterWithSlot (arr) {
for (const item of arr) {
if (item.children && item.children.length > 0) {
this.reWriterWithSlot(item.children)
const temp = Object.assign({}, item)
temp.children = {}
this.dataList.push(temp)
} else {
this.dataList.push(item)
item.scopedSlots = { title: 'title' }
}
}
},
initExpandedKeys (arr) {
if (arr && arr.length > 0) {
const keys = []
for (const item of arr) {
if (item.children && item.children.length > 0) {
keys.push(item.id)
}
}
this.expandedKeys = [...keys]
} else {
this.expandedKeys = []
}
},
onCheck (checkedKeys, info) {
if (!this.multi) {
const arr = checkedKeys.checked.filter(item => this.checkedKeys.indexOf(item) < 0)
this.checkedKeys = [...arr]
this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef]
} else {
if (this.checkStrictly) {
this.checkedKeys = checkedKeys.checked
} else {
this.checkedKeys = checkedKeys
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
},
onSelect (selectedKeys, info) {
// 取消关联的情况下才走onSelect的逻辑
if (this.checkStrictly) {
const keys = []
keys.push(selectedKeys[0])
if (!this.checkedKeys || this.checkedKeys.length === 0 || !this.multi) {
this.checkedKeys = [...keys]
this.checkedRows = [info.node.dataRef]
} else {
const currKey = info.node.dataRef.key
if (this.checkedKeys.indexOf(currKey) >= 0) {
this.checkedKeys = this.checkedKeys.filter(item => item !== currKey)
} else {
this.checkedKeys.push(...keys)
}
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
},
onExpand (expandedKeys) {
this.expandedKeys = expandedKeys
this.autoExpandParent = false
},
handleSubmit () {
if (!this.checkedKeys || this.checkedKeys.length === 0) {
this.$emit('ok', '')
} else {
const checkRow = this.getCheckedRows(this.checkedKeys)
const keyStr = this.checkedKeys.join(',')
this.$emit('ok', checkRow, keyStr)
}
this.handleClear()
},
handleCancel () {
this.handleClear()
},
handleClear () {
this.visible = false
this.checkedKeys = []
},
getParentKey (currKey, treeData) {
let parentKey
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i]
if (node.children) {
if (node.children.some(item => item.key === currKey)) {
parentKey = node.key
} else if (this.getParentKey(currKey, node.children)) {
parentKey = this.getParentKey(currKey, node.children)
}
}
}
return parentKey
},
onSearch (value) {
const expandedKeys = this.dataList.map((item) => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.key, this.treeData)
}
return null
}).filter((item, i, self) => item && self.indexOf(item) === i)
Object.assign(this, {
expandedKeys,
searchValue: value,
autoExpandParent: true
})
},
// 根据 checkedKeys 获取 rows
getCheckedRows (checkedKeys) {
const forChildren = (list, key) => {
for (const item of list) {
if (item.id === key) {
return item
}
if (item.children instanceof Array) {
const value = forChildren(item.children, key)
if (value != null) {
return value
}
}
}
return null
}
const rows = []
for (const key of checkedKeys) {
const row = forChildren(this.treeData, key)
if (row != null) {
rows.push(row)
}
}
return rows
},
switchCheckStrictly (v) {
if (v === 1) {
this.checkStrictly = false
} else if (v === 2) {
this.checkStrictly = true
}
},
isFullscreen (val) {
this.fullscreen = val
}
}
}
</script>
<style lang="less" scoped>
// 限制部门选择树高度,避免部门太多时点击确定不便
.my-dept-select-tree{
height:350px;
&.fullscreen{
height: calc(100vh - 250px);
}
overflow-y: scroll;
}
.drawer-bootom-button {
position: absolute;
bottom: 0;
width: 100%;
border-top: 1px solid #e8e8e8;
padding: 10px 16px;
text-align: right;
left: 0;
background: #fff;
border-radius: 0 0 2px 2px;
}
</style>