parent
e4c2151603
commit
78ce11a336
@ -1,51 +0,0 @@ |
|||||||
<script type="text/jsx"> |
|
||||||
export default { |
|
||||||
name: 'DefaultEmptyImage', |
|
||||||
|
|
||||||
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> |
|
||||||
<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,40 +0,0 @@ |
|||||||
<script type="text/jsx"> |
|
||||||
/** |
|
||||||
* 从ant-design-vue处拷贝 |
|
||||||
*/ |
|
||||||
import DefaultEmptyImage from "./DefaultEmptyImage" |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "AEmpty", |
|
||||||
|
|
||||||
functional: true, |
|
||||||
|
|
||||||
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} |
|
||||||
|
|
||||||
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,47 +0,0 @@ |
|||||||
.ant-empty { |
|
||||||
padding: 20px; |
|
||||||
margin: 0 8px; |
|
||||||
font-size: 14px; |
|
||||||
line-height: 22px; |
|
||||||
text-align: center; |
|
||||||
|
|
||||||
&-image { |
|
||||||
height: 100px; |
|
||||||
margin-bottom: 8px; |
|
||||||
|
|
||||||
img { |
|
||||||
height: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
svg { |
|
||||||
height: 100%; |
|
||||||
margin: auto; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
&-description { |
|
||||||
margin: 0; |
|
||||||
} |
|
||||||
|
|
||||||
&-footer { |
|
||||||
margin-top: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
&-normal { |
|
||||||
margin: 32px 0; |
|
||||||
color: rgba(0, 0, 0, .25); |
|
||||||
|
|
||||||
.ant-empty-image { |
|
||||||
height: 40px; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
&-small { |
|
||||||
margin: 8px 0; |
|
||||||
color: rgba(0, 0, 0, .25); |
|
||||||
|
|
||||||
.ant-empty-image { |
|
||||||
height: 35px; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,24 +0,0 @@ |
|||||||
<script type="text/jsx"> |
|
||||||
export default { |
|
||||||
name: "LoadingMask", |
|
||||||
|
|
||||||
functional: true, |
|
||||||
|
|
||||||
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> |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
@ -1,48 +0,0 @@ |
|||||||
<script type="text/jsx"> |
|
||||||
import cssVariables from '@/asset/style/variables.scss' |
|
||||||
import LoadingMask from './LoadingMask' |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "AbstractDialog", |
|
||||||
|
|
||||||
functional: true, |
|
||||||
|
|
||||||
props: { |
|
||||||
value: Boolean, |
|
||||||
loading: {type: Boolean, default: true}, |
|
||||||
width: {type: String, default: '30%'} |
|
||||||
}, |
|
||||||
|
|
||||||
render(h, context) { |
|
||||||
const {data, children, listeners, props: {value, loading, width}} = context |
|
||||||
|
|
||||||
const onClose = function () { |
|
||||||
return listeners.close ? () => listeners.close(false) : () => ({}) |
|
||||||
}() |
|
||||||
|
|
||||||
return ( |
|
||||||
<el-dialog |
|
||||||
v-drag-dialog |
|
||||||
visible={value} |
|
||||||
custom-class="abstract-dialog" |
|
||||||
top={cssVariables.dialogTop} |
|
||||||
width={width} |
|
||||||
on-close={onClose} |
|
||||||
{...data} |
|
||||||
> |
|
||||||
<div class="abstract-dialog-container"> |
|
||||||
<LoadingMask show={loading}/> |
|
||||||
<el-scrollbar> |
|
||||||
<div class="abstract-dialog-body"> |
|
||||||
{children} |
|
||||||
</div> |
|
||||||
<div style="margin-bottom: 17px"/> |
|
||||||
</el-scrollbar> |
|
||||||
</div> |
|
||||||
</el-dialog> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style lang="scss" src="./style.scss"></style> |
|
||||||
@ -1,18 +0,0 @@ |
|||||||
.abstract-dialog { |
|
||||||
.el-dialog__body { |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.abstract-dialog-container { |
|
||||||
position: relative; |
|
||||||
width: 100%; |
|
||||||
|
|
||||||
.abstract-dialog-body { |
|
||||||
padding: 24px 24px 0 |
|
||||||
} |
|
||||||
|
|
||||||
.el-form { |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,24 +0,0 @@ |
|||||||
<script type="text/jsx"> |
|
||||||
export default { |
|
||||||
name: "AbstractForm", |
|
||||||
|
|
||||||
functional: true, |
|
||||||
|
|
||||||
render(h, context) { |
|
||||||
const {data, children} = context |
|
||||||
return ( |
|
||||||
<el-form |
|
||||||
ref="form" |
|
||||||
label-position="right" |
|
||||||
label-width="100px" |
|
||||||
label-suffix=":" |
|
||||||
size="small" |
|
||||||
status-icon |
|
||||||
{...data} |
|
||||||
> |
|
||||||
<el-row>{children}</el-row> |
|
||||||
</el-form> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
@ -1,25 +0,0 @@ |
|||||||
<script type="text/jsx"> |
|
||||||
export default { |
|
||||||
name: "AbstractFormItem", |
|
||||||
|
|
||||||
functional: true, |
|
||||||
|
|
||||||
props: {full: Boolean, thin: Boolean, label: String, prop: String}, |
|
||||||
|
|
||||||
render(h, context) { |
|
||||||
const {full, thin, label, prop} = context.props |
|
||||||
let props = {xs: 24, sm: 12, lg: 8} |
|
||||||
|
|
||||||
if (full) props = {span: 24} |
|
||||||
else if (thin) props.lg = 12 |
|
||||||
|
|
||||||
return ( |
|
||||||
<el-col {...{props}}> |
|
||||||
<el-form-item label={label} prop={prop}> |
|
||||||
{context.children} |
|
||||||
</el-form-item> |
|
||||||
</el-col> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
@ -1,28 +0,0 @@ |
|||||||
<script type="jsx"> |
|
||||||
export default { |
|
||||||
name: "AbstractPagination", |
|
||||||
|
|
||||||
functional: true, |
|
||||||
|
|
||||||
props: { |
|
||||||
model: { |
|
||||||
type: Object, |
|
||||||
default: () => ({total: 0, page: 1, pageSize: 15}) |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
render(h, context) { |
|
||||||
const {model: {total, page, pageSize}} = context.props |
|
||||||
return ( |
|
||||||
<el-pagination |
|
||||||
background |
|
||||||
current-page={page} |
|
||||||
page-size={pageSize} |
|
||||||
total={total} |
|
||||||
layout="total, prev, pager, next, jumper" |
|
||||||
{...context.data} |
|
||||||
/> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
@ -1,32 +0,0 @@ |
|||||||
<script type="text/jsx"> |
|
||||||
import Empty from "@/component/Empty" |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "AbstractTable", |
|
||||||
|
|
||||||
functional: true, |
|
||||||
|
|
||||||
render(h, context) { |
|
||||||
const {data, children} = context |
|
||||||
|
|
||||||
if (!data.scopedSlots) { |
|
||||||
data.scopedSlots = {} |
|
||||||
} |
|
||||||
if (!data.scopedSlots.empty) { |
|
||||||
data.scopedSlots.empty = () => <Empty/> |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<el-table |
|
||||||
ref="table" |
|
||||||
current-row-key="id" |
|
||||||
row-key="id" |
|
||||||
highlight-current-row |
|
||||||
{...data} |
|
||||||
> |
|
||||||
{children} |
|
||||||
</el-table> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
@ -1,39 +0,0 @@ |
|||||||
import Vue from 'vue' |
|
||||||
import Main from './main.vue' |
|
||||||
import {mergeObj} from "@/util" |
|
||||||
|
|
||||||
const GuideConstructor = Vue.extend(Main) |
|
||||||
|
|
||||||
let instance |
|
||||||
|
|
||||||
const defaultOptions = { |
|
||||||
stageBackground: '#ffffff', |
|
||||||
doneBtnText: '完成', |
|
||||||
closeBtnText: '关闭', |
|
||||||
nextBtnText: '下一步', |
|
||||||
prevBtnText: '上一步', |
|
||||||
} |
|
||||||
|
|
||||||
const guide = function (steps, index, beforeExit) { |
|
||||||
if (instance) instance.exit() |
|
||||||
else { |
|
||||||
instance = new GuideConstructor({data: {options: defaultOptions}}) |
|
||||||
instance.$mount() |
|
||||||
document.body.appendChild(instance.$el) |
|
||||||
} |
|
||||||
|
|
||||||
instance.steps = steps |
|
||||||
instance.beforeExit = beforeExit |
|
||||||
|
|
||||||
this.$nextTick(() => instance.start(index)) |
|
||||||
} |
|
||||||
|
|
||||||
guide.config = function (config) { |
|
||||||
mergeObj(defaultOptions, config) |
|
||||||
} |
|
||||||
|
|
||||||
guide.exit = function (force) { |
|
||||||
instance && instance.exit(force) |
|
||||||
} |
|
||||||
|
|
||||||
export default guide |
|
||||||
@ -1,237 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<el-popover ref="popover" :value="showPopover" trigger="manual"> |
|
||||||
<div class="guide-popover-container"> |
|
||||||
<div v-html="step.content"/> |
|
||||||
|
|
||||||
<div class="guide-popover-action"> |
|
||||||
<div class="action-close"> |
|
||||||
<el-button size="mini" type="dashed" plain @click="exit"> |
|
||||||
{{ hasDone ? step.doneBtnText : step.closeBtnText }} |
|
||||||
</el-button> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="action-step"> |
|
||||||
<el-button v-if="showPrevBtn" class="prev-btn" size="mini" plain @click="previous"> |
|
||||||
{{ step.prevBtnText }} |
|
||||||
</el-button> |
|
||||||
<el-button v-if="showNextBtn" class="next-btn" size="mini" plain @click="next"> |
|
||||||
{{ step.nextBtnText }} |
|
||||||
</el-button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div slot="reference" v-show="showStage" id="guide-stage" :style="stageStyle"/> |
|
||||||
</el-popover> |
|
||||||
|
|
||||||
<div v-if="showStage" id="guide-highlight-element-cover" :style="stageStyle" @click.prevent.stop/> |
|
||||||
|
|
||||||
<div v-if="showOverlay" id="guide-overlay"/> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
/** |
|
||||||
* 由driver.js改造而来 |
|
||||||
*/ |
|
||||||
import {debounce, isEmpty, deepClone} from "@/util" |
|
||||||
import {addHighlightClasses, getCalculatedPosition, removeHighlightClasses} from "./utils" |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "Guide", |
|
||||||
|
|
||||||
data() { |
|
||||||
return { |
|
||||||
steps: [], |
|
||||||
options: {}, |
|
||||||
beforeExit: null, |
|
||||||
isActive: false, |
|
||||||
showStage: false, |
|
||||||
showOverlay: false, |
|
||||||
currentStep: 0, |
|
||||||
moving: false, |
|
||||||
stageStyle: '', |
|
||||||
highlightedElement: null, |
|
||||||
lastHighlightedElement: null |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
computed: { |
|
||||||
showPopover() { |
|
||||||
return !this.moving && this.showStage |
|
||||||
}, |
|
||||||
showPrevBtn() { |
|
||||||
return this.step.forceShowPrevBtn || this.currentStep > 0 && this.steps[this.currentStep - 1] |
|
||||||
}, |
|
||||||
showNextBtn() { |
|
||||||
return this.step.forceShowNextBtn || this.steps[this.currentStep + 1] |
|
||||||
}, |
|
||||||
hasDone() { |
|
||||||
return this.currentStep === this.steps.length - 1 |
|
||||||
}, |
|
||||||
step() { |
|
||||||
if (!this.isActive || this.steps.length <= 0 || !this.steps[this.currentStep]) { |
|
||||||
return {} |
|
||||||
} |
|
||||||
|
|
||||||
return { |
|
||||||
...this.options, |
|
||||||
...this.steps[this.currentStep], |
|
||||||
index: this.currentStep |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
watch: { |
|
||||||
isActive(v) { |
|
||||||
document.body.classList[v ? 'add' : 'remove']('overflow-hidden') |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
methods: { |
|
||||||
start(index = 0) { |
|
||||||
if (!this.steps || this.steps.length === 0) throw new Error('请传入步骤') |
|
||||||
|
|
||||||
if (index === 'last') index = this.steps.length - 1 |
|
||||||
else if (index === 'first') index = 0 |
|
||||||
|
|
||||||
if (index > this.steps.length - 1 || !this.steps[index]) { |
|
||||||
console.error('error index:', index, 'error steps:', deepClone(this.steps)) |
|
||||||
throw new Error('起始步骤有误') |
|
||||||
} |
|
||||||
|
|
||||||
this.isActive = true |
|
||||||
this.currentStep = index |
|
||||||
this.showOverlay = false |
|
||||||
this.showStage = false |
|
||||||
|
|
||||||
this.$_highlight() |
|
||||||
|
|
||||||
this.showOverlay = true |
|
||||||
this.showStage = true |
|
||||||
}, |
|
||||||
exit(force) { |
|
||||||
if (!this.isActive) return |
|
||||||
|
|
||||||
if (!force && this.beforeExit) { |
|
||||||
const result = this.beforeExit(this.hasDone) |
|
||||||
if (result && result.then) { |
|
||||||
result.then(this.$_clear) |
|
||||||
} |
|
||||||
else result !== false && this.$_clear() |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
this.$_clear() |
|
||||||
}, |
|
||||||
|
|
||||||
previous() { |
|
||||||
if (!this.isActive || this.moving) return |
|
||||||
this.moving = true |
|
||||||
|
|
||||||
if (this.step.onPrevious) { |
|
||||||
const result = this.step.onPrevious() |
|
||||||
if (result && result.then) { |
|
||||||
result.then(this.$_movePrevious, this.$_moveEnd) |
|
||||||
} |
|
||||||
else result ? this.$_movePrevious() : this.$_moveEnd() |
|
||||||
} |
|
||||||
else this.$_movePrevious() |
|
||||||
}, |
|
||||||
next() { |
|
||||||
if (!this.isActive || this.moving) return |
|
||||||
this.moving = true |
|
||||||
|
|
||||||
if (this.step.onNext) { |
|
||||||
const result = this.step.onNext() |
|
||||||
if (result && result.then) { |
|
||||||
result.then(this.$_moveNext, this.$_moveEnd) |
|
||||||
} |
|
||||||
else result ? this.$_moveNext() : this.$_moveEnd() |
|
||||||
} |
|
||||||
else this.$_moveNext() |
|
||||||
}, |
|
||||||
|
|
||||||
resize() { |
|
||||||
if (!this.highlightedElement) return |
|
||||||
this.$_setStageStyle(getCalculatedPosition(this.highlightedElement)) |
|
||||||
this.$nextTick(this.$refs.popover.updatePopper) |
|
||||||
}, |
|
||||||
|
|
||||||
$_movePrevious() { |
|
||||||
if (!this.isActive) return |
|
||||||
this.currentStep-- |
|
||||||
this.$_highlight() |
|
||||||
}, |
|
||||||
$_moveNext() { |
|
||||||
if (!this.isActive) return |
|
||||||
this.currentStep++ |
|
||||||
this.$_highlight() |
|
||||||
}, |
|
||||||
$_moveEnd() { |
|
||||||
this.$refs.popover.updatePopper() |
|
||||||
this.moving = false |
|
||||||
}, |
|
||||||
$_clear() { |
|
||||||
if (!this.isActive) return |
|
||||||
this.isActive = false |
|
||||||
this.showOverlay = false |
|
||||||
this.showStage = false |
|
||||||
this.currentStep = 0 |
|
||||||
this.steps = [] |
|
||||||
this.beforeExit = null |
|
||||||
this.highlightedElement && removeHighlightClasses(this.highlightedElement) |
|
||||||
this.highlightedElement = null |
|
||||||
this.lastHighlightedElement = null |
|
||||||
}, |
|
||||||
$_highlight() { |
|
||||||
if (isEmpty(this.step.element)) throw new Error(`step${this.currentStep}中element为空`) |
|
||||||
|
|
||||||
const el = document.querySelector(this.step.element) |
|
||||||
|
|
||||||
if (!el) throw new Error(`${this.step.element} 没有该元素`) |
|
||||||
|
|
||||||
if (el === this.highlightedElement) return |
|
||||||
|
|
||||||
this.highlightedElement && removeHighlightClasses(this.highlightedElement) |
|
||||||
|
|
||||||
this.lastHighlightedElement = this.highlightedElement |
|
||||||
this.highlightedElement = el |
|
||||||
|
|
||||||
el.scrollIntoView({block: 'center'}) |
|
||||||
|
|
||||||
this.$_setStageStyle(getCalculatedPosition(el)) |
|
||||||
|
|
||||||
window.setTimeout(() => { |
|
||||||
this.$_moveEnd() |
|
||||||
this.step.onHighlighted && this.step.onHighlighted() |
|
||||||
}, 300) |
|
||||||
|
|
||||||
addHighlightClasses(el) |
|
||||||
}, |
|
||||||
$_setStageStyle({top, left, right, bottom}) { |
|
||||||
const width = right - left |
|
||||||
const height = bottom - top |
|
||||||
this.stageStyle = |
|
||||||
`width:${width}px;` + |
|
||||||
`height:${height}px;` + |
|
||||||
`top:${top}px;` + |
|
||||||
`left:${left}px;` + |
|
||||||
`background-color:${this.step.stageBackground}` |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
mounted() { |
|
||||||
this.resize = debounce(this.resize) |
|
||||||
|
|
||||||
window.addEventListener('resize', this.resize) |
|
||||||
|
|
||||||
this.$once('hook:beforeDestroy', () => { |
|
||||||
window.removeEventListener('resize', this.resize) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style lang="scss" src="./style.scss"></style> |
|
||||||
@ -1,70 +0,0 @@ |
|||||||
.guide-highlighted-element { |
|
||||||
z-index: 1002 !important; |
|
||||||
} |
|
||||||
|
|
||||||
.guide-position-relative { |
|
||||||
position: relative !important; |
|
||||||
} |
|
||||||
|
|
||||||
.guide-fix-stacking { |
|
||||||
z-index: auto !important; |
|
||||||
opacity: 1.0 !important; |
|
||||||
transform: none !important; |
|
||||||
filter: none !important; |
|
||||||
perspective: none !important; |
|
||||||
transform-style: flat !important; |
|
||||||
will-change: unset !important; |
|
||||||
} |
|
||||||
|
|
||||||
.guide-popover-container { |
|
||||||
min-width: 300px; |
|
||||||
|
|
||||||
.guide-popover-action { |
|
||||||
position: relative; |
|
||||||
height: 100%; |
|
||||||
width: 100%; |
|
||||||
margin-top: 20px; |
|
||||||
|
|
||||||
.action-close { |
|
||||||
display: inline-block; |
|
||||||
} |
|
||||||
|
|
||||||
.action-step { |
|
||||||
display: inline-block; |
|
||||||
right: 0; |
|
||||||
position: absolute; |
|
||||||
|
|
||||||
.prev-btn { |
|
||||||
position: absolute; |
|
||||||
right: 90px; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#guide-stage { |
|
||||||
position: absolute; |
|
||||||
background: white; |
|
||||||
transition: all 0.3s; |
|
||||||
z-index: 1001 !important; |
|
||||||
border-radius: 2px; |
|
||||||
/*background: transparent !important; |
|
||||||
outline: 5000px solid rgba(0, 0, 0, 0.75);*/ |
|
||||||
} |
|
||||||
|
|
||||||
#guide-overlay { |
|
||||||
background: black; |
|
||||||
position: fixed; |
|
||||||
top: 0; |
|
||||||
left: 0; |
|
||||||
bottom: 0; |
|
||||||
right: 0; |
|
||||||
opacity: 0.75; |
|
||||||
z-index: 1000 !important; |
|
||||||
} |
|
||||||
|
|
||||||
#guide-highlight-element-cover { |
|
||||||
position: absolute; |
|
||||||
background: transparent !important; |
|
||||||
z-index: 1003 !important; |
|
||||||
} |
|
||||||
@ -1,106 +0,0 @@ |
|||||||
const CLASS_HIGHLIGHTED_ELEMENT = 'guide-highlighted-element' |
|
||||||
const CLASS_POSITION_RELATIVE = 'guide-position-relative' |
|
||||||
const CLASS_FIX_STACKING_CONTEXT = 'guide-fix-stacking' |
|
||||||
|
|
||||||
export function getStyleProperty(element, propertyName, prefixVendor = false) { |
|
||||||
if (prefixVendor) { |
|
||||||
const prefixes = ['', '-webkit-', '-ms-', 'moz-', '-o-'] |
|
||||||
for (let counter = 0; counter < prefixes.length; counter++) { |
|
||||||
const prefixedProperty = prefixes[counter] + propertyName |
|
||||||
const foundValue = getStyleProperty(element, prefixedProperty) |
|
||||||
|
|
||||||
if (foundValue) return foundValue |
|
||||||
} |
|
||||||
|
|
||||||
return '' |
|
||||||
} |
|
||||||
|
|
||||||
let propertyValue = '' |
|
||||||
|
|
||||||
if (element.currentStyle) { |
|
||||||
propertyValue = element.currentStyle[propertyName] |
|
||||||
} |
|
||||||
else if (document.defaultView && document.defaultView.getComputedStyle) { |
|
||||||
propertyValue = |
|
||||||
document |
|
||||||
.defaultView |
|
||||||
.getComputedStyle(element, null) |
|
||||||
.getPropertyValue(propertyName) |
|
||||||
} |
|
||||||
|
|
||||||
return propertyValue && propertyValue.toLowerCase ? propertyValue.toLowerCase() : propertyValue |
|
||||||
} |
|
||||||
|
|
||||||
export function removeHighlightClasses(element) { |
|
||||||
element.classList.remove(CLASS_HIGHLIGHTED_ELEMENT) |
|
||||||
element.classList.remove(CLASS_POSITION_RELATIVE) |
|
||||||
|
|
||||||
const stackFixes = document.querySelectorAll(`.${CLASS_FIX_STACKING_CONTEXT}`) |
|
||||||
for (let counter = 0; counter < stackFixes.length; counter++) { |
|
||||||
stackFixes[counter].classList.remove(CLASS_FIX_STACKING_CONTEXT) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export function addHighlightClasses(element) { |
|
||||||
element.classList.add(CLASS_HIGHLIGHTED_ELEMENT) |
|
||||||
|
|
||||||
if (canMakeRelative(element)) { |
|
||||||
element.classList.add(CLASS_POSITION_RELATIVE) |
|
||||||
} |
|
||||||
|
|
||||||
fixStackingContext(element) |
|
||||||
} |
|
||||||
|
|
||||||
export function getCalculatedPosition(element) { |
|
||||||
const body = document.body |
|
||||||
const documentElement = document.documentElement |
|
||||||
|
|
||||||
const scrollTop = window.pageYOffset || documentElement.scrollTop || body.scrollTop |
|
||||||
const scrollLeft = window.pageXOffset || documentElement.scrollLeft || body.scrollLeft |
|
||||||
const elementRect = element.getBoundingClientRect() |
|
||||||
|
|
||||||
return ({ |
|
||||||
top: elementRect.top + scrollTop, |
|
||||||
left: elementRect.left + scrollLeft, |
|
||||||
right: elementRect.left + scrollLeft + elementRect.width, |
|
||||||
bottom: elementRect.top + scrollTop + elementRect.height, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
function canMakeRelative(element) { |
|
||||||
const currentPosition = getStyleProperty(element, 'position') |
|
||||||
const avoidPositionsList = ['absolute', 'fixed', 'relative'] |
|
||||||
|
|
||||||
return avoidPositionsList.indexOf(currentPosition) === -1 |
|
||||||
} |
|
||||||
|
|
||||||
function fixStackingContext(element) { |
|
||||||
let parentNode = element.parentNode |
|
||||||
while (parentNode) { |
|
||||||
if (!parentNode.tagName || parentNode.tagName.toLowerCase() === 'body') { |
|
||||||
break |
|
||||||
} |
|
||||||
|
|
||||||
const zIndex = getStyleProperty(parentNode, 'z-index') |
|
||||||
const opacity = parseFloat(getStyleProperty(parentNode, 'opacity')) |
|
||||||
const transform = getStyleProperty(parentNode, 'transform', true) |
|
||||||
const transformStyle = getStyleProperty(parentNode, 'transform-style', true) |
|
||||||
const transformBox = getStyleProperty(parentNode, 'transform-box', true) |
|
||||||
const filter = getStyleProperty(parentNode, 'filter', true) |
|
||||||
const perspective = getStyleProperty(parentNode, 'perspective', true) |
|
||||||
|
|
||||||
if ( |
|
||||||
/[0-9]+/.test(zIndex) |
|
||||||
|| opacity < 1 |
|
||||||
|| (transform && transform !== 'none') |
|
||||||
|| (transformStyle && transformStyle !== 'flat') |
|
||||||
|| (transformBox && transformBox !== 'border-box') |
|
||||||
|| (filter && filter !== 'none') |
|
||||||
|| (perspective && perspective !== 'none') |
|
||||||
) { |
|
||||||
parentNode.classList.add(CLASS_FIX_STACKING_CONTEXT) |
|
||||||
} |
|
||||||
|
|
||||||
parentNode = parentNode.parentNode |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,32 +0,0 @@ |
|||||||
import Vue from 'vue' |
|
||||||
import Main from './main.vue' |
|
||||||
|
|
||||||
const Constructor = Vue.extend(Main) |
|
||||||
|
|
||||||
let instance |
|
||||||
|
|
||||||
const signature = function ({image, lineWidth = 6, lineColor = '#000000', onConfirm}) { |
|
||||||
if (instance) instance.close() |
|
||||||
else { |
|
||||||
instance = new Constructor().$mount() |
|
||||||
document.body.appendChild(instance.$el) |
|
||||||
} |
|
||||||
|
|
||||||
instance.image = image |
|
||||||
instance.lineWidth = lineWidth |
|
||||||
instance.lineColor = lineColor |
|
||||||
instance.onConfirm = onConfirm |
|
||||||
instance.init() |
|
||||||
instance.visible = true |
|
||||||
|
|
||||||
return instance |
|
||||||
} |
|
||||||
|
|
||||||
signature.close = function () { |
|
||||||
if (instance) { |
|
||||||
instance.close() |
|
||||||
instance = null |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export default signature |
|
||||||
@ -1,161 +0,0 @@ |
|||||||
<template> |
|
||||||
<div v-show="visible" class="signature-board"> |
|
||||||
<canvas |
|
||||||
ref="canvas" |
|
||||||
@mousedown.prevent="drawStart" |
|
||||||
@mousemove.prevent="drawMove" |
|
||||||
@mouseup.prevent="drawEnd" |
|
||||||
@touchstart.prevent="drawStart" |
|
||||||
@touchmove.prevent="drawMove" |
|
||||||
@touchend.prevent="drawEnd" |
|
||||||
/> |
|
||||||
|
|
||||||
<div class="signature-board__footer"> |
|
||||||
<el-button plain size="small" @click="clear">清 空</el-button> |
|
||||||
<el-button plain size="small" @click="close">关 闭</el-button> |
|
||||||
<el-button size="small" type="primary" @click="confirm">确 定</el-button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
/* |
|
||||||
* 突然发现没办法做到竖屏横屏都支持,坑爹 |
|
||||||
* */ |
|
||||||
|
|
||||||
function getEventPoint(e) { |
|
||||||
let x, y |
|
||||||
if (['mousedown', 'mousemove'].includes(e.type)) { |
|
||||||
x = e.clientX - e.target.offsetLeft |
|
||||||
y = e.clientY - e.target.offsetTop |
|
||||||
} |
|
||||||
else { |
|
||||||
x = e.changedTouches[0].clientX - e.target.offsetLeft |
|
||||||
y = e.changedTouches[0].clientY - e.target.offsetTop |
|
||||||
} |
|
||||||
return {x, y} |
|
||||||
} |
|
||||||
|
|
||||||
export default { |
|
||||||
name: 'SignatureBoard', |
|
||||||
|
|
||||||
props: { |
|
||||||
image: String, |
|
||||||
lineWidth: Number, |
|
||||||
lineColor: String, |
|
||||||
onConfirm: Function |
|
||||||
}, |
|
||||||
|
|
||||||
data() { |
|
||||||
return { |
|
||||||
visible: false, |
|
||||||
drew: false, |
|
||||||
drawing: false, |
|
||||||
startX: 0, |
|
||||||
startY: 0 |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
methods: { |
|
||||||
drawStart(e) { |
|
||||||
const {x, y} = getEventPoint(e) |
|
||||||
this.drew = true |
|
||||||
this.drawing = true |
|
||||||
this.startX = x |
|
||||||
this.startY = y |
|
||||||
this.ctx.strokeStyle = this.lineColor |
|
||||||
this.ctx.lineWidth = this.lineWidth |
|
||||||
}, |
|
||||||
|
|
||||||
drawMove(e) { |
|
||||||
if (!this.drawing) return |
|
||||||
const {x, y} = getEventPoint(e) |
|
||||||
this.ctx.beginPath() |
|
||||||
this.ctx.moveTo(this.startX, this.startY) |
|
||||||
this.ctx.lineTo(x, y) |
|
||||||
this.ctx.stroke() |
|
||||||
this.ctx.closePath() |
|
||||||
this.startY = y |
|
||||||
this.startX = x |
|
||||||
}, |
|
||||||
|
|
||||||
drawEnd() { |
|
||||||
this.drawing = false |
|
||||||
}, |
|
||||||
|
|
||||||
getBase64() { |
|
||||||
if (!this.drew) return null |
|
||||||
return this.$refs.canvas.toDataURL() |
|
||||||
}, |
|
||||||
|
|
||||||
resize() { |
|
||||||
const canvas = this.$refs.canvas |
|
||||||
|
|
||||||
canvas.height = window.innerHeight |
|
||||||
canvas.width = window.innerWidth |
|
||||||
}, |
|
||||||
|
|
||||||
init() { |
|
||||||
this.resize() |
|
||||||
|
|
||||||
this.ctx = this.$refs.canvas.getContext('2d') |
|
||||||
this.ctx.lineCap = 'round' |
|
||||||
this.ctx.lineJoin = 'round' |
|
||||||
|
|
||||||
if (this.image) { |
|
||||||
let img = new Image() |
|
||||||
img.src = this.image |
|
||||||
img.onload = () => { |
|
||||||
this.ctx.drawImage(img, 0, 0, img.width, img.height) |
|
||||||
this.drew = true |
|
||||||
img = null |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
clear() { |
|
||||||
this.drew = false |
|
||||||
this.drawing = false |
|
||||||
this.$refs.canvas && (this.$refs.canvas.height = window.innerHeight) |
|
||||||
}, |
|
||||||
|
|
||||||
close() { |
|
||||||
this.visible = false |
|
||||||
this.clear() |
|
||||||
}, |
|
||||||
|
|
||||||
confirm() { |
|
||||||
this.onConfirm && this.onConfirm(this.getBase64()) |
|
||||||
this.visible = false |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
mounted() { |
|
||||||
window.addEventListener('resize', this.resize) |
|
||||||
|
|
||||||
this.$once('hook:beforeDestroy', () => { |
|
||||||
this.clear() |
|
||||||
window.removeEventListener('resize', this.resize) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style lang="scss"> |
|
||||||
.signature-board { |
|
||||||
position: fixed; |
|
||||||
background-color: #ffffff; |
|
||||||
top: 0; |
|
||||||
right: 0; |
|
||||||
height: 100vh; |
|
||||||
width: 100vw; |
|
||||||
z-index: 100; |
|
||||||
overflow: hidden; |
|
||||||
|
|
||||||
&__footer { |
|
||||||
position: absolute; |
|
||||||
bottom: 20px; |
|
||||||
right: 20px; |
|
||||||
} |
|
||||||
} |
|
||||||
</style> |
|
||||||
@ -1,11 +1,7 @@ |
|||||||
import Guide from './Guide' |
|
||||||
import ImageViewer from './ImageViewer' |
import ImageViewer from './ImageViewer' |
||||||
import Verify from './Verify' |
import Verify from './Verify' |
||||||
import Signature from './SignautreBoard' |
|
||||||
|
|
||||||
export default function (Vue) { |
export default function (Vue) { |
||||||
Vue.prototype.$guide = Guide |
|
||||||
Vue.prototype.$image = ImageViewer |
Vue.prototype.$image = ImageViewer |
||||||
Vue.prototype.$verify = Verify |
Vue.prototype.$verify = Verify |
||||||
Vue.prototype.$signature = Signature |
|
||||||
} |
} |
||||||
|
|||||||
@ -1,12 +0,0 @@ |
|||||||
export function createWorker(func, data, callback) { |
|
||||||
const blob = new Blob(['(' + func.toString() + ')()']) |
|
||||||
const url = window.URL.createObjectURL(blob) |
|
||||||
const worker = new Worker(url) |
|
||||||
|
|
||||||
worker.onmessage = callback |
|
||||||
worker.postMessage(data) |
|
||||||
|
|
||||||
window.URL.revokeObjectURL(url) |
|
||||||
|
|
||||||
return worker |
|
||||||
} |
|
||||||
@ -1,64 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<div class="tip-row"> |
|
||||||
基于driver.js改造的用户导航 |
|
||||||
</div> |
|
||||||
|
|
||||||
<div> |
|
||||||
<el-button plain @click="onClick">开始导航</el-button> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div id="example-guide-step1" class="example-guide-step">我是步骤1</div> |
|
||||||
<div id="example-guide-step2" class="example-guide-step">我是步骤2</div> |
|
||||||
<div id="example-guide-step3" class="example-guide-step">我是步骤3</div> |
|
||||||
<div id="example-guide-step4" class="example-guide-step">我是步骤4</div> |
|
||||||
<div id="example-guide-step5" class="example-guide-step">我是步骤5</div> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import {elSuccess, elConfirm} from "@/util/message" |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "guidePage", |
|
||||||
|
|
||||||
methods: { |
|
||||||
onClick() { |
|
||||||
this.$guide([ |
|
||||||
{ |
|
||||||
element: '#example-guide-step1', |
|
||||||
content: 'onNext钩子示例', |
|
||||||
onNext: () => elConfirm('确认进行下一步吗?', false) |
|
||||||
}, |
|
||||||
{ |
|
||||||
element: '#example-guide-step2', |
|
||||||
content: 'onPrevious钩子展示', |
|
||||||
onPrevious: () => elConfirm('确认回到上一步吗?', false) |
|
||||||
}, |
|
||||||
{ |
|
||||||
element: '#example-guide-step3', |
|
||||||
content: 'onHighlighted钩子示例', |
|
||||||
onHighlighted: () => elSuccess('到达第三步!') |
|
||||||
}, |
|
||||||
{ |
|
||||||
element: '#example-guide-step4', |
|
||||||
content: `<h1>内容可以是html字符串</h1><img src="https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=4018557288,1217151095&fm=26&gp=0.jpg">`, |
|
||||||
}, |
|
||||||
{ |
|
||||||
element: '#example-guide-step5', |
|
||||||
content: '最后一步', |
|
||||||
}, |
|
||||||
]) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
.example-guide-step { |
|
||||||
margin-top: 50px; |
|
||||||
height: 200px; |
|
||||||
width: 300px; |
|
||||||
background-color: blueviolet; |
|
||||||
} |
|
||||||
</style> |
|
||||||
@ -1,31 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<div class="tip-row"> |
|
||||||
搞了个手写签名,竖屏横屏不可兼得 |
|
||||||
</div> |
|
||||||
<div> |
|
||||||
<img :src="src" style="width: 100%;height: 500px;object-fit: contain"> |
|
||||||
</div> |
|
||||||
<el-button plain @click="signature">点击签名</el-button> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: 'signaturePage', |
|
||||||
|
|
||||||
data() { |
|
||||||
return { |
|
||||||
src: null |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
methods: { |
|
||||||
signature() { |
|
||||||
this.$signature({ |
|
||||||
image: this.src, onConfirm: img => this.src = img |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
Loading…
Reference in new issue