@ -1,5 +1,5 @@ |
|||||||
import Vue from 'vue' |
import Vue from 'vue' |
||||||
import SvgIcon from '@/components/SvgIcon' |
import SvgIcon from '@/component/SvgIcon' |
||||||
|
|
||||||
Vue.component('svg-icon', SvgIcon) |
Vue.component('svg-icon', SvgIcon) |
||||||
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 605 B |
|
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 418 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 944 B |
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 669 B After Width: | Height: | Size: 669 B |
|
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 335 B |
|
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 623 B |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 522 B After Width: | Height: | Size: 522 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 440 B After Width: | Height: | Size: 440 B |
@ -0,0 +1,128 @@ |
|||||||
|
<template> |
||||||
|
<div class="form-anchor"> |
||||||
|
<div |
||||||
|
v-for="i in data" |
||||||
|
:key="i.ref" |
||||||
|
:class="{'active':i.ref === cur}" |
||||||
|
@click="() => click(i.ref)" |
||||||
|
> |
||||||
|
{{ i.label }} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import {debounce} from "@/util" |
||||||
|
import {getScroll, getOffsetTop, scrollTo} from "@/util/browser" |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "FormAnchor", |
||||||
|
|
||||||
|
props: { |
||||||
|
reference: Function, |
||||||
|
data: {type: Array, default: () => []}, |
||||||
|
offsetTop: Number, |
||||||
|
bounds: Number, |
||||||
|
getContainer: { |
||||||
|
type: Function, |
||||||
|
default: () => document.querySelector('#app .app-main>.el-scrollbar>.el-scrollbar__wrap') |
||||||
|
}, |
||||||
|
}, |
||||||
|
|
||||||
|
data: () => ({cur: null}), |
||||||
|
|
||||||
|
methods: { |
||||||
|
getCur(offsetTop = 0, bounds = 5) { |
||||||
|
const sections = [] |
||||||
|
const container = this.getContainer() |
||||||
|
this.data.forEach(({ref}) => { |
||||||
|
const target = this.getDomFromRef(ref) |
||||||
|
const top = getOffsetTop(target, container) |
||||||
|
if (top < offsetTop + bounds) { |
||||||
|
sections.push({ref, top}) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
if (sections.length) { |
||||||
|
const max = sections.reduce((prev, curr) => (curr.top > prev.top ? curr : prev)) |
||||||
|
return max.ref |
||||||
|
} |
||||||
|
return '' |
||||||
|
}, |
||||||
|
|
||||||
|
getDomFromRef(ref) { |
||||||
|
const refs = this.reference() |
||||||
|
return refs[ref]['$el'] || refs[ref][0]['$el'] |
||||||
|
}, |
||||||
|
|
||||||
|
click(ref) { |
||||||
|
if (!ref || this.animating || !this.reference) return |
||||||
|
this.cur = ref |
||||||
|
const {offsetTop, getContainer, targetOffset} = this |
||||||
|
const container = getContainer() |
||||||
|
const scrollTop = getScroll(container, true) |
||||||
|
const targetElement = this.getDomFromRef(ref) |
||||||
|
if (!targetElement) return |
||||||
|
const eleOffsetTop = getOffsetTop(targetElement, container) |
||||||
|
let y = scrollTop + eleOffsetTop |
||||||
|
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0 |
||||||
|
this.animating = true |
||||||
|
scrollTo(y, {callback: () => this.animating = false, getContainer}) |
||||||
|
}, |
||||||
|
|
||||||
|
scroll() { |
||||||
|
if (this.animating || !this.reference) return |
||||||
|
const {offsetTop, bounds, targetOffset} = this |
||||||
|
this.cur = this.getCur(targetOffset !== undefined ? targetOffset : offsetTop || 0, bounds) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
mounted() { |
||||||
|
this.scroll() |
||||||
|
|
||||||
|
const container = this.getContainer() |
||||||
|
this.handleScroll = debounce(this.scroll) |
||||||
|
|
||||||
|
container.addEventListener('scroll', this.handleScroll) |
||||||
|
this.$once('hook:beforeDestroy', () => { |
||||||
|
container.removeEventListener('scroll', this.handleScroll) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss"> |
||||||
|
@import "~@/asset/style/variables.scss"; |
||||||
|
|
||||||
|
.form-anchor { |
||||||
|
position: fixed; |
||||||
|
right: 0; |
||||||
|
top: 200px; |
||||||
|
min-width: 72px; |
||||||
|
overflow: hidden; |
||||||
|
border-radius: 4px 0 0 4px; |
||||||
|
background: #fff; |
||||||
|
box-shadow: 0 2px 12px 0 rgba(166, 167, 173, 0.5); |
||||||
|
z-index: 100; |
||||||
|
|
||||||
|
> div { |
||||||
|
width: inherit; |
||||||
|
height: 28px; |
||||||
|
line-height: 28px; |
||||||
|
text-align: center; |
||||||
|
color: #7C7E85; |
||||||
|
padding: 0 10px; |
||||||
|
font-size: 12px; |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
&:hover, |
||||||
|
&.active { |
||||||
|
color: $--color-primary |
||||||
|
} |
||||||
|
|
||||||
|
&:not(:last-child) { |
||||||
|
box-shadow: 0 1px 0 0 rgba(166, 167, 173, 0.1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,129 @@ |
|||||||
|
<template> |
||||||
|
<el-popover |
||||||
|
ref="popover" |
||||||
|
:disabled="error.length === 0" |
||||||
|
placement="top" |
||||||
|
width="256" |
||||||
|
trigger="click" |
||||||
|
popper-class="form-validate-info" |
||||||
|
> |
||||||
|
<div class="form-validate-info-title">表单校验信息</div> |
||||||
|
<div class="form-validate-info-content"> |
||||||
|
<div v-for="i in error" :key="i.prop" class="form-validate-info-item" @click="() => onClick(i)"> |
||||||
|
<i class="el-icon-circle-close"/> |
||||||
|
<div class="form-validate-info-item-msg">{{ i.msg }}</div> |
||||||
|
<div class="form-validate-info-item-label">{{ i.label }}</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<template slot="reference"> |
||||||
|
<span v-show="error.length > 0" class="form-validate-info-ref"> |
||||||
|
<i class="el-icon-circle-close"/> |
||||||
|
{{ error.length }} |
||||||
|
</span> |
||||||
|
</template> |
||||||
|
</el-popover> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: "FormValidateInfo", |
||||||
|
|
||||||
|
props: {form: Function}, |
||||||
|
|
||||||
|
data() { |
||||||
|
return { |
||||||
|
visible: false, |
||||||
|
error: [] |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
methods: { |
||||||
|
onValidate(prop, success, msg) { |
||||||
|
const alreadyIndex = this.error.findIndex(i => i.prop === prop) |
||||||
|
if (alreadyIndex !== -1) { |
||||||
|
if (success) this.error.splice(alreadyIndex, 1) |
||||||
|
else this.$set(this.error, alreadyIndex, {...this.error[alreadyIndex], msg}) |
||||||
|
} |
||||||
|
else { |
||||||
|
if (success) return |
||||||
|
const formItem = this.findFormItem(prop) |
||||||
|
this.error.push({prop, msg, label: formItem.label}) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
onClick({prop}) { |
||||||
|
const formItem = this.findFormItem(prop) |
||||||
|
formItem && formItem.$el.scrollIntoView({block: 'center'}) |
||||||
|
this.$refs.popover.doClose() |
||||||
|
}, |
||||||
|
|
||||||
|
findFormItem(prop) { |
||||||
|
return this.form().fields.find(i => i.prop === prop) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
mounted() { |
||||||
|
const form = this.form() |
||||||
|
form.$on('validate', this.onValidate) |
||||||
|
this.$once('hook:beforeDestroy', () => { |
||||||
|
form.$off('validate', this.onValidate) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss"> |
||||||
|
@import "~@/asset/style/variables.scss"; |
||||||
|
|
||||||
|
.form-validate-info { |
||||||
|
&.el-popper { |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
|
||||||
|
&-ref { |
||||||
|
padding: 0 10px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
&-title { |
||||||
|
text-align: center; |
||||||
|
padding: 5px 16px 4px; |
||||||
|
border-bottom: 1px solid #f0f0f0; |
||||||
|
} |
||||||
|
|
||||||
|
&-content { |
||||||
|
max-height: 300px; |
||||||
|
overflow: overlay; |
||||||
|
} |
||||||
|
|
||||||
|
&-item { |
||||||
|
padding: 8px 16px; |
||||||
|
background-color: #fff; |
||||||
|
border-bottom: 1px solid #f0f0f0; |
||||||
|
cursor: pointer; |
||||||
|
transition: background-color .3s; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
background-color: #e6f7ff; |
||||||
|
} |
||||||
|
|
||||||
|
.el-icon-circle-close { |
||||||
|
float: left; |
||||||
|
margin-top: 4px; |
||||||
|
margin-right: 12px; |
||||||
|
padding-bottom: 22px; |
||||||
|
} |
||||||
|
|
||||||
|
&-label { |
||||||
|
margin-top: 2px; |
||||||
|
color: rgba(0, 0, 0, .45); |
||||||
|
font-size: 12px; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&-ref, |
||||||
|
&-item .el-icon-circle-close { |
||||||
|
color: $--color-danger; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
||||||
@ -1,5 +1,5 @@ |
|||||||
<script type="text/jsx"> |
<script type="text/jsx"> |
||||||
import Empty from "@/components/Empty" |
import Empty from "@/component/Empty" |
||||||
|
|
||||||
export default { |
export default { |
||||||
name: "AbstractTable", |
name: "AbstractTable", |
||||||
@ -1,5 +1,5 @@ |
|||||||
<script type="text/jsx"> |
<script type="text/jsx"> |
||||||
import LoadingMask from '@/components/LoadingMask' |
import LoadingMask from '@/component/LoadingMask' |
||||||
|
|
||||||
export default { |
export default { |
||||||
name: "FormDialog", |
name: "FormDialog", |
||||||
@ -1,4 +1,4 @@ |
|||||||
@import "~@/assets/styles/variables.scss"; |
@import "~@/asset/style/variables.scss"; |
||||||
|
|
||||||
.liner-progress { |
.liner-progress { |
||||||
position: relative; |
position: relative; |
||||||
@ -1,7 +1,7 @@ |
|||||||
<script type="text/jsx"> |
<script type="text/jsx"> |
||||||
import {store, init} from '../store' |
import {store, init} from '../store' |
||||||
import FormDialog from "@/components/FormDialog" |
import FormDialog from "@/component/FormDialog" |
||||||
import {createLimitTree, getNodeId} from "@/utils/tree" |
import {createLimitTree, getNodeId} from "@/util/tree" |
||||||
|
|
||||||
export default { |
export default { |
||||||
components: {FormDialog}, |
components: {FormDialog}, |
||||||
@ -1,5 +1,5 @@ |
|||||||
<script type="text/jsx"> |
<script type="text/jsx"> |
||||||
import {isExternal} from '@/utils/validate' |
import {isExternal} from '@/util/validate' |
||||||
|
|
||||||
export default { |
export default { |
||||||
name: 'SvgIcon', |
name: 'SvgIcon', |
||||||
@ -1,4 +1,4 @@ |
|||||||
@import "~@/assets/styles/variables.scss"; |
@import "~@/asset/style/variables.scss"; |
||||||
|
|
||||||
.disabled .el-upload--picture-card { |
.disabled .el-upload--picture-card { |
||||||
display: none; |
display: none; |
||||||
@ -1,6 +1,6 @@ |
|||||||
import Vue from 'vue' |
import Vue from 'vue' |
||||||
import Main from './main.vue' |
import Main from './main.vue' |
||||||
import {mergeObj} from "@/utils" |
import {mergeObj} from "@/util" |
||||||
|
|
||||||
const GuideConstructor = Vue.extend(Main) |
const GuideConstructor = Vue.extend(Main) |
||||||
|
|
||||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 379 B |
|
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B |
|
Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 305 B After Width: | Height: | Size: 305 B |