parent
f727314a67
commit
5794f76fe0
@ -0,0 +1,217 @@ |
||||
<template> |
||||
<div> |
||||
<slot name="reference"/> |
||||
<transition :name="transition" @after-enter="handleAfterEnter" @after-leave="handleAfterLeave"> |
||||
<div |
||||
ref="popper" |
||||
v-show="!disabled && showPopper" |
||||
class="el-popover el-popper" |
||||
:class="[popperClass, content && 'el-popover--plain']" |
||||
:style="{ width: width + 'px' }" |
||||
:id="tooltipId" |
||||
> |
||||
<div class="el-popover__title" v-if="title"> |
||||
<slot name="title">{{title}}</slot> |
||||
</div> |
||||
<slot>{{ content }}</slot> |
||||
</div> |
||||
</transition> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import Popper from '@ele/mixins/vue-popper' |
||||
import {on, off} from 'element-ui/lib/utils/dom' |
||||
import {addClass, removeClass} from 'element-ui/lib/utils/dom' |
||||
import {generateId} from 'element-ui/lib/utils/util' |
||||
|
||||
export default { |
||||
name: "ElPopover", |
||||
|
||||
mixins: [Popper], |
||||
|
||||
props: { |
||||
trigger: { |
||||
type: String, |
||||
default: 'click', |
||||
validator: value => ['click', 'focus', 'hover', 'manual'].indexOf(value) > -1 |
||||
}, |
||||
openDelay: {type: Number, default: 0}, |
||||
closeDelay: {type: Number, default: 200}, |
||||
transition: {type: String, default: 'fade-in-liner'}, |
||||
popperClass: String, |
||||
title: String, |
||||
disabled: Boolean, |
||||
content: String, |
||||
width: {}, |
||||
arrowOffset: {type: Number, default: 0} |
||||
}, |
||||
|
||||
data() { |
||||
return {} |
||||
}, |
||||
|
||||
computed: { |
||||
tooltipId() { |
||||
return `el-popover-${generateId()}` |
||||
} |
||||
}, |
||||
|
||||
watch: { |
||||
showPopper(val) { |
||||
if (this.disabled) return |
||||
this.$emit(val ? 'show' : 'hide') |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
doToggle() { |
||||
this.showPopper = !this.showPopper |
||||
}, |
||||
|
||||
doShow() { |
||||
this.showPopper = true |
||||
}, |
||||
|
||||
doClose() { |
||||
this.showPopper = false |
||||
}, |
||||
|
||||
handleFocus() { |
||||
addClass(this.referenceElm, 'focusing') |
||||
if (this.trigger === 'click' || this.trigger === 'focus') this.showPopper = true |
||||
}, |
||||
|
||||
handleClick() { |
||||
removeClass(this.referenceElm, 'focusing') |
||||
}, |
||||
|
||||
handleBlur() { |
||||
removeClass(this.referenceElm, 'focusing') |
||||
if (this.trigger === 'click' || this.trigger === 'focus') this.showPopper = false |
||||
}, |
||||
|
||||
handleMouseEnter() { |
||||
window.clearTimeout(this._timer) |
||||
if (this.openDelay) { |
||||
this._timer = setTimeout(() => this.showPopper = true, this.openDelay) |
||||
} |
||||
else this.showPopper = true |
||||
}, |
||||
|
||||
handleKeydown(ev) { |
||||
if (ev.keyCode === 27 && this.trigger !== 'manual') { // esc |
||||
this.doClose() |
||||
} |
||||
}, |
||||
|
||||
handleMouseLeave() { |
||||
window.clearTimeout(this._timer) |
||||
if (this.closeDelay) { |
||||
this._timer = setTimeout(() => this.showPopper = false, this.closeDelay) |
||||
} |
||||
else this.showPopper = false |
||||
}, |
||||
|
||||
handleDocumentClick(e) { |
||||
let reference = this.reference || this.$refs.reference |
||||
const popper = this.popper || this.$refs.popper |
||||
|
||||
if (!reference && this.$slots.reference && this.$slots.reference[0]) { |
||||
reference = this.referenceElm = this.$slots.reference[0].elm |
||||
} |
||||
if (!this.$el || |
||||
!reference || |
||||
this.$el.contains(e.target) || |
||||
reference.contains(e.target) || |
||||
!popper || |
||||
popper.contains(e.target)) return |
||||
this.showPopper = false |
||||
}, |
||||
|
||||
handleAfterEnter() { |
||||
this.$emit('after-enter') |
||||
}, |
||||
|
||||
handleAfterLeave() { |
||||
this.$emit('after-leave') |
||||
this.doDestroy() |
||||
}, |
||||
|
||||
cleanup() { |
||||
if (this.openDelay || this.closeDelay) { |
||||
window.clearTimeout(this._timer) |
||||
} |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
let reference = this.referenceElm = this.reference || this.$refs.reference |
||||
const popper = this.popper || this.$refs.popper |
||||
|
||||
if (!reference && this.$slots.reference && this.$slots.reference[0]) { |
||||
reference = this.referenceElm = this.$slots.reference[0].elm |
||||
} |
||||
|
||||
if (reference) { |
||||
addClass(reference, 'el-popover__reference') |
||||
|
||||
if (this.trigger !== 'click') { |
||||
on(reference, 'focusin', () => { |
||||
this.handleFocus() |
||||
const instance = reference.__vue__ |
||||
if (instance && typeof instance.focus === 'function') { |
||||
instance.focus() |
||||
} |
||||
}) |
||||
on(popper, 'focusin', this.handleFocus) |
||||
on(reference, 'focusout', this.handleBlur) |
||||
on(popper, 'focusout', this.handleBlur) |
||||
} |
||||
on(reference, 'keydown', this.handleKeydown) |
||||
on(reference, 'click', this.handleClick) |
||||
} |
||||
if (this.trigger === 'click') { |
||||
on(reference, 'click', this.doToggle) |
||||
on(document, 'click', this.handleDocumentClick) |
||||
} |
||||
else if (this.trigger === 'hover') { |
||||
on(reference, 'mouseenter', this.handleMouseEnter) |
||||
on(popper, 'mouseenter', this.handleMouseEnter) |
||||
on(reference, 'mouseleave', this.handleMouseLeave) |
||||
on(popper, 'mouseleave', this.handleMouseLeave) |
||||
} |
||||
else if (this.trigger === 'focus') { |
||||
if (reference.querySelector('input, textarea')) { |
||||
on(reference, 'focusin', this.doShow) |
||||
on(reference, 'focusout', this.doClose) |
||||
} |
||||
else { |
||||
on(reference, 'mousedown', this.doShow) |
||||
on(reference, 'mouseup', this.doClose) |
||||
} |
||||
} |
||||
}, |
||||
|
||||
beforeDestroy() { |
||||
this.cleanup() |
||||
|
||||
const reference = this.reference |
||||
|
||||
off(reference, 'click', this.doToggle) |
||||
off(reference, 'mouseup', this.doClose) |
||||
off(reference, 'mousedown', this.doShow) |
||||
off(reference, 'focusin', this.doShow) |
||||
off(reference, 'focusout', this.doClose) |
||||
off(reference, 'mousedown', this.doShow) |
||||
off(reference, 'mouseup', this.doClose) |
||||
off(reference, 'mouseleave', this.handleMouseLeave) |
||||
off(reference, 'mouseenter', this.handleMouseEnter) |
||||
off(document, 'click', this.handleDocumentClick) |
||||
}, |
||||
|
||||
deactivated() { |
||||
this.cleanup() |
||||
}, |
||||
} |
||||
</script> |
||||
@ -0,0 +1,13 @@ |
||||
import Message from "./components/Message" |
||||
import Popover from './components/Popover' |
||||
import Select from './components/Select/Select' |
||||
|
||||
const components = [Popover, Select] |
||||
|
||||
export default function (Vue) { |
||||
components.forEach(component => { |
||||
Vue.component(component.name, component) |
||||
}) |
||||
|
||||
Vue.prototype.$message = Message |
||||
} |
||||
@ -1,92 +0,0 @@ |
||||
<template> |
||||
<div> |
||||
<el-input :value="value" :readonly="readonly" clearable @clear="$emit('clear')" @focus="dialogVisible=true"/> |
||||
<dialog-form |
||||
v-drag-dialog |
||||
v-model="dialogVisible" |
||||
:loading="loading" |
||||
append-to-body |
||||
title="选择行政区域" |
||||
@close="closeDialog" |
||||
> |
||||
<el-tree |
||||
ref="tree" |
||||
:data="limit ? limitTree : regionTree" |
||||
:expand-on-click-node="false" |
||||
node-key="id" |
||||
@node-click="nodeClick" |
||||
> |
||||
<span slot-scope="{data}" class="el-tree-node__label"> |
||||
{{limit ? data.name + `(${data.value})` : data.name}} |
||||
</span> |
||||
</el-tree> |
||||
</dialog-form> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import {mapState} from 'vuex' |
||||
import DialogForm from '@/components/DialogForm' |
||||
import {createLimitTree, getNodeId} from "@/utils/tree" |
||||
|
||||
export default { |
||||
name: "RegionTreeSelector", |
||||
|
||||
components: {DialogForm}, |
||||
|
||||
props: { |
||||
value: String, |
||||
readonly: Boolean, |
||||
getChildrenOnClick: Boolean, |
||||
limit: Boolean, |
||||
limitApi: Function |
||||
}, |
||||
|
||||
data() { |
||||
return { |
||||
loading: false, |
||||
dialogVisible: false, |
||||
limitTree: [] |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
...mapState('dataCache', { |
||||
regionTree: state => state.regionTree |
||||
}) |
||||
}, |
||||
|
||||
methods: { |
||||
closeDialog() { |
||||
this.dialogVisible = false |
||||
}, |
||||
|
||||
nodeClick(obj) { |
||||
const payload = [obj] |
||||
if (this.getChildrenOnClick) { |
||||
let ids = getNodeId(obj.children) |
||||
ids.unshift(obj.id) |
||||
payload.push(ids) |
||||
} |
||||
|
||||
this.$emit('select', ...payload) |
||||
|
||||
this.closeDialog() |
||||
}, |
||||
|
||||
init() { |
||||
this.loading = true |
||||
const hasInit = this.regionTree.length > 0 |
||||
const promise = () => hasInit ? Promise.resolve() : this.$store.dispatch('dataCache/initRegion') |
||||
promise() |
||||
.then(() => this.limit ? this.limitApi() : Promise.resolve()) |
||||
.then(data => data && (this.limitTree = createLimitTree(this.regionTree, data))) |
||||
.finally(() => this.loading = false) |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
this.init() |
||||
} |
||||
} |
||||
</script> |
||||
@ -0,0 +1,82 @@ |
||||
<script type="text/jsx"> |
||||
import DialogForm from "@/components/DialogForm" |
||||
import {createLimitTree, getNodeId} from "@/utils/tree" |
||||
|
||||
export default { |
||||
components: {DialogForm}, |
||||
|
||||
data() { |
||||
return { |
||||
loading: false, |
||||
visible: false, |
||||
handler: null, |
||||
limitTree: [], |
||||
getChildrenOnClick: false, |
||||
limit: false, |
||||
limitApi: null |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
regionTree() { |
||||
return this.$store.state.dataCache.regionTree |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
closeDialog() { |
||||
this.visible = false |
||||
}, |
||||
|
||||
nodeClick(obj) { |
||||
const payload = [obj] |
||||
if (this.getChildrenOnClick) { |
||||
let ids = getNodeId(obj.children) |
||||
ids.unshift(obj.id) |
||||
payload.push(ids) |
||||
} |
||||
|
||||
this.handler(payload) |
||||
|
||||
this.closeDialog() |
||||
}, |
||||
|
||||
init() { |
||||
this.loading = true |
||||
const hasInit = this.regionTree.length > 0 |
||||
const promise = () => hasInit ? Promise.resolve() : this.$store.dispatch('dataCache/initRegion') |
||||
return promise() |
||||
.then(() => this.limit ? this.limitApi() : Promise.resolve()) |
||||
.then(data => data && (this.limitTree = createLimitTree(this.regionTree, data))) |
||||
.finally(() => this.loading = false) |
||||
} |
||||
}, |
||||
|
||||
render() { |
||||
return ( |
||||
<dialog-form |
||||
v-model={this.visible} |
||||
directives={[{name: 'drag-dialog'}]} |
||||
class="tree-dialog" |
||||
title="选择行政区域" |
||||
loading={this.loading} |
||||
appendToBody={true} |
||||
v-on:close={this.closeDialog} |
||||
> |
||||
<el-tree |
||||
data={this.limit ? this.limitTree : this.regionTree} |
||||
expand-on-click-node={false} |
||||
node-key="id" |
||||
v-on:node-click={this.nodeClick} |
||||
> |
||||
{({data}) => ( |
||||
<span className="el-tree-node__label"> |
||||
{this.limit ? data.name + `(${data.value})` : data.name} |
||||
</span> |
||||
)} |
||||
</el-tree> |
||||
</dialog-form> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
@ -0,0 +1,85 @@ |
||||
<script type="text/jsx"> |
||||
import Vue from 'vue' |
||||
import TreeDialog from "./TreeDialog" |
||||
|
||||
const TreeDialogConstructor = Vue.extend(TreeDialog) |
||||
|
||||
let limit, full |
||||
|
||||
export default { |
||||
name: "RegionTreeSelector", |
||||
|
||||
props: { |
||||
value: String, |
||||
readonly: Boolean, |
||||
size: String, |
||||
getChildrenOnClick: Boolean, |
||||
limit: Boolean, |
||||
limitApi: Function |
||||
}, |
||||
|
||||
data() { |
||||
return { |
||||
dialogVisible: false, |
||||
limitTree: [] |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
openDialog() { |
||||
if (this.limit) { |
||||
this.initLimit() |
||||
return limit.visible = true |
||||
} |
||||
else { |
||||
!full && this.initFull() |
||||
full.visible = true |
||||
} |
||||
}, |
||||
|
||||
handler(payload) { |
||||
this.$emit('select', ...payload) |
||||
}, |
||||
|
||||
initLimit() { |
||||
if (limit) { |
||||
limit.getChildrenOnClick = this.getChildrenOnClick |
||||
limit.limit = this.limit |
||||
limit.limitApi = this.limitApi |
||||
return limit.init() |
||||
} |
||||
|
||||
const data = {...this.$props, handler: this.handler} |
||||
limit = new TreeDialogConstructor({data}).$mount() |
||||
document.body.appendChild(limit.$el) |
||||
|
||||
return limit.init() |
||||
}, |
||||
|
||||
initFull() { |
||||
if (full) { |
||||
return full.getChildrenOnClick = this.getChildrenOnClick |
||||
} |
||||
|
||||
const data = {getChildrenOnClick: this.getChildrenOnClick, handler: this.handler} |
||||
full = new TreeDialogConstructor({data}).$mount() |
||||
document.body.appendChild(full.$el) |
||||
|
||||
return full.init() |
||||
} |
||||
}, |
||||
|
||||
render() { |
||||
return ( |
||||
<el-input |
||||
value={this.value} |
||||
readonly={this.readonly} |
||||
size={this.size} |
||||
clearable |
||||
v-on:clear={() => this.$emit('clear')} |
||||
v-on:focus={this.openDialog} |
||||
/> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
Loading…
Reference in new issue