初始化代码

This commit is contained in:
2025-12-22 17:13:05 +08:00
parent ed0de08e3a
commit 1f7e9d401b
2947 changed files with 526137 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
<!--
* @Descripttion: 左侧边栏广告
* @Author: xiao li
* @Date: 2020-07-06 12:17:06
* @LastEditors: xiao li
* @LastEditTime: 2021-03-15 13:22:26
-->
<template>
<div class="lb-ad" :class="!adSwitch ? 'ad-collapse' : ''">
<div class="ad-main">
<!-- <lb-image src="@/assets/icon/apps.png"/> -->
</div>
<div class="flod" @click="handleFold">{{adSwitch ? '折叠' : '展开'}}</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data () {
return {
key: '广告'
}
},
created () {
},
methods: {
handleFold () {
let {adSwitch} = this
this.$store.commit('handleAdSwitch', !adSwitch)
}
},
computed: {
...mapGetters(['adSwitch'])
}
}
</script>
<style lang="scss" scoped>
.lb-ad{
width: 220px;
position: fixed;
transition: width 0.2s linear;
top: 70px;
right: 0;
height: 1000px;
z-index: 2;
.ad-main{
position: absolute;
top: 0;
left: 0;
width: 220px;
background: #fff;
height: 100%;
font-size: 14px;
padding: 10px;
}
.flod{
position: absolute;
top: 0;
bottom: 0;
left: -20px;
margin: auto;
width: 20px;
height: 80px;
background: #BFBFBF;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
cursor: pointer;
color: #fff;
font-size: 14px;
}
}
.ad-collapse{
width: 0px;
}
</style>

View File

@@ -0,0 +1,28 @@
import Vue from 'vue'
import TopNav from './topNav.vue'
import LbButton from './lbButton.vue'
import LbSwitch from './lbSwitch.vue'
import LbTips from './lbTips.vue'
import LbPage from './lbPage.vue'
import LbClassifyTitle from './lbClassifyTitle.vue'
import LbUpload from './lbUpload.vue'
import LbToolTips from './lbToolTips.vue'
import LbUploadCover from './lbUploadCover.vue'
import LbUeditor from './lbUeditor.vue'
import LbCover from './lbCover.vue'
import LbImage from './lbImage.vue'
import LbMap from './lbMap.vue'
Vue.component('top-nav', TopNav)
Vue.component('lb-button', LbButton)
Vue.component('lb-switch', LbSwitch)
Vue.component('lb-tips', LbTips)
Vue.component('lb-page', LbPage)
Vue.component('lb-classify-title', LbClassifyTitle)
Vue.component('lb-upload', LbUpload)
Vue.component('lb-tool-tips', LbToolTips)
Vue.component('lb-upload-cover', LbUploadCover)
Vue.component('lb-ueditor', LbUeditor)
Vue.component('lb-cover', LbCover)
Vue.component('lb-image', LbImage)
Vue.component('lb-map', LbMap)

View File

