parent
d375bf5619
commit
2e835ae853
File diff suppressed because it is too large
Load Diff
@ -1,48 +1,48 @@ |
||||
<template> |
||||
<div |
||||
class="el-select-dropdown el-popper" |
||||
:class="[{ 'is-multiple': $parent.multiple }, popperClass]" |
||||
:style="{ minWidth }" |
||||
class="el-select-dropdown el-popper" |
||||
:class="[{ 'is-multiple': $parent.multiple }, popperClass]" |
||||
:style="{ minWidth }" |
||||
> |
||||
<slot/> |
||||
</div> |
||||
</template> |
||||
|
||||
<script type="text/babel"> |
||||
import Popper from '@ele/mixins/vue-popper' |
||||
|
||||
export default { |
||||
name: 'ElSelectDropdown', |
||||
|
||||
componentName: 'ElSelectDropdown', |
||||
|
||||
mixins: [Popper], |
||||
|
||||
data() { |
||||
return { |
||||
minWidth: '' |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
popperClass() { |
||||
return this.$parent.popperClass |
||||
} |
||||
}, |
||||
|
||||
watch: { |
||||
'$parent.inputWidth'() { |
||||
this.minWidth = this.$parent.$el.getBoundingClientRect().width + 'px' |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
this.referenceElm = this.$parent.$refs.reference.$el |
||||
this.$parent.popperElm = this.popperElm = this.$el |
||||
this.$on('updatePopper', () => { |
||||
this.$parent.visible && this.updatePopper() |
||||
}) |
||||
this.$on('destroyPopper', this.destroyPopper) |
||||
import Popper from '@ele/mixins/vue-popper' |
||||
|
||||
export default { |
||||
name: 'ElSelectDropdown', |
||||
|
||||
componentName: 'ElSelectDropdown', |
||||
|
||||
mixins: [Popper], |
||||
|
||||
data() { |
||||
return { |
||||
minWidth: '' |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
popperClass() { |
||||
return this.$parent.popperClass |
||||
} |
||||
}, |
||||
|
||||
watch: { |
||||
'$parent.inputWidth'() { |
||||
this.minWidth = this.$parent.$el.getBoundingClientRect().width + 'px' |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
this.referenceElm = this.$parent.$refs.reference.$el |
||||
this.$parent.popperElm = this.popperElm = this.$el |
||||
this.$on('updatePopper', () => { |
||||
this.$parent.visible && this.updatePopper() |
||||
}) |
||||
this.$on('destroyPopper', this.destroyPopper) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,25 +1,25 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: "AbstractFormItem", |
||||
export default { |
||||
name: "AbstractFormItem", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: {full: Boolean, dense: Boolean, label: String, prop: String}, |
||||
props: {full: Boolean, dense: Boolean, label: String, prop: String}, |
||||
|
||||
render(h, context) { |
||||
const {full, dense, label, prop} = context.props |
||||
let attrs = {xs: 24, sm: 12, lg: 8} |
||||
render(h, context) { |
||||
const {full, dense, label, prop} = context.props |
||||
let attrs = {xs: 24, sm: 12, lg: 8} |
||||
|
||||
if (full) attrs = {span: 24} |
||||
else if (dense) attrs.lg = 12 |
||||
if (full) attrs = {span: 24} |
||||
else if (dense) attrs.lg = 12 |
||||
|
||||
return ( |
||||
<el-col {...{attrs}}> |
||||
<el-form-item label={label} prop={prop}> |
||||
{context.children} |
||||
</el-form-item> |
||||
</el-col> |
||||
) |
||||
} |
||||
return ( |
||||
<el-col {...{attrs}}> |
||||
<el-form-item label={label} prop={prop}> |
||||
{context.children} |
||||
</el-form-item> |
||||
</el-col> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,23 +1,23 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: "AbstractForm", |
||||
export default { |
||||
name: "AbstractForm", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
render(h, context) { |
||||
const {data, children} = context |
||||
return ( |
||||
<el-form |
||||
ref="form" |
||||
label-position="right" |
||||
label-width="100px" |
||||
size="small" |
||||
status-icon |
||||
{...data} |
||||
> |
||||
<el-row>{children}</el-row> |
||||
</el-form> |
||||
) |
||||
} |
||||
render(h, context) { |
||||
const {data, children} = context |
||||
return ( |
||||
<el-form |
||||
ref="form" |
||||
label-position="right" |
||||
label-width="100px" |
||||
size="small" |
||||
status-icon |
||||
{...data} |
||||
> |
||||
<el-row>{children}</el-row> |
||||
</el-form> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,36 +1,36 @@ |
||||
<script type="text/jsx"> |
||||
import Empty from "@/components/Empty" |
||||
import Empty from "@/components/Empty" |
||||
|
||||
export default { |
||||
name: "AbstractTable", |
||||
export default { |
||||
name: "AbstractTable", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
render(h, context) { |
||||
const {data, children, scopedSlots} = context |
||||
render(h, context) { |
||||
const {data, children, scopedSlots} = context |
||||
|
||||
if (!scopedSlots.empty) { |
||||
scopedSlots.empty = () => <Empty/> |
||||
} |
||||
|
||||
return ( |
||||
<el-table |
||||
ref="table" |
||||
current-row-key="id" |
||||
row-key="id" |
||||
highlight-current-row |
||||
{...data} |
||||
> |
||||
{children} |
||||
{ |
||||
Object |
||||
.entries(scopedSlots) |
||||
.map(([name, slot]) => ( |
||||
name !== 'default' && <slot slot={name}>{slot()}</slot> |
||||
)) |
||||
} |
||||
</el-table> |
||||
) |
||||
if (!scopedSlots.empty) { |
||||
scopedSlots.empty = () => <Empty/> |
||||
} |
||||
|
||||
return ( |
||||
<el-table |
||||
ref="table" |
||||
current-row-key="id" |
||||
row-key="id" |
||||
highlight-current-row |
||||
{...data} |
||||
> |
||||
{children} |
||||
{ |
||||
Object |
||||
.entries(scopedSlots) |
||||
.map(([name, slot]) => ( |
||||
name !== 'default' && <slot slot={name}>{slot()}</slot> |
||||
)) |
||||
} |
||||
</el-table> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,11 +1,11 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: "ContextMenuItem", |
||||
export default { |
||||
name: "ContextMenuItem", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
render(h, context) { |
||||
return <li class="contextmenu-item" {...context.data}>{context.children}</li> |
||||
} |
||||
render(h, context) { |
||||
return <li class="contextmenu-item" {...context.data}>{context.children}</li> |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,51 +1,51 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: 'DefaultEmptyImage', |
||||
export default { |
||||
name: 'DefaultEmptyImage', |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
render() { |
||||
return ( |
||||
<svg width="184" height="152" viewBox="0 0 184 152" xmlns="http://www.w3.org/2000/svg"> |
||||
<g fill="none" fillRule="evenodd"> |
||||
<g transform="translate(24 31.67)"> |
||||
<ellipse |
||||
fillOpacity=".8" |
||||
fill="#F5F5F7" |
||||
cx="67.797" |
||||
cy="106.89" |
||||
rx="67.797" |
||||
ry="12.668" |
||||
/> |
||||
<path |
||||
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z" |
||||
fill="#AEB8C2" |
||||
/> |
||||
<path |
||||
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z" |
||||
fill="url(#linearGradient-1)" |
||||
transform="translate(13.56)" |
||||
/> |
||||
<path |
||||
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z" |
||||
fill="#F5F5F7" |
||||
/> |
||||
<path |
||||
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z" |
||||
fill="#DCE0E6" |
||||
/> |
||||
</g> |
||||
render() { |
||||
return ( |
||||
<svg width="184" height="152" viewBox="0 0 184 152" xmlns="http://www.w3.org/2000/svg"> |
||||
<g fill="none" fillRule="evenodd"> |
||||
<g transform="translate(24 31.67)"> |
||||
<ellipse |
||||
fillOpacity=".8" |
||||
fill="#F5F5F7" |
||||
cx="67.797" |
||||
cy="106.89" |
||||
rx="67.797" |
||||
ry="12.668" |
||||
/> |
||||
<path |
||||
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z" |
||||
fill="#AEB8C2" |
||||
/> |
||||
<path |
||||
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z" |
||||
fill="url(#linearGradient-1)" |
||||
transform="translate(13.56)" |
||||
/> |
||||
<path |
||||
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z" |
||||
fill="#F5F5F7" |
||||
/> |
||||
<path |
||||
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z" |
||||
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z" |
||||
fill="#DCE0E6" |
||||
/> |
||||
<g transform="translate(149.65 15.383)" fill="#FFF"> |
||||
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815"/> |
||||
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"/> |
||||
</g> |
||||
</g> |
||||
</svg> |
||||
) |
||||
} |
||||
<path |
||||
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z" |
||||
fill="#DCE0E6" |
||||
/> |
||||
<g transform="translate(149.65 15.383)" fill="#FFF"> |
||||
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815"/> |
||||
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"/> |
||||
</g> |
||||
</g> |
||||
</svg> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,38 +1,38 @@ |
||||
<script type="text/jsx"> |
||||
import DefaultEmptyImage from "./DefaultEmptyImage" |
||||
import DefaultEmptyImage from "./DefaultEmptyImage" |
||||
|
||||
export default { |
||||
name: "AEmpty", |
||||
export default { |
||||
name: "AEmpty", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
description: String, |
||||
image: [String, Object], |
||||
imageStyle: [String, Object] |
||||
}, |
||||
props: { |
||||
description: String, |
||||
image: [String, Object], |
||||
imageStyle: [String, Object] |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {description = '暂无数据', image, imageStyle} = context.props |
||||
const prefixCls = 'ant-empty' |
||||
const cls = {[prefixCls]: true} |
||||
render(h, context) { |
||||
const {description = '暂无数据', image, imageStyle} = context.props |
||||
const prefixCls = 'ant-empty' |
||||
const cls = {[prefixCls]: true} |
||||
|
||||
let imageNode = null |
||||
if (typeof image === 'string') { |
||||
imageNode = <img alt={description} src={image}/> |
||||
} |
||||
else { |
||||
imageNode = <DefaultEmptyImage/> |
||||
} |
||||
return ( |
||||
<div class={cls} {...{on: context.listeners}}> |
||||
<div class={`${prefixCls}-image`} style={imageStyle}>{imageNode}</div> |
||||
{description && <p class={`${prefixCls}-description`}>{description}</p>} |
||||
{context.children && <div class={`${prefixCls}-footer`}>{context.children}</div>} |
||||
</div> |
||||
) |
||||
let imageNode = null |
||||
if (typeof image === 'string') { |
||||
imageNode = <img alt={description} src={image}/> |
||||
} |
||||
else { |
||||
imageNode = <DefaultEmptyImage/> |
||||
} |
||||
return ( |
||||
<div class={cls} {...{on: context.listeners}}> |
||||
<div class={`${prefixCls}-image`} style={imageStyle}>{imageNode}</div> |
||||
{description && <p class={`${prefixCls}-description`}>{description}</p>} |
||||
{context.children && <div class={`${prefixCls}-footer`}>{context.children}</div>} |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" src="./style.scss"></style> |
||||
|
||||
@ -1,54 +1,54 @@ |
||||
<script type="text/jsx"> |
||||
import LoadingMask from '@/components/LoadingMask' |
||||
|
||||
export default { |
||||
name: "FormDialog", |
||||
|
||||
functional: true, |
||||
|
||||
props: { |
||||
value: Boolean, |
||||
title: String, |
||||
loading: {type: Boolean, default: true}, |
||||
top: {type: String, default: '50px'}, |
||||
width: {type: String, default: '30%'} |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {value, title, loading, top, width} = context.props |
||||
const l = context.listeners |
||||
|
||||
function onClose() { |
||||
l.input && l.input(false) |
||||
} |
||||
|
||||
return ( |
||||
<el-dialog |
||||
v-drag-dialog |
||||
visible={value} |
||||
custom-class="form-dialog" |
||||
top={top} |
||||
width={width} |
||||
on-close={onClose} |
||||
{...context.data} |
||||
> |
||||
<slot slot="title" name="title">{title}</slot> |
||||
|
||||
<div class="form-dialog-container"> |
||||
<LoadingMask show={loading}/> |
||||
<el-scrollbar> |
||||
<div class="form-dialog-body"> |
||||
{context.children} |
||||
</div> |
||||
<div style="margin-bottom: 17px"/> |
||||
</el-scrollbar> |
||||
</div> |
||||
|
||||
<slot slot="footer" name="footer"/> |
||||
</el-dialog> |
||||
) |
||||
import LoadingMask from '@/components/LoadingMask' |
||||
|
||||
export default { |
||||
name: "FormDialog", |
||||
|
||||
functional: true, |
||||
|
||||
props: { |
||||
value: Boolean, |
||||
title: String, |
||||
loading: {type: Boolean, default: true}, |
||||
top: {type: String, default: '50px'}, |
||||
width: {type: String, default: '30%'} |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {value, title, loading, top, width} = context.props |
||||
const l = context.listeners |
||||
|
||||
function onClose() { |
||||
l.input && l.input(false) |
||||
} |
||||
|
||||
return ( |
||||
<el-dialog |
||||
v-drag-dialog |
||||
visible={value} |
||||
custom-class="form-dialog" |
||||
top={top} |
||||
width={width} |
||||
on-close={onClose} |
||||
{...context.data} |
||||
> |
||||
<slot slot="title" name="title">{title}</slot> |
||||
|
||||
<div class="form-dialog-container"> |
||||
<LoadingMask show={loading}/> |
||||
<el-scrollbar> |
||||
<div class="form-dialog-body"> |
||||
{context.children} |
||||
</div> |
||||
<div style="margin-bottom: 17px"/> |
||||
</el-scrollbar> |
||||
</div> |
||||
|
||||
<slot slot="footer" name="footer"/> |
||||
</el-dialog> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" src="./style.scss"></style> |
||||
|
||||
@ -1,23 +1,23 @@ |
||||
<script type="text/jsx"> |
||||
//从muse-ui处搬运,https://muse-ui.org/#/zh-CN/progress |
||||
export default { |
||||
name: "LinerProgress", |
||||
//从muse-ui处搬运,https://muse-ui.org/#/zh-CN/progress |
||||
export default { |
||||
name: "LinerProgress", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: {show: Boolean}, |
||||
props: {show: Boolean}, |
||||
|
||||
render(h, context) { |
||||
if (context.props.show) { |
||||
return ( |
||||
<div class="liner-progress"> |
||||
<div class="liner-progress-background"/> |
||||
<div class="linear-progress-indeterminate"/> |
||||
</div> |
||||
) |
||||
} |
||||
render(h, context) { |
||||
if (context.props.show) { |
||||
return ( |
||||
<div class="liner-progress"> |
||||
<div class="liner-progress-background"/> |
||||
<div class="linear-progress-indeterminate"/> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" src="./style.scss"></style> |
||||
|
||||
@ -1,24 +1,24 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: "LoadingMask", |
||||
export default { |
||||
name: "LoadingMask", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: {show: Boolean}, |
||||
props: {show: Boolean}, |
||||
|
||||
render(h, context) { |
||||
if (context.props.show) { |
||||
return ( |
||||
<div class="el-loading-mask"> |
||||
<div class="el-loading-spinner"> |
||||
<svg viewBox="25 25 50 50" class="circular"> |
||||
<circle cx="50" cy="50" r="20" fill="none" class="path"/> |
||||
</svg> |
||||
<p>加载中...</p> |
||||
</div> |
||||
render(h, context) { |
||||
if (context.props.show) { |
||||
return ( |
||||
<div class="el-loading-mask"> |
||||
<div class="el-loading-spinner"> |
||||
<svg viewBox="25 25 50 50" class="circular"> |
||||
<circle cx="50" cy="50" r="20" fill="none" class="path"/> |
||||
</svg> |
||||
<p>加载中...</p> |
||||
</div> |
||||
) |
||||
} |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,84 +1,84 @@ |
||||
<script type="text/jsx"> |
||||
import {store, init} from '../store' |
||||
import FormDialog from "@/components/FormDialog" |
||||
import {createLimitTree, getNodeId} from "@/utils/tree" |
||||
import {store, init} from '../store' |
||||
import FormDialog from "@/components/FormDialog" |
||||
import {createLimitTree, getNodeId} from "@/utils/tree" |
||||
|
||||
export default { |
||||
components: {FormDialog}, |
||||
export default { |
||||
components: {FormDialog}, |
||||
|
||||
data() { |
||||
return { |
||||
loading: false, |
||||
visible: false, |
||||
handler: null, |
||||
limitTree: [], |
||||
getChildrenOnSelect: false, |
||||
limit: false, |
||||
limitApi: null |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
regionTree() { |
||||
return store.data |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
loading: false, |
||||
visible: false, |
||||
handler: null, |
||||
limitTree: [], |
||||
getChildrenOnSelect: false, |
||||
limit: false, |
||||
limitApi: null |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
closeDialog() { |
||||
this.visible = false |
||||
}, |
||||
computed: { |
||||
regionTree() { |
||||
return store.data |
||||
} |
||||
}, |
||||
|
||||
nodeClick(obj) { |
||||
const payload = [obj] |
||||
methods: { |
||||
closeDialog() { |
||||
this.visible = false |
||||
}, |
||||
|
||||
if (this.getChildrenOnSelect) { |
||||
const ids = getNodeId(obj.children) |
||||
ids.unshift(obj.id) |
||||
payload.push(ids) |
||||
} |
||||
nodeClick(obj) { |
||||
const payload = [obj] |
||||
|
||||
this.handler(payload) |
||||
if (this.getChildrenOnSelect) { |
||||
const ids = getNodeId(obj.children) |
||||
ids.unshift(obj.id) |
||||
payload.push(ids) |
||||
} |
||||
|
||||
this.closeDialog() |
||||
}, |
||||
this.handler(payload) |
||||
|
||||
init() { |
||||
this.loading = true |
||||
const hasInit = this.regionTree.length > 0 |
||||
const promise = () => hasInit ? Promise.resolve() : init() |
||||
return promise() |
||||
.then(() => this.limit ? this.limitApi() : Promise.resolve()) |
||||
.then(data => data && (this.limitTree = createLimitTree(this.regionTree, data))) |
||||
.finally(() => this.loading = false) |
||||
} |
||||
this.closeDialog() |
||||
}, |
||||
|
||||
render() { |
||||
return ( |
||||
<form-dialog |
||||
v-model={this.visible} |
||||
directives={[{name: 'drag-dialog'}]} |
||||
class="tree-dialog" |
||||
title="选择行政区域" |
||||
loading={this.loading} |
||||
appendToBody={true} |
||||
v-on:close={this.closeDialog} |
||||
init() { |
||||
this.loading = true |
||||
const hasInit = this.regionTree.length > 0 |
||||
const promise = () => hasInit ? Promise.resolve() : init() |
||||
return promise() |
||||
.then(() => this.limit ? this.limitApi() : Promise.resolve()) |
||||
.then(data => data && (this.limitTree = createLimitTree(this.regionTree, data))) |
||||
.finally(() => this.loading = false) |
||||
} |
||||
}, |
||||
|
||||
render() { |
||||
return ( |
||||
<form-dialog |
||||
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} |
||||
> |
||||
<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"> |
||||
{({data}) => ( |
||||
<span className="el-tree-node__label"> |
||||
{this.limit ? data.name + `(${data.value})` : data.name} |
||||
</span> |
||||
)} |
||||
</el-tree> |
||||
</form-dialog> |
||||
) |
||||
} |
||||
)} |
||||
</el-tree> |
||||
</form-dialog> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,79 +1,79 @@ |
||||
<script type="text/jsx"> |
||||
import Vue from 'vue' |
||||
import TreeDialog from "./TreeDialog" |
||||
import common from '../mixin' |
||||
import Vue from 'vue' |
||||
import TreeDialog from "./TreeDialog" |
||||
import common from '../mixin' |
||||
|
||||
const TreeDialogConstructor = Vue.extend(TreeDialog) |
||||
const TreeDialogConstructor = Vue.extend(TreeDialog) |
||||
|
||||
let limit, full |
||||
let limit, full |
||||
|
||||
export default { |
||||
name: "RegionTreeSelector", |
||||
export default { |
||||
name: "RegionTreeSelector", |
||||
|
||||
mixins: [common], |
||||
mixins: [common], |
||||
|
||||
data() { |
||||
return { |
||||
dialogVisible: false, |
||||
limitTree: [] |
||||
data() { |
||||
return { |
||||
dialogVisible: false, |
||||
limitTree: [] |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
openDialog() { |
||||
if (this.limit) { |
||||
this.initLimit() |
||||
return limit.visible = true |
||||
} |
||||
else { |
||||
!full && this.initFull() |
||||
full.visible = true |
||||
} |
||||
}, |
||||
|
||||
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.getChildrenOnSelect = this.getChildrenOnSelect |
||||
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) |
||||
handler(payload) { |
||||
this.$emit('select', ...payload) |
||||
}, |
||||
|
||||
initLimit() { |
||||
if (limit) { |
||||
limit.getChildrenOnSelect = this.getChildrenOnSelect |
||||
limit.limit = this.limit |
||||
limit.limitApi = this.limitApi |
||||
return limit.init() |
||||
}, |
||||
} |
||||
|
||||
initFull() { |
||||
if (full) { |
||||
return full.getChildrenOnSelect = this.getChildrenOnSelect |
||||
} |
||||
const data = {...this.$props, handler: this.handler} |
||||
limit = new TreeDialogConstructor({data}).$mount() |
||||
document.body.appendChild(limit.$el) |
||||
|
||||
const data = {getChildrenOnSelect: this.getChildrenOnSelect, handler: this.handler} |
||||
full = new TreeDialogConstructor({data}).$mount() |
||||
document.body.appendChild(full.$el) |
||||
return limit.init() |
||||
}, |
||||
|
||||
return full.init() |
||||
initFull() { |
||||
if (full) { |
||||
return full.getChildrenOnSelect = this.getChildrenOnSelect |
||||
} |
||||
}, |
||||
|
||||
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} |
||||
/> |
||||
) |
||||
const data = {getChildrenOnSelect: this.getChildrenOnSelect, 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> |
||||
|
||||
@ -1,18 +1,18 @@ |
||||
<script> |
||||
const Tree = () => import('./Tree').then(_ => _.default) |
||||
const Tab = () => import('./Tab').then(_ => _.default) |
||||
const Tree = () => import('./Tree').then(_ => _.default) |
||||
const Tab = () => import('./Tab').then(_ => _.default) |
||||
|
||||
export default { |
||||
name: "RegionSelector", |
||||
export default { |
||||
name: "RegionSelector", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
type: {type: String, default: 'tab', validator: v => ['tree', 'tab'].includes(v)} |
||||
}, |
||||
props: { |
||||
type: {type: String, default: 'tab', validator: v => ['tree', 'tab'].includes(v)} |
||||
}, |
||||
|
||||
render(h, context) { |
||||
return h(context.props.type === 'tree' ? Tree : Tab, context.data) |
||||
} |
||||
render(h, context) { |
||||
return h(context.props.type === 'tree' ? Tree : Tab, context.data) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,24 +1,24 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: "SearchFormItem", |
||||
export default { |
||||
name: "SearchFormItem", |
||||
|
||||
inject: { |
||||
searchForm: { |
||||
default: () => ({width: 'auto'}) |
||||
} |
||||
}, |
||||
inject: { |
||||
searchForm: { |
||||
default: () => ({width: 'auto'}) |
||||
} |
||||
}, |
||||
|
||||
props: {label: String}, |
||||
props: {label: String}, |
||||
|
||||
render() { |
||||
const {width} = this.searchForm |
||||
return ( |
||||
<el-col style={{width}} span={100}> |
||||
<el-form-item label={this.label}> |
||||
{this.$slots.default} |
||||
</el-form-item> |
||||
</el-col> |
||||
) |
||||
} |
||||
render() { |
||||
const {width} = this.searchForm |
||||
return ( |
||||
<el-col style={{width}} span={100}> |
||||
<el-form-item label={this.label}> |
||||
{this.$slots.default} |
||||
</el-form-item> |
||||
</el-col> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,124 +1,124 @@ |
||||
<script type="text/jsx"> |
||||
import {getElementInnerWidth} from '@/utils/browser' |
||||
import {getElementInnerWidth} from '@/utils/browser' |
||||
|
||||
export default { |
||||
name: "SearchForm", |
||||
export default { |
||||
name: "SearchForm", |
||||
|
||||
provide() { |
||||
return { |
||||
searchForm: this |
||||
} |
||||
}, |
||||
|
||||
props: { |
||||
labelWidth: {type: String, default: '120px'}, |
||||
xs: {type: Number, default: 1},// <768px |
||||
sm: {type: Number, default: 2},// >=768px |
||||
md: {type: Number, default: 3}, // >=998px |
||||
lg: {type: Number, default: 4} // >=1200px |
||||
}, |
||||
|
||||
data() { |
||||
return { |
||||
showCollapse: false, |
||||
collapse: true, |
||||
num: 0, |
||||
width: 'auto' |
||||
} |
||||
}, |
||||
provide() { |
||||
return { |
||||
searchForm: this |
||||
} |
||||
}, |
||||
|
||||
props: { |
||||
labelWidth: {type: String, default: '120px'}, |
||||
xs: {type: Number, default: 1},// <768px |
||||
sm: {type: Number, default: 2},// >=768px |
||||
md: {type: Number, default: 3}, // >=998px |
||||
lg: {type: Number, default: 4} // >=1200px |
||||
}, |
||||
|
||||
data() { |
||||
return { |
||||
showCollapse: false, |
||||
collapse: true, |
||||
num: 0, |
||||
width: 'auto' |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
handleCollapse() { |
||||
this.collapse = !this.collapse |
||||
}, |
||||
|
||||
getElementNumInRow() { |
||||
const vw = getElementInnerWidth(this.$el.parentNode) |
||||
|
||||
if (vw < 768) return this.xs |
||||
else if (vw < 998) return this.sm |
||||
else if (vw < 1200) return this.md |
||||
return this.lg |
||||
}, |
||||
|
||||
resize() { |
||||
const num = this.getElementNumInRow() |
||||
this.num = num |
||||
this.width = `${100 / num}%` |
||||
this.showCollapse = num < this.$slots.default.length |
||||
} |
||||
methods: { |
||||
handleCollapse() { |
||||
this.collapse = !this.collapse |
||||
}, |
||||
|
||||
mounted() { |
||||
this.resize() |
||||
getElementNumInRow() { |
||||
const vw = getElementInnerWidth(this.$el.parentNode) |
||||
|
||||
window.addEventListener('resize', this.resize) |
||||
|
||||
this.$once('hook:beforeDestroy', () => { |
||||
window.removeEventListener('resize', this.resize) |
||||
}) |
||||
if (vw < 768) return this.xs |
||||
else if (vw < 998) return this.sm |
||||
else if (vw < 1200) return this.md |
||||
return this.lg |
||||
}, |
||||
|
||||
render() { |
||||
const slots = this.$slots.default |
||||
const collapseChildren = [] |
||||
if (this.showCollapse) { |
||||
//此处直接对el-row使用v-show会无效 |
||||
collapseChildren.push( |
||||
<el-collapse-transition> |
||||
<div v-show={!this.collapse}> |
||||
<el-row gutter={20}> |
||||
{slots.slice(this.num)} |
||||
</el-row> |
||||
</div> |
||||
</el-collapse-transition> |
||||
) |
||||
const collapseSlot = this.$scopedSlots.collapse |
||||
|
||||
collapseChildren.push( |
||||
collapseSlot ? |
||||
collapseSlot({collapse: this.collapse, handle: this.handleCollapse}) |
||||
: |
||||
<div class="searchForm__collapse"> |
||||
<el-button |
||||
type="text" |
||||
size="mini" |
||||
icon={this.collapse ? 'el-icon-arrow-down' : 'el-icon-arrow-up'} |
||||
on-click={this.handleCollapse} |
||||
> |
||||
{this.collapse ? '展开' : '收起'} |
||||
</el-button> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<el-form class="searchForm" label-position="right" label-width={this.labelWidth} size="small"> |
||||
<el-row gutter={20}> |
||||
{this.num > slots.length ? slots : slots.slice(0, this.num)} |
||||
</el-row> |
||||
{this.showCollapse && collapseChildren} |
||||
</el-form> |
||||
resize() { |
||||
const num = this.getElementNumInRow() |
||||
this.num = num |
||||
this.width = `${100 / num}%` |
||||
this.showCollapse = num < this.$slots.default.length |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
this.resize() |
||||
|
||||
window.addEventListener('resize', this.resize) |
||||
|
||||
this.$once('hook:beforeDestroy', () => { |
||||
window.removeEventListener('resize', this.resize) |
||||
}) |
||||
}, |
||||
|
||||
render() { |
||||
const slots = this.$slots.default |
||||
const collapseChildren = [] |
||||
if (this.showCollapse) { |
||||
//此处直接对el-row使用v-show会无效 |
||||
collapseChildren.push( |
||||
<el-collapse-transition> |
||||
<div v-show={!this.collapse}> |
||||
<el-row gutter={20}> |
||||
{slots.slice(this.num)} |
||||
</el-row> |
||||
</div> |
||||
</el-collapse-transition> |
||||
) |
||||
const collapseSlot = this.$scopedSlots.collapse |
||||
|
||||
collapseChildren.push( |
||||
collapseSlot ? |
||||
collapseSlot({collapse: this.collapse, handle: this.handleCollapse}) |
||||
: |
||||
<div class="searchForm__collapse"> |
||||
<el-button |
||||
type="text" |
||||
size="mini" |
||||
icon={this.collapse ? 'el-icon-arrow-down' : 'el-icon-arrow-up'} |
||||
on-click={this.handleCollapse} |
||||
> |
||||
{this.collapse ? '展开' : '收起'} |
||||
</el-button> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<el-form class="searchForm" label-position="right" label-width={this.labelWidth} size="small"> |
||||
<el-row gutter={20}> |
||||
{this.num > slots.length ? slots : slots.slice(0, this.num)} |
||||
</el-row> |
||||
{this.showCollapse && collapseChildren} |
||||
</el-form> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.searchForm { |
||||
margin-bottom: -15px; |
||||
.searchForm { |
||||
margin-bottom: -15px; |
||||
|
||||
> .el-row { |
||||
display: flex; |
||||
flex-direction: row; |
||||
flex-wrap: wrap; |
||||
} |
||||
> .el-row { |
||||
display: flex; |
||||
flex-direction: row; |
||||
flex-wrap: wrap; |
||||
} |
||||
|
||||
.searchForm__collapse { |
||||
position: relative; |
||||
bottom: 20px; |
||||
margin: 0 auto; |
||||
text-align: center; |
||||
} |
||||
.searchForm__collapse { |
||||
position: relative; |
||||
bottom: 20px; |
||||
margin: 0 auto; |
||||
text-align: center; |
||||
} |
||||
} |
||||
</style> |
||||
|
||||
@ -1,75 +1,75 @@ |
||||
<script type="text/jsx"> |
||||
//样式搬的https://antdv.com/components/skeleton-cn/ |
||||
import QSkeleton from './index' |
||||
//样式搬的https://antdv.com/components/skeleton-cn/ |
||||
import QSkeleton from './index' |
||||
|
||||
export default { |
||||
name: "PageSkeleton", |
||||
export default { |
||||
name: "PageSkeleton", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
render() { |
||||
const single = ( |
||||
<div class="page-skeleton"> |
||||
<div class="page-skeleton__header"> |
||||
<QSkeleton type="circle"/> |
||||
</div> |
||||
<div class="page-skeleton__content"> |
||||
<QSkeleton tag="h3"/> |
||||
<ul class="page-skeleton__paragraph">{new Array(3).fill(<QSkeleton/>)}</ul> |
||||
</div> |
||||
render() { |
||||
const single = ( |
||||
<div class="page-skeleton"> |
||||
<div class="page-skeleton__header"> |
||||
<QSkeleton type="circle"/> |
||||
</div> |
||||
) |
||||
<div class="page-skeleton__content"> |
||||
<QSkeleton tag="h3"/> |
||||
<ul class="page-skeleton__paragraph">{new Array(3).fill(<QSkeleton/>)}</ul> |
||||
</div> |
||||
</div> |
||||
) |
||||
|
||||
return <div class="page-skeleton-wrapper">{new Array(4).fill(single)}</div> |
||||
} |
||||
return <div class="page-skeleton-wrapper">{new Array(4).fill(single)}</div> |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
@import "~@/assets/styles/variables.scss"; |
||||
@import "~@/assets/styles/variables.scss"; |
||||
|
||||
.page-skeleton-wrapper { |
||||
margin: $page-view-margin; |
||||
} |
||||
|
||||
.page-skeleton-wrapper { |
||||
margin: $page-view-margin; |
||||
.page-skeleton { |
||||
width: 100%; |
||||
display: table; |
||||
|
||||
& + & { |
||||
margin-top: 20px; |
||||
} |
||||
|
||||
.page-skeleton { |
||||
width: 100%; |
||||
display: table; |
||||
&__header, |
||||
&__content { |
||||
display: table-cell; |
||||
} |
||||
|
||||
& + & { |
||||
margin-top: 20px; |
||||
} |
||||
&__header { |
||||
padding-top: 2px; |
||||
padding-right: 16px; |
||||
vertical-align: top; |
||||
} |
||||
|
||||
&__header, |
||||
&__content { |
||||
display: table-cell; |
||||
} |
||||
&__content { |
||||
width: 100%; |
||||
|
||||
&__header { |
||||
padding-top: 2px; |
||||
padding-right: 16px; |
||||
vertical-align: top; |
||||
> .q-skeleton { |
||||
width: 50%; |
||||
} |
||||
|
||||
&__content { |
||||
width: 100%; |
||||
.page-skeleton__paragraph { |
||||
margin-top: 28px; |
||||
padding: 0; |
||||
|
||||
> .q-skeleton { |
||||
width: 50%; |
||||
.q-skeleton:last-child:not(:first-child):not(:nth-child(2)) { |
||||
width: 61%; |
||||
} |
||||
|
||||
.page-skeleton__paragraph { |
||||
margin-top: 28px; |
||||
padding: 0; |
||||
|
||||
.q-skeleton:last-child:not(:first-child):not(:nth-child(2)) { |
||||
width: 61%; |
||||
} |
||||
|
||||
.q-skeleton + .q-skeleton { |
||||
margin-top: 16px; |
||||
} |
||||
.q-skeleton + .q-skeleton { |
||||
margin-top: 16px; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
</style> |
||||
|
||||
@ -1,50 +1,50 @@ |
||||
<script type="text/jsx"> |
||||
//由quasar处搬运,https://quasar.dev/vue-components/skeleton |
||||
import {skeletonAnimations, skeletonTypes} from './constant' |
||||
//由quasar处搬运,https://quasar.dev/vue-components/skeleton |
||||
import {skeletonAnimations, skeletonTypes} from './constant' |
||||
|
||||
export default { |
||||
name: 'QSkeleton', |
||||
export default { |
||||
name: 'QSkeleton', |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
type: { |
||||
type: String, |
||||
validator: v => skeletonTypes.includes(v), |
||||
default: 'text' |
||||
}, |
||||
animation: { |
||||
type: String, |
||||
validator: v => skeletonAnimations.includes(v), |
||||
default: 'wave' |
||||
}, |
||||
className: String, |
||||
tag: {type: String, default: 'div'}, |
||||
dark: Boolean, |
||||
square: Boolean, |
||||
bordered: Boolean, |
||||
size: String, |
||||
width: String, |
||||
height: String |
||||
props: { |
||||
type: { |
||||
type: String, |
||||
validator: v => skeletonTypes.includes(v), |
||||
default: 'text' |
||||
}, |
||||
animation: { |
||||
type: String, |
||||
validator: v => skeletonAnimations.includes(v), |
||||
default: 'wave' |
||||
}, |
||||
className: String, |
||||
tag: {type: String, default: 'div'}, |
||||
dark: Boolean, |
||||
square: Boolean, |
||||
bordered: Boolean, |
||||
size: String, |
||||
width: String, |
||||
height: String |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {type, animation, className, tag, dark, square, bordered, size, width, height} = context.props |
||||
const style = size !== undefined ? {width: size, height: size} : {width, height} |
||||
const classes = `q-skeleton--${dark === true ? 'dark' : 'light'} q-skeleton--type-${type}` + |
||||
(animation !== 'none' ? ` q-skeleton--anim-${animation}` : '') + |
||||
(square === true ? ' q-skeleton--square' : '') + |
||||
(bordered === true ? ' q-skeleton--bordered' : '') + |
||||
(className !== undefined ? ' ' + className : '') |
||||
render(h, context) { |
||||
const {type, animation, className, tag, dark, square, bordered, size, width, height} = context.props |
||||
const style = size !== undefined ? {width: size, height: size} : {width, height} |
||||
const classes = `q-skeleton--${dark === true ? 'dark' : 'light'} q-skeleton--type-${type}` + |
||||
(animation !== 'none' ? ` q-skeleton--anim-${animation}` : '') + |
||||
(square === true ? ' q-skeleton--square' : '') + |
||||
(bordered === true ? ' q-skeleton--bordered' : '') + |
||||
(className !== undefined ? ' ' + className : '') |
||||
|
||||
return h(tag, { |
||||
staticClass: 'q-skeleton', |
||||
class: classes, |
||||
style, |
||||
on: context.$listeners |
||||
}, context.children) |
||||
} |
||||
return h(tag, { |
||||
staticClass: 'q-skeleton', |
||||
class: classes, |
||||
style, |
||||
on: context.$listeners |
||||
}, context.children) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" src="./style.scss"></style> |
||||
|
||||
@ -1,50 +1,50 @@ |
||||
<script type="text/jsx"> |
||||
import {isExternal} from '@/utils/validate' |
||||
import {isExternal} from '@/utils/validate' |
||||
|
||||
export default { |
||||
name: 'SvgIcon', |
||||
export default { |
||||
name: 'SvgIcon', |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
icon: String, |
||||
className: String |
||||
}, |
||||
props: { |
||||
icon: String, |
||||
className: String |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {listeners} = context |
||||
const {icon, className} = context.props |
||||
const c = 'svg-icon ' + (className || '') |
||||
if (isExternal(icon)) { |
||||
const style = { |
||||
mask: `url(${this.icon}) no-repeat 50% 50%`, |
||||
'-webkit-mask': `url(${this.icon}) no-repeat 50% 50%` |
||||
} |
||||
return <div {...{on: listeners}} class={'svg-external-icon ' + c} style={style}/> |
||||
} |
||||
else { |
||||
const iconName = `#icon-${icon}` |
||||
return ( |
||||
<svg {...{on: listeners}} class={c} aria-hidden="true"> |
||||
<use href={iconName}/> |
||||
</svg> |
||||
) |
||||
render(h, context) { |
||||
const {listeners} = context |
||||
const {icon, className} = context.props |
||||
const c = 'svg-icon ' + (className || '') |
||||
if (isExternal(icon)) { |
||||
const style = { |
||||
mask: `url(${this.icon}) no-repeat 50% 50%`, |
||||
'-webkit-mask': `url(${this.icon}) no-repeat 50% 50%` |
||||
} |
||||
return <div {...{on: listeners}} class={'svg-external-icon ' + c} style={style}/> |
||||
} |
||||
else { |
||||
const iconName = `#icon-${icon}` |
||||
return ( |
||||
<svg {...{on: listeners}} class={c} aria-hidden="true"> |
||||
<use href={iconName}/> |
||||
</svg> |
||||
) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style> |
||||
.svg-icon { |
||||
width: 1em; |
||||
height: 1em; |
||||
fill: currentColor; |
||||
overflow: hidden; |
||||
} |
||||
.svg-icon { |
||||
width: 1em; |
||||
height: 1em; |
||||
fill: currentColor; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.svg-external-icon { |
||||
background-color: currentColor; |
||||
mask-size: cover !important; |
||||
display: inline-block; |
||||
} |
||||
.svg-external-icon { |
||||
background-color: currentColor; |
||||
mask-size: cover !important; |
||||
display: inline-block; |
||||
} |
||||
</style> |
||||
|
||||
@ -1,47 +1,47 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: "ZoomControl", |
||||
export default { |
||||
name: "ZoomControl", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
value: {type: Number, default: 100}, |
||||
step: {type: Number, default: 20}, |
||||
min: {type: Number, default: 10}, |
||||
max: {type: Number, default: 200} |
||||
}, |
||||
props: { |
||||
value: {type: Number, default: 100}, |
||||
step: {type: Number, default: 20}, |
||||
min: {type: Number, default: 10}, |
||||
max: {type: Number, default: 200} |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {value, step, max, min} = context.props |
||||
const input = context.listeners.input |
||||
render(h, context) { |
||||
const {value, step, max, min} = context.props |
||||
const input = context.listeners.input |
||||
|
||||
function zoomIn() { |
||||
const zoom = value + step |
||||
if (zoom <= max) input(zoom) |
||||
} |
||||
|
||||
function zoomOut() { |
||||
const zoom = value - step |
||||
if (zoom >= min) input(zoom) |
||||
} |
||||
function zoomIn() { |
||||
const zoom = value + step |
||||
if (zoom <= max) input(zoom) |
||||
} |
||||
|
||||
return ( |
||||
<div class="zoom-wrapper"> |
||||
<el-button circle icon="el-icon-minus" size="small" title="缩小" type="info" on-click={zoomOut}/> |
||||
<span class="zoom-number">{value}%</span> |
||||
<el-button circle icon="el-icon-plus" size="small" title="放大" type="info" on-click={zoomIn}/> |
||||
</div> |
||||
) |
||||
function zoomOut() { |
||||
const zoom = value - step |
||||
if (zoom >= min) input(zoom) |
||||
} |
||||
|
||||
return ( |
||||
<div class="zoom-wrapper"> |
||||
<el-button circle icon="el-icon-minus" size="small" title="缩小" type="info" on-click={zoomOut}/> |
||||
<span class="zoom-number">{value}%</span> |
||||
<el-button circle icon="el-icon-plus" size="small" title="放大" type="info" on-click={zoomIn}/> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style> |
||||
.zoom-wrapper .zoom-number { |
||||
color: #657180; |
||||
padding: 0 10px; |
||||
display: inline-block; |
||||
width: 60px; |
||||
text-align: center; |
||||
} |
||||
.zoom-wrapper .zoom-number { |
||||
color: #657180; |
||||
padding: 0 10px; |
||||
display: inline-block; |
||||
width: 60px; |
||||
text-align: center; |
||||
} |
||||
</style> |
||||
|
||||
@ -1,41 +1,41 @@ |
||||
<template> |
||||
<el-tree |
||||
ref="tree" |
||||
:data="data" |
||||
:expand-on-click-node="false" |
||||
:props="{label:'name'}" |
||||
:filter-node-method="filterNodeMethod" |
||||
highlight-current |
||||
node-key="id" |
||||
v-on="$listeners" |
||||
ref="tree" |
||||
:data="data" |
||||
:expand-on-click-node="false" |
||||
:props="{label:'name'}" |
||||
:filter-node-method="filterNodeMethod" |
||||
highlight-current |
||||
node-key="id" |
||||
v-on="$listeners" |
||||
/> |
||||
</template> |
||||
|
||||
<script> |
||||
import {getAll} from "@/api/system/category" |
||||
import {getAll} from "@/api/system/category" |
||||
|
||||
export default { |
||||
name: "CategoryTree", |
||||
export default { |
||||
name: "CategoryTree", |
||||
|
||||
props: {filterNodeMethod: Function}, |
||||
props: {filterNodeMethod: Function}, |
||||
|
||||
computed: { |
||||
data() { |
||||
return this.$store.state.dataCache.categoryTree |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
filter(v){ |
||||
this.$refs.tree.filter(v) |
||||
} |
||||
}, |
||||
computed: { |
||||
data() { |
||||
return this.$store.state.dataCache.categoryTree |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
if (this.data.length > 0) return |
||||
getAll() |
||||
.then(data => this.$store.commit('dataCache/categories', data)) |
||||
.finally(() => this.loading = false) |
||||
methods: { |
||||
filter(v) { |
||||
this.$refs.tree.filter(v) |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
if (this.data.length > 0) return |
||||
getAll() |
||||
.then(data => this.$store.commit('dataCache/categories', data)) |
||||
.finally(() => this.loading = false) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,40 +1,40 @@ |
||||
<template> |
||||
<el-select :value="value" :disabled="disabled" multiple size="small" @input="emit"> |
||||
<el-option |
||||
v-for="user in data" |
||||
:key="user.id" |
||||
:label="user.name" |
||||
:value="user.id" |
||||
v-for="user in data" |
||||
:key="user.id" |
||||
:label="user.name" |
||||
:value="user.id" |
||||
/> |
||||
</el-select> |
||||
</template> |
||||
|
||||
<script> |
||||
import {getUsers} from '@/api/system/user' |
||||
import {getUsers} from '@/api/system/user' |
||||
|
||||
export default { |
||||
name: "SimpleMultipleUserSelector", |
||||
export default { |
||||
name: "SimpleMultipleUserSelector", |
||||
|
||||
props: { |
||||
value: Array, |
||||
disabled: Boolean |
||||
}, |
||||
props: { |
||||
value: Array, |
||||
disabled: Boolean |
||||
}, |
||||
|
||||
data() { |
||||
return { |
||||
data: [] |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
emit(v) { |
||||
this.$emit('input', v) |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
data: [] |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
getUsers({page: 1, pageSize: 9999}) |
||||
.then(({list}) => this.data = list) |
||||
methods: { |
||||
emit(v) { |
||||
this.$emit('input', v) |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
getUsers({page: 1, pageSize: 9999}) |
||||
.then(({list}) => this.data = list) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,29 +1,29 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: 'Hamburger', |
||||
export default { |
||||
name: 'Hamburger', |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: {active: Boolean}, |
||||
props: {active: Boolean}, |
||||
|
||||
render(h, context) { |
||||
const {active} = context.props |
||||
const className = {'navbar-icon': true, 'el-icon-s-unfold': true, 'hamburger': true, 'is-active': active} |
||||
return ( |
||||
<div {...context.data}> |
||||
<i class={className}/> |
||||
</div> |
||||
) |
||||
} |
||||
render(h, context) { |
||||
const {active} = context.props |
||||
const className = {'navbar-icon': true, 'el-icon-s-unfold': true, 'hamburger': true, 'is-active': active} |
||||
return ( |
||||
<div {...context.data}> |
||||
<i class={className}/> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style> |
||||
.hamburger { |
||||
font-size: 20px; |
||||
} |
||||
.hamburger { |
||||
font-size: 20px; |
||||
} |
||||
|
||||
.hamburger.is-active { |
||||
transform: rotate(180deg); |
||||
} |
||||
.hamburger.is-active { |
||||
transform: rotate(180deg); |
||||
} |
||||
</style> |
||||
|
||||
@ -1,26 +1,26 @@ |
||||
<script type="text/jsx"> |
||||
import {sidebarLogoUrl, title} from '@/config' |
||||
import {sidebarLogoUrl, title} from '@/config' |
||||
|
||||
export default { |
||||
name: 'SidebarLogo', |
||||
export default { |
||||
name: 'SidebarLogo', |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: {collapse: Boolean}, |
||||
props: {collapse: Boolean}, |
||||
|
||||
render(h, context) { |
||||
const {collapse} = context.props |
||||
render(h, context) { |
||||
const {collapse} = context.props |
||||
|
||||
const logoClass = {'sidebar-logo-container': true, 'collapse': collapse} |
||||
const logoClass = {'sidebar-logo-container': true, 'collapse': collapse} |
||||
|
||||
return ( |
||||
<div class={logoClass}> |
||||
<router-link class="sidebar-logo-link" to="/"> |
||||
{sidebarLogoUrl && <img src={sidebarLogoUrl} class="sidebar-logo"/>} |
||||
{!collapse && <h1 class="sidebar-title">{title}</h1>} |
||||
</router-link> |
||||
</div> |
||||
) |
||||
} |
||||
return ( |
||||
<div class={logoClass}> |
||||
<router-link class="sidebar-logo-link" to="/"> |
||||
{sidebarLogoUrl && <img src={sidebarLogoUrl} class="sidebar-logo"/>} |
||||
{!collapse && <h1 class="sidebar-title">{title}</h1>} |
||||
</router-link> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,72 +1,72 @@ |
||||
<script type="text/jsx"> |
||||
import SidebarItemContent from './SidebarItemContent' |
||||
import SidebarItemContent from './SidebarItemContent' |
||||
|
||||
function getOnlyChild(item) { |
||||
const children = item.children || [] |
||||
function getOnlyChild(item) { |
||||
const children = item.children || [] |
||||
|
||||
if (children.length === 1) return item.alwaysShow ? null : children[0] |
||||
if (children.length === 1) return item.alwaysShow ? null : children[0] |
||||
|
||||
if (children.length < 1) return {...item, path: undefined, children: undefined} |
||||
if (children.length < 1) return {...item, path: undefined, children: undefined} |
||||
|
||||
return null |
||||
} |
||||
return null |
||||
} |
||||
|
||||
function renderNode(h, {item, showParent, collapse}) { |
||||
let onlyOneChild = getOnlyChild(item) |
||||
function renderNode(h, {item, showParent, collapse}) { |
||||
let onlyOneChild = getOnlyChild(item) |
||||
|
||||
const showSingle = onlyOneChild && !onlyOneChild.children |
||||
const showSingle = onlyOneChild && !onlyOneChild.children |
||||
|
||||
if (showSingle) { |
||||
const {icon, title} = onlyOneChild.meta |
||||
if (showSingle) { |
||||
const {icon, title} = onlyOneChild.meta |
||||
|
||||
return ( |
||||
<el-menu-item index={onlyOneChild.fullPath}> |
||||
<SidebarItemContent icon={icon} title={title}/> |
||||
</el-menu-item> |
||||
) |
||||
} |
||||
else { |
||||
const {icon, title} = item.meta |
||||
return ( |
||||
<el-menu-item index={onlyOneChild.fullPath}> |
||||
<SidebarItemContent icon={icon} title={title}/> |
||||
</el-menu-item> |
||||
) |
||||
} |
||||
else { |
||||
const {icon, title} = item.meta |
||||
|
||||
let children = item.children.map(child => renderNode(h, { |
||||
item: child, |
||||
showParent, |
||||
collapse |
||||
})) |
||||
let children = item.children.map(child => renderNode(h, { |
||||
item: child, |
||||
showParent, |
||||
collapse |
||||
})) |
||||
|
||||
//弹出菜单如果包裹滚动条,则在触发mouseleave时,不会触发父菜单的mouseleave事件 |
||||
if (collapse) { |
||||
//弹出菜单显示父级信息 |
||||
if (showParent) { |
||||
children = [ |
||||
<div class="popover-menu__title el-menu-item"> |
||||
<SidebarItemContent icon={icon} title={title}/> |
||||
</div>, |
||||
<div class="el-menu el-menu--inline">{children}</div> |
||||
] |
||||
} |
||||
//弹出菜单如果包裹滚动条,则在触发mouseleave时,不会触发父菜单的mouseleave事件 |
||||
if (collapse) { |
||||
//弹出菜单显示父级信息 |
||||
if (showParent) { |
||||
children = [ |
||||
<div class="popover-menu__title el-menu-item"> |
||||
<SidebarItemContent icon={icon} title={title}/> |
||||
</div>, |
||||
<div class="el-menu el-menu--inline">{children}</div> |
||||
] |
||||
} |
||||
|
||||
return ( |
||||
<el-submenu index={item.fullPath} popper-append-to-body> |
||||
<SidebarItemContent slot="title" icon={icon} title={title}/> |
||||
{children} |
||||
</el-submenu> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<el-submenu index={item.fullPath} popper-append-to-body> |
||||
<SidebarItemContent slot="title" icon={icon} title={title}/> |
||||
{children} |
||||
</el-submenu> |
||||
) |
||||
} |
||||
} |
||||
|
||||
export default { |
||||
functional: true, |
||||
export default { |
||||
functional: true, |
||||
|
||||
props: { |
||||
item: Object, |
||||
showParent: Boolean, |
||||
collapse: Boolean |
||||
}, |
||||
props: { |
||||
item: Object, |
||||
showParent: Boolean, |
||||
collapse: Boolean |
||||
}, |
||||
|
||||
render(h, context) { |
||||
return renderNode(h, context.props) |
||||
} |
||||
render(h, context) { |
||||
return renderNode(h, context.props) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,31 +1,31 @@ |
||||
<script type="text/jsx"> |
||||
export default { |
||||
name: "SidebarItemContent", |
||||
export default { |
||||
name: "SidebarItemContent", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
icon: String, |
||||
title: String |
||||
}, |
||||
props: { |
||||
icon: String, |
||||
title: String |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {icon, title} = context.props |
||||
const vnodes = [] |
||||
render(h, context) { |
||||
const {icon, title} = context.props |
||||
const vnodes = [] |
||||
|
||||
let iconComponent |
||||
let iconComponent |
||||
|
||||
if (icon) { |
||||
const isElIcon = icon.startsWith('el-icon-') |
||||
iconComponent = isElIcon ? <i class={`svg-icon ${icon}`}/> : <svg-icon icon={icon}/> |
||||
} |
||||
else iconComponent = <span class="svg-icon"/> |
||||
if (icon) { |
||||
const isElIcon = icon.startsWith('el-icon-') |
||||
iconComponent = isElIcon ? <i class={`svg-icon ${icon}`}/> : <svg-icon icon={icon}/> |
||||
} |
||||
else iconComponent = <span class="svg-icon"/> |
||||
|
||||
vnodes.push(iconComponent) |
||||
vnodes.push(iconComponent) |
||||
|
||||
if (title) vnodes.push(<span slot="title" class="sidebar-item-content">{title}</span>) |
||||
if (title) vnodes.push(<span slot="title" class="sidebar-item-content">{title}</span>) |
||||
|
||||
return vnodes |
||||
} |
||||
return vnodes |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,149 +1,149 @@ |
||||
<script type="text/jsx"> |
||||
import {mapState} from 'vuex' |
||||
import Logo from './components/Logo' |
||||
import SidebarItem from './components/SidebarItem' |
||||
import {isExternal} from "@/utils/validate" |
||||
import {mapState} from 'vuex' |
||||
import Logo from './components/Logo' |
||||
import SidebarItem from './components/SidebarItem' |
||||
import {isExternal} from "@/utils/validate" |
||||
|
||||
export default { |
||||
name: 'sidebar', |
||||
export default { |
||||
name: 'sidebar', |
||||
|
||||
components: {SidebarItem, Logo}, |
||||
components: {SidebarItem, Logo}, |
||||
|
||||
data() { |
||||
return { |
||||
mouseOutside: true |
||||
data() { |
||||
return { |
||||
mouseOutside: true |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
...mapState('app', ['device']), |
||||
|
||||
...mapState('resource', ['sidebarMenus']), |
||||
|
||||
...mapState('setting', ['showLogo', 'sidebarCollapse', 'sidebarUniqueOpen', 'sidebarShowParent', 'sidebarAutoHidden']), |
||||
|
||||
activeMenu() { |
||||
const route = this.$route |
||||
const {meta, path} = route |
||||
if (path.startsWith('/redirect')) { |
||||
return path.replace('/redirect', '') |
||||
} |
||||
return meta.activeMenu || path |
||||
}, |
||||
|
||||
computed: { |
||||
...mapState('app', ['device']), |
||||
|
||||
...mapState('resource', ['sidebarMenus']), |
||||
|
||||
...mapState('setting', ['showLogo', 'sidebarCollapse', 'sidebarUniqueOpen', 'sidebarShowParent', 'sidebarAutoHidden']), |
||||
|
||||
activeMenu() { |
||||
const route = this.$route |
||||
const {meta, path} = route |
||||
if (path.startsWith('/redirect')) { |
||||
return path.replace('/redirect', '') |
||||
} |
||||
return meta.activeMenu || path |
||||
}, |
||||
|
||||
//仅在pc端可折叠 |
||||
collapse() { |
||||
return this.sidebarCollapse && this.device === 'pc' |
||||
}, |
||||
|
||||
hideSidebar() { |
||||
return this.sidebarAutoHidden && this.mouseOutside |
||||
|| this.sidebarCollapse && this.device === 'mobile' |
||||
}, |
||||
|
||||
sidebarClass() { |
||||
return { |
||||
'sidebar-container': true, |
||||
'mobile': this.device === 'mobile', |
||||
'collapse-sidebar': this.collapse, |
||||
'hide-sidebar': this.hideSidebar |
||||
} |
||||
} |
||||
//仅在pc端可折叠 |
||||
collapse() { |
||||
return this.sidebarCollapse && this.device === 'pc' |
||||
}, |
||||
|
||||
watch: { |
||||
//由于elMenu的initOpenedMenu()不会触发select事件,所以手动实现菜单收起 |
||||
'$route.path'(nv, ov) { |
||||
//如果是redirect刷新,则跳过 |
||||
if (nv === `/redirect${ov}`) return |
||||
const menu = this.$refs.menu |
||||
if (!menu) return |
||||
const item = menu.items[this.activeMenu] |
||||
if (!item) menu.openedMenus = [] |
||||
else this.select(item.index, item.indexPath, item, false) |
||||
}, |
||||
|
||||
hideSidebar(v) { |
||||
if (v) document.addEventListener('mousemove', this.moveEvent) |
||||
else document.removeEventListener('mousemove', this.moveEvent) |
||||
} |
||||
hideSidebar() { |
||||
return this.sidebarAutoHidden && this.mouseOutside |
||||
|| this.sidebarCollapse && this.device === 'mobile' |
||||
}, |
||||
|
||||
methods: { |
||||
moveEvent(e) { |
||||
if (e.clientX <= 15) this.mouseOutside = false |
||||
}, |
||||
|
||||
select(index, indexPath, item, jump = true) { |
||||
//开启手风琴模式时,激活没有子级的菜单时收起其它展开项 |
||||
if (this.sidebarUniqueOpen && indexPath.length === 1) { |
||||
const menu = this.$refs.menu |
||||
const opened = menu.openedMenus |
||||
opened.forEach(i => i !== index && menu.closeMenu(i)) |
||||
} |
||||
|
||||
//mobile时激活隐藏侧边栏 |
||||
this.device === 'mobile' && this.$store.commit('setting/sidebarCollapse', true) |
||||
|
||||
if (!jump) return |
||||
|
||||
//外部链接时打开新窗口 |
||||
if (isExternal(index)) { |
||||
return window.open(index) |
||||
} |
||||
|
||||
if (this.$route.path === index) { |
||||
this.$store.commit('tagsView/delCachedView', this.$route) |
||||
this.$nextTick(() => this.$router.replace({path: '/redirect' + this.$route.fullPath})) |
||||
} |
||||
else this.$router.push(index) |
||||
sidebarClass() { |
||||
return { |
||||
'sidebar-container': true, |
||||
'mobile': this.device === 'mobile', |
||||
'collapse-sidebar': this.collapse, |
||||
'hide-sidebar': this.hideSidebar |
||||
} |
||||
} |
||||
}, |
||||
|
||||
watch: { |
||||
//由于elMenu的initOpenedMenu()不会触发select事件,所以手动实现菜单收起 |
||||
'$route.path'(nv, ov) { |
||||
//如果是redirect刷新,则跳过 |
||||
if (nv === `/redirect${ov}`) return |
||||
const menu = this.$refs.menu |
||||
if (!menu) return |
||||
const item = menu.items[this.activeMenu] |
||||
if (!item) menu.openedMenus = [] |
||||
else this.select(item.index, item.indexPath, item, false) |
||||
}, |
||||
|
||||
mounted() { |
||||
if (this.sidebarAutoHidden) { |
||||
document.addEventListener('mousemove', this.moveEvent) |
||||
} |
||||
hideSidebar(v) { |
||||
if (v) document.addEventListener('mousemove', this.moveEvent) |
||||
else document.removeEventListener('mousemove', this.moveEvent) |
||||
} |
||||
}, |
||||
|
||||
this.$once('hook:beforeDestroy', () => { |
||||
document.removeEventListener('mousemove', this.moveEvent) |
||||
}) |
||||
methods: { |
||||
moveEvent(e) { |
||||
if (e.clientX <= 15) this.mouseOutside = false |
||||
}, |
||||
|
||||
render() { |
||||
const menu = ( |
||||
<el-menu |
||||
ref="menu" |
||||
collapse={this.collapse} |
||||
collapse-transition={false} |
||||
default-active={this.activeMenu} |
||||
unique-opened={this.sidebarUniqueOpen} |
||||
mode="vertical" |
||||
on-select={this.select} |
||||
> |
||||
{this.sidebarMenus.map(route => ( |
||||
<sidebar-item |
||||
key={route.path} |
||||
show-parent={this.sidebarShowParent} |
||||
collapse={this.sidebarCollapse} |
||||
item={route} |
||||
/> |
||||
))} |
||||
</el-menu> |
||||
) |
||||
|
||||
return ( |
||||
<aside |
||||
class={this.sidebarClass} |
||||
on-mouseenter={() => this.mouseOutside = false} |
||||
on-mouseleave={() => this.mouseOutside = true} |
||||
> |
||||
{this.showLogo && <logo collapse={this.collapse}/>} |
||||
{<el-scrollbar noresize native>{menu}</el-scrollbar>} |
||||
</aside> |
||||
) |
||||
select(index, indexPath, item, jump = true) { |
||||
//开启手风琴模式时,激活没有子级的菜单时收起其它展开项 |
||||
if (this.sidebarUniqueOpen && indexPath.length === 1) { |
||||
const menu = this.$refs.menu |
||||
const opened = menu.openedMenus |
||||
opened.forEach(i => i !== index && menu.closeMenu(i)) |
||||
} |
||||
|
||||
//mobile时激活隐藏侧边栏 |
||||
this.device === 'mobile' && this.$store.commit('setting/sidebarCollapse', true) |
||||
|
||||
if (!jump) return |
||||
|
||||
//外部链接时打开新窗口 |
||||
if (isExternal(index)) { |
||||
return window.open(index) |
||||
} |
||||
|
||||
if (this.$route.path === index) { |
||||
this.$store.commit('tagsView/delCachedView', this.$route) |
||||
this.$nextTick(() => this.$router.replace({path: '/redirect' + this.$route.fullPath})) |
||||
} |
||||
else this.$router.push(index) |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
if (this.sidebarAutoHidden) { |
||||
document.addEventListener('mousemove', this.moveEvent) |
||||
} |
||||
|
||||
this.$once('hook:beforeDestroy', () => { |
||||
document.removeEventListener('mousemove', this.moveEvent) |
||||
}) |
||||
}, |
||||
|
||||
render() { |
||||
const menu = ( |
||||
<el-menu |
||||
ref="menu" |
||||
collapse={this.collapse} |
||||
collapse-transition={false} |
||||
default-active={this.activeMenu} |
||||
unique-opened={this.sidebarUniqueOpen} |
||||
mode="vertical" |
||||
on-select={this.select} |
||||
> |
||||
{this.sidebarMenus.map(route => ( |
||||
<sidebar-item |
||||
key={route.path} |
||||
show-parent={this.sidebarShowParent} |
||||
collapse={this.sidebarCollapse} |
||||
item={route} |
||||
/> |
||||
))} |
||||
</el-menu> |
||||
) |
||||
|
||||
return ( |
||||
<aside |
||||
class={this.sidebarClass} |
||||
on-mouseenter={() => this.mouseOutside = false} |
||||
on-mouseleave={() => this.mouseOutside = true} |
||||
> |
||||
{this.showLogo && <logo collapse={this.collapse}/>} |
||||
{<el-scrollbar noresize native>{menu}</el-scrollbar>} |
||||
</aside> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" src="./style.scss"></style> |
||||
|
||||
@ -1,49 +1,49 @@ |
||||
<script type="text/jsx"> |
||||
const animations = [ |
||||
{name: '无', value: ''}, |
||||
{name: '烟花', value: 'firework'}, |
||||
{name: '上帝之光', value: 'godrays'}, |
||||
{name: '简单粒子', value: 'moveFollowMouse'}, |
||||
{name: '球', value: 'particleBall'}, |
||||
{name: '网络', value: 'particleNetwork'}, |
||||
{name: '波浪', value: 'particleWave'}, |
||||
{name: '雨', value: 'reflectRain'}, |
||||
{name: '流星雨', value: 'sparkRain'}, |
||||
{name: '阳光', value: 'sunlight'}, |
||||
] |
||||
const animations = [ |
||||
{name: '无', value: ''}, |
||||
{name: '烟花', value: 'firework'}, |
||||
{name: '上帝之光', value: 'godrays'}, |
||||
{name: '简单粒子', value: 'moveFollowMouse'}, |
||||
{name: '球', value: 'particleBall'}, |
||||
{name: '网络', value: 'particleNetwork'}, |
||||
{name: '波浪', value: 'particleWave'}, |
||||
{name: '雨', value: 'reflectRain'}, |
||||
{name: '流星雨', value: 'sparkRain'}, |
||||
{name: '阳光', value: 'sunlight'}, |
||||
] |
||||
|
||||
export default { |
||||
name: "SetAnimation", |
||||
export default { |
||||
name: "SetAnimation", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
value: String, |
||||
customClass: String |
||||
}, |
||||
props: { |
||||
value: String, |
||||
customClass: String |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {value, customClass} = context.props |
||||
return ( |
||||
<el-dropdown class={customClass} size="medium" trigger="click" onCommand={context.listeners.select}> |
||||
<el-button type="text" title="选择背景动画"> |
||||
<svg-icon icon="show"/> |
||||
</el-button> |
||||
<el-dropdown-menu slot="dropdown"> |
||||
{ |
||||
animations.map(animation => ( |
||||
<el-dropdown-item |
||||
key={animation.value} |
||||
disabled={animation.value === value} |
||||
command={animation.value} |
||||
> |
||||
{animation.name} |
||||
</el-dropdown-item> |
||||
)) |
||||
} |
||||
</el-dropdown-menu> |
||||
</el-dropdown> |
||||
) |
||||
} |
||||
render(h, context) { |
||||
const {value, customClass} = context.props |
||||
return ( |
||||
<el-dropdown class={customClass} size="medium" trigger="click" onCommand={context.listeners.select}> |
||||
<el-button type="text" title="选择背景动画"> |
||||
<svg-icon icon="show"/> |
||||
</el-button> |
||||
<el-dropdown-menu slot="dropdown"> |
||||
{ |
||||
animations.map(animation => ( |
||||
<el-dropdown-item |
||||
key={animation.value} |
||||
disabled={animation.value === value} |
||||
command={animation.value} |
||||
> |
||||
{animation.name} |
||||
</el-dropdown-item> |
||||
)) |
||||
} |
||||
</el-dropdown-menu> |
||||
</el-dropdown> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
@ -1,15 +1,15 @@ |
||||
<script> |
||||
export default { |
||||
created() { |
||||
const {params, query} = this.$route |
||||
//如果使用了name+params传参 |
||||
if ('params' in query) { |
||||
const {name, params} = JSON.parse(query.params) |
||||
this.$router.replace({name, params}) |
||||
} |
||||
else this.$router.replace({path: `/${params.path}`, query}) |
||||
}, |
||||
export default { |
||||
created() { |
||||
const {params, query} = this.$route |
||||
//如果使用了name+params传参 |
||||
if ('params' in query) { |
||||
const {name, params} = JSON.parse(query.params) |
||||
this.$router.replace({name, params}) |
||||
} |
||||
else this.$router.replace({path: `/${params.path}`, query}) |
||||
}, |
||||
|
||||
render: h => h() |
||||
} |
||||
render: h => h() |
||||
} |
||||
</script> |
||||
|
||||
@ -1,98 +1,98 @@ |
||||
<script type="text/jsx"> |
||||
import CountTo from 'vue-count-to' |
||||
import CountTo from 'vue-count-to' |
||||
|
||||
export default { |
||||
name: "PanelGroupItem", |
||||
export default { |
||||
name: "PanelGroupItem", |
||||
|
||||
functional: true, |
||||
functional: true, |
||||
|
||||
props: { |
||||
icon: String, |
||||
color: String, |
||||
text: String, |
||||
value: Number |
||||
}, |
||||
props: { |
||||
icon: String, |
||||
color: String, |
||||
text: String, |
||||
value: Number |
||||
}, |
||||
|
||||
render(h, context) { |
||||
const {icon, color, text, value} = context.props |
||||
return ( |
||||
<el-row class="card-panel" type="flex" justify="space-between" {...context.data}> |
||||
<div class="card-panel-icon-wrapper" style={'color:' + color}> |
||||
<svg-icon icon={icon} class-name="card-panel-icon"/> |
||||
</div> |
||||
<div class="card-panel-description"> |
||||
<div class="card-panel-text">{text}</div> |
||||
<CountTo class="card-panel-num" end-val={value} start-val={0}/> |
||||
</div> |
||||
</el-row> |
||||
) |
||||
} |
||||
render(h, context) { |
||||
const {icon, color, text, value} = context.props |
||||
return ( |
||||
<el-row class="card-panel" type="flex" justify="space-between" {...context.data}> |
||||
<div class="card-panel-icon-wrapper" style={'color:' + color}> |
||||
<svg-icon icon={icon} class-name="card-panel-icon"/> |
||||
</div> |
||||
<div class="card-panel-description"> |
||||
<div class="card-panel-text">{text}</div> |
||||
<CountTo class="card-panel-num" end-val={value} start-val={0}/> |
||||
</div> |
||||
</el-row> |
||||
) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.card-panel { |
||||
height: 108px; |
||||
cursor: pointer; |
||||
font-size: 12px; |
||||
position: relative; |
||||
overflow: hidden; |
||||
color: #666; |
||||
background: #fff; |
||||
border-radius: 4px; |
||||
-webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1); |
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1); |
||||
border-color: rgba(0, 0, 0, .05); |
||||
.card-panel { |
||||
height: 108px; |
||||
cursor: pointer; |
||||
font-size: 12px; |
||||
position: relative; |
||||
overflow: hidden; |
||||
color: #666; |
||||
background: #fff; |
||||
border-radius: 4px; |
||||
-webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1); |
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1); |
||||
border-color: rgba(0, 0, 0, .05); |
||||
|
||||
&:hover .card-panel-icon-wrapper { |
||||
color: #fff; |
||||
} |
||||
&:hover .card-panel-icon-wrapper { |
||||
color: #fff; |
||||
} |
||||
|
||||
.card-panel-icon-wrapper { |
||||
margin: 14px 0 14px 14px; |
||||
padding: 16px; |
||||
transition: all 0.38s ease-out; |
||||
border-radius: 6px; |
||||
} |
||||
.card-panel-icon-wrapper { |
||||
margin: 14px 0 14px 14px; |
||||
padding: 16px; |
||||
transition: all 0.38s ease-out; |
||||
border-radius: 6px; |
||||
} |
||||
|
||||
.card-panel-icon { |
||||
font-size: 48px; |
||||
} |
||||
.card-panel-icon { |
||||
font-size: 48px; |
||||
} |
||||
|
||||
.card-panel-description { |
||||
font-weight: bold; |
||||
margin: 26px 20px 0 26px; |
||||
.card-panel-description { |
||||
font-weight: bold; |
||||
margin: 26px 20px 0 26px; |
||||
|
||||
.card-panel-text { |
||||
line-height: 18px; |
||||
color: rgba(0, 0, 0, 0.45); |
||||
font-size: 16px; |
||||
margin-bottom: 12px; |
||||
} |
||||
.card-panel-text { |
||||
line-height: 18px; |
||||
color: rgba(0, 0, 0, 0.45); |
||||
font-size: 16px; |
||||
margin-bottom: 12px; |
||||
} |
||||
|
||||
.card-panel-num { |
||||
font-size: 20px; |
||||
} |
||||
.card-panel-num { |
||||
font-size: 20px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@media (max-width: 550px) { |
||||
.card-panel-description { |
||||
display: none; |
||||
} |
||||
@media (max-width: 550px) { |
||||
.card-panel-description { |
||||
display: none; |
||||
} |
||||
|
||||
.card-panel-icon-wrapper { |
||||
float: none !important; |
||||
width: 100%; |
||||
height: 100%; |
||||
margin: 0 !important; |
||||
.card-panel-icon-wrapper { |
||||
float: none !important; |
||||
width: 100%; |
||||
height: 100%; |
||||
margin: 0 !important; |
||||
|
||||
.svg-icon { |
||||
display: block; |
||||
margin: 14px auto !important; |
||||
float: none !important; |
||||
} |
||||
.svg-icon { |
||||
display: block; |
||||
margin: 14px auto !important; |
||||
float: none !important; |
||||
} |
||||
} |
||||
} |
||||
|
||||
</style> |
||||
|
||||
@ -1,88 +1,88 @@ |
||||
<template> |
||||
<tree-select |
||||
:value="value" |
||||
:data="tree" |
||||
:disabled="disabled" |
||||
:props="props" |
||||
size="small" |
||||
popper-append-to-body |
||||
@change="change" |
||||
@input="emit" |
||||
:value="value" |
||||
:data="tree" |
||||
:disabled="disabled" |
||||
:props="props" |
||||
size="small" |
||||
popper-append-to-body |
||||
@change="change" |
||||
@input="emit" |
||||
/> |
||||
</template> |
||||
|
||||
<script> |
||||
import TreeSelect from '@/components/TreeSelect' |
||||
import {getAll} from "@/api/system/category" |
||||
import {isEmpty} from "@/utils" |
||||
import {elError} from "@/utils/message" |
||||
import TreeSelect from '@/components/TreeSelect' |
||||
import {getAll} from "@/api/system/category" |
||||
import {isEmpty} from "@/utils" |
||||
import {elError} from "@/utils/message" |
||||
|
||||
export default { |
||||
name: "CategorySelector", |
||||
export default { |
||||
name: "CategorySelector", |
||||
|
||||
components: {TreeSelect}, |
||||
components: {TreeSelect}, |
||||
|
||||
props: { |
||||
value: {required: true}, |
||||
disabled: Boolean, |
||||
selected: {type: Array, default: () => []} |
||||
}, |
||||
props: { |
||||
value: {required: true}, |
||||
disabled: Boolean, |
||||
selected: {type: Array, default: () => []} |
||||
}, |
||||
|
||||
data() { |
||||
return { |
||||
props: { |
||||
label: 'name', |
||||
children: 'children', |
||||
disabled: '_disabled' |
||||
} |
||||
data() { |
||||
return { |
||||
props: { |
||||
label: 'name', |
||||
children: 'children', |
||||
disabled: '_disabled' |
||||
} |
||||
}, |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
tree() { |
||||
const arr = this.$store.state.dataCache.categoryTree |
||||
this.addDisabledAttr(arr) |
||||
return arr |
||||
}, |
||||
selectableCategories() { |
||||
return this.$store.state.dataCache.categories.filter(i => i.type === 1) |
||||
}, |
||||
computed: { |
||||
tree() { |
||||
const arr = this.$store.state.dataCache.categoryTree |
||||
this.addDisabledAttr(arr) |
||||
return arr |
||||
}, |
||||
selectableCategories() { |
||||
return this.$store.state.dataCache.categories.filter(i => i.type === 1) |
||||
}, |
||||
}, |
||||
|
||||
methods: { |
||||
init() { |
||||
if (this.tree.length === 0) { |
||||
getAll().then(data => this.$store.commit('dataCache/categories', data)) |
||||
} |
||||
}, |
||||
|
||||
addDisabledAttr(arr) { |
||||
arr.forEach(i => { |
||||
i._disabled = i.type !== 1 |
||||
i.children && this.addDisabledAttr(i.children) |
||||
}) |
||||
}, |
||||
methods: { |
||||
init() { |
||||
if (this.tree.length === 0) { |
||||
getAll().then(data => this.$store.commit('dataCache/categories', data)) |
||||
} |
||||
}, |
||||
|
||||
change(v) { |
||||
if (isEmpty(v) || this.selected.length <= 0) return |
||||
if (this.selected.includes(v)) { |
||||
elError('该分类已选择') |
||||
this.emit(null) |
||||
} |
||||
}, |
||||
addDisabledAttr(arr) { |
||||
arr.forEach(i => { |
||||
i._disabled = i.type !== 1 |
||||
i.children && this.addDisabledAttr(i.children) |
||||
}) |
||||
}, |
||||
|
||||
emit(v) { |
||||
this.$emit('input', v) |
||||
if (!isEmpty(v)) { |
||||
let item = this.selectableCategories.find(i => i.id === v) |
||||
this.$emit('get-name', item ? item.name : null) |
||||
} |
||||
else this.$emit('get-name', null) |
||||
change(v) { |
||||
if (isEmpty(v) || this.selected.length <= 0) return |
||||
if (this.selected.includes(v)) { |
||||
elError('该分类已选择') |
||||
this.emit(null) |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
this.init() |
||||
emit(v) { |
||||
this.$emit('input', v) |
||||
if (!isEmpty(v)) { |
||||
let item = this.selectableCategories.find(i => i.id === v) |
||||
this.$emit('get-name', item ? item.name : null) |
||||
} |
||||
else this.$emit('get-name', null) |
||||
} |
||||
}, |
||||
|
||||
mounted() { |
||||
this.init() |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue