This commit is contained in:
zzc
2025-03-28 18:28:06 +08:00
commit 939c43f281
206 changed files with 30419 additions and 0 deletions

284
src/views/401.vue Normal file
View File

@@ -0,0 +1,284 @@
<template>
<div class="error-container">
<div class="error-content">
<el-row :gutter="20">
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
<div class="pic-error">
<img alt="401" class="pic-error-parent" src="@/assets/error_images/401.png" />
<img alt="401" class="pic-error-child left" src="@/assets/error_images/cloud.png" />
<img alt="401" class="pic-error-child" src="@/assets/error_images/cloud.png" />
<img alt="401" class="pic-error-child" src="@/assets/error_images/cloud.png" />
</div>
</el-col>
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
<div class="bullshit">
<div class="bullshit-oops">
{{ oops }}
</div>
<div class="bullshit-headline">
{{ headline }}
</div>
<div class="bullshit-info">
{{ info }}
</div>
<a class="bullshit-return-home" href="#/index">{{ jumpTime }}s&nbsp;{{ btn }}</a>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: 'Page401',
data() {
return {
jumpTime: 5,
oops: '抱歉!',
headline: '您没有操作权限...',
info: '当前帐号没有操作权限,请联系管理员。',
btn: '返回',
timer: 0,
}
},
mounted() {
this.timeChange()
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
timeChange() {
this.timer = setInterval(() => {
if (this.jumpTime) {
this.jumpTime--
} else {
this.$router.push({ path: '/' })
this.$store.dispatch('tabsBar/delOthersRoutes', {
path: '/',
})
clearInterval(this.timer)
}
}, 1000)
},
},
}
</script>
<style lang="scss" scoped>
.error-container {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
.error-content {
.pic-error {
position: relative;
float: left;
width: 120%;
overflow: hidden;
&-parent {
width: 100%;
}
&-child {
position: absolute;
&.left {
top: 17px;
left: 220px;
width: 80px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1s;
animation-fill-mode: forwards;
}
&.mid {
top: 10px;
left: 420px;
width: 46px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1.2s;
animation-fill-mode: forwards;
}
&.right {
top: 100px;
left: 500px;
width: 62px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1s;
animation-fill-mode: forwards;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&-oops {
margin-bottom: 20px;
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: $base-color-blue;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&-headline {
margin-bottom: 10px;
font-size: 20px;
font-weight: bold;
line-height: 24px;
color: #222;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&-info {
margin-bottom: 30px;
font-size: 13px;
line-height: 21px;
color: $base-color-gray;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&-return-home {
display: block;
float: left;
width: 110px;
height: 36px;
font-size: 14px;
line-height: 36px;
color: #fff;
text-align: center;
cursor: pointer;
background: $base-color-blue;
border-radius: 100px;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
opacity: 0;
transform: translateY(60px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
}
}
}
</style>

284
src/views/404.vue Normal file
View File

@@ -0,0 +1,284 @@
<template>
<div class="error-container">
<div class="error-content">
<el-row :gutter="20">
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
<div class="pic-error">
<img alt="401" class="pic-error-parent" src="@/assets/error_images/404.png" />
<img alt="401" class="pic-error-child left" src="@/assets/error_images/cloud.png" />
<img alt="401" class="pic-error-child" src="@/assets/error_images/cloud.png" />
<img alt="401" class="pic-error-child" src="@/assets/error_images/cloud.png" />
</div>
</el-col>
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
<div class="bullshit">
<div class="bullshit-oops">
{{ oops }}
</div>
<div class="bullshit-headline">
{{ headline }}
</div>
<div class="bullshit-info">
{{ info }}
</div>
<a class="bullshit-return-home" href="#/index">{{ jumpTime }}s&nbsp;{{ btn }}</a>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: 'Page404',
data() {
return {
jumpTime: 5,
oops: '抱歉!',
headline: '当前页面不存在...',
info: '请检查您输入的网址是否正确,或点击下面的按钮返回首页。',
btn: '返回首页',
timer: 0,
}
},
mounted() {
this.timeChange()
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
timeChange() {
this.timer = setInterval(() => {
if (this.jumpTime) {
this.jumpTime--
} else {
this.$router.push({ path: '/' })
this.$store.dispatch('tabsBar/delOthersRoutes', {
path: '/',
})
clearInterval(this.timer)
}
}, 1000)
},
},
}
</script>
<style lang="scss" scoped>
.error-container {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
.error-content {
.pic-error {
position: relative;
float: left;
width: 120%;
overflow: hidden;
&-parent {
width: 100%;
}
&-child {
position: absolute;
&.left {
top: 17px;
left: 220px;
width: 80px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1s;
animation-fill-mode: forwards;
}
&.mid {
top: 10px;
left: 420px;
width: 46px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1.2s;
animation-fill-mode: forwards;
}
&.right {
top: 100px;
left: 500px;
width: 62px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1s;
animation-fill-mode: forwards;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&-oops {
margin-bottom: 20px;
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: $base-color-blue;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&-headline {
margin-bottom: 10px;
font-size: 20px;
font-weight: bold;
line-height: 24px;
color: #222;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&-info {
margin-bottom: 30px;
font-size: 13px;
line-height: 21px;
color: $base-color-gray;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&-return-home {
display: block;
float: left;
width: 110px;
height: 36px;
font-size: 14px;
line-height: 36px;
color: #fff;
text-align: center;
cursor: pointer;
background: $base-color-blue;
border-radius: 100px;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
opacity: 0;
transform: translateY(60px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
}
}
}
</style>

View File

@@ -0,0 +1,83 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="权限码" prop="permission">
<el-input v-model="form.permission" autocomplete="off" />
</el-form-item>
<el-form-item label="描述" prop="desc">
<el-input v-model="form.desc" autocomplete="off" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit, doAdd } from '@/api/roleManagement'
export default {
name: 'RoleManagementEdit',
data() {
return {
id: '',
form: {
name: '',
permission: '',
desc: '',
},
rules: {
permission: [{ required: true, trigger: 'blur', message: '请输入权限码' }],
name: [{ required: true, trigger: 'blur', message: '请输入名称' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = {
name: row.name,
permission: row.permission,
desc: row.desc,
}
this.id = row.id
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
if (this.id) {
const { msg } = await doEdit(this.id, this.form)
this.$baseMessage(msg, 'success')
} else {
const { msg } = await doAdd(this.form)
this.$baseMessage(msg, 'success')
}
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,82 @@
<template>
<div class="cxshMini-article-container">
<el-button style="margin-bottom: 10px" type="primary" @click="handlePublish">发布</el-button>
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="用户名" prop="username">
<el-input v-model.trim="form.username" autocomplete="off" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model.trim="form.password" autocomplete="off" type="password" />
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input v-model.trim="form.nickname" autocomplete="off" />
</el-form-item>
<el-form-item label="权限" prop="permissions">
<el-select v-model="form.permissions" multiple placeholder="请选择权限">
<el-option v-for="item in permissionList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="应用" prop="appId">
<el-select v-model="form.appId" placeholder="请选择应用">
<el-option v-for="item in applicationList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="头像" prop="avatar">
<single-upload
style="width: 100px; height: 100px"
upload-url="http://127.0.0.1:3999/management/api/common/upload"
@upload-success="handleUploadSuccess"
/>
</el-form-item>
</el-form>
<quill-editor v-model="content" :options="editorOptions" />
</div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor'
import 'quill/dist/quill.snow.css'
import SingleUpload from '@/components/SingleUpload'
export default {
name: 'CxshMiniArticle',
components: { quillEditor, SingleUpload },
data() {
return {
form: {
username: '',
password: '',
nickname: '',
permissions: '',
appId: '',
avatar: '',
},
permissionList: [],
applicationList: [],
rules: {
username: [{ required: true, trigger: 'blur', message: '请输入用户名' }],
password: [{ required: true, trigger: 'blur', message: '请输入密码' }],
permissions: [{ required: true, trigger: 'blur', message: '请选择权限' }],
appId: [{ required: true, trigger: 'blur', message: '请选择应用' }],
},
content: '',
editorOptions: {
theme: 'snow',
},
}
},
created() {
this.fetchData()
},
methods: {
handlePublish() {
console.log(11111, this.content)
console.log('发布')
},
async fetchData() {},
handleUploadSuccess(res) {
this.form.avatar = res.data.url
},
},
}
</script>

View File

@@ -0,0 +1,78 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="描述" prop="desc">
<el-input v-model="form.desc" autocomplete="off" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit, doAdd } from '@/api/roleManagement'
export default {
name: 'CxshMiniAnliCategoryEdit',
data() {
return {
id: '',
form: {
name: '',
desc: '',
},
rules: {
name: [{ required: true, trigger: 'blur', message: '请输入名称' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = {
name: row.name,
permission: row.permission,
desc: row.desc,
}
this.id = row.id
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
if (this.id) {
const { msg } = await doEdit(this.id, this.form)
this.$baseMessage(msg, 'success')
} else {
const { msg } = await doAdd(this.form)
this.$baseMessage(msg, 'success')
}
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,130 @@
<template>
<div class="cxshMini-anli-category-container">
<vab-query-form>
<vab-query-form-left-panel :span="12">
<el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button>
<el-button icon="el-icon-delete" type="danger" @click="handleDelete">批量删除</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel :span="12">
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model.trim="queryForm.permission" clearable placeholder="请输入查询条件" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-table v-loading="listLoading" :data="list" :element-loading-text="elementLoadingText" @selection-change="setSelectRows">
<el-table-column show-overflow-tooltip type="selection" />
<el-table-column label="名称" prop="name" show-overflow-tooltip />
<el-table-column label="权限码" prop="permission" show-overflow-tooltip />
<el-table-column label="描述" prop="desc" show-overflow-tooltip />
<el-table-column label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<edit ref="edit" @fetch-data="fetchData" />
</div>
</template>
<script>
import { doDelete, getList } from '@/api/roleManagement'
import Edit from './components/CxshMiniAnliCategoryEdit'
export default {
name: 'CxshMiniAnliCategory',
components: { Edit },
data() {
return {
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 10,
permission: '',
},
timeOutID: null,
}
},
created() {
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
setSelectRows(val) {
this.selectRows = val
},
handleEdit(row) {
if (row.id) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: [row.id] })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids: ids.split(',') })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data } = await getList(this.queryForm)
this.list = data.list
this.total = data.totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
},
}
</script>

View File

@@ -0,0 +1,100 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择 APP 类型">
<el-option label="小程序" :value="1" />
<el-option label="H5" :value="2" />
<el-option label="后台管理" :value="3" />
</el-select>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="form.description" autocomplete="off" />
</el-form-item>
<el-form-item label="APP图标" prop="appIcon">
<single-upload
style="width: 100px; height: 100px"
upload-url="http://127.0.0.1:3999/management/api/common/upload"
@upload-success="handleUploadSuccess"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit, doAdd } from '@/api/appManagement'
import SingleUpload from '@/components/SingleUpload'
export default {
name: 'AppManagementEdit',
components: { SingleUpload },
data() {
return {
id: '',
form: {
name: '',
type: '',
description: '',
icon: '',
},
rules: {
name: [{ required: true, trigger: 'blur', message: '请输入名称' }],
type: [{ required: true, trigger: 'blur', message: '请选择类型' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
handleUploadSuccess(url) {
this.form.icon = url
},
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = {
name: row.name,
description: row.description,
icon: row.icon,
}
this.id = row.id
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
if (this.id) {
const { msg } = await doEdit(this.id, this.form)
this.$baseMessage(msg, 'success')
} else {
const { msg } = await doAdd(this.form)
this.$baseMessage(msg, 'success')
}
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,146 @@
<template>
<div class="appManagement-container">
<el-divider content-position="left">
演示环境仅做基础功能展示若想实现不同角色的真实菜单配置需将settings.js路由加载模式改为all模式由后端全面接管路由渲染与权限控制
</el-divider>
<vab-query-form>
<vab-query-form-left-panel :span="12">
<el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button>
<el-button icon="el-icon-delete" type="danger" @click="handleDelete">批量删除</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel :span="12">
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model.trim="queryForm.permission" clearable placeholder="请输入查询条件" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-table v-loading="listLoading" :data="list" :element-loading-text="elementLoadingText" @selection-change="setSelectRows">
<el-table-column show-overflow-tooltip type="selection" />
<el-table-column align="center" label="图标" width="100">
<template slot-scope="scope">
<!-- 使用 show-overflow-tooltip 显示图片并添加 tooltip -->
<el-tooltip class="item" :content="scope.row.name" effect="dark" placement="top">
<img alt="image" :src="scope.row.icon" style="width: 50px; height: 50px; object-fit: cover; border-radius: 50%" />
</el-tooltip>
</template>
</el-table-column>
<el-table-column align="center" label="名称" prop="name" show-overflow-tooltip />
<el-table-column align="center" label="类型" show-overflow-tooltip>
<template #default="{ row }">
{{ row.type === 1 ? '小程序' : row.type === 2 ? 'H5' : '后台管理' }}
</template>
</el-table-column>
<el-table-column align="center" label="描述" prop="description" show-overflow-tooltip />
<el-table-column label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<edit ref="edit" @fetch-data="fetchData" />
</div>
</template>
<script>
import { doDelete, getList } from '@/api/appManagement'
import { formatTime } from '@/utils'
import Edit from './components/AppManagementEdit'
export default {
name: 'AppManagement',
components: { Edit },
data() {
return {
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 10,
permission: '',
},
timeOutID: null,
}
},
created() {
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
setSelectRows(val) {
this.selectRows = val
},
handleEdit(row) {
if (row.id) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: [row.id] })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids: ids.split(',') })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data } = await getList(this.queryForm)
this.list = data.list
this.total = data.totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
},
}
</script>

View File

@@ -0,0 +1,100 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择 APP 类型">
<el-option label="小程序" :value="1" />
<el-option label="H5" :value="2" />
<el-option label="后台管理" :value="3" />
</el-select>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="form.description" autocomplete="off" />
</el-form-item>
<el-form-item label="APP图标" prop="appIcon">
<single-upload
style="width: 100px; height: 100px"
upload-url="http://127.0.0.1:3999/management/api/common/upload"
@upload-success="handleUploadSuccess"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit, doAdd } from '@/api/appManagement'
import SingleUpload from '@/components/SingleUpload'
export default {
name: 'AppManagementEdit',
components: { SingleUpload },
data() {
return {
id: '',
form: {
name: '',
type: '',
description: '',
icon: '',
},
rules: {
name: [{ required: true, trigger: 'blur', message: '请输入名称' }],
type: [{ required: true, trigger: 'blur', message: '请选择类型' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
handleUploadSuccess(url) {
this.form.icon = url
},
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = {
name: row.name,
description: row.description,
icon: row.icon,
}
this.id = row.id
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
if (this.id) {
const { msg } = await doEdit(this.id, this.form)
this.$baseMessage(msg, 'success')
} else {
const { msg } = await doAdd(this.form)
this.$baseMessage(msg, 'success')
}
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,146 @@
<template>
<div class="appManagement-container">
<el-divider content-position="left">
演示环境仅做基础功能展示若想实现不同角色的真实菜单配置需将settings.js路由加载模式改为all模式由后端全面接管路由渲染与权限控制
</el-divider>
<vab-query-form>
<vab-query-form-left-panel :span="12">
<el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button>
<el-button icon="el-icon-delete" type="danger" @click="handleDelete">批量删除</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel :span="12">
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model.trim="queryForm.permission" clearable placeholder="请输入查询条件" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-table v-loading="listLoading" :data="list" :element-loading-text="elementLoadingText" @selection-change="setSelectRows">
<el-table-column show-overflow-tooltip type="selection" />
<el-table-column align="center" label="图标" width="100">
<template slot-scope="scope">
<!-- 使用 show-overflow-tooltip 显示图片并添加 tooltip -->
<el-tooltip class="item" :content="scope.row.name" effect="dark" placement="top">
<img alt="image" :src="scope.row.icon" style="width: 50px; height: 50px; object-fit: cover; border-radius: 50%" />
</el-tooltip>
</template>
</el-table-column>
<el-table-column align="center" label="名称" prop="name" show-overflow-tooltip />
<el-table-column align="center" label="类型" show-overflow-tooltip>
<template #default="{ row }">
{{ row.type === 1 ? '小程序' : row.type === 2 ? 'H5' : '后台管理' }}
</template>
</el-table-column>
<el-table-column align="center" label="描述" prop="description" show-overflow-tooltip />
<el-table-column label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<edit ref="edit" @fetch-data="fetchData" />
</div>
</template>
<script>
import { doDelete, getList } from '@/api/appManagement'
import { formatTime } from '@/utils'
import Edit from './components/AppManagementEdit'
export default {
name: 'AppManagement',
components: { Edit },
data() {
return {
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 10,
permission: '',
},
timeOutID: null,
}
},
created() {
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
setSelectRows(val) {
this.selectRows = val
},
handleEdit(row) {
if (row.id) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: [row.id] })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids: ids.split(',') })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data } = await getList(this.queryForm)
this.list = data.list
this.total = data.totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
},
}
</script>

View File

@@ -0,0 +1,83 @@
<template>
<el-card shadow="hover">
<template #header>
<vab-icon icon="send-plane-2-line" />
<!-- 计划 -->
<el-tag class="card-header-tag" type="success">祝用框架的小伙伴都能住上别墅开上保时捷</el-tag>
</template>
<el-table :data="tableData" height="283px" row-key="title">
<el-table-column align="center" label="拖拽" width="50px">
<template #default="{}">
<vab-icon :icon="['fas', 'arrows-alt']" style="cursor: pointer" />
</template>
</el-table-column>
<el-table-column width="20px" />
<el-table-column label="目标" prop="title" width="230px" />
<el-table-column label="进度" width="220px">
<template #default="{ row }">
<el-progress :color="row.color" :percentage="row.percentage" />
</template>
</el-table-column>
<el-table-column width="50px" />
<el-table-column label="完成时间" prop="endTIme" />
</el-table>
</el-card>
</template>
<script>
import Sortable from 'sortablejs'
export default {
data() {
return {
tableData: [
{
title: '帮助中小企业盈利1个亿',
endTIme: '2099-12-31',
percentage: 50,
color: '#95de64',
},
{
title: '帮助10万个人',
endTIme: '2029-12-31',
percentage: 8,
color: '#69c0ff',
},
{
title: '交个帅气的男朋友',
endTIme: '2021-12-31',
percentage: 76,
color: '#1890FF',
},
{
title: 'vue-admin-better标星过1K',
endTIme: '2020-03-31',
percentage: 100,
color: '#ffc069',
},
{
title: '活到老,学到老',
endTIme: '2094-12-16',
percentage: 25,
color: '#5cdbd3',
},
{
title: '变成像尤雨溪一样优秀的人',
endTIme: '此生无望',
percentage: 1,
color: '#b37feb',
},
],
}
},
mounted() {
const tbody = document.querySelector('.el-table__body-wrapper tbody')
const _this = this
Sortable.create(tbody, {
onEnd({ newIndex, oldIndex }) {
const currRow = _this.tableData.splice(oldIndex, 1)[0]
_this.tableData.splice(newIndex, 0, currRow)
},
})
},
}
</script>

View File

@@ -0,0 +1,85 @@
<template>
<el-card class="version-information" shadow="hover">
<template #header>
<vab-icon icon="information-line" />
</template>
<el-scrollbar>
<table class="table">
<tr>
<td>vue</td>
<td>{{ dependencies['vue'] }}</td>
<td>@vue/cli</td>
<td>{{ devDependencies['@vue/cli-service'] }}</td>
</tr>
<tr>
<td>vuex</td>
<td>{{ dependencies['vuex'] }}</td>
<td>vue-router</td>
<td>{{ dependencies['vue-router'] }}</td>
</tr>
<tr>
<td>element-ui</td>
<td>{{ dependencies['element-ui'] }}</td>
<td>axios</td>
<td>{{ dependencies['axios'] }}</td>
</tr>
<tr>
<td>授权渠道</td>
<td colspan="3">
<a href="https://vuejs-core.cn/authorization" target="_blank">
<el-button style="margin-left: 10px" type="primary">PRO付费版本 购买源码 699</el-button>
</a>
<a href="https://vuejs-core.cn/authorization" target="_blank">
<el-button style="margin-left: 10px" type="primary">Plus付费版本 购买源码 799</el-button>
</a>
<a href="https://github.com/zxwk1998/vue-admin-better/" target="_blank">
<el-button style="margin-left: 10px" type="warning">开源免费版</el-button>
</a>
</td>
</tr>
</table>
</el-scrollbar>
</el-card>
</template>
<script>
import { dependencies, devDependencies } from '../../../../package.json'
export default {
data() {
return {
updateTime: process.env.VUE_APP_UPDATE_TIME,
dependencies,
devDependencies,
}
},
}
</script>
<style lang="scss" scoped>
.version-information {
.table {
width: 100%;
color: #666;
border-collapse: collapse;
background-color: #fff;
td {
position: relative;
padding: 9px 15px;
overflow: hidden;
font-size: 14px;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
border: 1px solid #e6e6e6;
&:nth-child(odd) {
width: 20%;
text-align: right;
background-color: #f7f7f7;
}
}
}
}
</style>

601
src/views/index/index.vue Normal file
View File

@@ -0,0 +1,601 @@
<template>
<div class="index-container">
<el-row :gutter="20">
<el-col :lg="24" :md="24" :sm="24" :xl="24" :xs="24">
<el-alert v-if="noticeList">
<div style="display: flex; align-items: center; justify-content: center">
<a href="https://github.com/zxwk1998/vue-admin-better" target="_blank">
<img
src="https://img.shields.io/github/stars/zxwk1998/vue-admin-better?style=flat-square&label=Stars&logo=github"
style="margin-right: 10px"
/>
</a>
<p v-html="noticeList.notice"></p>
</div>
</el-alert>
</el-col>
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="never">
<div slot="header">
<span>访问量</span>
</div>
<vab-chart autoresize :option="fwl" />
<div class="bottom">
<span>
日均访问量:
{{ config1.endVal }}
</span>
</div>
</el-card>
</el-col>
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="never">
<div slot="header">
<span>授权数</span>
</div>
<vab-chart autoresize :option="sqs" />
<div class="bottom">
<span>
总授权数:
{{ config2.endVal }}
</span>
</div>
</el-card>
</el-col>
<el-col v-for="(item, index) in iconList" :key="index" :lg="3" :md="3" :sm="6" :xl="3" :xs="12">
<router-link target="_blank" :to="item.link">
<el-card class="icon-panel" shadow="never">
<vab-icon :icon="['fas', item.icon]" :style="{ color: item.color }" />
<p>{{ item.title }}</p>
</el-card>
</router-link>
</el-col>
<el-col :lg="11" :md="24" :sm="24" :xl="11" :xs="24">
<el-card class="card" shadow="never">
<div slot="header">
<span>依赖信息</span>
<!-- <div style="float: right">部署时间:{{ updateTime }}</div> -->
</div>
<table class="table">
<tr>
<td>@vue/cli版本</td>
<td>{{ devDependencies['@vue/cli-service'] }}</td>
<td>vue版本</td>
<td>{{ dependencies['vue'] }}</td>
</tr>
<tr>
<td>vuex版本</td>
<td>{{ dependencies['vuex'] }}</td>
<td>vue-router版本</td>
<td>{{ dependencies['vue-router'] }}</td>
</tr>
<tr>
<td>element-ui版本</td>
<td>{{ dependencies['element-ui'] }}</td>
<td>axios版本</td>
<td>{{ dependencies['axios'] }}</td>
</tr>
<tr>
<td>eslint版本</td>
<td>{{ devDependencies['eslint'] }}</td>
<td>prettier版本</td>
<td>{{ devDependencies['prettier'] }}</td>
</tr>
<tr>
<td>sass版本</td>
<td>{{ devDependencies['sass'] }}</td>
<td>mockjs版本</td>
<td>{{ dependencies['mockjs'] }}</td>
</tr>
<tr>
<td>layouts版本</td>
<td>{{ dependencies['layouts'] }}</td>
<td>lodash版本</td>
<td>{{ dependencies['lodash'] }}</td>
</tr>
</table>
<br />
<el-alert :closable="false" :title="userAgent" type="info" />
<br />
</el-card>
</el-col>
<el-col :lg="13" :md="13" :sm="24" :xl="13" :xs="24">
<el-card shadow="never">
<div slot="header">
<span>其他信息</span>
</div>
<div style="text-align: center">
<vab-colorful-icon icon-class="vab" style="font-size: 140px" />
<h1 style="font-size: 30px">vue-admin-better</h1>
</div>
<div class="bottom-btn">
<el-popover placement="top" trigger="hover" width="250">
<p>
请我们喝杯咖啡付款后联系qq
783963206我们将邀请您加入我们的讨论群谢谢您愿意支持开源加群获取文档及基础模板群内大佬众多希望能帮到大家如情况不允许请勿勉强
</p>
<el-image :src="require('@/assets/zfb_kf.jpg')" />
<a slot="reference" target="_blank">
<el-button type="primary">QQ讨论群基础版文档</el-button>
</a>
</el-popover>
<a href="https://github.com/zxwk1998/vue-admin-better" target="_blank">
<el-button type="plain">vue2.x版本 github下载源码点star</el-button>
</a>
<a href="https://gitee.com/chu1204505056/vue-admin-better" target="_blank">
<el-button type="plain">vue2.x版本 码云下载源码点star</el-button>
</a>
<a href="https://github.com/zxwk1998/vue-admin-arco" target="_blank">
<el-button type="plain">vue3.x版本 github下载源码点star</el-button>
</a>
<a href="https://vuejs-core.cn/admin-pro" target="_blank">
<el-button type="primary">Admin Pro 699</el-button>
</a>
<a href="https://vuejs-core.cn/admin-plus" target="_blank">
<el-button type="primary">Admin Plus 799</el-button>
</a>
<a href="https://vuejs-core.cn/shop-vite" target="_blank">
<el-button type="success">Shop Vite 1899</el-button>
</a>
<a @click="handleChangeTheme">
<el-button type="danger">修改主题和布局</el-button>
</a>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import VabChart from '@/plugins/echarts'
import { dependencies, devDependencies } from '../../../package.json'
import { getNoticeList } from '@/api/notice'
export default {
name: 'Index',
components: {
VabChart,
},
data() {
return {
timer: 0,
updateTime: process.env.VUE_APP_UPDATE_TIME,
nodeEnv: process.env.NODE_ENV,
dependencies: dependencies,
devDependencies: devDependencies,
config1: {
startVal: 0,
endVal: this.$baseLodash.random(20000, 60000),
decimals: 0,
prefix: '',
suffix: '',
separator: ',',
duration: 8000,
},
config2: {
startVal: 0,
endVal: this.$baseLodash.random(1000, 20000),
decimals: 0,
prefix: '',
suffix: '',
separator: ',',
duration: 8000,
},
config3: {
startVal: 0,
endVal: this.$baseLodash.random(1000, 20000),
decimals: 0,
prefix: '',
suffix: '',
separator: ',',
duration: 8000,
},
//访问量
fwl: {
color: ['#1890FF', '#36CBCB', '#4ECB73', '#FBD437', '#F2637B', '#975FE5'],
backgroundColor: 'rgba(252,252,252,0)',
grid: {
top: '4%',
left: '2%',
right: '4%',
bottom: '0%',
containLabel: true,
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: [],
axisTick: {
alignWithLabel: true,
},
},
],
yAxis: [
{
type: 'value',
},
],
series: [
{
name: '访问量',
type: 'line',
data: [],
smooth: true,
areaStyle: {},
},
],
},
//授权数
sqs: {
color: ['#1890FF', '#36CBCB', '#4ECB73', '#FBD437', '#F2637B', '#975FE5'],
backgroundColor: 'rgba(252,252,252,0)',
grid: {
top: '4%',
left: '2%',
right: '4%',
bottom: '0%',
containLabel: true,
},
xAxis: [
{
type: 'category',
/*boundaryGap: false,*/
data: ['0时', '4时', '8时', '12时', '16时', '20时', '24时'],
axisTick: {
alignWithLabel: true,
},
},
],
yAxis: [
{
type: 'value',
},
],
series: [
{
name: '授权数',
type: 'bar',
barWidth: '60%',
data: [10, 52, 20, 33, 39, 33, 22],
},
],
},
//词云
cy: {
grid: {
top: '4%',
left: '2%',
right: '4%',
bottom: '0%',
},
series: [
{
type: 'wordCloud',
gridSize: 15,
sizeRange: [12, 40],
rotationRange: [0, 0],
width: '100%',
height: '100%',
textStyle: {
normal: {
color() {
const arr = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#975FE5']
let index = Math.floor(Math.random() * arr.length)
return arr[index]
},
},
},
data: [
{
name: 'vue-admin-better',
value: 15000,
},
{
name: 'element',
value: 10081,
},
{
name: 'beautiful',
value: 9386,
},
{
name: 'vue',
value: 6500,
},
{
name: 'zxwk1998',
value: 6000,
},
{
name: 'good',
value: 4500,
},
{
name: 'success',
value: 3800,
},
{
name: 'never',
value: 3000,
},
{
name: 'boy',
value: 2500,
},
{
name: 'girl',
value: 2300,
},
{
name: 'github',
value: 2000,
},
{
name: 'hbuilder',
value: 1900,
},
{
name: 'dcloud',
value: 1800,
},
{
name: 'china',
value: 1700,
},
{
name: '1204505056',
value: 1600,
},
{
name: '972435319',
value: 1500,
},
{
name: 'young',
value: 1200,
},
{
name: 'old',
value: 1100,
},
{
name: 'vuex',
value: 900,
},
{
name: 'router',
value: 800,
},
{
name: 'money',
value: 700,
},
{
name: 'qingdao',
value: 800,
},
{
name: 'yantai',
value: 9000,
},
{
name: 'author is very cool',
value: 9200,
},
],
},
],
},
//更新日志
reverse: true,
activities: [],
noticeList: [],
//其他信息
userAgent: navigator.userAgent,
//卡片图标
iconList: [
{
icon: 'video',
title: '视频播放器',
link: '/vab/player',
color: '#ffc069',
},
{
icon: 'table',
title: '表格',
link: '/vab/table/comprehensiveTable',
color: '#5cdbd3',
},
{
icon: 'laptop-code',
title: '源码',
link: 'https://github.com/zxwk1998/vue-admin-better',
color: '#b37feb',
},
{
icon: 'book',
title: '书籍',
link: '',
color: '#69c0ff',
},
{
icon: 'bullhorn',
title: '公告',
link: '',
color: '#ff85c0',
},
{
icon: 'gift',
title: '礼物',
link: '',
color: '#ffd666',
},
{
icon: 'balance-scale-left',
title: '公平的世界',
link: '',
color: '#ff9c6e',
},
{
icon: 'coffee',
title: '休息一下',
link: '',
color: '#95de64',
},
],
}
},
created() {
this.fetchData()
},
beforeDestroy() {
clearInterval(this.timer)
},
mounted() {
let base = +new Date(2020, 1, 1)
let oneDay = 24 * 3600 * 1000
let date = []
let data = [Math.random() * 1500]
let now = new Date(base)
const addData = (shift) => {
now = [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/')
date.push(now)
data.push(this.$baseLodash.random(20000, 60000))
if (shift) {
date.shift()
data.shift()
}
now = new Date(+new Date(now) + oneDay)
}
for (let i = 1; i < 6; i++) {
addData()
}
addData(true)
this.fwl.xAxis[0].data = date
this.fwl.series[0].data = data
this.timer = setInterval(() => {
addData(true)
this.fwl.xAxis[0].data = date
this.fwl.series[0].data = data
}, 3000)
},
methods: {
handleClick(e) {
this.$baseMessage(`点击了${e.name},这里可以写跳转`)
},
handleZrClick() {},
handleChangeTheme() {
this.$baseEventBus.$emit('theme')
},
async fetchData() {
const res = await getNoticeList()
this.noticeList = res.data
},
},
}
</script>
<style lang="scss" scoped>
.index-container {
padding: 0 !important;
margin: 0 !important;
background: #f5f7f8 !important;
::v-deep {
.el-alert {
padding: $base-padding;
&--info.is-light {
min-height: 82px;
padding: $base-padding;
margin-bottom: 15px;
color: #909399;
background-color: $base-color-white;
border: 1px solid #ebeef5;
}
}
.el-card__body {
.echarts {
width: 100%;
height: 115px;
}
}
}
.card {
::v-deep {
.el-card__body {
.echarts {
width: 100%;
height: 305px;
}
}
}
}
.bottom {
padding-top: 20px;
margin-top: 5px;
color: #595959;
text-align: left;
border-top: 1px solid $base-border-color;
}
.table {
width: 100%;
color: #666;
border-collapse: collapse;
background-color: #fff;
td {
position: relative;
min-height: 20px;
padding: 9px 15px;
font-size: 14px;
line-height: 20px;
border: 1px solid #e6e6e6;
&:nth-child(odd) {
width: 20%;
text-align: right;
background-color: #f7f7f7;
}
}
}
.icon-panel {
height: 117px;
text-align: center;
cursor: pointer;
svg {
font-size: 40px;
}
p {
margin-top: 10px;
}
}
.bottom-btn {
button {
margin: 5px 10px 15px 0;
}
}
}
</style>

296
src/views/login/index.vue Normal file
View File

@@ -0,0 +1,296 @@
<template>
<div class="login-container">
<el-row>
<el-col :lg="16" :md="12" :sm="24" :xl="16" :xs="24">
<div style="color: transparent">占位符</div>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
<el-form ref="form" class="login-form" label-position="left" :model="form" :rules="rules">
<div class="title">hello !</div>
<div class="title-tips">欢迎来到{{ title }}</div>
<el-form-item prop="username" style="margin-top: 40px">
<span class="svg-container svg-container-admin">
<vab-icon :icon="['fas', 'user']" />
</span>
<el-input v-model.trim="form.username" v-focus placeholder="请输入用户名" tabindex="1" type="text" />
</el-form-item>
<el-form-item prop="password">
<span class="svg-container">
<vab-icon :icon="['fas', 'lock']" />
</span>
<el-input
:key="passwordType"
ref="password"
v-model.trim="form.password"
placeholder="请输入密码"
tabindex="2"
:type="passwordType"
@keyup.enter.native="handleLogin"
/>
<span v-if="passwordType === 'password'" class="show-password" @click="handlePassword">
<vab-icon :icon="['fas', 'eye-slash']" />
</span>
<span v-else class="show-password" @click="handlePassword">
<vab-icon :icon="['fas', 'eye']" />
</span>
</el-form-item>
<el-button class="login-btn" :loading="loading" type="primary" @click="handleLogin">登录</el-button>
<router-link to="/register">
<div style="margin-top: 20px">注册</div>
</router-link>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import { isPassword } from '@/utils/validate'
import { mapActions } from 'vuex'
export default {
name: 'Login',
directives: {
focus: {
inserted(el) {
el.querySelector('input').focus()
},
},
},
data() {
const validateusername = (rule, value, callback) => {
if ('' == value) {
callback(new Error('用户名不能为空'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (!isPassword(value)) {
callback(new Error('密码不能少于6位'))
} else {
callback()
}
}
return {
nodeEnv: process.env.NODE_ENV,
title: this.$baseTitle,
form: {
username: '',
password: '',
},
rules: {
username: [
{
required: true,
trigger: 'blur',
validator: validateusername,
},
],
password: [
{
required: true,
trigger: 'blur',
validator: validatePassword,
},
],
},
loading: false,
passwordType: 'password',
redirect: undefined,
timeOutID: null,
}
},
watch: {
$route: {
handler(route) {
this.redirect = (route.query && route.query.redirect) || '/'
},
immediate: true,
},
},
created() {
document.body.style.overflow = 'hidden'
},
beforeDestroy() {
document.body.style.overflow = 'auto'
clearTimeout(this.timeOutID)
},
mounted() {
this.form.username = ''
this.form.password = ''
},
methods: {
...mapActions({
changeLogo: 'settings/changeLogo',
}),
handlePassword() {
this.passwordType === 'password' ? (this.passwordType = '') : (this.passwordType = 'password')
this.$nextTick(() => {
this.$refs.password.focus()
})
},
handleLogin() {
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true
this.$store
.dispatch('user/login', this.form)
.then(() => {
const routerPath = this.redirect === '/404' || this.redirect === '/401' ? '/' : this.redirect
// this.changeLogo('https://file.lihailezzc.com/resource/527ac7f6b4a45cd46f9f2954dca39fd0.jpg')
this.$router.push(routerPath).catch(() => {})
this.loading = false
})
.catch(() => {
this.loading = false
})
} else {
return false
}
})
},
},
}
</script>
<style lang="scss" scoped>
.login-container {
height: 100vh;
background: url('~@/assets/login_images/background.jpg') center center fixed no-repeat;
background-size: cover;
.title {
font-size: 54px;
font-weight: 500;
color: rgba(14, 18, 26, 1);
}
.title-tips {
margin-top: 29px;
font-size: 26px;
font-weight: 400;
color: rgba(14, 18, 26, 1);
text-overflow: ellipsis;
white-space: nowrap;
}
.login-btn {
display: inherit;
width: 220px;
height: 60px;
margin-top: 5px;
border: 0;
&:hover {
opacity: 0.9;
}
}
.login-form {
position: relative;
max-width: 100%;
margin: calc((100vh - 425px) / 2) 10% 10%;
overflow: hidden;
.forget-password {
width: 100%;
margin-top: 40px;
text-align: left;
.forget-pass {
width: 129px;
height: 19px;
font-size: 20px;
font-weight: 400;
color: rgba(92, 102, 240, 1);
}
}
}
.tips {
margin-bottom: 10px;
font-size: $base-font-size-default;
color: $base-color-white;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.title-container {
position: relative;
.title {
margin: 0 auto 40px auto;
font-size: 34px;
font-weight: bold;
color: $base-color-blue;
text-align: center;
}
}
.svg-container {
position: absolute;
top: 14px;
left: 15px;
z-index: $base-z-index;
font-size: 16px;
color: #d7dee3;
cursor: pointer;
user-select: none;
}
.show-password {
position: absolute;
top: 14px;
right: 25px;
font-size: 16px;
color: #d7dee3;
cursor: pointer;
user-select: none;
}
::v-deep {
.el-form-item {
padding-right: 0;
margin: 20px 0;
color: #454545;
background: transparent;
border: 1px solid transparent;
border-radius: 2px;
&__content {
min-height: $base-input-height;
line-height: $base-input-height;
}
&__error {
position: absolute;
top: 100%;
left: 18px;
font-size: $base-font-size-small;
line-height: 18px;
color: $base-color-red;
}
}
.el-input {
box-sizing: border-box;
input {
height: 58px;
padding-left: 45px;
font-size: $base-font-size-default;
line-height: 58px;
color: $base-font-color;
background: #f6f4fc;
border: 0;
caret-color: $base-font-color;
}
}
}
}
</style>

View File

@@ -0,0 +1,147 @@
<template>
<div class="goods-list-container">
<vab-query-form>
<vab-query-form-right-panel :span="24">
<el-form ref="form" :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model="queryForm.title" placeholder="商品名称" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" native-type="submit" type="primary" @click="handleQuery">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-row :gutter="20">
<el-col v-for="(item, index) in list" :key="index" :lg="8" :md="8" :sm="8" :xl="6" :xs="24">
<el-card :body-style="{ padding: '0px' }" shadow="hover">
<div class="goods-list-card-body">
<div class="goods-list-tag-group">
<el-tag v-if="item.isRecommend" hit type="success">推荐</el-tag>
<el-tag v-if="item.status === 0" hit type="danger">缺货</el-tag>
</div>
<div class="goods-list-image-group">
<img class="goods-list-image" :src="item.image" />
</div>
<div class="goods-list-title">
{{ item.title }}
</div>
<div class="goods-list-description">
{{ item.description }}
</div>
<div class="goods-list-price">
<span>¥ {{ item.price }} </span>
</div>
</div>
</el-card>
</el-col>
</el-row>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</template>
<script>
import { getList } from '@/api/goodsList'
export default {
name: 'Goods',
components: {},
data() {
return {
queryForm: {
pageNo: 1,
pageSize: 20,
title: '',
},
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
elementLoadingText: '正在加载...',
}
},
created() {
this.fetchData()
},
methods: {
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
handleQuery() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data, totalCount } = await getList(this.queryForm)
this.list = data
this.total = totalCount
},
},
}
</script>
<style lang="scss" scoped>
.goods-list-container {
.goods-list-card-body {
position: relative;
text-align: center;
cursor: pointer;
.goods-list-tag-group {
position: absolute;
top: 10px;
right: 5px;
z-index: 9;
}
.goods-list-image-group {
height: 400px;
overflow: hidden;
.goods-list-image {
width: 100%;
height: 400px;
transition: all ease-in-out 0.3s;
&:hover {
transform: scale(1.1);
}
}
}
.goods-list-title {
margin: 8px 0;
font-size: 16px;
font-weight: bold;
}
.goods-list-description {
font-size: 14px;
color: #808695;
}
.goods-list-price {
margin: 8px 0;
font-size: 14px;
color: $base-color-orange;
s {
color: #c5c8ce;
}
}
}
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<div>
<el-form ref="form" label-width="120px" :model="form" :rules="rules">
<el-form-item label-width="0">
<el-alert show-icon>请务必仔细填写并核对</el-alert>
</el-form-item>
<el-form-item label="付款账户" prop="payAccount">
<el-input v-model="form.payAccount" />
</el-form-item>
<el-form-item label="收款账户" prop="gatheringAccount">
<el-input v-model="form.gatheringAccount" />
</el-form-item>
<el-form-item label="收款人姓名" prop="gatheringName">
<el-input v-model="form.gatheringName" />
</el-form-item>
<el-form-item label="转账金额" prop="price">
<el-input v-model="form.price" />
</el-form-item>
</el-form>
<div class="pay-button-group">
<el-button type="primary" @click="handleSubmit">下一步</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
form: {
payAccount: 'XXXXXXXXXXXXXXXX',
gatheringAccount: '',
gatheringName: 'zxwk1998',
price: '100',
},
rules: {
payAccount: [{ required: true, message: '请选择付款账户', trigger: 'blur' }],
gatheringAccount: [
{ required: true, message: '请输入收款账户', trigger: 'blur' },
{ type: 'email', message: '账户名应为邮箱格式', trigger: 'blur' },
],
gatheringName: [{ required: true, message: '请输入收款人姓名', trigger: 'blur' }],
price: [
{ required: true, message: '请输入转账金额', trigger: 'blur' },
{ pattern: /^(\d+)((?:\.\d+)?)$/, message: '请输入合法金额数字' },
],
},
}
},
methods: {
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.$emit('change-step', 2, this.form)
}
})
},
},
}
</script>
<style lang="scss" scoped>
.pay-button-group {
display: block;
margin: 20px auto;
text-align: center;
}
</style>

View File

@@ -0,0 +1,78 @@
<template>
<div>
<el-form ref="form" label-width="120px" :model="form" :rules="rules">
<el-form-item label-width="0">
<el-alert show-icon>确认转账后资金将直接打入对方账户无法退回</el-alert>
</el-form-item>
<el-form-item label="付款账户:">
{{ infoData.payAccount }}
</el-form-item>
<el-form-item label="收款账户:">
{{ infoData.gatheringAccount }}
</el-form-item>
<el-form-item label="收款人姓名:">
{{ infoData.gatheringName }}
</el-form-item>
<el-form-item label="转账金额:">
<strong>
{{ infoData.price }}
</strong>
</el-form-item>
<el-form-item label="支付密码:" prop="password">
<el-input v-model="form.password" type="password" />
</el-form-item>
</el-form>
<div class="pay-button-group">
<el-button :loading="loading" type="primary" @click="handleSubmit">提交</el-button>
<el-button @click="handlePrev">上一步</el-button>
</div>
</div>
</template>
<script>
export default {
props: {
infoData: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
form: {
password: '123456',
},
rules: {
password: [{ required: true, message: '请输入支付密码', trigger: 'blur' }],
},
loading: false,
}
},
methods: {
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true
setTimeout(() => {
this.$emit('change-step', 3)
this.loading = false
}, 2000)
} else {
this.loading = false
}
})
},
handlePrev() {
this.$emit('change-step', 1)
},
},
}
</script>
<style lang="scss" scoped>
.pay-button-group {
display: block;
margin: 20px auto;
text-align: center;
}
</style>

View File

@@ -0,0 +1,93 @@
<template>
<div>
<div class="pay-top-content">
<vab-icon class="pay-success" :icon="['fas', 'check-circle']" />
<p>支付成功</p>
</div>
<el-form ref="form" class="pay-bottom" label-width="120px" :model="form" :rules="rules">
<el-form-item label="付款账户:">
{{ infoData.payAccount }}
</el-form-item>
<el-form-item label="收款账户:">
{{ infoData.gatheringAccount }}
</el-form-item>
<el-form-item label="收款人姓名:">
{{ infoData.gatheringName }}
</el-form-item>
<el-form-item label="转账金额:">
<strong>
{{ infoData.price }}
</strong>
</el-form-item>
</el-form>
<div class="pay-button-group">
<el-button type="primary" @click="handlePrev">再转一笔</el-button>
</div>
</div>
</template>
<script>
export default {
props: {
infoData: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
form: {
password: '123456',
},
rules: {
password: [{ required: true, message: '请输入支付密码', trigger: 'blur' }],
},
loading: false,
}
},
methods: {
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true
setTimeout(() => {
this.$emit('change-step', 3)
this.loading = false
}, 2000)
} else {
this.loading = false
}
})
},
handlePrev() {
this.$emit('change-step', 1)
},
},
}
</script>
<style lang="scss" scoped>
.pay-top-content {
text-align: center;
.pay-success {
display: block;
margin: 20px auto 5px auto;
font-size: 40px;
color: $base-color-green;
}
}
.pay-bottom {
padding: 20px;
margin-top: 20px;
background: #f5f7f8;
border: 1px dashed $base-color-gray;
}
.pay-button-group {
display: block;
margin: 20px auto;
text-align: center;
}
</style>

View File

@@ -0,0 +1,53 @@
<template>
<div class="pay-container">
<el-row :gutter="20">
<el-col
:lg="{ span: 14, offset: 5 }"
:md="{ span: 20, offset: 2 }"
:sm="{ span: 20, offset: 2 }"
:xl="{ span: 12, offset: 6 }"
:xs="24"
>
<el-steps :active="active" align-center class="steps" :space="200">
<el-step title="填写转账信息" />
<el-step title="确认转账信息" />
<el-step title="完成" />
</el-steps>
<step1 v-if="active === 1" @change-step="handleSetStep" />
<step2 v-if="active === 2" :info-data="form" @change-step="handleSetStep" />
<step3 v-if="active === 3" :info-data="form" @change-step="handleSetStep" />
</el-col>
</el-row>
</div>
</template>
<script>
import Step1 from './components/Step1'
import Step2 from './components/Step2'
import Step3 from './components/Step3'
export default {
name: 'Pay',
components: { Step1, Step2, Step3 },
data() {
return {
active: 1,
form: {},
}
},
methods: {
handleSetStep(active, form) {
this.active = active
if (form) this.form = Object.assign(this.form, form)
},
},
}
</script>
<style lang="scss" scoped>
.pay-container {
.steps {
justify-content: center;
margin-bottom: 20px;
}
}
</style>

