Commit d2c4018b authored by kang.nie@inzymeits.com's avatar kang.nie@inzymeits.com
Browse files

初始化代码

parent de8a8ae5
Pipeline #3105 failed with stages
in 0 seconds
<template>
<header :class="isHome ? '' : 'top-nav-seize'">
<div class="top-nav">
<div class="top-nav-container">
<div class="top-nav-icon-wrap">
<img v-if="logo" :src="logo" class="top-nav-logo" alt="logo">
<div class="top-nav-icon-title" />
</div>
<el-menu :default-active="activeIndex" class="top-nav-menu" mode="horizontal" @select="handleSelect">
<el-menu-item index="Home">首页</el-menu-item>
<el-menu-item v-if="managementRouteName" :index="managementRouteName">管理后台</el-menu-item>
</el-menu>
<div class="top-nav-user-wrap">
<img :src="require('@/assets/img/default-avatar.png')" class="avatar" alt="头像">
<el-dropdown :hide-on-click="false" @command="handleCommand">
<span class="el-dropdown-link">
Hi, {{ name }} <i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<!-- <el-dropdown-item command="update-password">修改密码</el-dropdown-item> -->
<el-dropdown-item command="exit-login">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</div>
</header>
</template>
<script>
import { mapGetters } from 'vuex'
import asyncRoutes from '@/router/asyncRoutes'
export default {
name: 'TopNav',
data() {
const isHome = this.$route.name === 'HomePage'
return {
// logo
logo: this.$store.getters.logo,
// 当前激活的索引
activeIndex: '',
// 是否展示白底黑字(首页滚动超过60px也会展示白底黑字)
showBlack: !isHome,
// 管理后台地址
managementRouteName: '',
// 导航条背景色
navBgColor: `rgba(255, 255, 255, ${isHome ? 0 : 1})`
}
},
computed: {
...mapGetters(['avatar', 'name', 'addRoutes']),
/**
* 判断当前是否是首页
*/
isHome() {
// 处理一次页面滚动方法,相当于监听路由切换
this.pageScroll()
return this.$route.name === 'HomePage'
}
},
mounted() {
const { name } = this.$route
// 动态路由
asyncRoutes.forEach((route, index) => {
// 如果当前第一级目录是选中状态
if (route.name === name) {
this.activeIndex = route.name
}
// 如果孩子节点中有当前激活的菜单项
if (route.children && route.children.length > 0) {
// 遍历孩子节点
route.children.forEach(child => {
// 如果当前是激活的菜单
if (child.name === name) {
this.activeIndex = route.name
}
})
}
})
// 获取当前管理后台的路径
if (this.addRoutes && this.addRoutes.length > 0) {
this.addRoutes.forEach(route => {
// 如果当前是管理页面
if (['Management', 'Search', 'Audit'].includes(route.name) && route.children && route.children.length > 0 && !this.managementRouteName) {
this.managementRouteName = route.children[0].name
}
})
}
},
destroyed() {
window.onscroll = null
},
methods: {
/**
* 页面滚动方法
*/
pageScroll() {
// 滑动到顶部的距离
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
// 当前路由名称
const { name } = this.$route
// 如果是首页
const isNotHome = name !== 'HomePage'
// 当前滚动距离大于30,展示黑底白字
this.showBlack = isNotHome ? true : scrollTop > 30
// 背景色的透明度
this.navBgColor = `rgba(255, 255, 255, ${(isNotHome || scrollTop > 60) ? 1 : (scrollTop / 60).toFixed(2)})`
},
/**
* 菜单名称
*/
menuName(menu) {
const { name } = this.$route
// 激活的菜单项
let menuActive = menu.children ? menu.children.find(m => m.name === name) : null
// 如果当前是激活项
if (!menuActive) {
menuActive = menu.children[0]
}
return menuActive.meta.title
},
/**
* 当前选中的菜单
*/
handleSelect(name) {
this.$router.push({ name })
},
/**
* 当前点击的用户信息模块
* @param command 当前命令
*/
handleCommand(command) {
switch (command) {
case 'update-password': // 更新密码
break
case 'exit-login': // 退出登录
this.$store.dispatch('user/logout')
this.$router.push({
name: 'Login'
})
break
}
},
toSearch(type) {
if (type === 1) {
this.$router.push({ name: 'CarAuth1' })
} else {
this.$router.push({ name: 'CarAuth2' })
}
}
}
}
</script>
<style lang="scss">
.top-nav {
background: #ffffff;
height: 60px;
position: fixed;
top: 0px;
left: 0px;
right: 0px;
z-index: 1000;
.top-nav-container {
display: flex;
padding: 0 32px;
height: 100%;
}
.top-nav-icon-wrap {
align-items: center;
display: flex;
flex: 1;
height: 100%;
.top-nav-logo {
width: auto;
height: 28px;
}
.top-nav-icon-title {
background-image: url('../../assets/img/icon-nav-title.svg');
background-size: 100% 100%;
width: 140px;
height: 28px;
}
}
.top-nav-menu {
border-bottom: 0px;
}
.top-nav-user-wrap {
align-items: center;
display: flex;
padding-left: 24px;
.avatar {
margin-right: 16px;
width: 32px;
height: 32px;
}
}
.el-menu {
background-color: transparent;
color: #212026;
.el-menu-item {
background-color: transparent !important;
border-bottom: 0px;
color: #212026;
font-weight: 500;
padding: 0 24px;
position: relative;
&.is-active, &:hover {
color: #2A68FF;
i {
color: #2A68FF;
}
&:after {
background: #FFFFFF;
bottom: 16px;
content: "";
display: block;
position: absolute;
left: 50%;
width: 14px;
height: 2px;
transform: translateX(-50%);
}
}
}
.el-submenu {
&.is-active {
.el-submenu__title {
color: #2A68FF;
i {
color: #2A68FF;
}
}
}
.el-submenu__title {
background-color: transparent;
border-bottom: 0px;
color: #212026;
font-weight: 500;
padding: 0 24px;
&:hover {
color: #2A68FF;
}
}
}
}
.el-dropdown {
color: #212026;
}
&.reverse {
.el-menu {
.el-menu-item {
color: #ffffff;
&:hover {
color: #ffffff;
font-weight: 700;
}
&.is-active {
font-weight: 700;
}
}
.el-submenu__title {
color: #ffffff;
&:hover {
color: #FFFFFF;
font-weight: 700;
}
}
}
.el-dropdown {
color: #FFFFFF;
}
}
.el-dropdown-link {
cursor: pointer;
}
}
.top-nav-seize {
width: 100%;
height: 60px;
}
</style>
<style scoped>
.el-menu--horizontal .el-menu .el-menu-item,
.el-dropdown-menu__item {
padding: 0 14px;
}
</style>
<template>
<transition name="viewer-fade">
<div ref="el-image-viewer__wrapper" :style="{ 'z-index': zIndex }" tabindex="-1" class="el-image-viewer__wrapper">
<div class="el-image-viewer__mask" />
<!-- CLOSE -->
<span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
<i class="el-icon-circle-close" />
</span>
<!-- ARROW -->
<template v-if="!isSingle">
<span
:class="{ 'is-disabled': !infinite && isFirst }"
class="el-image-viewer__btn el-image-viewer__prev"
@click="prev"
>
<i class="el-icon-arrow-left" />
</span>
<span
:class="{ 'is-disabled': !infinite && isLast }"
class="el-image-viewer__btn el-image-viewer__next"
@click="next"
>
<i class="el-icon-arrow-right" />
</span>
</template>
<!-- ACTIONS -->
<div class="el-image-viewer__btn el-image-viewer__actions">
<div class="el-image-viewer__actions__inner">
<i class="el-icon-zoom-out" @click="handleActions('zoomOut')" />
<i class="el-icon-zoom-in" @click="handleActions('zoomIn')" />
<i class="el-image-viewer__actions__divider" />
<i :class="mode.icon" @click="toggleMode" />
<i class="el-image-viewer__actions__divider" />
<i class="el-icon-refresh-left" @click="handleActions('anticlocelise')" />
<i class="el-icon-refresh-right" @click="handleActions('clocelise')" />
</div>
</div>
<!-- CANVAS -->
<div class="el-image-viewer__canvas">
<template v-for="(url, i) in urlList">
<img
v-if="i === index"
ref="img"
:key="url"
:src="currentImg"
:style="imgStyle"
class="el-image-viewer__img"
@load="handleImgLoad"
@error="handleImgError"
@mousedown="handleMouseDown"
>
</template>
</div>
</div>
</transition>
</template>
<script>
import { on, off } from 'element-ui/src/utils/dom'
import { rafThrottle, isFirefox } from 'element-ui/src/utils/util'
const Mode = {
CONTAIN: {
name: 'contain',
icon: 'el-icon-full-screen'
},
ORIGINAL: {
name: 'original',
icon: 'el-icon-c-scale-to-original'
}
}
const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'
export default {
name: 'ElImageViewer',
props: {
urlList: {
type: Array,
default: () => []
},
zIndex: {
type: Number,
default: 2000
},
onSwitch: {
type: Function,
default: () => {}
},
onClose: {
type: Function,
default: () => {}
},
initialIndex: {
type: Number,
default: 0
}
},
data() {
return {
index: this.initialIndex,
isShow: false,
infinite: true,
loading: false,
mode: Mode.CONTAIN,
transform: {
scale: 1,
deg: 0,
offsetX: 0,
offsetY: 0,
enableTransition: false
}
}
},
computed: {
isSingle() {
return this.urlList.length <= 1
},
isFirst() {
return this.index === 0
},
isLast() {
return this.index === this.urlList.length - 1
},
currentImg() {
return this.urlList[this.index]
},
imgStyle() {
const { scale, deg, offsetX, offsetY, enableTransition } = this.transform
const style = {
transform: `scale(${scale}) rotate(${deg}deg)`,
transition: enableTransition ? 'transform .3s' : '',
'margin-left': `${offsetX}px`,
'margin-top': `${offsetY}px`
}
if (this.mode === Mode.CONTAIN) {
style.maxWidth = style.maxHeight = '100%'
}
return style
}
},
watch: {
index: {
handler: function(val) {
this.reset()
this.onSwitch(val)
}
},
currentImg(val) {
this.$nextTick(_ => {
const $img = this.$refs.img[0]
if (!$img.complete) {
this.loading = true
}
})
}
},
mounted() {
this.deviceSupportInstall()
// add tabindex then wrapper can be focusable via Javascript
// focus wrapper so arrow key can't cause inner scroll behavior underneath
this.$refs['el-image-viewer__wrapper'].focus()
},
methods: {
hide() {
this.deviceSupportUninstall()
this.onClose()
},
deviceSupportInstall() {
this._keyDownHandler = rafThrottle(e => {
const keyCode = e.keyCode
switch (keyCode) {
// ESC
case 27:
this.hide()
break
// SPACE
case 32:
this.toggleMode()
break
// LEFT_ARROW
case 37:
this.prev()
break
// UP_ARROW
case 38:
this.handleActions('zoomIn')
break
// RIGHT_ARROW
case 39:
this.next()
break
// DOWN_ARROW
case 40:
this.handleActions('zoomOut')
break
}
})
this._mouseWheelHandler = rafThrottle(e => {
const delta = e.wheelDelta ? e.wheelDelta : -e.detail
if (delta > 0) {
this.handleActions('zoomIn', {
zoomRate: 0.015,
enableTransition: false
})
} else {
this.handleActions('zoomOut', {
zoomRate: 0.015,
enableTransition: false
})
}
})
on(document, 'keydown', this._keyDownHandler)
on(document, mousewheelEventName, this._mouseWheelHandler)
},
deviceSupportUninstall() {
off(document, 'keydown', this._keyDownHandler)
off(document, mousewheelEventName, this._mouseWheelHandler)
this._keyDownHandler = null
this._mouseWheelHandler = null
},
handleImgLoad(e) {
this.loading = false
},
handleImgError(e) {
this.loading = false
e.target.alt = '加载失败'
},
handleMouseDown(e) {
if (this.loading || e.button !== 0) return
const { offsetX, offsetY } = this.transform
const startX = e.pageX
const startY = e.pageY
this._dragHandler = rafThrottle(ev => {
this.transform.offsetX = offsetX + ev.pageX - startX
this.transform.offsetY = offsetY + ev.pageY - startY
})
on(document, 'mousemove', this._dragHandler)
on(document, 'mouseup', ev => {
off(document, 'mousemove', this._dragHandler)
})
e.preventDefault()
},
reset() {
this.transform = {
scale: 1,
deg: 0,
offsetX: 0,
offsetY: 0,
enableTransition: false
}
},
toggleMode() {
if (this.loading) return
const modeNames = Object.keys(Mode)
const modeValues = Object.values(Mode)
const index = modeValues.indexOf(this.mode)
const nextIndex = (index + 1) % modeNames.length
this.mode = Mode[modeNames[nextIndex]]
this.reset()
},
prev() {
if (this.isFirst && !this.infinite) return
const len = this.urlList.length
this.index = (this.index - 1 + len) % len
},
next() {
if (this.isLast && !this.infinite) return
const len = this.urlList.length
this.index = (this.index + 1) % len
},
handleActions(action, options = {}) {
if (this.loading) return
const { zoomRate, rotateDeg, enableTransition } = {
zoomRate: 0.2,
rotateDeg: 90,
enableTransition: true,
...options
}
const { transform } = this
switch (action) {
case 'zoomOut':
if (transform.scale > 0.2) {
transform.scale = parseFloat((transform.scale - zoomRate).toFixed(3))
}
break
case 'zoomIn':
transform.scale = parseFloat((transform.scale + zoomRate).toFixed(3))
break
case 'clocelise':
transform.deg += rotateDeg
break
case 'anticlocelise':
transform.deg -= rotateDeg
break
}
transform.enableTransition = enableTransition
}
}
}
</script>
<template>
<div class="com-uploader">
<!-- 显示单个文件 -->
<template v-if="listType === 'picture'">
<div v-if="image" class="el-upload">
<div class="show-picture">
<img :src="image">
<div class="operate-mask" @click.stop>
<i class="el-icon-zoom-in" @click.stop="() => onPreview(image, 0)" />
<i v-if="mode === 'edit'" class="el-icon-delete" @click.stop="() => onRemove()" />
</div>
</div>
</div>
<PadBridge :upload-type="uploadType" :sign-key="signKey" :attachment-url="attachmentUrl" v-else>
<el-upload
ref="fileUpload"
:class="image ? 'uploaded' : ''"
:show-file-list="showFileList"
:action="action"
:accept="accept"
:headers="headers"
:data="postData"
:name="name"
:disabled="uploadLoading"
:before-upload="beforeUpload"
:on-progress="onUploading"
:on-success="(res, file, fileList) => onSuccess(res, file, fileList)"
:on-error="onError"
>
<div v-loading="uploadLoading" class="upload-text">
<p class="desc">{{ placeholder }}</p>
</div>
</el-upload>
</PadBridge>
</template>
<!-- 显示多个文件 -->
<div v-else class="upload-list-drag">
<draggable v-if="imageList.length > 0" v-model="imageList" group="people" class="el-upload-list el-upload-list--picture-card" @start="drag=true" @end="drag=false">
<div v-for="(item, index) in imageList" :key="index" class="el-upload uploaded">
<div class="show-picture">
<div v-if="item.fileName && item.fileName.indexOf('.pdf') > 0" class="preview-pdf">
<div class="pdf-container" />
</div>
<img v-else :src="item.accessUrl">
<div class="operate-mask" @click.stop>
<i class="el-icon-zoom-in" @click.stop="() => onPreview(item, index)" />
<i v-if="mode === 'edit'" class="el-icon-delete" @click.stop="() => onRemove(index)" />
</div>
</div>
</div>
</draggable>
<template v-if="imageList.length < limit">
<PadBridge :upload-type="uploadType" :sign-key="signKey" :attachment-url="attachmentUrl">
<el-upload
ref="fileUpload"
:show-file-list="showFileList"
:action="action"
:limit="limit"
:accept="accept"
:headers="headers"
:data="postData"
:name="name"
:disabled="uploadLoading"
:before-upload="beforeUpload"
:on-progress="onUploading"
:on-success="(res, file, fileList) => onSuccess(res, file, fileList)"
:on-error="onError"
:on-exceed="onExceed"
list-type="picture-card"
>
<div v-loading="uploadLoading" slot="default" class="upload-text">
<p class="desc">{{ placeholder }}</p>
</div>
</el-upload>
</PadBridge>
</template>
</div>
<image-viewer v-if="showViewer" :z-index="zIndex" :initial-index="imageIndex" :on-close="closeViewer" :url-list="previewList" />
</div>
</template>
<script>
// 编辑使用示例
// <uploader v-model="form.picture" :data="{rootPath: 'user/', path: 'compnay/'}" @on-error="onUploadError" @on-success="onUploadSuccess" @on-remove="onUploadRemove" />
// 预览使用示例
// <uploader v-model="form.picture" mode="preview" />
import PadBridge from '@/components/PadBridge'
import draggable from 'vuedraggable'
import { getToken } from '@/utils/auth'
import ImageViewer from './image-viewer.vue'
import { imageViewer } from '@/utils/upload'
export default {
name: 'Uploader',
components: {
draggable,
ImageViewer,
PadBridge
},
props: {
// 文件列表的类型, picture: 单个文件上传, picture-card: 展示图片列表
listType: {
type: String,
default: 'picture'
},
// 上传文件提示
placeholder: {
type: String,
default: '上传文件'
},
// 是否显示已上传文件列表, 仅对listType = 'picture'有效
showFileList: {
type: Boolean,
default: false
},
// 文件上传地址
action: {
type: String,
default: `${process.env.VUE_APP_BASIC_API}file/upload`
},
// 文件上传类型
accept: {
type: String,
default: 'image/png, image/jpeg, image/jpg'
},
// 是否需要加密
security: {
type: Boolean,
default: false
},
// 设置上传的请求头部
headers: {
type: Object,
default() {
return {
Authorization: `bearer ${getToken()}`
}
}
},
// 上传时附带的额外参数, 必填
postData: {
type: Object,
default() {
return {
rootPath: '',
path: ''
}
}
},
// 上传的文件字段名
name: {
type: String,
default: 'file'
},
// 最大允许上传个数 仅对listType = 'picture-card'有效
limit: {
type: Number,
default: 0
},
// v-model值
value: {
type: [String, Array],
default: ''
},
// 展示方式 edit:编辑模式, preview:预览模式
mode: {
type: String,
default: 'edit'
},
beforeUpload: {
type: Function,
default: function() { }
},
uploadType: {
type: Array,
default: undefined
},
signKey: {
type: String,
default: ''
},
// 附件信息
attachmentUrl: {
type: String,
default: ''
}
},
data() {
return {
image: '',
imageList: [],
previewList: [],
showViewer: false,
zIndex: 2000,
imageIndex: 0,
prevOverflow: '',
uploadLoading: false
}
},
watch: {
value(val) {
if (this.listType === 'picture') {
this.image = val
} else {
this.imageList = val || []
}
}
},
created() {
if (this.listType === 'picture') {
this.image = this.value
} else {
this.imageList = this.value || []
}
},
methods: {
/**
* 上传文件
*/
onUploading() {
// 设置当前正在loading中
this.uploadLoading = true
},
// beforeUpload是props,不是$emit,使用成$meit会导致beforeUpload无法校验文件
onBefore(file) {
// const isLt10M = file.size / 1024 / 1024 < 10
// if (!isLt10M) this.$message.error('上传图片大小不能超过 10MB!')
// return isLt10M
if (!this.data.rootPath || !this.data.path) {
this.$message.error('rootPath或path参数未配置')
return false
} else if (this.listType === 'picture-card' && this.limit > 0 && this.imageList.length >= this.limit) {
this.$message.error('已超过最大允许上传个数')
return false
}
this.$emit('on-before', file)
},
async onSuccess(res, file, fileList) {
const data = res.data || {}
try {
if (this.security) {
data.accessUrl = await imageViewer(data.uuid)
}
} catch (error) {
console.error('文件上传失败:', error)
}
// 关闭loading框
this.uploadLoading = false
if (res.success && data.accessUrl) {
this.$emit('on-success', data, file, fileList)
if (this.listType === 'picture') {
this.image = data.accessUrl
this.$emit('input', this.image)
} else {
this.imageList.push(data)
this.$emit('input', this.imageList)
}
this.$emit('upload-success', data)
} else {
this.$emit('on-error')
}
},
onError(err, file, fileList) {
// 关闭loading框
this.uploadLoading = false
this.$emit('on-error', err, file, fileList)
},
onExceed(files, fileList) {
this.$message.error('已超过最大允许上传个数')
this.$emit('on-exceed', files, fileList)
},
onPreview(img, index) {
// 如果当前是pdf
if (img && img.fileName && img.fileName.indexOf('.pdf') > 0) {
window.open(img.accessUrl, '_blank')
return
}
this.imageIndex = index
if (this.listType === 'picture') {
this.previewList = [img]
} else {
this.previewList = this.imageList.map(item => {
return item.accessUrl
})
}
this.prevOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
this.showViewer = true
},
closeViewer() {
document.body.style.overflow = this.prevOverflow
this.showViewer = false
},
onRemove(index) {
if (this.listType === 'picture') {
this.image = ''
this.$emit('on-remove', '')
this.$emit('input', this.image)
} else {
this.imageList.splice(index, 1)
this.$emit('on-remove', index)
this.$emit('input', this.imageList)
}
}
}
}
</script>
<style lang="scss">
.com-uploader {
.show-picture {
display: flex;
align-items: center;
width: 68px;
height: 68px;
position: relative;
> img {
width: 100%;
display: block;
}
&:hover {
.operate-mask {
opacity: 1;
.el-icon-delete,
.el-icon-zoom-in {
display: inline-block;
cursor: pointer;
color: #fff;
}
}
}
}
.operate-mask {
position: absolute;
width: 100%;
height: 22px;
left: 0;
bottom: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
background-color: rgba(0, 0, 0, 0.5);
transition: opacity 0.3s;
display: flex;
align-items: center;
justify-content: center;
.el-icon-delete,
.el-icon-zoom-in {
width: 50%;
height: 22px;
line-height: 22px;
flex: 1;
display: none;
font-size: 12px;
opacity: 0.8;
}
.el-icon-delete {
border-left: 1px solid #fff;
}
}
.uploaded {
.el-upload {
border-radius: 6px;
}
}
.el-upload {
background: #F7F8FA url(placeholder.svg) no-repeat center 11px;
border: 1px dashed #D7DBDF;
width: 68px;
height: 68px;
line-height: 1;
border-radius: 4px;
cursor: pointer;
position: relative;
overflow: hidden;
display: inline-block;
&:hover {
border-color: #ddd;
}
}
.upload-text {
margin: 0 auto;
width: 68px;
height: 68px;
position: absolute;
top: 0;
left: 0;
.desc {
margin: 44px auto 0;
font-size: 12px;
color: #9c9d9d;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
}
}
.upload-list-drag {
display: flex;
flex-wrap: wrap;
line-height: 1;
.el-upload {
margin: 0 10px 0 0;
}
}
.el-icon-circle-close {
color: #fff;
}
.preview-pdf {
align-items: center;
cursor: pointer;
display: flex;
width: 100%;
height: 100%;
justify-content: center;
.pdf-container {
align-items: center;
background: #FF5F61;
display: flex;
width: 28px;
height: 36px;
justify-content: center;
position: relative;
&:before {
color: #FFFFFF;
content: "P";
display: block;
font-size: 16px;
font-weight: 900;
}
&:after {
background: linear-gradient(127deg, #FFE6E7 0%, #FFC5C6 100%);
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.12);
content: "";
display: block;
width: 7px;
height: 6px;
position: absolute;
right: 0;
top: 0
}
}
}
}
</style>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ico_添加图片@2x</title>
<g id="运营" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="运营-商品管理-商品配置-配置-出账02" transform="translate(-433.000000, -1337.000000)" fill="#C3C7CF">
<g id="编组-47" transform="translate(382.000000, 1260.000000)">
<g id="编组-21" transform="translate(32.000000, 66.000000)">
<g id="编组-33" transform="translate(19.000000, 11.000000)">
<path d="M26,18.0411377 L26,22 C26,23.5976809 24.75108,24.9036609 23.1762728,24.9949073 L23,25 L18.9624023,25 L18.9624023,23 L23,23 C23.5128358,23 23.9355072,22.6139598 23.9932723,22.1166211 L24,22 L24,18.0411377 L26,18.0411377 Z M6,18.0114746 L6,22 C6,22.5128358 6.38604019,22.9355072 6.88337887,22.9932723 L7,23 L10.8862305,23 L10.8862305,25 L7,25 C5.40231912,25 4.09633912,23.75108 4.00509269,22.1762728 L4,22 L4,18.0114746 L6,18.0114746 Z M17.3120117,15 L21.4470215,20.4682617 L8.56689453,20.4682617 L11.6721191,16.602417 L14.2423096,19.1248779 L17.3120117,15 Z M10.5,10 C11.3284271,10 12,10.6715729 12,11.5 C12,12.3284271 11.3284271,13 10.5,13 C9.67157288,13 9,12.3284271 9,11.5 C9,10.6715729 9.67157288,10 10.5,10 Z M23,5 C24.5976809,5 25.9036609,6.24891996 25.9949073,7.82372721 L26,8 L26,12.052002 L24,12.052002 L24,8 C24,7.48716416 23.6139598,7.06449284 23.1166211,7.00672773 L23,7 L18.9785156,7 L18.9785156,5 L23,5 Z M11.0678711,5 L11.0678711,7 L7,7 C6.48716416,7 6.06449284,7.38604019 6.00672773,7.88337887 L6,8 L6,12.0449219 L4,12.0449219 L4,8 C4,6.40231912 5.24891996,5.09633912 6.82372721,5.00509269 L7,5 L11.0678711,5 Z" id="形状结合"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<div>
<div data-vjs-player>
<video ref="videoNode" class="video-js vjs-default-skin vjs-big-play-centered">抱歉,您的浏览器不支持</video>
</div>
</div>
</template>
<script>
import videojs from 'video.js'
import 'video.js/dist/video-js.css'
export default {
name: 'VideoPlayer',
props: {
src: {
type: String,
default: () => ''
},
controls: {
type: Boolean,
default: () => true
}
},
data() {
return {
player: null
}
},
watch: {
src: {
handler(val, oldVal) {
if (val === oldVal) {
return false
}
if (this.player) {
this.loading = true // 重新显示加载状态
const myPlayer = this.player
myPlayer.off('timeupdate') // 清空时间
myPlayer.reset() // 重置播放器
myPlayer.pause() // 暂停播放
myPlayer.src([ // 重新设置播放源
{
src: this.src,
type: 'video/mp4'
}
])
myPlayer.load(this.src) // 重新加载视频
myPlayer.play() // 播放视频
myPlayer.one('playing', function() {
// 加载完成,开始播放
this.loading = false // 隐藏加载状态
})
}
}
}
},
mounted() {
// 初始化播放器
const options = {
autoplay: true, // 自动播放
controls: this.controls, // 控制条
preload: 'auto', // 自动加载
errorDisplay: true, // 错误展示
// fluid: true, // 跟随外层容器变化大小,跟随的是外层宽度
width: 864, // 视频的宽度
height: 486, // 视频的高度
aspectRatio: '16:9', // 视频的宽高比
// controlBar: false, // 设为false不渲染控制条DOM元素,只设置controls为false虽然不展示,但是存在
// textTrackDisplay: false, // 不渲染字幕相关DOM
userActions: {
hotkeys: true // 是否支持热键
},
notSupportedMessage: '此视频暂无法播放,请稍后再试',
sources: [
{
src: this.src,
// type: 'rtmp/flv',
type: 'video/mp4'
}
]
}
this.player = videojs(
this.$refs.videoNode,
options,
function onPlayerReady() {
videojs.log('Your player is ready!')
}
)
},
beforeDestroy() {
if (this.player) {
this.player.dispose()
}
}
}
</script>
import Vue from 'vue'
import PageTitle from './PageTitle'
import Steps from './Steps'
import PageModule from './PageModule'
Vue.component('PageTitle', PageTitle)
Vue.component('Steps', Steps)
Vue.component('PageModule', PageModule)
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import router from './router'
import store from './store'
import '@/utils/os'
import '@/permission'
import '@/components'
import '@/styles/index.scss'
import 'normalize.css/normalize.css'
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
import router from './router'
import store from './store'
import { getToken } from '@/utils/auth'
import './resource'
let initPower = false
router.beforeEach(async(to, from, next) => {
// 查询logo
await store.dispatch('dict/getLogo')
// 如果当前是登录页或者重置密码页,则直接跳转
if (['Login', 'ResetPassword'].includes(to.name)) {
return next()
}
// 获取当前token
const hasToken = getToken()
// 如果当前没有登录过,则直接跳转去登录
if (!hasToken) {
return next({
name: 'Login',
query: to.query
})
}
// 如果初始化过权限
if (initPower) {
await store.dispatch('stepView/addCachedView', [to, from])
// 如果初始化设备
store.dispatch('user/getDeviceInfo')
} else {
initPower = true
try {
// 获取当前用户有权限的菜单
const menus = await store.dispatch('user/getMenus')
// 获取有权限的路由
const accessRoutes = await store.dispatch('permission/generateRoutes', menus)
// 刷新字典项
store.dispatch('dict/getDictionary')
// 动态添加
router.addRoutes(accessRoutes)
return next({ ...to, replace: true })
} catch (error) {
console.error(error)
}
}
next()
})
// 初始化路由配置上报后端存储
import asyncRoutes from '@/router/asyncRoutes'
import apiConf from '@/api-conf'
function getBtnResource(route) {
const name = route.name || ''
const apiData = apiConf[`${name}`] || {}
const resource = apiData.resource || []
if (!resource.length) return
const list = []
resource.forEach(item => {
list.push({
resourceCode: item.id,
resourceUiPath: '',
resourceUrl: item.api ? item.api.join() : '',
resourceName: item.name,
displaySort: 0,
resourceType: 1,
description: '',
icon: ''
})
})
return list
}
function getPageResource(routes) {
const res = []
routes.forEach(item => {
const tmp = { ...item }
if (tmp.children && tmp.children.length > 0) {
tmp.children = getPageResource(tmp.children)
}
const api = apiConf[tmp.name] ? apiConf[tmp.name].api : []
res.push({
resourceCode: tmp.name,
resourceUiPath: tmp.path,
resourceUrl: api.length ? api.join() : '',
resourceName: tmp.meta && tmp.meta.title,
displaySort: 0,
resourceType: 0,
description: '',
icon: tmp.meta && tmp.meta.icon,
children: tmp.children || getBtnResource(tmp)
})
})
return res
}
const resource = getPageResource(asyncRoutes)
console.log(resource, 'all resource')
export default resource
import management from './management'
import realnameEnterprise from './realname-enterprise'
import SpecialBusiness from './special-business'
import realnameCarEnterprise from './realname-car-enterprise'
import realnamePerson from './realname-person'
import personChange from './person-change'
import Search from './search'
import Audit from './audit'
export default [
{
path: '/',
name: 'Home',
meta: { title: '首页' },
redirect: '/home',
component: () => import('@/components/Layout'),
children: [
{
path: '/home',
name: 'HomePage',
hideMenu: true,
meta: { title: '首页' },
component: () => import('@/views/home')
}
]
},
management,
realnameEnterprise,
SpecialBusiness,
realnameCarEnterprise,
realnamePerson,
personChange,
Search,
Audit
]
const router = {
path: '/audit',
name: 'Audit',
redirect: '/audit/audit-list',
meta: { title: '人工审核', icon: 'basic' },
component: () => import('@/components/Layout'),
hideMenu: true,
children: [
{
name: 'AuditList',
path: '/audit/audit-list',
meta: { title: '待审核列表查询' },
component: () => import('@/views/audit/audit-list')
},
{
path: '/audit/rnr-list',
name: 'WorkOrderRnrList',
meta: { title: '平台车辆认证查询' },
component: () => import('@/views/audit/rnr-list')
}
]
}
export default router
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
base: process.env.VUE_APP_ROUTER,
mode: 'history',
routes: [
{
path: '/login',
name: 'Login',
component: () => import('@/views/login'),
children: []
},
{
path: '/reset/password',
name: 'ResetPassword',
component: () => import('@/views/login/reset-password.vue')
},
{
path: '/404',
name: '404',
component: () => import('@/views/404')
}
]
})
const router = {
path: '/management',
name: 'Management',
redirect: '/management/account',
meta: { title: '基础管理', icon: 'basic' },
component: () => import('@/components/Layout'),
showMenu: false,
children: [
{
name: 'OrganizationManagement',
path: '/management/organization',
meta: { title: '组织管理' },
component: () => import('@/views/management/organization')
},
{
name: 'UserManagement',
path: '/management/account',
meta: { title: '账号管理' },
component: () => import('@/views/management/account')
},
{
name: 'CarCardManagement',
path: '/management/car-card',
meta: { title: '车卡管理' },
component: () => import('@/views/management/car-card')
},
{
name: 'CarInfoManagement',
path: '/management/car-info',
meta: { title: '车辆管理' },
component: () => import('@/views/management/car-info')
},
{
name: 'AgreementManagement',
path: '/management/agreement',
meta: { title: '协议管理' },
component: () => import('@/views/management/agreement')
}
]
}
export default router
const router = {
path: '/person/change',
name: 'PersonChange',
redirect: '/person/change/1',
meta: { title: '变更责任人' },
component: () => import('@/components/Layout'),
hideMenu: true,
children: [
{
name: 'PersonChangeStep1',
path: '/person/change/step1',
meta: { title: '变更责任人信息', keepAlive: true },
component: () => import('@/views/person-change/step1')
},
{
name: 'PersonChangeStep2',
path: '/person/change/step2',
meta: { title: '完成人脸识别', keepAlive: true },
component: () => import('@/views/person-change/step2')
}
]
}
export default router
const router = {
path: '/car-enterprise/realname',
name: 'CarEnterpriseRealname',
redirect: '/car-enterprise/realname/1',
meta: { title: '车企实名认证' },
component: () => import('@/components/Layout'),
hideMenu: true,
children: [
{
name: 'CarEnterpriseRealnameStep1',
path: '/car-enterprise/realname/step1',
meta: { title: '车卡信息录入', keepAlive: true },
component: () => import('@/views/realname-car-enterprise/step1')
},
{
name: 'CarEnterpriseRealnameStep2',
path: '/car-enterprise/realname/step2',
meta: { title: '填写企业信息', keepAlive: true },
component: () => import('@/views/realname-car-enterprise/step2')
},
{
name: 'CarEnterpriseRealnameStep3',
path: '/car-enterprise/realname/step3',
meta: { title: '填写责任人信息', keepAlive: true },
component: () => import('@/views/realname-car-enterprise/step3')
},
{
name: 'CarEnterpriseRealnameStep4',
path: '/car-enterprise/realname/step4',
meta: { title: '完成人脸识别', keepAlive: true },
component: () => import('@/views/realname-car-enterprise/step4')
},
{
name: 'CarEnterpriseRealnameFlow1',
path: '/car-enterprise/realname/flow1',
meta: { title: '车卡信息录入', keepAlive: true },
component: () => import('@/views/realname-car-enterprise/flow1')
},
{
name: 'CarEnterpriseRealnameFlow2',
path: '/car-enterprise/realname/flow2',
meta: { title: '填写企业信息', keepAlive: true },
component: () => import('@/views/realname-car-enterprise/flow2')
},
{
name: 'CarEnterpriseRealnameFlow3',
path: '/car-enterprise/realname/flow3',
meta: { title: '填写责任人信息', keepAlive: true },
component: () => import('@/views/realname-car-enterprise/flow3')
}
]
}
export default router
const router = {
path: '/enterprise/realname',
name: 'EnterpriseRealname',
redirect: '/enterprise/realname/1',
meta: { title: '企业实名认证' },
component: () => import('@/components/Layout'),
hideMenu: true,
children: [
{
name: 'EnterpriseRealnameStepPad1',
path: '/enterprise/realname/step1-pad',
meta: { title: '车卡信息录入', keepAlive: true },
component: () => import('@/views/realname-enterprise/step1-pad')
},
{
name: 'EnterpriseRealnameStep1',
path: '/enterprise/realname/step1',
meta: { title: '车卡信息录入', keepAlive: true },
component: () => import('@/views/realname-enterprise/step1')
},
{
name: 'EnterpriseRealnameStep2',
path: '/enterprise/realname/step2',
meta: { title: '填写企业信息', keepAlive: true },
component: () => import('@/views/realname-enterprise/step2')
},
{
name: 'EnterpriseRealnameStep3',
path: '/enterprise/realname/step3',
meta: { title: '填写责任人信息', keepAlive: true },
component: () => import('@/views/realname-enterprise/step3')
},
{
name: 'EnterpriseRealnameStep4',
path: '/enterprise/realname/step4',
meta: { title: '完成人脸识别', keepAlive: true },
component: () => import('@/views/realname-enterprise/step4')
},
{
name: 'EnterpriseRealnameResult',
path: '/enterprise/realname/result',
meta: { title: '校验结果', keepAlive: true },
component: () => import('@/views/realname-enterprise/result')
}
]
}
export default router
const router = {
path: '/person/realname',
name: 'PersonRealname',
redirect: '/person/realname/step1',
meta: { title: '自然人实名认证' },
component: () => import('@/components/Layout'),
hideMenu: true,
children: [
{
name: 'PersonRealnameStep1',
path: '/person/realname/step1',
meta: { title: '车卡信息录入', keepAlive: true },
component: () => import('@/views/realname-person/step1')
},
{
name: 'PersonRealnameStep2',
path: '/person/realname/step2',
meta: { title: '填写客户信息', keepAlive: true },
component: () => import('@/views/realname-person/step2')
},
{
name: 'PersonRealnameStep3',
path: '/person/realname/step3',
meta: { title: '完成人脸识别', keepAlive: true },
component: () => import('@/views/realname-person/step3')
},
{
name: 'PersonRealnameResult',
path: '/person/realname/result',
meta: { title: '校验结果', keepAlive: true },
component: () => import('@/views/realname-person/result')
}
]
}
export default router
const router = {
path: '/search',
name: 'Search',
redirect: '/search/car-auth1',
meta: { title: '查询统计', icon: 'count' },
component: () => import('@/components/Layout'),
hideMenu: true,
children: [
{
name: 'CarAuth1',
path: '/search/car-auth1',
meta: { title: '单车认证查询' },
component: () => import('@/views/search/car-auth1')
},
{
name: 'CarAuth2',
path: '/search/car-auth2',
meta: { title: '车辆认证查询' },
component: () => import('@/views/search/car-auth2')
},
{
name: 'SystemLog',
path: '/search/system-log',
meta: { title: '系统日志' },
component: () => import('@/views/search/system-log')
}
// {
// name: 'CheckAccount',
// path: '/search/check-account',
// meta: { title: '服务对账' },
// component: () => import('@/views/search/check-account')
// }
]
}
export default router
const router = {
path: '/special-business',
name: 'SpecialBusiness',
redirect: '/special-business/card-binding/1',
meta: { title: '特殊业务' },
component: () => import('@/components/Layout'),
hideMenu: true,
children: [
// 一车多卡绑定
{
name: 'CardBindingStepOne',
path: '/special-business/card-binding/1',
meta: { title: '一车多卡绑定-车卡信息录入' },
component: () => import('@/views/special-business/card-binding/step-one')
},
{
name: 'CardBindingStepTwo',
path: '/special-business/card-binding/2',
meta: { title: '一车多卡绑定-客户确认' },
component: () => import('@/views/special-business/card-binding/step-two')
},
// 卡解绑
{
name: 'CardUnbindingStepOne',
path: '/special-business/card-unbinding/1',
meta: { title: '卡解绑-车卡信息录入' },
component: () => import('@/views/special-business/card-unbinding/step-one')
},
{
name: 'CardUnbindingStepTwo',
path: '/special-business/card-unbinding/original2',
meta: { title: '卡解绑-客户确认' },
component: () => import('@/views/special-business/card-unbinding/step-two')
},
{
name: 'CardUnbindingStepTwoSnd',
path: '/special-business/card-unbinding/snd2',
meta: { title: '卡解绑-客户信息录入' },
component: () => import('@/views/special-business/card-unbinding/step-two-snd')
},
{
name: 'CardUnbindingStepThree',
path: '/special-business/card-unbinding/3',
meta: { title: '卡解绑-人脸识别' },
component: () => import('@/views/special-business/card-unbinding/step-three')
},
// 换件
{
name: 'ReplacementStepOne',
path: '/special-business/replacement/1',
meta: { title: '换件-车卡信息录入' },
component: () => import('@/views/special-business/replacement/step-one')
},
{
name: 'ReplacementStepTwo',
path: '/special-business/replacement/2',
meta: { title: '换件-客户确认' },
component: () => import('@/views/special-business/replacement/step-two')
},
// 车企实名解绑
{
name: 'CarUnbindingStepOne',
path: '/special-business/car-unbinding/1',
meta: { title: '车企实名解绑-车卡信息' },
component: () => import('@/views/special-business/car-unbinding/step-one')
},
{
name: 'CarUnbindingStepTwo',
path: '/special-business/car-unbinding/2',
meta: { title: '车企实名解绑-车企信息' },
component: () => import('@/views/special-business/car-unbinding/step-two')
},
// 企业实名解绑
{
name: 'EnterpriseUnbindingStepOne',
path: '/special-business/enterprise-unbinding/1',
meta: { title: '企业实名解绑-车卡信息' },
component: () => import('@/views/special-business/enterprise-unbinding/step-one')
},
{
name: 'EnterpriseUnbindingStepTwo',
path: '/special-business/enterprise-unbinding/2',
meta: { title: '企业实名解绑-车企信息' },
component: () => import('@/views/special-business/enterprise-unbinding/step-two')
}
]
}
export default router
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.user.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
roles: state => state.user.roles,
organId: state => state.user.organId,
organName: state => state.user.organName,
tenantNo: state => state.user.tenantNo,
permission_routes: state => state.permission.routes,
permissionBtns: state => state.permission.permissionBtns,
currentChildRoute: state => state.permission.currentChildRoute,
currentRouteTitle: state => state.permission.currentRouteTitle,
visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.stepView.cachedViews,
dict: state => state.dict.dictionary,
addRoutes: state => state.permission.addRoutes,
logo: state => state.dict.logo,
companyName: state => state.dict.companyName,
model: state => state.dict.model,
deviceType: state => state.user.deviceType,
agreements: state => state.dict.agreements,
attachmentUrl: state => state.attachment.attachmentUrl
}
export default getters
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment