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