View File

@@ -0,0 +1,23 @@
<template>
<div class="personalCenter-container">
<el-tabs :tab-position="tabPosition">
<el-tab-pane label="个人简介">个人简介</el-tab-pane>
<el-tab-pane label="基本设置">基本设置</el-tab-pane>
<el-tab-pane label="安全设置">安全设置</el-tab-pane>
<el-tab-pane label="账户绑定">安全设置</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
name: 'PersonalCenter',
data() {
return {
tabPosition: 'left',
}
},
created() {},
methods: {},
}
</script>

View File

@@ -0,0 +1,100 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择 APP 类型">
<el-option label="小程序" :value="1" />
<el-option label="H5" :value="2" />
<el-option label="后台管理" :value="3" />
</el-select>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="form.description" autocomplete="off" />
</el-form-item>
<el-form-item label="APP图标" prop="appIcon">
<single-upload
style="width: 100px; height: 100px"
upload-url="http://127.0.0.1:3999/management/api/common/upload"
@upload-success="handleUploadSuccess"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit, doAdd } from '@/api/appManagement'
import SingleUpload from '@/components/SingleUpload'
export default {
name: 'AppManagementEdit',
components: { SingleUpload },
data() {
return {
id: '',
form: {
name: '',
type: '',
description: '',
icon: '',
},
rules: {
name: [{ required: true, trigger: 'blur', message: '请输入名称' }],
type: [{ required: true, trigger: 'blur', message: '请选择类型' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
handleUploadSuccess(url) {
this.form.icon = url
},
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = {
name: row.name,
description: row.description,
icon: row.icon,
}
this.id = row.id
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
if (this.id) {
const { msg } = await doEdit(this.id, this.form)
this.$baseMessage(msg, 'success')
} else {
const { msg } = await doAdd(this.form)
this.$baseMessage(msg, 'success')
}
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,146 @@
<template>
<div class="appManagement-container">
<el-divider content-position="left">
演示环境仅做基础功能展示若想实现不同角色的真实菜单配置需将settings.js路由加载模式改为all模式由后端全面接管路由渲染与权限控制
</el-divider>
<vab-query-form>
<vab-query-form-left-panel :span="12">
<el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button>
<el-button icon="el-icon-delete" type="danger" @click="handleDelete">批量删除</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel :span="12">
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model.trim="queryForm.permission" clearable placeholder="请输入查询条件" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-table v-loading="listLoading" :data="list" :element-loading-text="elementLoadingText" @selection-change="setSelectRows">
<el-table-column show-overflow-tooltip type="selection" />
<el-table-column align="center" label="图标" width="100">
<template slot-scope="scope">
<!-- 使用 show-overflow-tooltip 显示图片并添加 tooltip -->
<el-tooltip class="item" :content="scope.row.name" effect="dark" placement="top">
<img alt="image" :src="scope.row.icon" style="width: 50px; height: 50px; object-fit: cover; border-radius: 50%" />
</el-tooltip>
</template>
</el-table-column>
<el-table-column align="center" label="名称" prop="name" show-overflow-tooltip />
<el-table-column align="center" label="类型" show-overflow-tooltip>
<template #default="{ row }">
{{ row.type === 1 ? '小程序' : row.type === 2 ? 'H5' : '后台管理' }}
</template>
</el-table-column>
<el-table-column align="center" label="描述" prop="description" show-overflow-tooltip />
<el-table-column label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<edit ref="edit" @fetch-data="fetchData" />
</div>
</template>
<script>
import { doDelete, getList } from '@/api/appManagement'
import { formatTime } from '@/utils'
import Edit from './components/AppManagementEdit'
export default {
name: 'AppManagement',
components: { Edit },
data() {
return {
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 10,
permission: '',
},
timeOutID: null,
}
},
created() {
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
setSelectRows(val) {
this.selectRows = val
},
handleEdit(row) {
if (row.id) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: [row.id] })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids: ids.split(',') })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data } = await getList(this.queryForm)
this.list = data.list
this.total = data.totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
},
}
</script>

View File

@@ -0,0 +1,64 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-divider content-position="left">这里就不具体写了请自行完善</el-divider>
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="name" prop="name">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="路径" prop="path">
<el-input v-model="form.path" autocomplete="off" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit } from '@/api/menuManagement'
export default {
name: 'MenuManagementEdit',
data() {
return {
form: {},
rules: {
id: [{ required: true, trigger: 'blur', message: '请输入路径' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = Object.assign({}, row)
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
const { msg } = await doEdit(this.form)
this.$baseMessage(msg, 'success')
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,147 @@
<template>
<div class="menuManagement-container">
<el-divider content-position="left">
演示环境仅做基础功能展示若想实现不同角色的真实菜单配置需将settings.js路由加载模式改为all模式由后端全面接管路由渲染与权限控制
</el-divider>
<el-row>
<el-col :lg="4" :md="8" :sm="24" :xl="4" :xs="24">
<el-tree :data="data" :default-expanded-keys="['root']" node-key="id" :props="defaultProps" @node-click="handleNodeClick" />
</el-col>
<el-col :lg="20" :md="16" :sm="24" :xl="20" :xs="24">
<vab-query-form>
<vab-query-form-top-panel :span="12">
<el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button>
</vab-query-form-top-panel>
</vab-query-form>
<el-table
v-loading="listLoading"
border
:data="list"
default-expand-all
:element-loading-text="elementLoadingText"
row-key="path"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="name" prop="name" show-overflow-tooltip />
<el-table-column label="路径" prop="path" show-overflow-tooltip />
<el-table-column label="是否隐藏" show-overflow-tooltip>
<template #default="{ row }">
<span>
{{ row.hidden ? '是' : '否' }}
</span>
</template>
</el-table-column>
<el-table-column label="是否一直显示当前节点" show-overflow-tooltip>
<template #default="{ row }">
<span>
{{ row.alwaysShow ? '是' : '否' }}
</span>
</template>
</el-table-column>
<el-table-column label="vue文件路径" prop="component" show-overflow-tooltip />
<el-table-column label="重定向" prop="redirect" show-overflow-tooltip />
<el-table-column label="标题" prop="meta.title" show-overflow-tooltip />
<el-table-column label="图标" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
<vab-icon v-if="row.meta.icon" :icon="['fas', row.meta.icon]" />
</span>
</template>
</el-table-column>
<el-table-column label="是否固定" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
{{ row.meta.affix ? '' : '' }}
</span>
</template>
</el-table-column>
<el-table-column label="是否无缓存" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
{{ row.meta.noKeepAlive ? '' : '' }}
</span>
</template>
</el-table-column>
<el-table-column label="badge" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
{{ row.meta.badge }}
</span>
</template>
</el-table-column>
<el-table-column label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<edit ref="edit" @fetch-data="fetchData" />
</div>
</template>
<script>
import { getRouterList as getList } from '@/api/router'
import { doDelete, getTree } from '@/api/menuManagement'
import Edit from './components/MenuManagementEdit'
export default {
name: 'MenuManagement',
components: { Edit },
data() {
return {
data: [],
defaultProps: {
children: 'children',
label: 'label',
},
list: [],
listLoading: true,
elementLoadingText: '正在加载...',
timeOutID: null,
}
},
async created() {
const roleData = await getTree()
this.data = roleData.data
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
handleEdit(row) {
if (row.path) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: row.id })
this.$baseMessage(msg, 'success')
this.fetchData()
})
}
},
async fetchData() {
this.listLoading = true
const { data } = await getList()
this.list = data
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
handleNodeClick() {
this.fetchData()
},
},
}
</script>

View File

@@ -0,0 +1,83 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="权限码" prop="permission">
<el-input v-model="form.permission" autocomplete="off" />
</el-form-item>
<el-form-item label="描述" prop="desc">
<el-input v-model="form.desc" autocomplete="off" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit, doAdd } from '@/api/roleManagement'
export default {
name: 'RoleManagementEdit',
data() {
return {
id: '',
form: {
name: '',
permission: '',
desc: '',
},
rules: {
permission: [{ required: true, trigger: 'blur', message: '请输入权限码' }],
name: [{ required: true, trigger: 'blur', message: '请输入名称' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = {
name: row.name,
permission: row.permission,
desc: row.desc,
}
this.id = row.id
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
if (this.id) {
const { msg } = await doEdit(this.id, this.form)
this.$baseMessage(msg, 'success')
} else {
const { msg } = await doAdd(this.form)
this.$baseMessage(msg, 'success')
}
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,133 @@
<template>
<div class="roleManagement-container">
<el-divider content-position="left">
演示环境仅做基础功能展示若想实现不同角色的真实菜单配置需将settings.js路由加载模式改为all模式由后端全面接管路由渲染与权限控制
</el-divider>
<vab-query-form>
<vab-query-form-left-panel :span="12">
<el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button>
<el-button icon="el-icon-delete" type="danger" @click="handleDelete">批量删除</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel :span="12">
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model.trim="queryForm.permission" clearable placeholder="请输入查询条件" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-table v-loading="listLoading" :data="list" :element-loading-text="elementLoadingText" @selection-change="setSelectRows">
<el-table-column show-overflow-tooltip type="selection" />
<el-table-column label="名称" prop="name" show-overflow-tooltip />
<el-table-column label="权限码" prop="permission" show-overflow-tooltip />
<el-table-column label="描述" prop="desc" show-overflow-tooltip />
<el-table-column label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<edit ref="edit" @fetch-data="fetchData" />
</div>
</template>
<script>
import { doDelete, getList } from '@/api/roleManagement'
import Edit from './components/RoleManagementEdit'
export default {
name: 'RoleManagement',
components: { Edit },
data() {
return {
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 10,
permission: '',
},
timeOutID: null,
}
},
created() {
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
setSelectRows(val) {
this.selectRows = val
},
handleEdit(row) {
if (row.id) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: [row.id] })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids: ids.split(',') })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data } = await getList(this.queryForm)
this.list = data.list
this.total = data.totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
},
}
</script>

View File

@@ -0,0 +1,116 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="用户名" prop="username">
<el-input v-model.trim="form.username" autocomplete="off" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model.trim="form.password" autocomplete="off" type="password" />
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input v-model.trim="form.nickname" autocomplete="off" />
</el-form-item>
<el-form-item label="权限" prop="permissions">
<el-select v-model="form.permissions" multiple placeholder="请选择权限">
<el-option v-for="item in permissionList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="应用" prop="appId">
<el-select v-model="form.appId" placeholder="请选择应用">
<el-option v-for="item in applicationList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="头像" prop="avatar">
<single-upload
style="width: 100px; height: 100px"
upload-url="http://127.0.0.1:3999/management/api/common/upload"
@upload-success="handleUploadSuccess"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit } from '@/api/userManagement'
import { getList } from '@/api/roleManagement'
import { getList as getApplicationList } from '@/api/appManagement'
import SingleUpload from '@/components/SingleUpload'
export default {
name: 'UserManagementEdit',
components: { SingleUpload },
data() {
return {
form: {
username: '',
password: '',
nickname: '',
permissions: '',
appId: '',
},
permissionList: [],
applicationList: [],
rules: {
username: [{ required: true, trigger: 'blur', message: '请输入用户名' }],
password: [{ required: true, trigger: 'blur', message: '请输入密码' }],
permissions: [{ required: true, trigger: 'blur', message: '请选择权限' }],
appId: [{ required: true, trigger: 'blur', message: '请选择应用' }],
},
title: '',
dialogFormVisible: false,
queryForm: {
pageNo: 1,
pageSize: 100,
},
}
},
created() {
this.fetchRoleData()
this.fetchApplicationData()
},
methods: {
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = Object.assign({}, row)
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
const { msg } = await doEdit(this.form)
this.$baseMessage(msg, 'success')
this.$emit('fetch-data')
this.close()
} else {
return false
}
})
},
async fetchRoleData() {
this.listLoading = true
const { data } = await getList(this.queryForm)
this.permissionList = data.list
},
async fetchApplicationData() {
const { data } = await getApplicationList(this.queryForm)
this.applicationList = data.list
},
handleUploadSuccess(url) {
this.form.avatar = url
},
},
}
</script>

View File

@@ -0,0 +1,152 @@
<template>
<div class="userManagement-container">
<vab-query-form>
<vab-query-form-left-panel :span="12">
<el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button>
<el-button icon="el-icon-delete" type="danger" @click="handleDelete">批量删除</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel :span="12">
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model.trim="queryForm.username" clearable placeholder="请输入用户名" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-table v-loading="listLoading" :data="list" :element-loading-text="elementLoadingText" @selection-change="setSelectRows">
<el-table-column show-overflow-tooltip type="selection" />
<el-table-column align="center" label="头像" width="100">
<template slot-scope="scope">
<!-- 使用 show-overflow-tooltip 显示图片并添加 tooltip -->
<el-tooltip class="item" :content="scope.row.name" effect="dark" placement="top">
<img alt="image" :src="scope.row.avatar" style="width: 50px; height: 50px; object-fit: cover; border-radius: 50%" />
</el-tooltip>
</template>
</el-table-column>
<el-table-column align="center" label="用户名" prop="username" show-overflow-tooltip />
<el-table-column align="center" label="APP" prop="appName" show-overflow-tooltip />
<el-table-column align="center" label="权限" show-overflow-tooltip>
<template #default="{ row }">
<el-tag v-for="(item, index) in row.permissionsName" :key="index">
{{ item }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="修改时间" show-overflow-tooltip>
<template #default="{ row }">
{{ formatTime(row.updatedAt) }}
</template>
</el-table-column>
<el-table-column align="center" label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<edit ref="edit" @fetch-data="fetchData" />
</div>
</template>
<script>
import { doDelete, getList } from '@/api/userManagement'
import Edit from './components/UserManagementEdit'
import { formatTime } from '@/utils'
export default {
name: 'UserManagement',
components: { Edit },
data() {
return {
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 10,
username: '',
},
timeOutID: null,
}
},
created() {
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
formatTime,
setSelectRows(val) {
this.selectRows = val
},
handleEdit(row) {
if (row.id) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: row.id })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data } = await getList(this.queryForm)
this.list = data.list
this.total = data.totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
},
}
</script>

