291 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			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>
 |