Files
dvcp_v2_webapp/ui/packages/common/AiPicker.vue
2023-04-18 10:04:58 +08:00

291 lines
7.2 KiB
Vue

<template>
<section class="AiPicker">
<ai-dialog-btn
custom-class=""
v-bind="$attrs"
append-to-body
@onConfirm="handleSave"
:customFooter="false"
:modal-append-to-body="false"
:close-on-click-modal="false"
@close="selected=[]"
@open="selected=$copy(picked)">
<template #btn v-if="$scopedSlots.default">
<slot :selected="picked"/>
</template>
<div class="AiWechatSelecter-container">
<div class="AiWechatSelecter-container__left">
<div class="AiWechatSelecter-header">
<div class="AiWechatSelecter-header__left">
<h2 class="active" v-text="pageTitle"/>
</div>
</div>
<el-scrollbar class="AiWechatSelecter-list">
<el-tree v-if="refreshTree" lazy :load="getOptions" :props="props">
<template slot-scope="{data}">
<el-row type="flex" align="middle" class="optionItem fill" @click.native.stop="handleSelect(data)">
<div class="fill overHide">
<ai-open-data v-if="data.openType" :type="data.openType" :openid="data[props.id]"/>
<p v-else v-text="data[props.label]"/>
</div>
<div class="iconfont iconSuccess color-primary" v-if="data.checked&&isSelected(data)"/>
</el-row>
</template>
</el-tree>
</el-scrollbar>
</div>
<div class="AiWechatSelecter-container__right">
<div class="AiWechatSelecter-header AiWechatSelecter-header__right">
<h2>已选择</h2>
<el-button size="mini" icon="el-icon-delete" @click="selected=[]">清空</el-button>
</div>
<el-scrollbar class="AiWechatSelecter-list">
<div class="tags-wrapper">
<el-tag v-for="(item, index) in selected" :key="item.id" closable @close="handleRemove(index)" type="info">
<ai-open-data class="fill overHide" v-if="item.openType" :type="item.openType" :openid="item[props.id]"/>
<p class="fill overHide" v-else v-text="item[props.label]"/>
</el-tag>
</div>
</el-scrollbar>
</div>
</div>
</ai-dialog-btn>
</section>
</template>
<script>
export default {
name: "AiPicker",
model: {
prop: "value",
event: "change"
},
props: {
value: {default: () => []},
action: {default: "/app/wxcp/wxdepartment/departList"},
instance: Function,
meta: {default: () => []},
pageTitle: {default: "部门"},
ops: {default: () => ({})},
multiple: Boolean,
timer: null,
},
data() {
return {
selected: [],
picked: [],
refreshTree: true
}
},
computed: {
props: v => ({id: 'id', label: 'name', ...v.ops})
},
watch: {
action(v) {
if (v) {
this.refreshTree = false
this.initValue()
this.$nextTick(() => this.refreshTree = true)
}
}
},
methods: {
getOptions(node, resolve) {
const {props: {id}} = this
if (node.level == 0) {
this.getTreeData().then(list => resolve(list || []))
} else {
const parent = node.data[id]
this.getTreeData({id: parent}).then(list => resolve(list || []))
}
},
getTreeData(params) {
const selected = params?.selected, {action} = this
if (selected) delete params.selected
return this.instance.post(action, selected, {params, headers: {'Content-Type': 'application/json'}}).then(res => {
if (res?.data) {
if (selected) {
this.picked = res.data.selected
}
return res.data.itemList.map(e => ({...e, checked: this.isSelected(e)}))
}
})
},
handleRemove(i) {
this.selected.splice(i, 1)
},
handleSave() {
const {id} = this.props
this.picked = this.$copy(this.selected)
this.$emit("change", this.selected.map(e => e[id]).filter(Boolean))
this.$emit("pick", this.selected)
},
isSelected(row) {
const {id: key} = this.props
return !!this.selected.find(e => e[key] == row[key])
},
initValue() {
const unwatch = this.$watch('value', (v) => {
if (this.selected.length > 0) unwatch && unwatch()
else if (!!v?.toString()) {
this.getTreeData({selected: v?.toString()})
unwatch && unwatch()
}
}, {immediate: true})
},
handleSelect(row) {
row.checked = !row.checked
if (row.checked) {
const current = this.$copy(row)
if (this.multiple) {
this.selected.push(current)
} else {
this.selected = [current]
}
} else {
if (this.multiple) {
const {id} = this.props, i = this.selected.findIndex(e => e[id] == row[id])
this.handleRemove(i)
} else this.selected = []
}
}
},
created() {
this.initValue()
}
}
</script>
<style lang="scss" scoped>
.color-primary {
color: $primaryColor;
}
.optionItem {
cursor: pointer;
user-select: none;
padding-right: 16px;
}
.AiOpenData {
pointer-events: none;
}
.AiWechatSelecter-container {
display: flex;
height: 480px;
.el-tree {
background: transparent;
.el-tree-node__content {
height: auto;
margin-top: 2px;
&:hover {
background: #f4f5f6;
}
}
.el-tree-node__expand-icon {
height: 24px;
}
}
& > div {
width: 280px;
background: #FCFCFC;
border: 1px solid #D0D4DC;
}
.AiWechatSelecter-list {
height: calc(100% - 40px);
padding: 8px 0;
:deep( .el-scrollbar__wrap ){
margin-bottom: 0 !important;
overflow-x: hidden;
}
}
.AiWechatSelecter-container__left {
flex: 1;
}
.AiWechatSelecter-container__right {
flex: 1;
margin-left: 20px;
.AiWechatSelecter-list {
.tags-wrapper {
padding: 0 8px;
display: flex;
flex-wrap: wrap;
}
.el-tag {
margin: 0 8px 8px 0;
color: #222222;
font-size: 14px;
display: flex;
align-items: center;
}
}
}
.AiWechatSelecter-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 8px 0 0;
border-bottom: 1px solid #D0D4DC;
background: #F5F7FA;
.AiWechatSelecter-header__left {
display: flex;
align-items: center;
h2 {
width: 60px;
height: 100%;
line-height: 40px;
color: #222222;
font-size: 14px;
text-align: center;
cursor: pointer;
border-bottom: 2px solid transparent;
&.active {
color: $primaryColor;
border-color: $primaryColor;
}
}
}
.el-button {
height: 28px;
padding: 7px 5px;
}
.el-input {
width: 160px;
}
}
.AiWechatSelecter-header__right {
padding: 0 8px;
h2 {
color: #222222;
font-size: 14px;
text-align: center;
}
}
}
:deep(.overHide) {
overflow: hidden;
}
</style>