View File

@@ -0,0 +1,332 @@
<template>
<div class="register-container">
<el-row>
<el-col :lg="16" :md="12" :sm="24" :xl="16" :xs="24">
<div style="color: transparent">占位符</div>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
<el-form ref="registerForm" class="register-form" :model="form" :rules="registerRules" size="mini">
<el-form-item prop="username">
<el-input
v-model.trim="form.username"
v-focus
auto-complete="off"
placeholder="请输入用户名"
style="margin-top: 20px"
type="text"
>
<vab-icon slot="prefix" :icon="['fas', 'user-alt']" />
</el-input>
</el-form-item>
<el-form-item prop="phone">
<el-input v-model.trim="form.phone" autocomplete="off" maxlength="11" placeholder="请输入手机号" show-word-limit type="text">
<vab-icon slot="prefix" :icon="['fas', 'mobile-alt']" />
</el-input>
</el-form-item>
<el-form-item prop="phoneCode" style="position: relative">
<el-input v-model.trim="form.phoneCode" placeholder="手机验证码" type="text">
<vab-icon slot="prefix" :icon="['fas', 'envelope-open']" />
</el-input>
<el-button class="show-pwd phone-code" :disabled="isGetphone" type="primary" @click="getPhoneCode">
{{ phoneCode }}
</el-button>
</el-form-item>
<el-form-item prop="password">
<el-input v-model.trim="form.password" autocomplete="new-password" placeholder="设置密码" type="password">
<vab-icon slot="prefix" :icon="['fas', 'unlock']" />
</el-input>
</el-form-item>
<el-form-item>
<el-button class="register-btn" type="primary" @click.native.prevent="handleReister">注册</el-button>
<router-link to="/login">
<div style="margin-top: 20px">登录</div>
</router-link>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import { isPassword, isPhone } from '@/utils/validate'
import { register } from '@/api/user'
export default {
username: 'Register',
directives: {
focus: {
inserted(el) {
el.querySelector('input').focus()
},
},
},
data() {
const validateusername = (rule, value, callback) => {
if ('' == value) {
callback(new Error('用户名不能为空'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (!isPassword(value)) {
callback(new Error('密码不能少于6位'))
} else {
callback()
}
}
const validatePhone = (rule, value, callback) => {
if (!isPhone(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
}
return {
isGetphone: false,
getPhoneIntval: null,
phoneCode: '获取验证码',
showRegister: false,
nodeEnv: process.env.NODE_ENV,
title: this.$baseTitle,
form: {},
registerRules: {
username: [
{ required: true, trigger: 'blur', message: '请输入用户名' },
{ max: 20, trigger: 'blur', message: '最多不能超过20个字' },
{ validator: validateusername, trigger: 'blur' },
],
phone: [
{ required: true, trigger: 'blur', message: '请输入手机号码' },
{ validator: validatePhone, trigger: 'blur' },
],
password: [
{ required: true, trigger: 'blur', message: '请输入密码' },
{ validator: validatePassword, trigger: 'blur' },
],
phoneCode: [{ required: true, trigger: 'blur', message: '请输入手机验证码' }],
},
loading: false,
passwordType: 'password',
}
},
created() {
document.body.style.overflow = 'hidden'
},
beforeDestroy() {
document.body.style.overflow = 'auto'
clearInterval(this.getPhoneIntval)
this.getPhoneIntval = null
},
methods: {
getPhoneCode() {
if (!isPhone(this.form.phone)) {
//this.$baseMessage('请输入手机号', 'error')
this.$refs['registerForm'].validateField('phone')
return
}
this.isGetphone = true
let n = 60
this.getPhoneIntval = setInterval(() => {
if (n > 0) {
n--
this.phoneCode = `重新获取(${n}s)`
} else {
clearInterval(this.getPhoneIntval)
this.getPhoneIntval = null
this.phoneCode = '获取验证码'
this.isGetphone = false
}
}, 1000)
},
handleReister() {
this.$refs['registerForm'].validate(async (valid) => {
if (valid) {
const param = {
username: this.form.username,
phone: this.form.phone,
password: this.form.password,
phoneCode: this.form.phoneCode,
}
const { msg } = await register(param)
this.$baseMessage(msg, 'success')
}
})
},
},
}
</script>
<style lang="scss" scoped>
.register-container {
height: 100vh;
background: url('~@/assets/login_images/background.jpg') center center fixed no-repeat;
background-size: cover;
.title {
font-size: 54px;
font-weight: 500;
color: rgba(14, 18, 26, 1);
}
.title-tips {
margin-top: 29px;
font-size: 26px;
font-weight: 400;
color: rgba(14, 18, 26, 1);
text-overflow: ellipsis;
white-space: nowrap;
}
.register-btn {
display: inherit;
width: 220px;
height: 60px;
margin-top: 5px;
border: 0;
&:hover {
opacity: 0.9;
}
}
.register-form {
position: relative;
max-width: 100%;
margin: calc((100vh - 499px) / 2) 10% 10%;
overflow: hidden;
.forget-password {
width: 100%;
margin-top: 40px;
text-align: left;
.forget-password {
width: 129px;
height: 19px;
font-size: 20px;
font-weight: 400;
color: rgba(92, 102, 240, 1);
}
}
.per-code {
width: 100px;
height: 36px;
font-size: 20px;
line-height: 36px;
color: #fff;
text-align: center;
cursor: pointer;
background: #bbc1ce;
}
.phone-code {
width: 120px;
height: 36px;
font-size: 14px;
color: #fff;
border-radius: 3px;
}
}
.tips {
margin-bottom: 10px;
font-size: $base-font-size-default;
color: $base-color-white;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.title-container {
position: relative;
.title {
margin: 0 auto 40px auto;
font-size: 34px;
font-weight: bold;
color: $base-color-blue;
text-align: center;
}
}
.svg-container {
position: absolute;
top: 14px;
left: 15px;
z-index: $base-z-index;
font-size: 16px;
color: #d7dee3;
cursor: pointer;
user-select: none;
}
.show-pwd {
position: absolute;
top: 14px;
right: 25px;
font-size: 16px;
color: $base-font-color;
cursor: pointer;
user-select: none;
}
::v-deep {
.el-form-item {
padding-right: 0;
margin: 20px 0;
color: #454545;
background: transparent;
border: 1px solid transparent;
border-radius: 2px;
&__content {
min-height: $base-input-height;
line-height: $base-input-height;
}
&__error {
position: absolute;
top: 100%;
left: 18px;
font-size: $base-font-size-small;
line-height: 18px;
color: $base-color-red;
}
}
.el-input {
box-sizing: border-box;
.el-input__count {
.el-input__count-inner {
background: transparent;
}
}
.el-input__prefix {
left: 15px;
line-height: 56px;
.svg-inline--fa {
width: 20px;
}
}
input {
height: 58px;
padding-left: 45px;
font-size: $base-font-size-default;
line-height: 58px;
color: $base-font-color;
background: #f6f4fc;
border: 0;
caret-color: $base-font-color;
}
}
}
}
</style>

16
src/views/test/index.vue Normal file
View File

@@ -0,0 +1,16 @@
<template>
<div class="test-container">
<el-divider content-position="left">你可以在这里写demo</el-divider>
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return { show: true }
},
created() {},
mounted() {},
methods: {},
}
</script>

View File

@@ -0,0 +1,20 @@
<template>
<div class="back-to-top-container">
<div v-for="(item, index) in 100" :key="index" style="padding: 20px">测试滚轮显示返回顶部-{{ index }}</div>
<!-- <el-tooltip placement="top" content="返回顶部"><vab-back-to-top transition-name="fade" /></el-tooltip> -->
</div>
</template>
<script>
export default {
name: 'BackToTop',
data() {
return {}
},
}
</script>
<style lang="scss" scoped>
.placeholder-container div {
margin: 10px;
}
</style>

View File

@@ -0,0 +1,176 @@
<template>
<div class="element-container">
<el-row :gutter="20">
<el-col :lg="18" :md="18" :sm="24" :xl="16" :xs="24">
<el-button type="primary" @click="dialogVisible = !dialogVisible">element全部文档点这里</el-button>
<el-dialog :fullscreen="true" title="element文档" :visible.sync="dialogVisible">
<iframe class="element-iframe" frameborder="0" src="https://element.eleme.cn/#/zh-CN/component/installation"></iframe>
</el-dialog>
<el-divider content-position="left">
Tag 标签
<a href="https://element.eleme.cn/#/zh-CN/component/tag" target="_blank">文档</a>
</el-divider>
<el-tag>标签一</el-tag>
<el-tag type="success">标签二</el-tag>
<el-tag type="info">标签三</el-tag>
<el-tag type="warning">标签四</el-tag>
<el-tag type="danger">标签五</el-tag>
<el-tag effect="dark">标签一</el-tag>
<el-tag effect="dark" type="success">标签二</el-tag>
<el-tag effect="dark" type="info">标签三</el-tag>
<el-tag effect="dark" type="warning">标签四</el-tag>
<el-tag effect="dark" type="danger">标签五</el-tag>
<el-divider content-position="left">
进度条
<a href="https://element.eleme.cn/#/zh-CN/component/progress" target="_blank">文档</a>
</el-divider>
<el-progress :percentage="50" />
<el-progress :percentage="100" status="success" />
<el-progress :percentage="100" status="warning" />
<el-progress :percentage="50" status="exception" />
<el-progress :percentage="70" :stroke-width="26" :text-inside="true" />
<el-progress :percentage="100" status="success" :stroke-width="24" :text-inside="true" />
<el-progress :percentage="80" status="warning" :stroke-width="22" :text-inside="true" />
<el-progress :percentage="50" status="exception" :stroke-width="20" :text-inside="true" />
<el-progress :percentage="0" type="circle" />
<el-progress :percentage="25" type="circle" />
<el-progress :percentage="100" status="success" type="circle" />
<el-progress :percentage="70" status="warning" type="circle" />
<el-progress :percentage="50" status="exception" type="circle" />
<el-divider content-position="left">
按钮
<a href="https://element.eleme.cn/#/zh-CN/component/button" target="_blank">文档</a>
</el-divider>
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="info">信息按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
<el-button plain>朴素按钮</el-button>
<el-button plain type="primary">主要按钮</el-button>
<el-button plain type="success">成功按钮</el-button>
<el-button plain type="info">信息按钮</el-button>
<el-button plain type="warning">警告按钮</el-button>
<el-button plain type="danger">危险按钮</el-button>
<el-button round>圆角按钮</el-button>
<el-button round type="primary">主要按钮</el-button>
<el-button round type="success">成功按钮</el-button>
<el-button round type="info">信息按钮</el-button>
<el-button round type="warning">警告按钮</el-button>
<el-button round type="danger">危险按钮</el-button>
<el-button circle icon="el-icon-search" />
<el-button circle icon="el-icon-edit" type="primary" />
<el-button circle icon="el-icon-check" type="success" />
<el-button circle icon="el-icon-message" type="info" />
<el-button circle icon="el-icon-star-off" type="warning" />
<el-button circle icon="el-icon-delete" type="danger" />
<el-button disabled>默认按钮</el-button>
<el-button disabled type="primary">主要按钮</el-button>
<el-button disabled type="success">成功按钮</el-button>
<el-button disabled type="info">信息按钮</el-button>
<el-button disabled type="warning">警告按钮</el-button>
<el-button disabled type="danger">危险按钮</el-button>
<el-button icon="el-icon-edit" type="primary" />
<el-button icon="el-icon-share" type="primary" />
<el-button icon="el-icon-delete" type="primary" />
<el-button icon="el-icon-search" type="primary">搜索</el-button>
<el-button type="primary">
上传
<i class="el-icon-upload el-icon--right"></i>
</el-button>
<el-button :loading="true" type="primary">加载中</el-button>
<el-divider content-position="left">
文字链接
<a href="https://element.eleme.cn/#/zh-CN/component/link" target="_blank">文档</a>
</el-divider>
<el-link href="https://element.eleme.io" target="_blank">默认链接</el-link>
<el-link type="primary">主要链接</el-link>
<el-link type="success">成功链接</el-link>
<el-link type="warning">警告链接</el-link>
<el-link type="danger">危险链接</el-link>
<el-link type="info">信息链接</el-link>
<el-link disabled>默认链接</el-link>
<el-link disabled type="primary">主要链接</el-link>
<el-link disabled type="success">成功链接</el-link>
<el-link disabled type="warning">警告链接</el-link>
<el-link disabled type="danger">危险链接</el-link>
<el-link disabled type="info">信息链接</el-link>
<el-link :underline="false">无下划线</el-link>
<el-link>有下划线</el-link>
<el-divider content-position="left">
头像
<a href="https://element.eleme.cn/#/zh-CN/component/avatar" target="_blank">文档</a>
</el-divider>
<el-avatar icon="el-icon-user-solid" />
<el-divider content-position="left">
页头
<a href="https://element.eleme.cn/#/zh-CN/component/page-header" target="_blank">文档</a>
</el-divider>
<el-page-header content="详情页面" />
<el-divider content-position="left">
面包屑
<a href="https://element.eleme.cn/#/zh-CN/component/breadcrumb" target="_blank">文档</a>
</el-divider>
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">活动管理</a></el-breadcrumb-item>
<el-breadcrumb-item>活动列表</el-breadcrumb-item>
<el-breadcrumb-item>活动详情</el-breadcrumb-item>
</el-breadcrumb>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'Element',
components: {},
data() {
return {
dialogVisible: false,
}
},
created() {},
mounted() {},
methods: {},
}
</script>
<style lang="scss" scoped>
.element-container {
::v-deep {
.el-dialog__wrapper {
position: fixed;
top: 20px;
right: 20px;
bottom: 20px;
left: 20px;
}
.el-tag,
.el-button,
.el-link {
margin: 5px;
}
.el-progress {
margin: 20px;
}
}
.element-iframe {
position: absolute;
top: 55px;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 89vh;
}
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
<!-- js代码错误实时提醒测试 -->
<div>{{ zxwk1998jiayou.zxwk1998jiayou }}</div>
</template>
<script>
export default {
name: 'ErrorTest',
data() {
return {}
},
}
</script>

View File

@@ -0,0 +1,24 @@
<template>
<div class="errorLog-container">
<el-divider content-position="left">这里会在顶部navbar上模拟一个控制台错误日志</el-divider>
<el-button type="primary" @click="handleError">点击模拟一个zxwk1998jiayou的错误</el-button>
<error-test v-if="show" />
</div>
</template>
<script>
import ErrorTest from './components/ErrorTest'
export default {
name: 'ErrorLog',
components: { ErrorTest },
data() {
return { show: false }
},
methods: {
handleError() {
this.show = true
},
},
}
</script>

View File

@@ -0,0 +1,97 @@
<template>
<div class="form-container">
<el-row :gutter="20">
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
<el-form ref="ruleForm" class="demo-ruleForm" label-width="100px" :model="ruleForm" :rules="rules">
<el-form-item label="活动名称" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="活动区域" prop="region">
<el-select v-model="ruleForm.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai" />
<el-option label="区域二" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="即时配送" prop="delivery">
<el-switch v-model="ruleForm.delivery" />
</el-form-item>
<el-form-item label="活动性质" prop="type">
<el-checkbox-group v-model="ruleForm.type">
<el-checkbox label="美食/餐厅线上活动" name="type" />
<el-checkbox label="地推活动" name="type" />
<el-checkbox label="线下主题活动" name="type" />
<el-checkbox label="单纯品牌曝光" name="type" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="特殊资源" prop="resource">
<el-radio-group v-model="ruleForm.resource">
<el-radio label="线上品牌商赞助" />
<el-radio label="线下场地免费" />
</el-radio-group>
</el-form-item>
<el-form-item label="活动形式" prop="desc">
<el-input v-model="ruleForm.desc" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'Form',
data() {
return {
ruleForm: {
name: '',
region: '',
delivery: false,
type: [],
resource: '',
desc: '',
},
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{
min: 3,
max: 5,
message: '长度在 3 到 5 个字符',
trigger: 'blur',
},
],
region: [{ required: true, message: '请选择活动区域', trigger: 'change' }],
type: [
{
type: 'array',
required: true,
message: '请至少选择一个活动性质',
trigger: 'change',
},
],
resource: [{ required: true, message: '请选择活动资源', trigger: 'change' }],
desc: [{ required: true, message: '请填写活动形式', trigger: 'blur' }],
},
}
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert('submit!')
} else {
return false
}
})
},
resetForm(formName) {
this.$refs[formName].resetFields()
},
},
}
</script>