@@ -0,0 +1,79 @@
<template>
<el-button
:disabled="isDisabled"
:type="type"
:plain="plain"
:round="round"
:icon="icon"
:size="size"
:loading='loading'
@click="handleClick"
>
<slot></slot>
</el-button>
</template>
<script>
export default {
props: {
type: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
plain: {
type: Boolean,
default: false
},
round: {
type: Boolean,
default: false
},
icon: {
type: String,
default: ''
},
size: {
type: String,
default: 'medium'
},
opType: {
type: String,
default: ''
},
loading: {
type: Boolean,
default: false
}
},
data () {
return {
isDisabled: this.disabled,
currentIndex: this.$store.state.operate.currentIndex
}
},
created () {
let {isOnly, auth, pagePermission} = this.$route.meta
let {disabled} = this
if (disabled) {
this.isDisabled = disabled
} else if (this.opType) {
if (isOnly) {
this.isDisabled = auth.indexOf(this.opType) === -1
} else {
this.isDisabled = pagePermission[this.currentIndex].auth.indexOf(this.opType) === -1
}
}
},
methods: {
handleClick () {
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,39 @@
<!--
* @Description: 标题栏
* @Author: xiao li
* @Date: 2021-07-04 00:16:14
* @LastEditTime: 2021-09-10 16:31:28
* @LastEditors: xiao li
-->
<template>
<div class="lb-goods-edit-classify mb-lg">
<div class="title">{{ title }}</div>
<span v-if="tips" class="tips">{{ tips }}</span>
</div>
</template>
<script>
export default {
props: ['title', 'tips']
}
</script>
<style lang="scss" scoped>
.lb-goods-edit-classify {
width: 100%;
border-top: 1px solid $lineColor;
display: flex;
align-items: center;
.title {
display: inline-block;
padding: 6px 15px;
color: $themeColor;
background: $columnBgColor;
margin-right: 10px;
}
.tips {
color: $tipsColor;
font-size: 12px;
}
}
</style>

View File

@@ -0,0 +1,375 @@
<template>
<div class="lb-cover-wrap">
<div
class="lb-cover"
:class="[size]"
@click="showUploadModel"
v-if="type === 'single'"
>
<lb-image v-if="cover.length > 0" :src="cover[cover.length - 1].url" />
<i v-else class="el-icon-plus"></i>
<i
v-if="cover.length > 0"
@click.stop="deleteCover"
class="el-icon-circle-close"
></i>
</div>
<div v-else-if="type === 'more'" class="lb-upload-more">
<vuedraggable @change="dragChange" v-model="cover">
<transition-group class="flex-warp">
<div
class="more-item"
:class="[size]"
v-for="(item, index) in cover"
:key="`drag_${index}`"
>
<lb-image :src="item.url" />
<div class="mask">
<i
@click="lookBigImg(item.url)"
class="el-icon-zoom-in"
v-if="item.url"
></i>
<i
@click="delImg(index)"
class="el-icon-delete"
v-if="isToDel"
></i>
</div>
</div>
</transition-group>
</vuedraggable>
<div class="flex-warp" v-if="cover.length < fileSize">
<div class="up-item" :class="[size]" @click="showUploadModel">
<i class="el-icon-plus"></i>
</div>
<lb-tool-tips class="ml-sm" v-if="tips">{{
`图片建议尺寸${tips}`
}}</lb-tool-tips>
</div>
<el-dialog
:visible.sync="centerDialogVisible"
width="800px"
center
:append-to-body="true"
>
<div class="dialog-inner-img">
<lb-image class="dialog-img" :src="viewImg" />
</div>
</el-dialog>
</div>
<lb-button type="primary" size="mini" @click="showUploadModel" v-else
>选择</lb-button
>
<block v-if="isToDel">
<lb-upload
:paramData="paramData"
:filesLength="fileList.length"
:fileSize="fileSize"
:multilineType="type"
:visibles.sync="showUpload"
:fileType="fileType"
:fileAccept="fileAccept"
@selectedFiles="selectedFiles"
></lb-upload>
</block>
</div>
</template>
<script>
import vuedraggable from 'vuedraggable'
export default {
components: {
vuedraggable
},
props: {
type: {
type: String,
default () {
return 'single'
}
},
size: {
type: String,
default () {
return 'big'
}
},
tips: {
type: String,
default () {
return ''
}
},
fileType: {
type: String,
default () {
return 'image'
}
},
fileAccept: {
type: String,
default () {
return ''
}
},
fileSize: {
type: Number,
default () {
return 9
}
},
fileList: {
type: [Array, String],
default () {
return []
}
},
isToDel: {
type: Boolean,
default () {
return true
}
},
paramData: {
type: Object,
default () {
return {}
}
}
},
data () {
return {
showUpload: false,
cover: [],
centerDialogVisible: false,
viewImg: ''
}
},
created () {
if (this.fileList && this.fileList.length) {
this.cover = this.fileList
}
},
methods: {
selectedFiles (img) {
if (!img.length) return false
let selectedImgs = this.type === 'single' ? [img[img.length - 1]] : img
this.cover = selectedImgs
this.$emit('selectedFiles', selectedImgs)
},
dragChange (e) {
this.$emit('moveFiles', this.cover)
},
showUploadModel () {
this.showUpload = true
},
lookBigImg (url) {
this.viewImg = url
this.centerDialogVisible = true
},
delImg (index) {
this.cover.splice(index, 1)
},
deleteCover () {
this.cover = []
this.$emit('selectedFiles', [])
}
},
watch: {
fileList: {
deep: true,
handler (val) {
if (val && typeof val === 'object') {
this.cover = val
}
}
}
}
}
</script>
<style lang="scss" scoped>
.lb-cover-wrap {
display: inline-block;
.lb-cover.small {
width: 60px;
height: 60px;
.el-image {
width: 60px;
height: 60px;
}
i {
font-size: 20px;
line-height: 60px;
}
.el-icon-circle-close {
font-size: 18px;
line-height: 18px;
}
}
.lb-cover.middle {
width: 80px;
height: 80px;
.el-image {
width: 80px;
height: 80px;
}
i {
font-size: 20px;
line-height: 80px;
}
.el-icon-circle-close {
font-size: 22px;
line-height: 22px;
}
}
.lb-cover {
width: 100px;
height: 100px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
text-align: center;
overflow: hidden;
position: relative;
.el-image {
width: 100px;
height: 100px;
}
i {
font-size: 26px;
line-height: 100px;
}
&:hover {
border: 1px dashed #09f;
.el-icon-circle-close {
display: inline-block;
}
}
.el-icon-circle-close {
display: none;
z-index: 10;
position: absolute;
right: 5px;
top: 5px;
line-height: 1;
}
}
.lb-upload-more {
width: 100%;
display: flex;
flex-wrap: wrap;
.more-item.small {
width: 60px;
height: 60px;
.el-image {
width: 60px;
height: 60px;
z-index: 5;
}
}
.more-item.middle {
width: 80px;
height: 80px;
.el-image {
width: 80px;
height: 80px;
z-index: 5;
}
}
.more-item {
width: 100px;
height: 100px;
border: 1px solid #d9d9d9;
border-radius: 6px;
position: relative;
margin-right: 10px;
margin-bottom: 10px;
overflow: hidden;
.el-image {
width: 100px;
height: 100px;
z-index: 5;
}
.mask {
display: none;
position: absolute;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
font-size: 20px;
color: #fff;
justify-content: center;
align-items: center;
z-index: 10;
top: 0;
left: 0;
i {
margin: 0 10px;
cursor: pointer;
}
}
&:hover {
.mask {
display: flex;
}
}
}
.up-item {
width: 100px;
height: 100px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
text-align: center;
i {
font-size: 26px;
line-height: 100px;
}
&:hover {
border: 1px dashed #09f;
}
}
.up-item.small {
width: 60px;
height: 60px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
text-align: center;
i {
font-size: 18px;
line-height: 60px;
}
&:hover {
border: 1px dashed #09f;
}
}
.up-item.middle {
width: 80px;
height: 80px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
text-align: center;
i {
font-size: 22px;
line-height: 80px;
}
&:hover {
border: 1px dashed #09f;
}
}
}
.dialog-inner-img {
display: flex;
justify-content: center;
align-items: center;
.dialog-img {
width: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,36 @@
<template>
<el-image :fit="fit" :src="src">
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</template>
<script>
export default {
props: {
fit: {
type: String,
default: 'cover'
},
src: {
type: String,
default: ''
}
}
}
</script>
<style lang="scss" scoped>
.el-image{
background: $bgThemeColor;
display: flex;
justify-content: center;
align-items: center;
.image-slot{
i{
font-size: 20px;
}
}
}
</style>

View File

@@ -0,0 +1,173 @@
<template>
<el-dialog
title="获取经纬度"
:visible.sync="centerDialogVisible"
width="600px"
center
:append-to-body="true"
>
<div class="dialog-inner">
<div class="map-search">
<el-input placeholder="输入地址" v-model="address"></el-input>
<lb-button size="mini" type="primary" @click="searchMapAddr"
> </lb-button
>
</div>
<div id="container"></div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="centerDialogVisible = false">{{
$t('action.cancel')
}}</el-button>
<el-button type="primary" @click="confirmLatLng">{{
$t('action.comfirm')
}}</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
props: {
dialogVisible: {
type: Boolean,
default: false
}
},
data () {
return {
centerDialogVisible: false,
map: null,
info: null,
address: '',
marker: '',
geocoder: null,
latLng: {
lat: 30.657535,
lng: 104.065783
}
}
},
methods: {
initMap () {
let that = this
let { lat, lng } = this.latLng
// 中心坐标
// eslint-disable-next-line no-undef
let center = new qq.maps.LatLng(lat, lng)
// eslint-disable-next-line no-undef
let map = new qq.maps.Map(
document.getElementById('container'),
{
center: center,
zoom: 12
}
)
that.map = map
// eslint-disable-next-line no-undef
that.info = new qq.maps.InfoWindow({
map: map
})
// eslint-disable-next-line no-undef
qq.maps.event.addListener(map, 'click', async function (val, el) {
if (that.marker) { that.marker.setMap(null) }
let { lat, lng } = val.latLng
that.latLng = val.latLng
// eslint-disable-next-line no-undef
that.marker = new qq.maps.Marker({
// 标记的位置
// eslint-disable-next-line no-undef
position: new qq.maps.LatLng(lat, lng),
map: map
})
that.info.open()
that.info.setContent(`<div style="margin:10px;">
<p>纬度:${lat}</p>
<p>经度:${lng}</p>
</div>`)
// eslint-disable-next-line no-undef
that.info.setPosition(new qq.maps.LatLng(lat, lng))
})
},
openQQMap () {
setTimeout(() => {
this.initMap()
this.initGeocoder()
}, 500)
},
/**
* @method 根据位置搜索坐标
*/
searchMapAddr () {
let { address } = this
this.geocoder.getLocation(address)
},
initGeocoder () {
let that = this
// eslint-disable-next-line no-undef
that.geocoder = new qq.maps.Geocoder()
// 设置服务请求成功的回调函数
that.geocoder.setComplete(function (result) {
let { lat, lng } = result.detail.location
that.latLng = result.detail.location
that.map.setCenter(result.detail.location)
// eslint-disable-next-line no-undef
that.marker = new qq.maps.Marker({
map: that.map,
position: result.detail.location
})
that.info.open()
that.info.setContent(`<div style="margin:10px;">
<p>纬度:${lat}</p>
<p>经度:${lng}</p>
</div>`)
// eslint-disable-next-line no-undef
that.info.setPosition(new qq.maps.LatLng(lat, lng))
})
// 若服务请求失败,则运行以下函数
that.geocoder.setError(function () {
that.$message.error('请输入包含市级的地址!')
})
},
/**
* @method 确定经纬度
*/
confirmLatLng () {
this.centerDialogVisible = false
this.$emit('selectedLatLng', this.latLng)
}
},
watch: {
dialogVisible (newValue, oldValue) {
if (newValue) {
this.centerDialogVisible = true
this.openQQMap()
}
},
centerDialogVisible (val) {
if (!val) {
this.$emit('update:dialogVisible', false)
}
}
}
}
</script>
<style lang="scss" scoped>
#container {
width: 500px;
height: 400px;
margin: 0 auto;
}
.dialog-inner {
.map-search {
display: flex;
justify-content: center;
margin-bottom: 20px;
.el-input {
width: 300px;
margin-right: 10px;
}
}
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<div class="lb-page">
<slot name="button">
<!-- 插入按钮 -->
</slot>
<div v-if="batch">
<div :class="[{ isShowBatch: isShowBatch }]">已选 {{ selected }} </div>
<div>
<span v-if="isShowBatch">批量</span>
<slot>
<!-- 插入按钮 -->
</slot>
</div>
</div>
<div v-if="slot">
<slot>
<!-- 插入按钮 -->
</slot>
</div>
<span v-else></span>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5, 10, 20]"
:page-size="currentPageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
v-if="isShowPage"
>
</el-pagination>
</div>
</template>
<script>
export default {
props: {
isShowPage: {
type: Boolean,
default: true
},
isShowBatch: {
type: Boolean,
default: true
},
batch: {
type: Boolean,
default: true
},
slot: {
type: Boolean,
default: false
},
page: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
total: {
type: Number,
default: 0
},
selected: {
type: Number,
default: 0
}
},
data () {
return {
currentPage: this.page,
currentPageSize: this.pageSize
}
},
methods: {
handleSizeChange (val) {
this.currentPageSize = val
this.$emit('handleSizeChange', val)
},
handleCurrentChange (val) {
this.currentPage = val
this.$emit('handleCurrentChange', val)
},
// 全选 反选
batchUpperAll (type) {
if (type === 1) {
this.$refs.multipleTable.clearSelection()
this.multipleSelection = this.tableData
this.tableData.map(item => {
this.$refs.multipleTable.toggleRowSelection(item)
})
} else {
let data = JSON.parse(JSON.stringify(this.multipleSelection))
let arr = []
data.map(item => {
arr.push(item.id)
})
this.$refs.multipleTable.clearSelection()
this.multipleSelection = []
this.tableData.map(item => {
if (!arr.includes(item.id)) {
this.multipleSelection.push(item)
this.$refs.multipleTable.toggleRowSelection(item)
}
})
}
}
},
watch: {
page (val) {
this.currentPage = val
},
pageSize (val) {
this.currentPageSize = val
}
}
}
</script>
<style lang="scss" scoped>
.lb-page {
width: 100%;
margin-top: 20px;
display: flex;
justify-content: space-between;
height: 40px;
font-size: 14px;
> div {
display: flex;
align-items: center;
white-space: nowrap;
> div.isShowBatch {
border-right: 1px solid #e8e8e8;
}
> div {
&:first-child {
height: 40px;
padding-right: 30px;
margin-right: 30px;
line-height: 40px;
}
.el-button {
margin-left: 10px;
}
}
}
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<el-switch
v-model="val"
:disabled='isDisabled'
:width='coreWidth'
:active-icon-class='activeIconClass'
:inactive-icon-class='inactiveIconClass'
:active-text='activeText'
:inactive-text='inactiveText'
:active-value='activeValue'
:inactive-value='inactiveValue'
:active-color='activeColor'
:inactive-color='inactiveColor'
:name='name'
@change="handleSwitchValue"
>
</el-switch>
</template>
<script>
export default {
props:
{
opType: {
type: String,
default: ''
},
value: {
type: [Boolean, String, Number],
default: false
},
disabled: {
type: Boolean,
default: false
},
width: {
type: Number,
default: 40
},
activeIconClass: {
type: String,
default: ''
},
inactiveIconClass: {
type: String,
default: ''
},
activeText: String,
inactiveText: String,
activeColor: {
type: String,
default: ''
},
inactiveColor: {
type: String,
default: ''
},
activeValue: {
type: [Boolean, String, Number],
default: true
},
inactiveValue: {
type: [Boolean, String, Number],
default: false
},
name: {
type: String,
default: ''
},
validateEvent: {
type: Boolean,
default: true
},
id: String
},
data () {
return {
val: this.value,
coreWidth: this.width,
isDisabled: this.disabled,
currentIndex: this.$store.state.operate.currentIndex
}
},
created () {
let {isOnly, auth, pagePermission} = this.$route.meta
if (this.opType) {
if (isOnly) {
this.isDisabled = auth.indexOf(this.opType) === -1
} else {
this.isDisabled = pagePermission[this.currentIndex].auth.indexOf(this.opType) === -1
}
}
},
computed: {
checked () {
return this.value === this.activeValue
}
},
methods: {
handleSwitchValue (val) {
this.$emit('change', val)
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,62 @@
<!--
* @Descripttion: 头部批注
* @Author: xiao li
* @Date: 2020-07-06 12:17:06
* @LastEditors: xiao li
* @LastEditTime: 2021-04-15 14:21:09
-->
<template>
<div class="lb-tips" :class="[type]">
<i class="iconfont" :class="[icon]" v-if="isIcon"></i>
<div class="custom-item">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
icon: {
type: String,
default: 'icon-warn'
},
type: {
type: String,
default: 'danger'
},
isIcon: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss" scoped>
.lb-tips {
width: 100%;
padding: 20px;
border-radius: 4px;
margin-bottom: 16px;
display: flex;
align-items: center;
justify-content: center;
border-left: 5px solid $primaryColor;
background: $primaryBgColor;
i{
color: $primaryColor;
margin-right: 10px;
}
.custom-item{
flex: 1;
}
}
.lb-tips.danger {
border-left: 5px solid $dangerColor;
background: $dangerBgColor;
i{
color: $dangerColor;
}
}
</style>

View File

@@ -0,0 +1,66 @@
<!--
* @Descripttion: 批注
* @Author: xiao li
* @Date: 2020-07-06 12:17:06
* @LastEditors: xiao li
* @LastEditTime: 2021-09-10 16:31:43
-->
<template>
<block>
<div class="lb-tool-tips" :class="[type]" v-if="mode === 'text'">
<slot></slot>
</div>
<el-tooltip
class="tool-tips"
effect="dark"
placement="right"
:style="{ paddingTop: `${padding}px` }"
v-if="mode === 'tooltip'"
>
<div class="content" slot="content"><slot></slot></div>
<i class="el-icon-question"></i>
</el-tooltip>
</block>
</template>
<script>
export default {
props: {
mode: {
type: String,
default: 'tooltip'
},
type: {
type: String,
default: 'c-caption'
},
padding: {
type: Number,
default: 10
}
}
}
</script>
<style lang="scss" scoped>
.lb-tool-tips {
font-size: 12px;
line-height: 1.6;
padding-top: 10px;
}
.lb-tool-tips.c-link {
cursor: pointer;
}
.tool-tips {
margin-left: 5px;
vertical-align: top;
font-size: 18px;
color: #333;
.content {
max-width: 300px;
}
.el-icon-question {
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,355 @@
<template>
<div>
<script ref="script" :name="name" type="text/plain"></script>
<lb-upload
type="more"
:fileType="fileType"
:visibles.sync="showDialog"
:isIframe="isIframe"
multilineType="more"
@selectedFiles="getChoiceFiles"
></lb-upload>
</div>
</template>
<script>
import LoadEvent from '@/utils/Event.js'
import Debounce from '@/utils/Debounce.js'
import Bus from '@/Bus'
import { mapGetters } from 'vuex'
export default {
name: 'VueUeditorWrap',
data () {
return {
showDialog: false,
fileType: 'image',
isIframe: false,
status: 0,
initValue: '',
defaultConfig: {
// VUE CLI 3 会添加 process.env.BASE_URL 的环境变量,而 VUE CLI 2 没有,所以借此设置 UEDITOR_HOME_URL能涵盖大部分 Vue 开发者的使用场景
UEDITOR_HOME_URL: process.env.NODE_ENV === 'production' ? window.lbConfig.jsPath + 'static/Ueditor/' : '/static/Ueditor/',
enableAutoSave: false,
// 编辑器不自动被内容撑高
autoHeightEnabled: false,
// 初始容器高度
initialFrameHeight: 500,
// 初始容器宽度
initialFrameWidth: 'auto',
// 上传文件接口(这个地址是我为了方便各位体验文件上传功能搭建的临时接口,请勿在生产环境使用!!!)
serverUrl: 'http://35.201.165.105:8000/controller.php',
// UEditor 资源文件的存放路径,如果你使用的是 vue-cli 生成的项目通常不需要设置该选项vue-ueditor-wrap 会自动处理常见的情况如果需要特殊配置参考下方的常见问题2
// UEDITOR_HOME_URL: '/static/Ueditor/',
zIndex: 5,
topOffset: false,
autoFloatEnabled: false,
toolbars: []
},
thatUeditor: {}
}
},
props: {
// v-model 实现方式
mode: {
type: String,
default: 'observer',
validator: function (value) {
// 1. observer 借助 MutationObserver API https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
// 2. listener 借助 UEditor 的 contentChange 事件 https://ueditor.baidu.com/doc/#UE.Editor:contentChange
return ['observer', 'listener'].indexOf(value) !== -1
}
},
value: {
type: String,
default: ''
},
config: {
type: Object,
default: function () {
return {}
}
},
init: {
type: Function,
default: function () {
return () => { }
}
},
destroy: {
type: Boolean,
default: false
},
ueditorType: {
type: Number,
default: 1
},
name: {
type: String,
default: ''
},
observerDebounceTime: {
type: Number,
default: 50,
validator: function (value) {
return value >= 20
}
},
observerOptions: {
type: Object,
default: function () {
// https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
return {
attributes: true, // 是否监听 DOM 元素的属性变化
attributeFilter: ['src', 'style', 'type', 'name'], // 只有在该数组中的属性值的变化才会监听
characterData: true, // 是否监听文本节点
childList: true, // 是否监听子节点
subtree: true // 是否监听后代元素
}
}
},
// 本组件提供对普通 Vue 项目和 Nuxt 项目开箱即 用的支持,但如果是自己搭建的 Vue SSR 项目,可能需要自行区分是客户端还是服务端环境并跳过环境检测,直接初始化
forceInit: {
type: Boolean,
default: false
}
},
created () {
Bus.$on('showLbUpload', (obj) => {
console.log(obj)
this.thatUeditor = obj.ueditor
let { uploadStatus } = this
if (uploadStatus) {
return false
} else {
this.showDialog = obj.val
this.isIframe = true
this.fileType = obj.fileType
this.$store.commit('handleUploadStatus', obj.val)
}
})
},
computed: {
mixedConfig () {
return Object.assign({}, this.defaultConfig, this.config)
},
...mapGetters(['uploadStatus'])
},
methods: {
// 添加自定义按钮(自定义按钮,自定义弹窗等操作从 2.2.0 版本 开始不再考虑直接集成,这会使得组件和 UEditor 过度耦合,但为了兼容一些老版用户的写法,这个方法依然保留)
registerButton ({ name, icon, tip, handler, index, UE = window.UE }) {
UE.registerUI(name, (editor, name) => {
editor.registerCommand(name, {
execCommand: () => {
handler(editor, name)
}
})
const btn = new UE.ui.Button({
name,
title: tip,
cssRules: `background-image: url(${icon}) !important;background-size: cover;`,
onclick () {
editor.execCommand(name)
}
})
editor.addListener('selectionchange', () => {
const state = editor.queryCommandState(name)
if (state === -1) {
btn.setDisabled(true)
btn.setChecked(false)
} else {
btn.setDisabled(false)
btn.setChecked(state)
}
})
return btn
}, index, this.id)
},
// 实例化编辑器
_initEditor () {
let toolbars = [
'fullscreen', 'source', '|',
'undo', 'redo', '|',
'bold', 'italic', 'underline', 'strikethrough', 'removeformat', 'formatmatch', 'autotypeset', 'pasteplain', '|',
// 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', '|',
'forecolor', 'backcolor', '|',
'fontfamily', 'fontsize', '|',
'justifyleft', 'justifycenter', 'justifyright', 'justifyjustify'
]
let arr = {
1: ['|', 'lbinsertimage', 'lbinsertvideo', 'lbinsertmusic'],
2: [],
3: ['|', 'lbinsertimage']
}
if (arr[this.ueditorType].length > 0) {
arr[this.ueditorType].map(item => {
toolbars.push(item)
})
}
this.defaultConfig.toolbars = [toolbars]
this.$refs.script.id = this.id = 'editor_' + Math.random().toString(16).slice(-6) // 这么做是为了支持 Vue SSR因为如果把 id 属性放在 data 里会导致服务端和客户端分别计算该属性的值,而造成 id 不匹配无法初始化的 BUG
this.init()
this.$emit('beforeInit', this.id, this.mixedConfig)
this.editor = window.UE.getEditor(this.id, this.mixedConfig)
this.editor.addListener('ready', () => {
if (this.status === 2) { // 使用 keep-alive 组件会出现这种情况
this.editor.setContent(this.value)
} else {
this.status = 2
this.$emit('ready', this.editor)
this.editor.setContent(this.initValue)
}
if (this.mode === 'observer' && window.MutationObserver) {
this._observerChangeListener()
} else {
this._normalChangeListener()
}
})
},
// 检测依赖,确保 UEditor 资源文件已加载完毕
_checkDependencies () {
return new Promise((resolve, reject) => {
// 判断ueditor.config.js和ueditor.all.js是否均已加载(仅加载完ueditor.config.js时UE对象和UEDITOR_CONFIG对象存在,仅加载完ueditor.all.js时UEDITOR_CONFIG对象存在,但为空对象)
let scriptsLoaded = !!window.UE && !!window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0 && !!window.UE.getEditor
if (scriptsLoaded) {
resolve()
} else if (window['$loadEnv']) { // 利用订阅发布确保同时渲染多个组件时不会重复创建script标签
window['$loadEnv'].on('scriptsLoaded', () => {
resolve()
})
} else {
window['$loadEnv'] = new LoadEvent()
// 如果在其他地方只引用ueditor.all.min.js在加载ueditor.config.js之后仍需要重新加载ueditor.all.min.js所以必须确保ueditor.config.js已加载
this._loadConfig().then(() => this._loadCore()).then(() => {
resolve()
window['$loadEnv'].emit('scriptsLoaded')
})
}
})
},
_loadConfig () {
return new Promise((resolve, reject) => {
if (window.UE && window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0) {
resolve()
return
}
let configScript = document.createElement('script')
configScript.type = 'text/javascript'
configScript.src = this.mixedConfig.UEDITOR_HOME_URL + 'ueditor.config.js'
document.getElementsByTagName('head')[0].appendChild(configScript)
configScript.onload = function () {
if (window.UE && window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0) {
resolve()
} else {
console.warn('加载ueditor.config.js失败,请检查您的配置地址UEDITOR_HOME_URL填写是否正确!\n', configScript.src)
}
}
})
},
_loadCore () {
return new Promise((resolve, reject) => {
if (window.UE && window.UE.getEditor) {
resolve()
return
}
let coreScript = document.createElement('script')
coreScript.type = 'text/javascript'
coreScript.src = this.mixedConfig.UEDITOR_HOME_URL + 'ueditor.all.min.js'
document.getElementsByTagName('head')[0].appendChild(coreScript)
coreScript.onload = function () {
if (window.UE && window.UE.getEditor) {
resolve()
} else {
console.warn('加载ueditor.all.min.js失败,请检查您的配置地址UEDITOR_HOME_URL填写是否正确!\n', coreScript.src)
}
}
})
},
// 设置内容
_setContent (value) {
value === this.editor.getContent() || this.editor.setContent(value)
},
contentChangeHandler () {
this.$emit('input', this.editor.getContent())
},
// 基于 UEditor 的 contentChange 事件
_normalChangeListener () {
this.editor.addListener('contentChange', this.contentChangeHandler)
},
// 基于 MutationObserver API
_observerChangeListener () {
const changeHandle = (mutationsList) => {
if (this.editor.document.getElementById('baidu_pastebin')) {
return
}
this.$emit('input', this.editor.getContent())
}
// 函数防抖
this.observer = new MutationObserver(Debounce(changeHandle, this.observerDebounceTime))
this.observer.observe(this.editor.body, this.observerOptions)
},
getChoiceFiles (files) {
console.log(files)
for (let i = 0, len = files.length; i < len; i++) {
let insetHtml
if (files[i].type === 1) {
insetHtml = `<img src="${files[i].url}" style="box-sizing: border-box !important; overflow-wrap: break-word !important; visibility: visible !important;"/>`
} else if (files[i].type === 2) {
insetHtml = `<audio src="${files[i].url}" controls="controls"></audio>`
} else if (files[i].type === 3) {
insetHtml = `<img width='343' height='220' class='edui-upload-video vjs-default-skin video-js' _url='${files[i].url}' style="background:url(https://lbqny.migugu.com/admin/public/videologo.gif) no-repeat center center; border:1px solid gray;" />`
} else if (files[i].type === 5) { // iframe标签
insetHtml = files[i].url
}
this.thatUeditor.execCommand('insertHtml', insetHtml)
}
}
},
deactivated () {
this.editor && this.editor.removeListener('contentChange', this.contentChangeHandler)
this.observer && this.observer.disconnect()
},
beforeDestroy () {
if (this.destroy && this.editor && this.editor.destroy) {
this.editor.destroy()
}
if (this.observer && this.observer.disconnect) {
this.observer.disconnect()
}
this.$store.commit('handleUploadStatus', false)
Bus.$off('showLbUpload')
},
// v-model语法糖实现
watch: {
value: {
handler (value) {
// 0: 尚未初始化 1: 开始初始化但尚未ready 2 初始化完成并已ready
value = value.replace('font-family: &quot;Microsoft Yahei&quot;;', 'font-family: Microsoft Yahei;')
switch (this.status) {
case 0:
this.status = 1
this.initValue = value;
// 判断执行环境是服务端还是客户端,这里的 process.client 是 Nuxt 添加的环境变量
(this.forceInit || (typeof process !== 'undefined' && process.client) || typeof window !== 'undefined') && this._checkDependencies().then(() => {
this.$refs.script ? this._initEditor() : this.$nextTick(() => this._initEditor())
})
break
case 1:
this.initValue = value
break
case 2:
this._setContent(value)
break
default:
break
}
},
immediate: true
},
showDialog (val) {
if (!val) {
this.$store.commit('handleUploadStatus', val)
}
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
<!--
* @Description:
* @Author: xiao li
* @Date: 2022-06-30 10:06:40
* @LastEditTime: 2022-09-07 15:23:24
* @LastEditors: xiao li
-->
<template>
<el-upload
class="avatar-uploader"
action="https://jsonplaceholder.typicode.com/posts/"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<lb-image v-if="coverImg" :src="coverImg" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</template>
<script>
export default {
props: ['coverImg'],
methods: {
handleAvatarSuccess (response, file) {
let imageUrl = URL.createObjectURL(file.raw)
this.$emit('uploadImg', imageUrl)
},
beforeAvatarUpload (file) {
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
}
return isLt2M
}
}
}
</script>
<style lang="scss" scoped>
.avatar-uploader {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 128px;
height: 128px;
}
.avatar-uploader:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 128px;
height: 128px;
line-height: 128px;
text-align: center;
}
.avatar {
width: 128px;
height: 128px;
display: block;
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<div class="lb-top-nav">
<div @click="goBack" class="nav-item" v-if="title">
<i class="iconfont icon-left" v-if="isBack"></i> {{ title }}
</div>
<div @click="goBack" class="nav-item" v-else-if="nav.length === 1">
<i class="iconfont icon-left" v-if="isBack"></i>
{{ $t('menu.' + nav[0].title) }}
</div>
<router-link
v-else-if="nav.length > 1"
v-for="(item, index) in nav"
tag="div"
class="nav-item"
:key="index"
active-class="nav-item-active"
:to="item.url"
>
{{ $t('menu.' + item.title) }}
</router-link>
</div>
</template>
<script>
export default {
props: {
active: {
type: Number,
default: 0
},
isBack: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
}
},
data () {
return {
activeNav: this.active,
nav: []
}
},
created () {
let { pagePermission } = this.$route.meta
if (pagePermission) {
this.nav = pagePermission
this.activeNav = this.nav[0].index
}
},
methods: {
goBack () {
if (!this.isBack) return
this.$router.back(-1)
}
}
}
</script>
<style lang="scss" scoped>
.lb-top-nav {
width: 100%;
height: 60px;
border-bottom: 1px solid #e1e1e1;
display: flex;
align-items: center;
padding: 0 10px;
font-size: 14px;
white-space: nowrap;
.nav-item {
height: 60px;
padding: 0 20px;
line-height: 60px;
cursor: pointer;
&::after {
position: absolute;
content: '';
width: 0%;
bottom: 0;
left: 0;
right: 0;
margin: auto;
height: 0px;
background: $themeColor;
transform: all 0.3 linear;
}
}
.nav-item-active {
color: $themeColor;
position: relative;
&::after {
width: 90%;
height: 2px;
}
}
}
</style>

View File

@@ -0,0 +1,50 @@
<template>
<div class="lb-container">
<top-nav :nav='nav' @changNav='handleNav'></top-nav>
<transition name="fade">
<router-view></router-view>
</transition>
</div>
</template>
<script>
export default {
data () {
return {
nav: []
}
},
created () {
this.nav = this.$route.meta.topMenu || []
},
methods: {
handleNav (index) {}
},
watch: {
$route: {
deep: true,
handler (val) {
this.nav = val.meta.topMenu || []
}
}
}
}
</script>
<style lang="scss" scoped>
.lb-container{
width: 100%;
height: 500px;
margin-left: 120px;
margin-top: 120px;
.fade-enter, .fade-leave-to {
opacity: 0
}
.fade-leave, .fade-enter-to {
opacity: 1
}
.fade-enter-active, .fade-leave-active {
transition: all .2s
}
}
</style>

View File

@@ -0,0 +1,46 @@
<!--
* @Descripttion:
* @Author: xiao li
* @Date: 2020-07-06 12:17:06
* @LastEditors: xiao li
* @LastEditTime: 2020-12-17 17:22:48
-->
<!-- 页面底部 -->
<template>
<div v-html="fHtml" class="lb-footer"></div>
</template>
<script>
export default {
data () {
return {
fHtml: ''
}
},
created () {
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.lb-footer{
width: 100%;
height: 49px;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid #ccc;
color: #ccc;
font-size: 12px;
z-index: 10;
background: #fff;
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: auto;
line-height: 1;
}
</style>

View File

@@ -0,0 +1,202 @@
<!-- 页面头部 -->
<template>
<div class="lb-header">
<div class="lb-left">
<div class="logo">
<!-- <el-image class="logo-img" :src="routesItem.logo" fit="cover" @click="$router.push('/')" ></el-image> -->
<div @click="$router.push('/')" class="flex-center logo-img c-base">
<img class="flex-center logo-img c-base" :src="banner" />
</div>
</div>
<div v-if="isIndex" class="admin-title"></div>
<div v-else class="menu-title">{{ $t('menu.' + title) }}</div>
</div>
<div class="lb-right">
<el-link :underline="false" @click="clearCache" :icon="icon">清除缓存</el-link>
<el-dropdown
v-if="!isWe7"
class="user-name"
trigger="click"
@command="handleCommand"
>
<span class="el-dropdown-link">
{{ username }}
<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="myaccount">编辑账户</el-dropdown-item>
<el-dropdown-item command="loginout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
// import Bus from '@/Bus.js'
import { mapState, mapMutations } from 'vuex'
export default {
data () {
return {
isIndex: true,
title: '',
banner: require('@/./style/image/zhnc.png'),
icon: 'el-icon-s-open',
notice_list: { data: [] },
visible: false,
isAdmin: window.lbConfig.isWe7 && window.lbConfig.is_founder,
agentUrl: '',
username: window.sessionStorage.getItem('ms_username'),
isWe7: window.lbConfig.isWe7,
dialogVisible: false
}
},
created () {
this.handleTitle(this.$route.meta.title)
this.handleAgentUrl()
},
computed: {
...mapState({
routesItem: state => state.routes
})
},
methods: {
...mapMutations(['changeRoutesItem']),
// 用户名下拉菜单选择事件
handleCommand (command) {
if (command === 'myaccount') {
this.$emit('handleAccount', true)
} else if (command === 'loginout') {
// this.$api.userlogout().then(res => {
// if (res.code === 200) {
this.changeRoutesItem({ key: 'isAuth', val: false })
sessionStorage.removeItem('lbtk') // 删除token
sessionStorage.removeItem('ms_username')
this.$router.push('/login')
window.location.reload()
// }
// })
}
},
clearCache () {
this.icon = 'el-icon-loading'
this.$api.base.clearCache().then(res => {
this.icon = 'el-icon-s-open'
if (res.code === 200) {
this.$message.success('清除成功!')
}
})
},
handleTitle (title) {
this.isIndex = !title
this.title = title
},
handleAgentUrl () {
let currentUrl = window.location.href
let agentUrl = currentUrl.slice(0, currentUrl.indexOf('#')) + '&s=/agent/index'
this.agentUrl = agentUrl
}
},
watch: {
$route: {
handler (val, oldVal) {
this.handleTitle(val.meta.title)
},
// 深度观察监听
deep: true
}
}
}
</script>
<style lang="scss" scoped>
.lb-header {
width: 100%;
height: 70px;
position: fixed;
top: 0;
left: 0;
background: #ffffff;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.lb-left {
display: flex;
height: 71px;
background: #1c2c3c;
border-bottom: 1px solid #f0f0f0;
.logo {
width: 120px;
height: 70px;
display: flex;
justify-content: center;
align-items: center;
.logo-img {
width: 45px;
height: 45px;
margin-bottom: 0;
border-radius: 50%;
cursor: pointer;
}
}
.admin-title {
padding: 0 32px;
display: flex;
align-items: center;
background: #ffffff;
span {
margin-right: 5px;
}
}
.menu-title {
height: 71px !important;
width: 140px;
border-right: 1px solid #eeeeee;
border-bottom: 1px solid #eeeeee;
height: 100%;
display: flex;
font-size: 15px;
font-weight: bold;
color: #666;
align-items: center;
justify-content: center;
background: #ffffff;
}
}
.lb-right {
display: flex;
justify-content: center;
align-items: center;
padding: 0 30px;
cursor: pointer;
.notice-item {
margin: 0 20px;
}
.el-dropdown-link {
cursor: pointer;
}
.el-dropdown-menu__item {
text-align: center;
}
span {
margin: 0 5px 0 20px;
}
.el-link {
margin-left: 10px;
}
}
}
</style>

View File

@@ -0,0 +1,313 @@
<!-- 公共组件 -->
<template>
<div class="home">
<lb-header @handleAccount="handleAccount"></lb-header>
<sidebar></sidebar>
<div
class="container"
:style="{
'margin-right': adSwitch ? '220px' : '0px',
'margin-left': sideBarSwitch ? '260px' : '120px'
}"
>
<!-- 内容区域 -->
<div class="main">
<transition name="fade" mode="out-in">
<keep-alive :max="1">
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
</transition>
<transition name="fade" mode="out-in">
<router-view v-if="!$route.meta.keepAlive"></router-view>
</transition>
</div>
<!-- <div
class="home-footer"
v-html="fHtml"
></div> -->
</div>
<!-- 右侧展开折叠栏 ad -->
<!-- <ad></ad> -->
<!-- <lb-footer></lb-footer> -->
<!-- 编辑账户 -->
<el-dialog title="编辑账户" :visible.sync="dialogVisible" width="520px">
<el-form
@submit.native.prevent
:model="accountForm"
:rules="accountFormRules"
ref="accountForm"
label-width="130px"
class="basic-form"
style="padding-right: 30px"
>
<el-form-item label="账号" prop="account">
<el-input
v-model="accountForm.account"
:disabled="true"
placeholder="账号"
></el-input>
</el-form-item>
<!-- <el-form-item
label="原密码"
prop="old_passwd"
>
<el-input
v-model="accountForm.old_passwd"
placeholder="请输入原密码"
></el-input>
</el-form-item> -->
<el-form-item label="新密码" prop="new_passwd">
<el-input
v-model="accountForm.new_passwd"
placeholder="请输入新密码"
></el-input>
</el-form-item>
<el-form-item label="确认新密码" prop="again_passwd">
<el-input
v-model="accountForm.again_passwd"
placeholder="请确认新密码"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('accountForm')">{{
$t('action.submit')
}}</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import lbHeader from './header'
import lbFooter from './footer'
import sidebar from './sidebar'
import ad from './ad'
import { mapGetters, mapState, mapMutations } from 'vuex'
export default {
name: 'Home',
data () {
let validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入密码'))
} else if (!/^(\S){6,20}$/.test(value)) {
callback(new Error('请输入6-20位非空白符的字符!'))
} else {
callback()
}
}
let validateAgainPassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请再次输入密码'))
} else if (value !== this.accountForm.new_passwd) {
callback(new Error('两次输入密码不一致'))
} else {
callback()
}
}
return {
isChangeRoutes: true,
fHtml: '',
dialogVisible: false,
accountForm: {
account: window.sessionStorage.getItem('ms_username'),
new_passwd: ''
},
accountFormRules: {
// old_passwd: {
// required: true,
// type: 'string',
// validator: validatePassword,
// trigger: 'blur'
// },
new_passwd: {
required: true,
type: 'string',
validator: validatePassword,
trigger: 'blur'
},
again_passwd: {
required: true,
type: 'string',
validator: validateAgainPassword,
trigger: 'blur'
}
},
authForm: {
pass: ''
},
authFormRules: {
pass: { required: true, type: 'string', message: '请输入获取到的激活口令', trigger: 'blur' }
},
modelAuthImg: 'https://lbqny.migugu.com/admin/card/WechatIMG322.jpeg',
modelAuthList: {
// https://lbqny.migugu.com/weixin/WechatIMG495.jpeg
longbing_card: 'https://lbqny.migugu.com/admin/card/WechatIMG322.jpeg',
longbing_radarstore: 'https://lbqny.migugu.com/admin/card/WechatIMG322.jpeg',
longbing_decorate: 'https://lbqny.migugu.com/admin/card/WechatIMG322.jpeg',
longbing_shortvideo: 'https://lbqny.migugu.com/admin/card/WechatIMG322.jpeg',
longbing_restaurant: 'https://lbqny.migugu.com/admin/card/WechatIMG322.jpeg',
longbing_member: 'https://lbqny.migugu.com/admin/card/WechatIMG322.jpeg'
}
}
},
components: {
lbHeader,
lbFooter,
sidebar,
ad
},
created () {
this.getCopyrightInfo()
},
computed: {
...mapGetters(['adSwitch', 'sideBarSwitch']),
...mapState({
routesItem: state => state.routes
})
},
methods: {
...mapMutations(['changeIsShowPrompt', 'changeRoutesItem']),
handleAccount (flag) {
this.dialogVisible = flag
},
submitForm (name) {
this.$refs[name].validate(valid => {
if (valid) {
let {
new_passwd: pass
} = JSON.parse(JSON.stringify(this.accountForm))
this.$api.base.updatePasswd({
pass
}).then(res => {
if (res.code === 200) {
this.$message.success(this.$t('tips.successRev'))
sessionStorage.removeItem('lbtk') // 删除token
sessionStorage.removeItem('ms_username') // 删除用户名
this.$router.push('/login')
}
})
}
})
},
submitAuthForm (name) {
this.$refs[name].validate(valid => {
if (valid) {
let { pass } = this.authForm
this.$api.baseGiveAuth({
pass
}).then(res => {
if (res.code === 200) {
this.$message.success(this.$t('tips.successSub'))
this.changeRoutesItem({ key: 'isFreeAuth', val: false })
}
})
}
})
},
getCopyrightInfo () {
let { systemCopyInfo } = this.routesItem
if (systemCopyInfo === 1) {
this.fHtml = ''
} else {
if (!systemCopyInfo.footerleft) {
this.fHtml =
`<div>Powered by <a class='el-link el-link--info' href="http://www.we7.cc"><b>微擎</b></a>
v${systemCopyInfo.version} © 2014-2015
<a class='el-link el-link--info' href="http://www.we7.cc">www.we7.cc</a></div>`
} else {
this.fHtml = systemCopyInfo.footerleft
}
if (systemCopyInfo.icp) {
this.fHtml = this.fHtml +
`<div>备案号:<a class='el-link el-link--info' href="http://www.miitbeian.gov.cn" target="_blank">${systemCopyInfo.icp}</a></div>`
}
}
}
}
}
</script>
<style lang="scss" scoped>
.home {
border: 1px solid transparent;
background: #f0f0f0;
.container {
margin: 70px 0 0 140px;
padding: 20px;
background: $bgThemeColor;
transition: margin 0.2s linear;
.main {
width: 100%;
background: #fff;
position: relative;
min-height: calc(100vh - 70px - 92px);
}
.home-footer {
height: 49px;
display: flex;
justify-content: center;
align-items: center;
color: #ccc;
font-size: 12px;
z-index: 10;
margin: 0 auto;
line-height: 1;
width: 50%;
}
}
}
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
.slide-fade-enter-active {
transition: all 2s ease;
}
.slide-fade-leave-active {
transition: all 0s ease;
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-leave,
.fade-enter-to {
opacity: 1;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,61 @@
<template>
<el-dialog :visible.sync="isShowPrompt" center :before-close="close">
<el-form>
<div solt="title" class="titleFont">服务到期通知</div>
<div class="delog">
<div>你开通的套餐{{promptData.status!==1?'已于':''}} {{ promptData.time }} {{promptData.status===1?'即将':''}}到期请联系所属运营商续费后使用</div>
<div>感谢对我们工作的理解与支持</div>
<el-button type="primary" size="small" class="pad" @click="close">我知道了</el-button>
</div>
</el-form>
</el-dialog>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
// props:{
// isShow:String
// },
data () {
return {
}
},
created () {},
computed: {
...mapState({
promptData: state => state.routes.promptData,
isShowPrompt: state => state.routes.isShowPrompt
})
},
methods: {
...mapMutations(['changeIsShowPrompt']),
close () {
this.changeIsShowPrompt(false)
},
handleClose (done) {
this.$confirm('确认关闭?')
.then(_ => {
this.changeIsShowPrompt(false)
})
.catch(_ => {})
}
}
}
</script>
<style scoped>
.delog {
display: flex;
align-items: center;
flex-direction: column;
justify-content: flex-start;
}
.titleFont {
text-align: center;
font-size: 20px;
color: #000000;
font-weight: bold;
}
.pad {
margin-top: 100px;
}
</style>

View File

@@ -0,0 +1,274 @@
<!-- 右侧边栏 -->
<template>
<div class="lb-sidebar">
<div class="menu">
<vue-scroll :ops="ops">
<ul class="menu-top" @click="showDialog">
<router-link
v-for="(item, index) in routes"
tag="li"
:key="index"
active-class="menu-active"
:to="item.path"
:style="{ marginTop: item.meta.menuName === 'diy' ? '50px' : '0' }"
v-show="!item.isHidden"
>
<i
v-if="item.meta.icon"
class="iconfont"
:class="item.meta.icon"
></i>
<img v-else :src="item.meta.img" />
{{ $t('menu.' + item.meta.menuName) }}
</router-link>
</ul>
</vue-scroll>
</div>
<div :class="subnav.length > 0 ? 'isopen' : ''" class="submenu">
<vue-scroll :ops="ops">
<el-collapse
v-for="(item, index) in subnav"
:key="index"
v-model="activeNames"
>
<div :title="$t('menu.' + item.name)" :name="index">
<div class="item" v-for="(items, indexs) in item.url" :key="indexs">
<router-link
tag="span"
active-class="el-collapse-item-active"
:to="items.url"
>{{ $t('menu.' + items.name) }}</router-link
>
</div>
</div>
</el-collapse>
</vue-scroll>
</div>
</div>
</template>
<script>
import { mapMutations, mapState } from 'vuex'
import vuescroll from 'vuescroll'
export default {
data () {
return {
isFirst: true,
routes: [], // 路由表
subnav: [], // 二级菜单表
activeNames: [], // 二级菜单展开的配置
ops: {
vuescroll: {
wheelScrollDuration: 600
},
scrollPanel: {
initialScrollY: false,
initialScrollX: false,
scrollingX: true,
scrollingY: true,
speed: 1000,
easing: undefined,
verticalNativeBarPos: 'right'
},
rail: {},
bar: {
showDelay: 500,
onlyShowBarOnScroll: true,
keepShow: false,
background: '#c1c1c1',
opacity: 0.5,
hoverStyle: false,
specifyBorderRadius: false,
minSize: false,
size: '6px',
disable: false
}
}
}
},
computed: {
...mapState({
promptData: state => state.routes.promptData
})
},
components: {
vuescroll
},
created () {
this.handleRoute()
this.handleSubnav(this.$route.name)
},
methods: {
...mapMutations([]),
showDialog () {
},
/**
* @method 处理路由表,渲染到侧边栏
*/
handleRoute () {
let { routes } = this.$store.getters // JSON.parse(localStorage.getItem('routes'))
// console.log(routes)
this.routes = routes.filter(item => {
if (!item.hidden) {
return item
}
})
},
/**
* @method 处理二级菜单导航
*/
handleSubnav (name) {
let { routes } = this
// console.log(routes)
for (let i = 0, len = routes.length; i < len; i++) {
let children = routes[i].children
for (let j = 0, l = children.length; j < l; j++) {
if (children[j].name === name) {
this.subnav = routes[i].meta.subNavName || []
this.$store.commit('handleSideBarSwitch', this.subnav.length > 0)
this.openSubnav()
break
}
}
}
},
/**
* @method 展开二级菜单
*/
openSubnav () {
let arr = []
this.subnav.forEach((item, index) => {
arr.push(index)
})
this.activeNames = arr
}
},
watch: {
$route: {
handler (val, oldVal) {
this.handleSubnav(val.name)
},
// 深度观察监听
deep: true
}
}
}
</script>
<style lang="scss" scoped>
.lb-sidebar {
position: fixed;
top: 70px;
left: 0;
display: flex;
height: calc(100% - 70px);
z-index: 2;
.menu {
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
width: 120px;
height: 100%;
background: #1c2c3c;
z-index: 3;
.menu-top {
width: 100%;
color: #fff;
font-size: 14px;
text-align: center;
line-height: 50px;
li {
width: 100%;
min-height: 50px;
padding: 15px 8px;
line-height: 20px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
i {
margin-right: 10px;
}
img {
display: inline-block;
width: 20px;
height: 20px;
margin-right: 10px;
}
&:hover {
color: #fff;
background-color: #26394b;
}
}
.menu-active {
background: #2d8cf0;
color: #fff;
&:hover {
color: #fff;
}
}
}
}
.submenu {
position: absolute;
top: 0;
left: 120px;
z-index: 2;
width: 139px;
height: 100%;
background: #fff;
display: none;
transition: all 0.2s linear;
.el-collapse {
margin: 0 auto;
border-top: 1px;
.item {
width: 100%;
cursor: pointer;
text-align: center;
span {
display: inline-block;
width: 100%;
line-height: 45px;
}
&:hover {
span {
background-color: #f5faff;
color: #2d8cf0;
}
.el-collapse-item-active {
color: #2d8cf0;
}
}
.el-collapse-item-active {
position: relative;
width: 100%;
display: inline-block;
background: #c9e9ff;
border-radius: 2px;
text-align: center;
color: #2d8cf0;
font-weight: bold;
}
.el-collapse-item-active:before {
content: '';
display: block;
width: 2px;
position: absolute;
top: 0;
bottom: 0;
right: 0;
background: #2d8cf0;
}
}
}
}
.isopen {
width: 139px;
display: block;
}
}
</style>