View File

@@ -0,0 +1,139 @@
<template>
<div class="colorful-icon-container">
<el-row :gutter="20">
<el-col :span="24">
<el-divider content-position="left">多彩图标在演示环境中使用的是cdn加速服务开发时需存储到本地点击图标即可复制源码</el-divider>
</el-col>
<el-col :span="24">
<el-form :inline="true" label-width="80px" @submit.native.prevent>
<el-form-item label="图标名称">
<el-input v-model="queryForm.title" />
</el-form-item>
<el-form-item label-width="0">
<el-button native-type="submit" type="primary" @click="queryData">搜索</el-button>
</el-form-item>
<!-- <el-form-item label-width="0">
<el-input :value="copyText" type="text"></el-input>
</el-form-item>-->
</el-form>
</el-col>
<el-col v-for="(item, index) in queryIcon" :key="index" :lg="2" :md="3" :sm="8" :xl="2" :xs="6">
<el-card shadow="hover" style="cursor: pointer" @click.native="handleCopyIcon(index, $event)">
<vab-colorful-icon :icon-class="`https://gcore.jsdelivr.net/gh/zxwk1998/zx-colorful-icon@master/${item}.svg`" />
</el-card>
<div class="icon-text">
{{ item }}
</div>
</el-col>
<el-col :span="24">
<el-pagination
:background="background"
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:page-sizes="[72, 144, 216, 288]"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</el-col>
</el-row>
</div>
</template>
<script>
import { getIconList } from '@/api/colorfulIcon'
import clip from '@/utils/clipboard'
export default {
name: 'ColorfulIcon',
data() {
return {
copyText: '',
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
background: true,
height: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryIcon: [],
queryForm: {
pageNo: 1,
pageSize: 72,
title: '',
},
}
},
created() {
this.fetchData()
},
methods: {
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
const { data, totalCount } = await getIconList(this.queryForm)
this.queryIcon = data
this.allIcon = data
this.total = totalCount
},
handleCopyIcon(index, event) {
//const copyText = `<vab-colorful-icon icon-class="https://gcore.jsdelivr.net/gh/zxwk1998/zx-colorful-icon@master/${this.queryIcon[index]}.svg" />`;
const copyText = `<vab-colorful-icon icon-class="${this.queryIcon[index]}" />`
this.copyText = copyText
clip(copyText, event)
},
},
}
</script>
<style lang="scss" scoped>
.colorful-icon-container {
::v-deep {
.el-card__body {
position: relative;
display: flex;
flex-direction: column;
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
svg:not(:root) {
font-size: 16px;
font-weight: bold;
color: $base-color-gray;
text-align: center;
vertical-align: middle;
pointer-events: none;
cursor: pointer;
}
.svg-external-icon {
width: 20px;
height: 18px;
}
}
}
.icon-text {
height: 30px;
margin-top: -15px;
overflow: hidden;
font-size: 12px;
line-height: 30px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>

View File

@@ -0,0 +1,128 @@
<template>
<div class="icon-container">
<el-row :gutter="20">
<el-col :span="24">
<el-divider content-position="left">点击图标即可复制源码</el-divider>
</el-col>
<el-col :span="24">
<el-form :inline="true" label-width="80px" @submit.native.prevent>
<el-form-item label="图标名称">
<el-input v-model="queryForm.title" />
</el-form-item>
<el-form-item label-width="0">
<el-button native-type="submit" type="primary" @click="queryData">搜索</el-button>
</el-form-item>
</el-form>
</el-col>
<el-col v-for="(item, index) in queryIcon" :key="index" :lg="2" :md="3" :sm="8" :xl="2" :xs="6">
<el-card shadow="hover" style="cursor: pointer" @click.native="handleCopyIcon(index, $event)">
<vab-icon :icon="['fas', item]" />
</el-card>
<div class="icon-text">
{{ item }}
</div>
</el-col>
<el-col :span="24">
<el-pagination
:background="background"
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:page-sizes="[72, 144, 216, 288]"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</el-col>
</el-row>
</div>
</template>
<script>
import clip from '@/utils/clipboard'
import { getIconList } from '@/api/icon'
export default {
name: 'AwesomeIcon',
data() {
return {
copyText: '',
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
background: true,
height: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryIcon: [],
queryForm: {
pageNo: 1,
pageSize: 72,
title: '',
},
}
},
created() {
this.fetchData()
},
methods: {
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
const { data, totalCount } = await getIconList(this.queryForm)
this.queryIcon = data
this.allIcon = data
this.total = totalCount
},
handleCopyIcon(index, event) {
const copyText = `<vab-icon :icon="['fas', '${this.queryIcon[index]}']"></vab-icon>`
this.copyText = copyText
clip(copyText, event)
},
},
}
</script>
<style lang="scss" scoped>
.icon-container {
::v-deep {
.el-card__body {
position: relative;
display: flex;
flex-direction: column;
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
svg:not(:root).svg-inline--fa {
font-size: 18px;
color: $base-color-gray;
text-align: center;
vertical-align: middle;
pointer-events: none;
cursor: pointer;
}
}
}
.icon-text {
height: 30px;
margin-top: -15px;
overflow: hidden;
font-size: 12px;
line-height: 30px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>

View File

@@ -0,0 +1,63 @@
<template>
<div class="loading-container">
<el-divider content-position="left">传统loading</el-divider>
<el-button type="primary" @click="handleLoading()">默认效果</el-button>
<el-button type="primary" @click="handleLoading(1)">效果1</el-button>
<el-button type="primary" @click="handleLoading(2)">效果2</el-button>
<el-button type="primary" @click="handleLoading(3)">效果3</el-button>
<el-button type="primary" @click="handleLoading(4)">效果4</el-button>
<el-button type="primary" @click="handleLoading(5)">效果5</el-button>
<el-button type="primary" @click="handleLoading(6)">效果6</el-button>
<el-button type="primary" @click="handleLoading(7)">效果7</el-button>
<el-button type="primary" @click="handleLoading(8)">效果8</el-button>
<el-button type="primary" @click="handleLoading(9)">效果9</el-button>
<br />
<br />
<br />
<el-divider content-position="left">多彩loading</el-divider>
<el-button type="primary" @click="handleColorfullLoading(1)">效果1</el-button>
<el-button type="primary" @click="handleColorfullLoading(2)">效果2</el-button>
<el-button type="primary" @click="handleColorfullLoading(3)">效果3</el-button>
<el-button type="primary" @click="handleColorfullLoading(4)">效果4</el-button>
</div>
</template>
<script>
export default {
name: 'Loading',
data() {
return {}
},
methods: {
handleLoading(index) {
const Loading = this.$baseLoading(index)
setTimeout(() => {
Loading.close()
}, 3000)
},
handleColorfullLoading(index) {
const Loading = this.$baseColorfullLoading(index)
setTimeout(() => {
Loading.close()
}, 3000)
},
},
}
</script>
<style lang="scss" scoped>
.loading-container {
::v-deep {
.el-button {
margin-top: 10px;
margin-right: 10px;
margin-left: 0;
}
.el-button + .el-button {
margin-right: 10px;
margin-left: 0;
}
}
}
</style>

View File

@@ -0,0 +1,229 @@
<template>
<div class="lodash-container">
<el-row :gutter="20">
<el-col :span="24">
<el-card shadow="hover">
<el-link href="https://www.lodashjs.com/" target="_blank" type="primary">lodashjs官网</el-link>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>去除数组array中的最后一个元素</span>
</div>
<div class="lodash-content">
this.$baseLodash.initial([1, 2, 3])
<br />
// => [1, 2]
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>返回数组 array的第一个元素</span>
</div>
<div class="lodash-content">
this.$baseLodash.head([1, 2, 3])
<br />
// => 1
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>合并数组</span>
</div>
<div class="lodash-content">
this.$baseLodash.concat([1],[2])
<br />
// => [1,2]
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>左切片</span>
</div>
<div class="lodash-content">
this.$baseLodash.drop([1, 2, 3],2切除的数量)
<br />
// => [3]
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>右切片</span>
</div>
<div class="lodash-content">
this.$baseLodash.dropRight([1, 2, 3],2切除的数量)
<br />
// => [1]
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>修改拼接</span>
</div>
<div class="lodash-content">
this.$baseLodash.join(['a', 'b', 'c'], '~');
<br />
// => 'a~b~c'
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>获取数组最后一个元素</span>
</div>
<div class="lodash-content">
this.$baseLodash.last(['a', 'b', 'c']);
<br />
// => 'c'
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>数组去重</span>
</div>
<div class="lodash-content">
this.$baseLodash.uniq(['a', 'b', 'a']);
<br />
// => ['a','b']
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>获取数组的最大值</span>
</div>
<div class="lodash-content">
this.$baseLodash.max([4, 2, 8, 6])
<br />
// => 8
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>获取数组的最小值</span>
</div>
<div class="lodash-content">
this.$baseLodash.min([4, 2, 8, 6])
<br />
// => 2
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>四舍五入(保留任意位小数)</span>
</div>
<div class="lodash-content">
this.$baseLodash.round(4.006,2保持几位小数)
<br />
// => 4.01
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>数组内数据相加</span>
</div>
<div class="lodash-content">
this.$baseLodash.sum([4, 2, 8, 6])
<br />
// => 20
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>返回随机数</span>
</div>
<div class="lodash-content">
this.$baseLodash.random(0, 5)
<br />
// => 0到5任意数
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>返回数组内的随机数</span>
</div>
<div class="lodash-content">
this.$baseLodash.sample([1, 2, 3, 4])
<br />
// => 数组1到4任意数
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="12" :sm="24" :xl="6" :xs="24">
<el-card shadow="hover">
<div slot="header">
<span>事件防抖动</span>
</div>
<div class="lodash-content">
this.$baseLodash.debounce(@click的事件,延迟的毫秒数)
<br />
// => 点击后多久不可以点击
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'Lodash',
data() {
return {}
},
created() {},
mounted() {},
methods: {},
}
</script>
<style lang="scss" scoped>
.lodash-container {
text-align: left;
::v-deep {
.lodash-content {
min-height: 150px;
}
}
}
</style>

View File

@@ -0,0 +1,77 @@
<template>
<div class="more-container">
<el-row :gutter="20">
<el-col :lg="8" :md="24" :sm="24" :xl="8" :xs="24">
<el-card>
<div slot="header">
开源版本
<el-button style="float: right; padding: 3px 0" type="text">永久免费 个人/商业使用</el-button>
</div>
<div>
<ul>
<li>永久开源免费支持横纵布局切换</li>
<li>
保留浏览器控制台打印即可免费商用页面中的作者信息可全部去除无需保留可快速入手框架包含打包优化公共外框布局layout本地化及自定义教程如需自定义版权及作者信息299自愿原则
</li>
<li>
开源地址
<a href="https://github.com/zxwk1998/vue-admin-better" target="_blank">如果有幸帮到了你麻烦给个star</a>
</li>
<li>提供讨论群专属文档QQ群 9724353191139183756</li>
</ul>
</div>
</el-card>
</el-col>
<el-col :lg="8" :md="24" :sm="24" :xl="8" :xs="24">
<el-card>
<div slot="header">
VIP群
<el-button style="float: right; padding: 3px 0" type="text">1002021年1月起不再提供此服务</el-button>
</div>
<div>
<ul>
<li>为避免引起误解不再针对开源用户提供付费vip群</li>
</ul>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'More',
components: {},
data() {
return { nodeEnv: process.env.NODE_ENV }
},
created() {},
mounted() {},
methods: {},
}
</script>
<style lang="scss" scoped>
.more-container {
::v-deep {
.el-card__body {
> div {
min-height: 220px;
padding: 20px;
> ul {
> li {
line-height: 30px;
}
}
> img {
display: block;
margin: 40px auto;
border: 1px solid #dedede;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,7 @@
<template>
<div class="menu1-container">
<el-alert :closable="false" title="嵌套路由 1" type="success">
<router-view />
</el-alert>
</div>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<div class="menu1-1-container">
<el-alert :closable="false" title="嵌套路由 1-1" type="success">
<router-view />
</el-alert>
</div>
</template>
<style lang="scss" scoped>
[class*='-container'] {
padding: 15px;
background: $base-color-white;
}
</style>

View File

@@ -0,0 +1,11 @@
<template>
<div class="menu1-1-1-container">
<el-alert :closable="false" title="嵌套路由 1-1-1" type="success" />
</div>
</template>
<style lang="scss" scoped>
[class*='-container'] {
padding: 15px;
background: $base-color-white;
}
</style>

View File

@@ -0,0 +1,121 @@
<template>
<div class="permissions-container">
<el-divider content-position="left">intelligence模式,前端根据permissions拦截路由(演示环境,默认使用此方案)</el-divider>
<el-form ref="form" :inline="true" :model="form">
<el-form-item label="切换账号">
<el-radio-group v-model="form.account">
<el-radio label="admin">admin</el-radio>
<el-radio label="editor">editor</el-radio>
<el-radio label="test">test</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleChangePermission">切换权限</el-button>
</el-form-item>
<el-form-item label="当前账号拥有的权限">
{{ JSON.stringify(permissions) }}
</el-form-item>
</el-form>
<el-divider content-position="left">按钮级权限演示</el-divider>
<el-button v-permissions="['admin']" type="primary">我是拥有["admin"]权限的按钮</el-button>
<el-button v-permissions="['editor']" type="primary">我是拥有["editor"]权限的按钮</el-button>
<el-button v-permissions="['test']" type="primary">我是拥有["test"]权限的按钮</el-button>
<br />
<br />
<el-divider content-position="left">all模式,路由以及view文件引入全部交给后端(权限复杂,且随时变更,建议使用此方案)</el-divider>
<p>
settings.js配置authentication为all即可完全交由后端控制,mock中有后端接口示例,权限繁琐,有几十种权限的项目直接用这种,
由于演示环境是mock数据模拟,可能无法展现此功能的配置,只做如下展示,便于您的理解
</p>
<br />
<el-row :gutter="20">
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
<el-table
border
:data="tableData"
default-expand-all
row-key="path"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="name" prop="name" show-overflow-tooltip />
<el-table-column label="path" prop="path" show-overflow-tooltip />
<el-table-column label="component" prop="component" show-overflow-tooltip />
<el-table-column label="redirect" prop="redirect" show-overflow-tooltip />
<el-table-column label="标题" prop="meta.title" show-overflow-tooltip />
<el-table-column label="图标" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
<vab-icon v-if="row.meta.icon" :icon="['fas', row.meta.icon]" />
</span>
</template>
</el-table-column>
<el-table-column label="是否固定" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
{{ row.meta.affix }}
</span>
</template>
</el-table-column>
<el-table-column label="是否无缓存" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
{{ row.meta.noKeepAlive }}
</span>
</template>
</el-table-column>
<el-table-column label="badge" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.meta">
{{ row.meta.badge }}
</span>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { tokenTableName } from '@/config'
import { getRouterList } from '@/api/router'
export default {
name: 'Permissions',
data() {
return {
form: {
account: '',
},
tableData: [],
res: [],
}
},
computed: {
...mapGetters({
username: 'user/username',
permissions: 'user/permissions',
}),
},
created() {
this.fetchData()
},
mounted() {
this.form.account = this.username
},
methods: {
handleChangePermission() {
localStorage.setItem(tokenTableName, `${this.form.account}-accessToken`)
location.reload()
},
async fetchData() {
const res = await getRouterList()
this.tableData = res.data
this.res = res
},
},
}
</script>

View File

@@ -0,0 +1,70 @@
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="标题" prop="title">
<el-input v-model.trim="form.title" autocomplete="off" />
</el-form-item>
<el-form-item label="作者" prop="author">
<el-input v-model.trim="form.author" autocomplete="off" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="save"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { doEdit } from '@/api/table'
export default {
name: 'TableEdit',
data() {
return {
form: {
title: '',
author: '',
},
rules: {
title: [{ required: true, trigger: 'blur', message: '请输入标题' }],
author: [{ required: true, trigger: 'blur', message: '请输入作者' }],
},
title: '',
dialogFormVisible: false,
}
},
created() {},
methods: {
showEdit(row) {
if (!row) {
this.title = '添加'
} else {
this.title = '编辑'
this.form = Object.assign({}, row)
}
this.dialogFormVisible = true
},
close() {
this.$refs['form'].resetFields()
this.form = this.$options.data().form
this.dialogFormVisible = false
this.$emit('fetch-data')
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid) {
const { msg } = await doEdit(this.form)
this.$baseMessage(msg, 'success')
this.$refs['form'].resetFields()
this.dialogFormVisible = false
this.$emit('fetch-data')
this.form = this.$options.data().form
} else {
return false
}
})
},
},
}
</script>

View File

@@ -0,0 +1,220 @@
<template>
<div class="table-container">
<vab-query-form>
<vab-query-form-left-panel>
<el-button icon="el-icon-plus" type="primary" @click="handleAdd">添加</el-button>
<el-button icon="el-icon-delete" type="danger" @click="handleDelete">删除</el-button>
<el-button type="primary" @click="testMessage">baseMessage</el-button>
<el-button type="primary" @click="testALert">baseAlert</el-button>
<el-button type="primary" @click="testConfirm">baseConfirm</el-button>
<el-button type="primary" @click="testNotify">baseNotify</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel>
<el-form ref="form" :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model="queryForm.title" placeholder="标题" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" native-type="submit" type="primary" @click="handleQuery">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-right-panel>
</vab-query-form>
<el-table
ref="tableSort"
v-loading="listLoading"
:data="list"
:element-loading-text="elementLoadingText"
:height="height"
@selection-change="setSelectRows"
@sort-change="tableSortChange"
>
<el-table-column show-overflow-tooltip type="selection" width="55" />
<el-table-column label="序号" show-overflow-tooltip width="95">
<template #default="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column label="标题" prop="title" show-overflow-tooltip />
<el-table-column label="作者" prop="author" show-overflow-tooltip />
<el-table-column label="头像" show-overflow-tooltip>
<template #default="{ row }">
<el-image v-if="imgShow" :preview-src-list="imageList" :src="row.img" />
</template>
</el-table-column>
<el-table-column label="点击量" prop="pageViews" show-overflow-tooltip sortable />
<el-table-column label="状态" show-overflow-tooltip>
<template #default="{ row }">
<el-tooltip class="item" :content="row.status" effect="dark" placement="top-start">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="时间" prop="datetime" show-overflow-tooltip width="200" />
<el-table-column label="操作" show-overflow-tooltip width="180px">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:background="background"
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<table-edit ref="edit" />
</div>
</template>
<script>
import { doDelete, getList } from '@/api/table'
import TableEdit from './components/TableEdit'
export default {
name: 'ComprehensiveTable',
components: {
TableEdit,
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger',
}
return statusMap[status]
},
},
data() {
return {
imgShow: true,
list: [],
imageList: [],
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
background: true,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 20,
title: '',
},
timeOutID: null,
}
},
computed: {
height() {
return this.$baseTableHeight()
},
},
created() {
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
mounted() {},
methods: {
tableSortChange() {
const imageList = []
this.$refs.tableSort.tableData.forEach((item, index) => {
imageList.push(item.img)
})
this.imageList = imageList
},
setSelectRows(val) {
this.selectRows = val
},
handleAdd() {
this.$refs['edit'].showEdit()
},
handleEdit(row) {
this.$refs['edit'].showEdit(row)
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: row.id })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids: ids })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
handleQuery() {
this.queryForm.pageNo = 1
this.fetchData()
},
async fetchData() {
this.listLoading = true
const { data, totalCount } = await getList(this.queryForm)
this.list = data
const imageList = []
data.forEach((item, index) => {
imageList.push(item.img)
})
this.imageList = imageList
this.total = totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 500)
},
testMessage() {
this.$baseMessage('test1', 'success')
},
testALert() {
this.$baseAlert('11')
this.$baseAlert('11', '自定义标题', () => {
/* 可以写回调; */
})
this.$baseAlert('11', null, () => {
/* 可以写回调; */
})
},
testConfirm() {
this.$baseConfirm(
'你确定要执行该操作?',
null,
() => {
/* 可以写回调; */
},
() => {
/* 可以写回调; */
}
)
},
testNotify() {
this.$baseNotify('测试消息提示', 'test', 'success', 'bottom-right')
},
},
}
</script>

View File

@@ -0,0 +1,440 @@
<template>
<div class="tree-container">
<el-row :gutter="20">
<el-col :lg="6" :md="24" :sm="24" :xl="6" :xs="24">
<el-divider content-position="left">常规树</el-divider>
<el-input v-model="filterText" placeholder="输入关键字过滤" />
<el-tree
ref="demoTree"
class="vab-filter-tree"
:data="data2"
:default-checked-keys="defaultCheckedKeys"
:default-expanded-keys="defaultExpendedKeys"
:expand-on-click-node="false"
:filter-node-method="filterNode"
:highlight-current="true"
node-key="id"
:props="defaultProps"
show-checkbox
@check="checkNode"
@node-click="nodeClick"
@node-collapse="nodeCollapse"
@node-expand="nodeExpand"
>
<template #defalut="{ node, data }" class="vab-custom-tree-node">
<span class="vab-tree-item">
<i v-if="node.data.rank == 4" class="el-icon-s-custom"></i>
{{ node.label }}
</span>
<span class="vab-tree-options">
<a v-if="node.data.rank !== 4" class="vab-tree-btn" title="添加" @click="() => append(node, data, 0)">
<i class="el-icon-plus"></i>
</a>
<a class="vab-tree-btn" title="编辑" @click="() => edit(node, data, 1)">
<i class="el-icon-edit"></i>
</a>
<a v-if="node.data.rank !== 1" class="vab-tree-btn" title="刪除" @click="() => remove(node, data)">
<i class="el-icon-delete"></i>
</a>
</span>
</template>
</el-tree>
</el-col>
<el-col :lg="6" :md="24" :sm="24" :xl="6" :xs="24">
<el-divider content-position="left">懒加载树</el-divider>
<el-input v-model.lazy="keyW" class="input-with-select" placeholder="请输入内容" :value="keyW" @keyup.enter.native="showTreeList" />
<div v-show="isShow" class="blur-tree">
<el-tree
ref="treeFilter"
class="vab-filter-tree"
:data="filterDevLlist"
default-expand-all
:expand-on-click-node="false"
highlight-current
node-key="indexCode"
:props="defaultProps"
@node-click="nodeClick"
>
<template #defalut="{ node }" class="vab-custom-tree-node">
<span class="vab-tree-item">
<i v-if="node.data.rank == 4" class="el-icon-s-custom"></i>
{{ node.label }}
</span>
<span class="vab-tree-options">
<a v-if="node.data.rank !== 4" class="vab-tree-btn" title="添加">
<i class="el-icon-plus"></i>
</a>
<a class="vab-tree-btn" title="编辑">
<i class="el-icon-edit"></i>
</a>
<a v-if="node.data.rank !== 1" class="vab-tree-btn" title="刪除">
<i class="el-icon-delete"></i>
</a>
</span>
</template>
</el-tree>
</div>
<div v-show="!isShow" class="el-tree-wrap">
<el-tree
ref="tree"
v-loading="loading"
class="vab-filter-tree"
:expand-on-click-node="false"
highlight-current
lazy
:load="loadNode"
node-key="indexCode"
:props="defaultProps"
@node-click="nodeClick"
>
<template #defalut="{ node }" class="vab-custom-tree-node">
<span class="vab-tree-item">
<i v-if="node.data.rank == 4" class="el-icon-s-custom"></i>
{{ node.label }}
</span>
<span class="vab-tree-options">
<!-- <a v-if="node.data.rank !== 4" class="vab-tree-btn" title="添加""><i class="el-icon-plus"></i></a> -->
<a class="vab-tree-btn" title="编辑">
<i class="el-icon-edit"></i>
</a>
<a v-if="node.data.rank !== 1" class="vab-tree-btn" title="刪除">
<i class="el-icon-delete"></i>
</a>
</span>
</template>
</el-tree>
</div>
</el-col>
<el-col :lg="6" :md="24" :sm="24" :xl="6" :xs="24">
<el-divider content-position="left">单选树</el-divider>
<el-select
ref="singleTree"
v-model="singleSelectTreeVal"
class="vab-tree-select"
clearable
popper-class="select-tree-popper"
value-key="id"
@clear="selectTreeClearHandle('single')"
>
<el-option :value="singleSelectTreeKey">
<el-tree
id="singleSelectTree"
ref="singleSelectTree"
:current-node-key="singleSelectTreeKey"
:data="selectTreeData"
:default-expanded-keys="selectTreeDefaultSelectedKeys"
:highlight-current="true"
node-key="id"
:props="selectTreeDefaultProps"
@node-click="selectTreeNodeClick"
>
<template #defalut="{ node }" class="vab-custom-tree-node">
<span class="vab-tree-item">{{ node.label }}</span>
</template>
</el-tree>
</el-option>
</el-select>
</el-col>
<el-col :lg="6" :md="24" :sm="24" :xl="6" :xs="24">
<el-divider content-position="left">多选树</el-divider>
<el-select
v-model="multipleSelectTreeVal"
class="vab-tree-select"
clearable
collapse-tags
multiple
popper-class="select-tree-popper"
@change="changeMultipleSelectTreeHandle"
@clear="selectTreeClearHandle('multiple')"
@remove-tag="removeSelectTreeTag"
>
<el-option :value="multipleSelectTreeKey">
<el-tree
id="multipleSelectTree"
ref="multipleSelectTree"
:current-node-key="multipleSelectTreeKey"
:data="selectTreeData"
:default-checked-keys="selectTreeDefaultSelectedKeys"
:default-expanded-keys="selectTreeDefaultSelectedKeys"
:highlight-current="true"
node-key="id"
:props="selectTreeDefaultProps"
show-checkbox
@check="multipleSelectTreeCheckNode"
/>
</el-option>
</el-select>
</el-col>
</el-row>
<!--添加/编辑节点弹框-------------------start-->
<el-dialog
class="tree-operate-dialog"
:title="dialogTitle"
:visible.sync="treeDialogVisible"
width="400px"
@close="treeDialogVisible = false"
>
<el-form ref="treeForm" :model="treeForm">
<el-form-item label="节点名称" required>
<el-input v-model="treeForm.name" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="treeDialogVisible = false"> </el-button>
<el-button type="primary" @click="saveTree"> </el-button>
</div>
</el-dialog>
<!--添加/编辑节点弹框-------------------end-->
</div>
</template>
<script>
import { getTreeList } from '@/api/tree'
export default {
name: 'Tree',
data() {
return {
dialogTitle: '添加节点',
treeFlag: 0,
treeDialogVisible: false,
treeForm: {
id: '',
name: '',
},
checkNodeKeys: [],
filterText: '',
data2: [],
defaultProps: {
children: 'children',
label: 'name',
},
defaultExpendedKeys: [],
defaultCheckedKeys: [],
loading: true,
keyW: '',
filterDevLlist: [],
isShow: false,
updateTree: true,
/* 单选树-多选树---------开始 */
selectLevel: 4, // 树可选叶子level等级
singleSelectTreeVal: '', //单选树默认label值
singleSelectTreeKey: '', //单选树默认key值
selectTreeData: [], //单选树的值
selectTreeDefaultSelectedKeys: [], //单选树默认展开的key值数组
selectTreeDefaultProps: {
children: 'children',
label: 'name',
},
multipleSelectTreeVal: [], //多选树默认label值
multipleSelectTreeKey: '', //多选树默认key值
/* 单选树-多选树---------结束 */
}
},
watch: {
filterText(val) {
this.$refs.demoTree.filter(val)
},
},
mounted() {
this.$nextTick(() => {
this.getTreeListFuc(1)
this.setCheckedKeys()
// 初始化单选树
this.initSingleTree('single')
// 初始化多选树
this.initSingleTree('multiple')
})
},
methods: {
// 树level小于n级展开方法
openTree(treeData, n) {
const each = (data) => {
data.forEach((e) => {
if (e.rank <= n) {
this.defaultExpendedKeys.push(e.id)
}
if (e.children.length > 0) {
each(e.children)
}
})
}
each(treeData)
},
// 获取tree数据
async getTreeListFuc(flag) {
const { data } = await getTreeList()
this.data2 = data
if (flag) {
this.openTree(this.data2, 2)
}
},
// 节点过滤操作
filterNode(value, data) {
if (!value) return true
return data.name.indexOf(value) !== -1
},
// 添加节点操作
append(node, data, flag) {
this.treeFlag = flag
this.dialogTitle = '添加节点'
this.treeForm = {
id: '',
name: '',
}
this.treeDialogVisible = true
},
// 编辑节点操作
edit(node, data, flag) {
this.treeFlag = flag
this.dialogTitle = '编辑节点'
this.treeForm = {
id: data.id,
name: data.name,
}
this.treeDialogVisible = true
},
// 删除节点操作
remove(node, data) {
this.$baseConfirm('你确定要删除该节点?', null, async () => {
const { msg } = getTreeList()
this.$baseMessage(msg, 'success')
this.getTreeListFuc(0)
})
},
// 保存添加和编辑
saveTree() {
this.$refs.treeForm.validate(async (valid) => {
if (valid) {
const { msg } = await getTreeList()
this.$baseMessage(msg, 'success')
this.treeDialogVisible = false
this.getTreeListFuc(0)
}
})
},
// 设置节点选中
setCheckedKeys() {
this.$refs.demoTree.setCheckedKeys([1])
},
// 点击叶子节点
nodeClick(data, node, el) {},
// 节点选中操作
checkNode(data, node, el) {
this.checkNodeKeys = node.checkedKeys
},
// 节点展开操作
nodeExpand(data, node, el) {
this.defaultExpendedKeys.push(data.id)
},
// 节点关闭操作
nodeCollapse(data, node, el) {
this.defaultExpendedKeys.splice(
this.defaultExpendedKeys.findIndex((item) => item.id === data.id),
1
)
},
async loadNode(node, resolve) {
if (node.level === 0) {
const { data } = await getTreeList()
this.loading = false
return resolve(data)
} else {
const { data } = await getTreeList()
return resolve(res.data)
}
},
//懒加载树输入框筛选方法
async showTreeList(value) {
if (typeof value === 'string') {
this.keyW = value.trim()
}
if (this.keyW.length !== 0) {
// 请求后台返回查询结果
let treeOption = {}
treeOption = {
keyWord: this.keyW,
}
const { data } = await getTreeList()
this.filterDevLlist = data
this.isShow = true
} else {
this.isShow = false
}
},
/* 单选/多选树方法-------------------开始 */
// 初始化单选树的值
async initSingleTree(treeType) {
const { data } = await getTreeList()
this.selectTreeData = data
this.$nextTick(() => {
this.selectTreeDefaultSelectedKeys = this.singleSelectTreeKey.split(',') // 设置默认展开
if (treeType == 'single') {
//单选树
this.$refs.singleSelectTree.setCurrentKey(this.singleSelectTreeKey) // 设置默认选中
} else {
// 多选树
this.$refs.multipleSelectTree.setCheckedKeys(this.selectTreeDefaultSelectedKeys)
}
})
},
// 清除单选树选中
selectTreeClearHandle(type) {
this.selectTreeDefaultSelectedKeys = []
this.clearSelected()
if (type == 'single') {
this.singleSelectTreeVal = ''
this.singleSelectTreeKey = ''
this.$refs.singleSelectTree.setCurrentKey('') // 设置默认选中
} else {
this.multipleSelectTreeVal = []
this.multipleSelectTreeKey = ''
this.$refs.multipleSelectTree.setCheckedKeys([])
}
},
/* 清空选中样式 */
clearSelected() {
const allNode = document.querySelectorAll('#singleSelectTree .el-tree-node')
allNode.forEach((element) => element.classList.remove('is-current'))
},
// select多选时移除某项操作
removeSelectTreeTag(val) {
const stack = JSON.parse(JSON.stringify(this.selectTreeData))
while (stack.length) {
const curr = stack.shift()
if (curr.name == val) {
return this.$refs.multipleSelectTree.setChecked(curr.id, false)
}
if (curr.children && curr.children.length) {
stack.unshift(...curr.children)
}
}
},
changeMultipleSelectTreeHandle(val) {},
// 点击叶子节点
selectTreeNodeClick(data, node, el) {
if (data.rank >= this.selectLevel) {
this.singleSelectTreeVal = data.name
this.singleSelectTreeKey = data.id
this.$refs.singleTree.blur()
}
},
// 节点选中操作
multipleSelectTreeCheckNode(data, node, el) {
const checkedNodes = this.$refs.multipleSelectTree.getCheckedNodes()
const keyArr = []
const valueArr = []
checkedNodes.forEach((item) => {
if (item.rank >= this.selectLevel) {
keyArr.push(item.id)
valueArr.push(item.name)
}
})
this.multipleSelectTreeVal = valueArr
this.multipleSelectTreeKey = keyArr.join(',')
},
/* 单选/多选树方法-------------------结束 */
},
}
</script>

View File

@@ -0,0 +1,26 @@
<template>
<div class="upload-container">
<el-divider content-position="left">演示环境可能无法模拟上传</el-divider>
<vab-upload ref="vabUpload" :limit="1" name="file" :size="2" url="/upload" />
<el-button type="primary" @click="handleShow({ key: 'value' })">模拟上传</el-button>
</div>
</template>
<script>
import VabUpload from '@/components/VabUpload'
export default {
name: 'Upload',
components: {
VabUpload,
},
data() {
return {}
},
methods: {
handleShow(data) {
this.$refs['vabUpload'].handleShow(data)
},
},
}
</script>

View File

@@ -0,0 +1,85 @@
<template>
<div class="webSocket-container">
<el-row :gutter="20">
<el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
<el-alert :closable="false" type="success">webSocket连接{{ status }}</el-alert>
<br />
<el-form ref="form" label-width="100px" :model="form" :rules="rules">
<el-form-item label="地址">
<el-input v-model="url" disabled />
</el-form-item>
<el-form-item label="消息" prop="message">
<el-input v-model="form.message" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">发送消息</el-button>
</el-form-item>
<el-form-item label="返回信息汇总">
{{ data }}
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'WebSocket',
components: {},
data() {
return {
url: 'ws://123.207.136.134:9010/ajaxchattest',
webSocket: null,
data: [],
status: '',
form: { message: null },
rules: {
message: [{ required: true, message: '请输入消息', trigger: 'blur' }],
},
}
},
created() {
this.init()
},
destroyed() {
this.webSocket.close()
},
methods: {
submit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.send(this.form.message)
} else {
return false
}
})
},
init() {
const wsuri = this.url
this.webSocket = new WebSocket(wsuri)
this.webSocket.onmessage = this.onmessage
this.webSocket.onopen = this.onopen
this.webSocket.onerror = this.onerror
this.webSocket.onclose = this.onclose
},
onopen() {
this.status = '成功'
},
onerror() {
this.status = '失败'
this.initWebSocket()
},
onmessage({ data }) {
//截掉测试webSocket地址广告
this.data.push(data.substring(0, data.length - 66))
},
send(Data) {
this.webSocket.send(Data)
},
onclose(e) {
this.status = '断开'
},
},
}
</script>