初始化项目

This commit is contained in:
liaozetao
2023-09-18 11:34:27 +08:00
commit d40ecdd575
50 changed files with 15509 additions and 0 deletions

4
.browserslistrc Normal file
View File

@@ -0,0 +1,4 @@
> 1%
last 2 versions
not dead
not ie 11

0
.env Normal file
View File

2
.env.development Normal file
View File

@@ -0,0 +1,2 @@
VUE_APP_API_BASE_URL='http://beta.admin.pekolive.com'
VUE_APP_DEBUG_MODE=true

2
.env.production Normal file
View File

@@ -0,0 +1,2 @@
VUE_APP_API_BASE_URL=http://admin.pekolive.com
VUE_APP_DEBUG_MODE=false

19
.eslintrc.js Normal file
View File

@@ -0,0 +1,19 @@
module.exports = {
root: true,
env: {
node: true,
jquery: true
},
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-unused-vars': 'off'
}
}

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# piko-admin-h5
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
babel.config.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
jsconfig.json Normal file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

12821
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

45
package.json Normal file
View File

@@ -0,0 +1,45 @@
{
"name": "piko-admin-h5",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --mode development",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"admin-lte": "^2.3.2",
"axios": "^1.5.0",
"bootstrap": "^3.3.5",
"bootstrap-datepicker": "^1.10.0",
"bootstrap-multiselect": "^0.9.15",
"bootstrap-select": "^1.13.18",
"bootstrap-table": "^1.10.1",
"core-js": "^3.8.3",
"eonasdan-bootstrap-datetimepicker": "^4.17.49",
"font-awesome": "^4.6.3",
"ionicons": "^2.0.1",
"jquery.md5": "^1.0.0",
"knockout": "^3.5.1",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"node-sass": "^9.0.0",
"popper.js": "^1.16.1",
"sass": "^1.67.0",
"sass-loader": "^13.3.2",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

40
public/index.html Normal file
View File

@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/bootstrap-combobox/1.1.8/css/bootstrap-combobox.css">
<link rel="stylesheet"
href="https://cdn.bootcdn.net/ajax/libs/jQuery-Validation-Engine/2.6.2/validationEngine.jquery.css">
<link rel="stylesheet"
href="https://cdn.bootcdn.net/ajax/libs/bootstrap-multiselect/1.1.1/css/bootstrap-multiselect.css">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-combobox/1.1.8/js/bootstrap-combobox.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jQuery-Validation-Engine/2.6.2/jquery.validationEngine.js"></script>
<script
src="https://cdn.bootcdn.net/ajax/libs/jQuery-Validation-Engine/2.6.2/languages/jquery.validationEngine-zh_CN.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-multiselect/1.1.1/js/bootstrap-multiselect.min.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
</head>
<body class="hold-transition skin-blue sidebar-mini">
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

18
src/App.vue Normal file
View File

@@ -0,0 +1,18 @@
<template>
<div>
<router-view/>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {};
},
watch: {},
created() {},
methods: {},
computed: {}
};
</script>

8
src/api/common/menu.js Normal file
View File

@@ -0,0 +1,8 @@
import request from "@/utils/request";
export function getMenuAll() {
return request({
url: '/admin/menu/getall',
method: 'get'
});
}

15
src/api/common/user.js Normal file
View File

@@ -0,0 +1,15 @@
import request from "@/utils/request";
export function getUser(adminId) {
return request({
url: '/admin/user/getone?uid=' + adminId,
method: 'get'
});
}
export function logout() {
return request({
url: '/login/logout',
method: 'get'
});
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

BIN
src/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/images/man.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,20 @@
<template>
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>Version</b> 2.0.0
</div>
<strong>Copyright &copy; 2023 <a href="">触海网络</a>.</strong> All rights
reserved.
</footer>
</template>
<script>
export default {
name: 'FooterView',
components: {
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,102 @@
<template>
<!-- Main Header -->
<header class="main-header">
<!-- Logo -->
<a class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><b>P</b>K</span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"><b>piko</b>管理系统</span>
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<!-- Navbar Right Menu -->
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a class="dropdown-toggle" data-toggle="dropdown" style="height:50px;">
<img :src="avatar" class="user-image" :alt="username">
<span class="hidden-xs"><span :text="username">{{ username }}</span></span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img :src="avatar" class="img-circle" :alt="username" />
<p>
<span :text="username">{{ username }}</span>
<small>上次登录<span
:text="lastTime">{{ lastTime }}</span></small>
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-right">
<a @click="logout" class="btn btn-default btn-flat" id="signOut">退出</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img :src="avatar" class="img-circle" :alt="username">
</div>
<div class="pull-left info">
<p><span :text="username">{{ username }}</span></p>
<a><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
<ul class="sidebar-menu" id="mainMenu">
<li class="header">MAIN NAVIGATION</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
</template>
<script>
import store from '@/store';
export default {
name: 'HeaderView',
data() {
return {
username: "",
avatar: "",
lastTime: ""
};
},
created() {
this.getUser();
this.username = store.getters.username;
this.avatar = store.getters.avatar;
this.lastTime = store.getters.lastTime;
},
methods: {
async getUser() {
await store.dispatch('getUser', store.getters.adminId);
},
logout() {
store.dispatch('logout');
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,147 @@
<template>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar" style="height: 100%; overflow: hidden; overflow: scroll;">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel">
<div class="pull-left image">
<img :src="avatar" class="img-circle" :alt="username">
</div>
<div class="pull-left info">
<p>{{ username }}</p>
<!-- Status -->
<a href="#"><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
<!-- search form (Optional) -->
<form method="get" class="sidebar-form" onsubmit="return false;">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search...">
<span class="input-group-btn">
<button name="search" id="search-btn" class="btn btn-flat" @click="search">
<i class="fa fa-search"></i>
</button>
</span>
</div>
</form>
<!-- /.search form -->
<!-- Sidebar Menu -->
<ul class="sidebar-menu">
<li class="header">主导航</li>
<!-- Optionally, you can add icons to the links -->
<li v-for="(parent, parentIndex) in parentMenus" :key="parent" :data-index="parentIndex" class="treeview">
<a>
<i :class="[parent.icon ? parent.icon : 'fa fa-link']"></i>
<span>{{ parent.name }}</span>
<span class="label pull-right bg-yellow" id='`ic${parent.id}`'>
{{ getChildLength(parent.id) }}
</span>
</a>
<ul class="treeview-menu" id='`menu${parent.id}`'>
<li v-for="(child, childIndex) in getChilds(parent.id)" :key="child" :data-index="childIndex">
<a :data-url="child.path" @click="handleClick(child)">
<i :class="[child.icon && child.icon != '' ? child.icon : 'fa fa-circle-o text-yellow']"></i>
<span>{{ child.name }}</span>
</a>
</li>
</ul>
</li>
</ul>
<!-- /.sidebar-menu -->
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper" style="height: 100%;">
<!-- Content Header (Page header) -->
<section class="content-header" :style="[childMenu.name && childMenu.name != '' ? 'display:block;' : 'display:none;']">
<h1>
{{ childMenu.name }}
<small>{{ childMenu.description }}</small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> {{ childMenu.parentName }}</a></li>
<li class="active">{{ childMenu.name }}</li>
</ol>
</section>
<!-- Main content -->
<section class="content" style="height: 100%; overflow: hidden; overflow: scroll;">
<!-- Your Page Content Here -->
<component :is="componentName"></component>
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
</template>
<script>
import store from '@/store';
import { notifyNotice } from '@/utils/notify';
export default {
name: 'MaintainerView',
data() {
return {
componentName: "",
username: "",
avatar: "",
parentMenus: [],
childMenus: [],
childMenu: {
name: "",
parentName: "",
description: "",
}
};
},
created() {
notifyNotice();
this.getMenu();
this.username = store.getters.username;
this.avatar = store.getters.avatar;
this.parentMenus = store.getters.parentMenus;
this.childMenus = store.getters.childMenus;
},
methods: {
async getMenu() {
await store.dispatch('getMenu');
},
getChilds(parentId) {
return this.childMenus.filter(v => v.parentid == parentId);
},
getChildLength(parentId) {
return this.childMenus.filter(v => v.parentid == parentId).length;
},
handleClick(menu) {
this.childMenu.name = menu.name;
this.childMenu.parentName = menu.parentstr;
this.childMenu.description = menu.description;
store.dispatch('getViewComponent', menu.path).then(componentName => {
this.componentName = componentName;
});
},
search() {
let text = $("input[type='text']").val();
this.childMenus = store.getters.childMenus.filter(v => v.name.indexOf(text) >= 0);
if (!this.childMenus.length || this.childMenus.length == 0) {
this.parentMenus = store.getters.parentMenus.filter(v => v.name.indexOf(text) >= 0);
} else {
let parentIds = this.childMenus.map(v => v.parentid);
let parentMenus = store.getters.parentMenus.filter(v => v.name.indexOf(text) >= 0);
if (parentMenus && parentMenus.length > 0) {
parentMenus.forEach(v => {
parentIds.push(v.id);
});
}
this.parentMenus = store.getters.parentMenus.filter(v1 => parentIds.filter(v2 => v1.id == v2).length > 0);
}
}
},
}
</script>
<style scoped>
@import '../../css/main.css';
</style>

8
src/constants/global.js Normal file
View File

@@ -0,0 +1,8 @@
export default {
KEY: "piko",
EXCLUDES: [
"/login/login.action",
"/login/sendSmsCode.action",
],
NEED_LOGOUT: "needLogout",
};

216
src/css/login.css Normal file
View File

@@ -0,0 +1,216 @@
@charset "utf-8";
/* CSS Document */
.main_box {
position: absolute;
top: 50%;
left: 50%;
margin-top: -160px;
margin-left: -250px;
padding: 30px;
width: 500px;
height: 320px;
background: url(../assets/images/login_bg.png);
border-radius: 10px;
}
.main_box .setting {
position: absolute;
top: 5px;
right: 10px;
width: 10px;
height: 10px;
}
.main_box .setting a {
color: #FF6600;
}
.main_box .setting a:hover {
color: #555;
}
.login_logo {
height: 45px;
line-height: 45px;
position: relative;
text-align: center;
}
.login_logo img {
height: 45px;
}
.login_msg {
text-align: center;
font-size: 16px;
}
.login_form {
padding-top: 20px;
font-size: 16px;
}
.login_box .form-control {
display: inline-block;
zoom: 1;
width: auto;
font-size: 18px;
}
.login_box .form-control.x319 {
width: 319px;
background: rgba(255, 255, 255, 0.6);
outline: none;
border-color: transparent;
}
.login_box .form-control.x164 {
width: 164px;
}
.login_box .form-group {
margin-bottom: 20px;
text-align: center;
}
.login_box .form-group label.t {
/*width: 120px;*/
text-align: right;
cursor: pointer;
}
.login_box .form-group.space {
padding-top: 15px;
border-top: 1px #FFF dotted;
}
.login_box .form-group img {
margin-top: 1px;
height: 32px;
vertical-align: top;
}
.login_box .m {
cursor: pointer;
}
.bottom {
text-align: center;
font-size: 12px;
}
/* 动态背景设置 */
#bubble-wrapper {
margin: 0;
padding: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
background: linear-gradient(to bottom right, #00BFFF, #00FFFF);
}
#bubble-wrapper li {
position: absolute;
bottom: -160px;
width: 40px;
height: 40px;
background-color: rgba(255, 255, 255, 0.15);
list-style: none;
animation: square 15s infinite;
transition-timing-function: linear;
}
#bubble-wrapper li:nth-child(1) {
left: 10%;
}
#bubble-wrapper li:nth-child(2) {
left: 20%;
width: 90px;
height: 90px;
animation-delay: 2s;
animation-duration: 7s;
}
#bubble-wrapper li:nth-child(3) {
left: 25%;
animation-delay: 4s;
}
#bubble-wrapper li:nth-child(4) {
left: 40%;
width: 60px;
height: 60px;
animation-duration: 8s;
background-color: rgba(255, 255, 255, 0.3);
}
#bubble-wrapper li:nth-child(5) {
left: 70%;
}
#bubble-wrapper li:nth-child(6) {
left: 80%;
width: 120px;
height: 120px;
animation-delay: 3s;
background-color: rgba(255, 255, 255, 0.2);
}
#bubble-wrapper li:nth-child(7) {
left: 32%;
width: 120px;
height: 120px;
animation-delay: 2s;
}
#bubble-wrapper li:nth-child(8) {
left: 55%;
width: 20px;
height: 20px;
animation-delay: 4s;
animation-duration: 15s;
}
#bubble-wrapper li:nth-child(9) {
left: 25%;
width: 30px;
height: 30px;
animation-delay: 2s;
animation-duration: 12s;
background-color: rgba(255, 255, 255, 0.3);
}
#bubble-wrapper li:nth-child(10) {
left: 85%;
width: 160px;
height: 160px;
animation-delay: 5s;
}
@keyframes square {
0% {
opacity: 0.5;
transform: translateY(0px) rotate(45deg);
}
25% {
opacity: 0.75;
transform: translateY(-400px) rotate(90deg)
}
50% {
opacity: 1;
transform: translateY(-600px) rotate(135deg);
}
100% {
opacity: 0;
transform: translateY(-1000px) rotate(180deg);
}
}

9
src/css/main.css Normal file
View File

@@ -0,0 +1,9 @@
@charset "utf-8";
.tbar {
margin-top: 8px;
}
.tbar span {
cursor: pointer;
}

99
src/css/supersized.css Normal file
View File

@@ -0,0 +1,99 @@
/*
Supersized - Fullscreen Slideshow jQuery Plugin
Version : 3.2.7
Site : www.buildinternet.com/project/supersized
Author : Sam Dunn
Company : One Mighty Roar (www.onemightyroar.com)
License : MIT License / GPL License
*/
* {
margin: 0;
padding: 0;
}
body {
background: #111;
height: 100%;
}
img {
border: none;
}
#supersized {
display: block;
position: fixed;
left: 0;
top: 0;
overflow: hidden;
z-index: -999;
height: 100%;
width: 100%;
}
#supersized img {
width: auto;
height: auto;
position: relative;
display: none;
outline: none;
border: none;
}
#supersized.speed img {
-ms-interpolation-mode: nearest-neighbor;
image-rendering: -moz-crisp-edges;
}
/*Speed*/
#supersized.quality img {
-ms-interpolation-mode: bicubic;
image-rendering: optimizeQuality;
}
/*Quality*/
#supersized li {
display: block;
list-style: none;
z-index: -30;
position: fixed;
overflow: hidden;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #111;
}
#supersized a {
width: 100%;
height: 100%;
display: block;
}
#supersized li.prevslide {
z-index: -20;
}
#supersized li.activeslide {
z-index: -10;
}
#supersized li.image-loading img {
visibility: hidden;
}
#supersized li.prevslide img,
#supersized li.activeslide img {
display: inline;
}
#supersized img {
max-width: none !important
}

35
src/main.js Normal file
View File

@@ -0,0 +1,35 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'jquery.md5'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min.js'
import 'font-awesome/css/font-awesome.min.css'
import 'ionicons/css/ionicons.min.css'
import 'admin-lte/dist/css/AdminLTE.min.css'
import 'admin-lte/dist/css/skins/skin-blue.min.css'
import 'admin-lte/dist/js/app.min.js'
import 'bootstrap-table/dist/bootstrap-table.css'
import 'bootstrap-table/dist/bootstrap-table.js'
import 'bootstrap-table/dist/locale/bootstrap-table-zh-CN.js'
import 'bootstrap-table/dist/extensions/editable/bootstrap-table-editable.js'
import 'bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css'
import 'bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js'
import 'bootstrap-datepicker/dist/locales/bootstrap-datepicker.zh-CN.min.js'
import 'eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css'
import 'eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js'
import 'bootstrap-select/dist/css/bootstrap-select.css'
import 'bootstrap-select/dist/js/bootstrap-select.min.js'
import components from '@/utils/components.js'
createApp(App).use(store).use(router).use(components).mount('#app')

28
src/router/index.js Normal file
View File

@@ -0,0 +1,28 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/home/index.vue'
import LoginView from '../views/login/index.vue'
const routes = [
{
path: '/',
name: 'login',
component: LoginView
},
{
path: '/home',
name: 'home',
component: HomeView
},
{
path: '/discoveryAdmin',
name: 'discoveryAdmin',
component: () => import('../views/discovery/DiscoveryAdminView.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router

25
src/store/index.js Normal file
View File

@@ -0,0 +1,25 @@
import { createStore } from 'vuex'
import user from './modules/user'
import menu from './modules/menu'
export default createStore({
state: {
},
getters: {
adminUser: state => state.user,
adminId: state => state.user.adminId,
username: state => state.user.username,
avatar: state => state.user.avatar,
lastTime: state => state.user.lastTime,
parentMenus: state => state.menu.parentMenus,
childMenus: state => state.menu.childMenus,
},
mutations: {
},
actions: {
},
modules: {
user,
menu,
}
})

61
src/store/modules/menu.js Normal file
View File

@@ -0,0 +1,61 @@
import { getMenuAll } from '@/api/common/menu';
import { setStore, getStore } from '@/utils/store';
import { toCamelCase, upperFirst } from '@/utils/string';
export default {
state: {
parentMenus: getStore({ name: 'parent_menus' }) || [],
childMenus: getStore({ name: 'child_menus' }) || [],
},
mutations: {
setParentMenus(state, parentMenus) {
state.parentMenus = parentMenus;
setStore({
name: "parent_menus",
content: state.parentMenus,
type: "session"
});
},
setChildMenus(state, childMenus) {
state.childMenus = childMenus;
setStore({
name: "child_menus",
content: state.childMenus,
type: "session"
});
}
},
actions: {
async getMenu({ commit }) {
const res = await getMenuAll();
if (res) {
const sortBy = (a, b) => {
if (a.showorder > b.showorder) {
return -1;
} else if (a.showorder < b.showorder) {
return 1;
} else {
return a.name.localeCompare(b.name);
}
};
if (res.parents && res.parents.length > 0) {
const parents = res.parents.sort(sortBy);
commit('setParentMenus', parents);
}
if (res.childs && res.childs.length > 0) {
const childs = res.childs.sort(sortBy);
commit('setChildMenus', childs);
}
}
},
getViewComponent(context, path) {
let component = "";
if (path && path.endsWith('.html')) {
const pathArray = path.split('/');
const routeName = toCamelCase(pathArray[pathArray.length - 1].replace('.html', ''));
component = upperFirst(routeName) + 'View';
}
return component;
}
},
};

85
src/store/modules/user.js Normal file
View File

@@ -0,0 +1,85 @@
import router from '@/router';
import { setStore, getStore } from '@/utils/store';
import { getUser, logout } from '@/api/common/user';
import { dateFormat } from '@/utils/date';
import avatar from '@/assets/images/man.jpg';
export default {
state: {
adminId: getStore({ name: 'admin_id' }) || 0,
username: getStore({ name: 'username' }) || "",
avatar: getStore({ name: 'avatar' }) || "",
lastTime: getStore({ name: 'last_time' }) || "",
},
mutations: {
updateUser(state, user) {
if (user.adminId) {
console.log(user.adminId);
state.adminId = user.adminId;
setStore({
name: "admin_id",
content: state.adminId,
type: "session"
});
}
if (user.username) {
console.log(user.username);
state.username = user.username;
setStore({
name: "username",
content: state.username,
type: "session"
});
}
let userAvatar = user.avatar;
if (userAvatar) {
if (userAvatar != "") {
userAvatar = userAvatar.startsWith('https') || userAvatar.startsWith('http') || avatar;
}
state.avatar = userAvatar;
setStore({
name: "avatar",
content: state.avatar,
type: "session"
});
}
if (user.lastTime) {
state.lastTime = user.lastTime;
setStore({
name: "last_time",
content: state.lastTime,
type: "session"
});
}
}
},
actions: {
async getUser({ commit }, adminId) {
const res = await getUser(adminId);
var data = res.entity || {};
commit("updateUser", {
avatar: data.headimg,
lastTime: dateFormat(new Date(data.lastlogin))
});
return data;
},
logout({ commit }) {
return new Promise((resolve, reject) => {
logout()
.then(res => {
commit("updateUser", {
adminId: 0,
username: "",
avatar: "",
lastTime: ""
});
resolve(res);
router.push("/");
}).catch(() => {
reject();
});
});
}
},
};

195
src/utils/ajaxfileupload.js Normal file
View File

@@ -0,0 +1,195 @@
jQuery.extend({
handleError: function (s, xhr, status, e) {
// If a local callback was specified, fire it
if (s.error) {
s.error.call(s.context || s, xhr, status, e);
}
// Fire the global callback
if (s.global) {
(s.context ? jQuery(s.context) : jQuery.event).trigger("ajaxError", [xhr, s, e]);
}
},
createUploadIframe: function (id, uri) {
//create frame
var frameId = 'jUploadFrame' + id;
var io;
if (window.ActiveXObject) {
io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
if (typeof uri == 'boolean') {
io.src = 'javascript:false';
}
else if (typeof uri == 'string') {
io.src = uri;
}
}
else {
io = document.createElement('iframe');
io.id = frameId;
io.name = frameId;
}
io.style.position = 'absolute';
io.style.top = '-1000px';
io.style.left = '-1000px';
document.body.appendChild(io);
return io
},
createUploadForm: function (id, fileElementId) {
//create form
var formId = 'jUploadForm' + id;
var fileId = 'jUploadFile' + id;
var form = $('<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
var oldElement = $('#' + fileElementId);
var newElement = $(oldElement).clone();
$(oldElement).attr('id', fileId);
$(oldElement).before(newElement);
$(oldElement).appendTo(form);
//set attributes
$(form).css('position', 'absolute');
$(form).css('top', '-1200px');
$(form).css('left', '-1200px');
$(form).appendTo('body');
return form;
},
ajaxFileUpload: function (s) {
// TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
s = jQuery.extend({}, jQuery.ajaxSettings, s);
var id = s.fileElementId;
var form = jQuery.createUploadForm(id, s.fileElementId);
var frameId = 'jUploadFrame' + id;
var formId = 'jUploadForm' + id;
// Watch for a new set of requests
if (s.global && !jQuery.active++) {
jQuery.event.trigger("ajaxStart");
}
var requestDone = false;
// Create the request object
var xml = {}
if (s.global)
jQuery.event.trigger("ajaxSend", [xml, s]);
// Wait for a response to come back
var uploadCallback = function (isTimeout) {
var io = document.getElementById(frameId);
try {
if (io.contentWindow) {
xml.responseText = io.contentWindow.document.body ? io.contentWindow.document.body.innerHTML : null;
xml.responseXML = io.contentWindow.document.XMLDocument ? io.contentWindow.document.XMLDocument : io.contentWindow.document;
} else if (io.contentDocument) {
xml.responseText = io.contentDocument.document.body ? io.contentDocument.document.body.innerHTML : null;
xml.responseXML = io.contentDocument.document.XMLDocument ? io.contentDocument.document.XMLDocument : io.contentDocument.document;
}
} catch (e) {
jQuery.handleError(s, xml, null, e);
}
if (xml || isTimeout == "timeout") {
requestDone = true;
var status;
try {
status = isTimeout != "timeout" ? "success" : "error";
// Make sure that the request was successful or notmodified
if (status != "error") {
// process the data (runs the xml through httpData regardless of callback)
var data = jQuery.uploadHttpData(xml, s.dataType);
// If a local callback was specified, fire it and pass it the data
if (s.success)
s.success(data, status);
// Fire the global callback
if (s.global)
jQuery.event.trigger("ajaxSuccess", [xml, s]);
} else
jQuery.handleError(s, xml, status);
} catch (e) {
status = "error";
jQuery.handleError(s, xml, status, e);
}
// The request was completed
if (s.global)
jQuery.event.trigger("ajaxComplete", [xml, s]);
// Handle the global AJAX counter
if (s.global && ! --jQuery.active)
jQuery.event.trigger("ajaxStop");
// Process result
if (s.complete)
s.complete(xml, status);
jQuery(io).unbind()
setTimeout(function () {
try {
$(io).remove();
$(form).remove();
} catch (e) {
jQuery.handleError(s, xml, null, e);
}
}, 100)
xml = null
}
}
// Timeout checker
if (s.timeout > 0) {
setTimeout(function () {
// Check to see if the request is still happening
if (!requestDone) uploadCallback("timeout");
}, s.timeout);
}
try {
form = $('#' + formId);
$(form).attr('action', s.url);
$(form).attr('method', 'POST');
$(form).attr('target', frameId);
if (form.encoding) {
form.encoding = 'multipart/form-data';
}
else {
form.enctype = 'multipart/form-data';
}
$(form).submit();
} catch (e) {
jQuery.handleError(s, xml, null, e);
}
if (window.attachEvent) {
document.getElementById(frameId).attachEvent('onload', uploadCallback);
}
else {
document.getElementById(frameId).addEventListener('load', uploadCallback, false);
}
return { abort: function () { } };
},
uploadHttpData: function (r, type) {
var data = !type;
data = type == "xml" || data ? r.responseXML : r.responseText;
// If the type is "script", eval it in global context
if (type == "script")
jQuery.globalEval(data);
// Get the JavaScript object, if JSON is used.
if (type == "json")
data = r.responseText;
var start = data.indexOf(">");
if (start != -1) {
var end = data.indexOf("<", start + 1);
if (end != -1) {
data = data.substring(start + 1, end);
}
}
eval("data = " + data);
// evaluate scripts within html
if (type == "html")
jQuery("<div>").html(data).evalScripts();
return data;
}
})

42
src/utils/bootstrap-table-helper.js vendored Normal file
View File

@@ -0,0 +1,42 @@
export default {
idstr: null,
// 是否有且只选择了一项
isSelectOne: function (idstr) {
if ($(idstr).bootstrapTable('getSelections').length == 1) {
return true;
}
return false;
},
//是否选择了至少一项
hasSelectAny: function (idstr) {
if ($(idstr).bootstrapTable('getSelections').length > 0) {
return true;
}
return false;
},
// 获取选择的一项
getOneSelectItem: function (idstr) {
return $(idstr).bootstrapTable('getSelections')[0];
},
// 已经选择的记录
getAllSelectItems: function (idstr) {
return $(idstr).bootstrapTable('getSelections');
},
getRowByUniqueId: function (idstr, id) {
return $(idstr).bootstrapTable('getRowByUniqueId', id);
},
// 已选择的项数量
selectLength: function (idstr) {
return $(idstr).bootstrapTable('getSelections').length;
},
// 刷新
doRefresh: function (idstr) {
$(idstr).bootstrapTable('refresh');
},
unCheckAll: function (idstr) {
$(idstr).bootstrapTable('uncheckAll');
},
doRefreshAndToPage1: function (idstr) {
$(idstr).bootstrapTable('selectPage', 1);
},
}

14
src/utils/components.js Normal file
View File

@@ -0,0 +1,14 @@
export default {
install: function (Vue) {
const files = require.context('@/views', true, /\.vue$/)
let components = {};
files.keys().forEach(key => {
components[key.replace(/(\.\/|\.vue)/g, '')] = files(key).default;
});
Object.keys(components).forEach(item => {
if (components[item].name) {
Vue.component(components[item].name, components[item]);
}
});
},
}

49
src/utils/date.js Normal file
View File

@@ -0,0 +1,49 @@
export const calcDate = (date1, date2) => {
var date3 = date2 - date1;
var days = Math.floor(date3 / (24 * 3600 * 1000));
// 计算天数后剩余的毫秒数
var leave1 = date3 % (24 * 3600 * 1000);
var hours = Math.floor(leave1 / (3600 * 1000));
// 计算小时数后剩余的毫秒数
var leave2 = leave1 % (3600 * 1000);
var minutes = Math.floor(leave2 / (60 * 1000));
// 计算分钟数后剩余的毫秒数
var leave3 = leave2 % (60 * 1000);
var seconds = Math.round(date3 / 1000);
return {
leave1,
leave2,
leave3,
days: days,
hours: hours,
minutes: minutes,
seconds: seconds
};
}
/**
* 日期格式化
*/
export function dateFormat(date) {
let format = 'yyyy-MM-dd hh:mm:ss';
if (date != 'Invalid Date') {
var o = {
"M+": date.getMonth() + 1, //month
"d+": date.getDate(), //day
"h+": date.getHours(), //hour
"m+": date.getMinutes(), //minute
"s+": date.getSeconds(), //second
"q+": Math.floor((date.getMonth() + 3) / 3), //quarter
"S": date.getMilliseconds() //millisecond
}
if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
(date.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(format))
format = format.replace(RegExp.$1,
RegExp.$1.length == 1 ? o[k] :
("00" + o[k]).substr(("" + o[k]).length));
return format;
}
return '';
}

59
src/utils/notify.js Normal file
View File

@@ -0,0 +1,59 @@
export const notifyNotice = () => {
var notifyInterval;
console.log(window.Notification.permission);
if (window.Notification) {
$.ajax({
url: "/admin/dynamic/verify/notifySwitch.action",
success: function (json) {
if (json == 'true') {
notifyInterval = setInterval(notify, 50000);
console.log('notifyInterval : ' + notifyInterval);
}
}
});
} else {
alert('浏览器不支持Notification');
}
function notify() {
$.ajax({
url: "/admin/dynamic/verify/notify.action",
success: function (json) {
if (json != null && json != "" && json != undefined && json != 'undefined') {
var dynamicCount = json.dynamic;
var voiceCount = json.voice;
if ((dynamicCount != null && dynamicCount > 0) || (voiceCount != null && voiceCount > 0)) {
if (Notification.permission == "granted") {
popNotice(dynamicCount, voiceCount);
} else if (Notification.permission != "denied") {
Notification.requestPermission(function (permission) {
console.log(permission);
popNotice(dynamicCount, voiceCount);
});
}
}
}
}
});
}
function popNotice(dynamic, voice) {
if (Notification.permission == "granted") {
var bodyStr = '您有';
if (dynamic) {
bodyStr += '【' + dynamic + '】条待审核动态';
}
if (voice) {
bodyStr += '【' + voice + '】条待审核声音瓶子';
}
var notification = new Notification("【66后台】系统推送", {
body: bodyStr,
type: 'info',
offset: 100,
});
notification.onclick = function () {
notification.close();
};
}
}
}

49
src/utils/request.js Normal file
View File

@@ -0,0 +1,49 @@
import axios from "axios";
import store from "@/store";
import global from "@/constants/global";
const service = axios.create();
service.interceptors.request.use(config => {
return config;
}, error => {
Promise.reject(error);
});
service.interceptors.response.use(res => {
userLogout(res.headers[global.NEED_LOGOUT]);
return res.data;
}, error => {
return Promise.reject(error);
});
$.ajaxSetup({
headers: {
'Access-Control-Allow-Origin': '*',
},
beforeSend: function(xhr) {
console.log(xhr);
},
complete: function(xhr) {
userLogout(xhr.getResponseHeader(global.NEED_LOGOUT));
}
});
/**
* 强制退出
*/
function userLogout(needLogout) {
try {
if (global.NEED_LOGOUT == needLogout) {
var win = window;
while (win != win.top) {
win = win.top;
}
store.dispatch("logout");
}
} catch (error) {
console.error(error);
}
}
export default service;

118
src/utils/store.js Normal file
View File

@@ -0,0 +1,118 @@
import { validateNull } from '@/utils/validate';
import global from '@/constants/global';
const keyName = global.KEY + '_';
/**
* 存储localStorage
*/
export const setStore = (params = {}) => {
let {
name,
content,
type,
} = params;
name = keyName + name;
let obj = {
dataType: typeof (content),
content: content,
type: type,
datetime: new Date().getTime()
};
if (type) window.sessionStorage.setItem(name, JSON.stringify(obj));
else window.localStorage.setItem(name, JSON.stringify(obj));
}
/**
* 获取localStorage
*/
export const getStore = (params = {}) => {
let {
name,
debug
} = params;
name = keyName + name;
let obj = {},
content;
obj = window.sessionStorage.getItem(name);
if (validateNull(obj)) obj = window.localStorage.getItem(name);
if (validateNull(obj)) return;
try {
obj = JSON.parse(obj);
} catch {
return obj;
}
if (debug) {
return obj;
}
if (obj.dataType == 'string') {
content = obj.content;
} else if (obj.dataType == 'number') {
content = Number(obj.content);
} else if (obj.dataType == 'boolean') {
content = eval(obj.content);
} else if (obj.dataType == 'object') {
content = obj.content;
}
return content;
}
/**
* 删除localStorage
*/
export const removeStore = (params = {}) => {
let {
name,
type
} = params;
name = keyName + name;
if (type) {
window.sessionStorage.removeItem(name);
} else {
window.localStorage.removeItem(name);
}
}
/**
* 获取全部localStorage
*/
export const getAllStore = (params = {}) => {
let list = [];
let {
type
} = params;
if (type) {
for (let i = 0; i <= window.sessionStorage.length; i++) {
list.push({
name: window.sessionStorage.key(i),
content: getStore({
name: window.sessionStorage.key(i),
type: 'session'
})
})
}
} else {
for (let i = 0; i <= window.localStorage.length; i++) {
list.push({
name: window.localStorage.key(i),
content: getStore({
name: window.localStorage.key(i),
})
})
}
}
return list;
}
/**
* 清空全部localStorage
*/
export const clearStore = (params = {}) => {
let { type } = params;
if (type) {
window.sessionStorage.clear();
} else {
window.localStorage.clear();
}
}

22
src/utils/string.js Normal file
View File

@@ -0,0 +1,22 @@
/**
* 下划线转驼峰
*/
export function toCamelCase(str) {
return str.replace(/_[a-z]/g, function (s) {
return s.substring(1).toUpperCase();
});
}
/**
* 首字母大写
*/
export function upperFirst(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* 首字母小写
*/
export function lowerFirst(str) {
return str.charAt(0).toLowerCase() + str.slice(1);
}

20
src/utils/validate.js Normal file
View File

@@ -0,0 +1,20 @@
/**
* 判断是否为空
*/
export function validateNull(val) {
if (typeof val === 'boolean') {
return false;
}
if (typeof val === 'number') {
return false;
}
if (val instanceof Array) {
if (val.length == 0) return true;
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true;
} else {
if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true;
return false;
}
return false;
}

5
src/views/AboutView.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@@ -0,0 +1,766 @@
<template>
<section class="content">
<div class="box box-primary">
<div class="box-body">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1 id="itemTitle"></h1>
</section>
<!-- .content -->
<div id="table"></div>
<div id="toolbar">
名称:<input type="text" class="input-sm" name="name" id="searchName">
上架:<select name="displayType" id="searchDisplayType" class="input-sm">
<option value="0">--全部--</option>
<option value="1">上架</option>
<option value="2">下架</option>
</select>
app:
<select name="searchAppId" id="searchAppId" class="input-m">
<option value="">--全部--</option>
</select>
跳转类型:<select name="jumpType" id="searchJumpType" class="input-sm">
<option value="0">--全部--</option>
<option value="1">跳转链接</option>
<option value="2">跳转房间</option>
<option value="3">跳转游戏</option>
<option value="5">跳转App页面</option>
</select>
<button id="btnSearch" class="btn btn-default">
<i class="glyphicon glyphicon-search"></i>搜索
</button>
<button id="btnAdd" class="btn btn-default">
<i class="glyphicon glyphicon-add"></i>增加
</button>
</div>
</div>
</div>
</section>
<!--新增/编辑发现弹窗-->
<div class="modal fade" id="discoveryModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title" id="discoveryModalLabel">新增</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="discoveryForm">
<input type="hidden" id="editId">
<div class="form-group">
<label for="name" class="col-sm-3 control-label">名称:</label>
<div class="col-sm-9">
<input type="text" class="form-control validate[required]" name="name" id="name">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">跳转类型:</label>
<div class="col-sm-9">
<input type="radio" name="jumpType" id="link" value="1">跳转链接
<input type="radio" name="jumpType" id="room" value="2">跳转房间
<input type="radio" name="jumpType" id="game" value="3">跳转游戏
<input type="radio" name="jumpType" id="app" value="5">跳转App页面
</div>
</div>
<div class="form-group" style="display:none" id="linkGroup">
<label for="jumpLink" class="col-sm-3 control-label">跳转链接:</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="link" id="jumpLink">
</div>
</div>
<div class="form-group" style="display:none" id="roomGroup">
<label for="jumpRoom" class="col-sm-3 control-label">跳转房间:</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="roomId" id="jumpRoom">
</div>
</div>
<div class="form-group" style="display:none" id="gameGroup">
<label for="jumpGame" class="col-sm-3 control-label">跳转游戏:</label>
<input type="hidden" id="gameId">
<select name="jumpGame" id="jumpGame" class="col-sm-7 input-sm">
</select>
</div>
<div class="form-group" style="display:none" id="jumpAppGroup">
<label for="routerType" class="col-sm-3 control-label">跳转类型值:</label>
<div class="col-sm-3">
<input type="text" class="col-sm-3 form-control" name="routerType" id="routerType">
</div>
<label for="routerValue" class="col-sm-3 control-label">跳转参数值:</label>
<div class="col-sm-3">
<input type="text" class="col-sm-3 form-control" name="routerValue" id="routerValue">
</div>
</div>
<div class="form-group">
<label for="pic" class="col-sm-3 control-label">活动图片(大图)</label>
<div class="col-sm-9">
<img src="" id="picImage" style="width:250px;height:90px;" alt="">
<input type="file" id="picFile" name="uploadFile"
accept="image/gif,image/jpeg,image/jpg,image/png,image/svga">
<button class="btn btn-success" type="button"
onclick="uploadfile('picFile','picImage','pic','picInfo')">上传</button>
<input type="hidden" id="pic" name="icon" class="form-control" />
<span id="picInfo" style="color:red;"></span>
</div>
</div>
<div class="form-group">
<label for="pic" class="col-sm-3 control-label">活动图片(小图)</label>
<div class="col-sm-9">
<img src="" id="picMinImage" style="width:250px;height:90px;" alt="">
<input type="file" id="picMinFile" name="uploadFile"
accept="image/gif,image/jpeg,image/jpg,image/png,image/svga">
<button class="btn btn-success" type="button"
onclick="uploadfile('picMinFile','picMinImage','minPic','minPicInfo')">上传</button>
<input type="hidden" id="minPic" name="icon" class="form-control" />
<span id="minPicInfo" style="color:red;"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">活动类型:</label>
<div class="col-sm-9">
<label class="radio-inline"><input type="radio" name="activityType" id="noOne" value=""
checked></label>
<label class="radio-inline"><input type="radio" name="activityType" id="sign"
value="1">签到</label>
<label class="radio-inline"><input type="radio" name="activityType" id="task"
value="2">任务</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">是否上架:</label>
<div class="col-sm-9">
<input type="radio" name="displayType" id="up" value="1">
<input type="radio" name="displayType" id="down" value="2">
</div>
</div>
<div class="form-group">
<label for="appId" class="col-sm-3 control-label">app:</label>
<select name="appId" id="appId" class="col-sm-2 validate[required]">
<option value="">请选择...</option>
</select>
</div>
<div class="form-group">
<label for="seqNo" class="col-sm-3 control-label">排序:</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="seqNo" id="seqNo">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="add">提交</button>
</div>
</div>
</div>
</div>
</template>
<script>
import TableHelper from '@/utils/bootstrap-table-helper';
import '@/utils/ajaxfileupload';
var app = {};
var validApp = {};
export default {
name: "DiscoveryAdminView",
created() {
this.$nextTick(function () {
this.initData();
});
},
methods: {
initData() {
$(function () {
getAppNames();
function getAppNames() {
$.get('/admin/appName/listAll', {}, function (res) {
if (res.rows.length > 0) {
renderSelect(res.rows);
renderAppObj(res.rows);
}
})
}
function renderSelect(data) {
var $select = $('#searchAppId');
for (var i = 0; i < data.length; i++) {
var $option = $('<option value="' + data[i].app + '" />');
$option.html(data[i].appName);
$select.append($option);
}
}
function renderAppObj(rows) {
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
app[row.app] = row.appName;
// 有效的app
if (row.status == 1) {
validApp[row.app] = row.appName;
}
}
}
$('#table').bootstrapTable('destroy');
//初始化表格数据
initTable();
// 查询刷新
$('#btnSearch').on('click', function () {
$('#table').bootstrapTable('destroy');
initTable();
});
// 新增家族弹窗
$("#btnAdd").on("click", function () {
clearForm("discoveryForm");
// 处理appId
$('#appId').empty();
$('#appId').append('<option value="">请选择...</option>');
for (var key in validApp) {
$('#appId').append('<option value="' + key + '">' + validApp[key] + '</option>');
}
$("#link").prop("checked", true);
$("#up").prop("checked", true);
$("#linkGroup").show();
$("#jumpAppGroup").hide();
$("#discoveryModal").modal('show');
$("#jumpLink").removeClass("validate[required]");
$("#jumpRoom").removeClass("validate[required]");
$("#jumpGame").removeClass("validate[required]");
$("#app").removeClass("validate[required]");
$("#noOne").prop("checked", true);
});
//跳转链接
$("#link").on("click", function () {
$("#linkGroup").show();
$("#roomGroup").hide();
$("#gameGroup").hide();
$("#jumpAppGroup").hide();
});
//跳转房间
$("#room").on("click", function () {
$("#linkGroup").hide();
$("#roomGroup").show();
$("#gameGroup").hide();
$("#jumpAppGroup").hide();
});
//跳转游戏
$("#game").on("click", function () {
$("#linkGroup").hide();
$("#roomGroup").hide();
$("#jumpAppGroup").hide();
getGame();
});
//跳转App页面
$("#app").on("click", function () {
$("#linkGroup").hide();
$("#roomGroup").hide();
$("#gameGroup").hide();
$("#jumpAppGroup").show();
});
//新增发现保存
$("#add").on("click", function () {
var id = $("#editId").val();
var name = $("#name").val();
var appId = $("#appId").val();
var isLink = $("#link").is(":checked");
var isRoom = $("#room").is(":checked");
var isGame = $("#game").is(":checked");
var isApp = $("#app").is(":checked");
var jumpType = null;
var link = null;
var roomId = null;
var gameId = null;
var routerType = null;
var routerValue = null;
if (isLink) {
jumpType = "1";
link = $("#jumpLink").val();
$("#jumpLink").addClass("validate[required]");
} else if (isRoom) {
jumpType = "2";
roomId = $("#jumpRoom").val();
$("#jumpRoom").addClass("validate[required]");
} else if (isGame) {
jumpType = "3";
gameId = $("#jumpGame").val();
$("#jumpGame").addClass("validate[required]");
} else if (isApp) {
jumpType = "5";
routerType = $("#routerType").val();
routerValue = $("#routerValue").val();
$("#routerType").addClass("validate[required]");
}
if (jumpType == null) {
$("#tipMsg").text("请选择跳转类型");
$("#tipModal").modal('show');
return;
}
if (!$('#appId').val()) {
$("#tipMsg").text("请选择app");
$("#tipModal").modal('show');
return;
}
var displayType = null;
var up = $("#up").is(":checked");
var down = $("#down").is(":checked");
if (up) {
displayType = "1";
} else if (down) {
displayType = "2";
}
var icon = $("#pic").val();
var minImage = $("#minPic").val();
var seqNo = $("#seqNo").val();
var activityType = null;
var sign = $("#sign").is(":checked");
var task = $("#task").is(":checked");
if (sign) {
activityType = "1";
} else if (task) {
activityType = "2";
}
if ($("#discoveryForm").validationEngine('validate')) {
$.ajax({
type: 'post',
url: '/admin/discovery/saveOrUpdate.action',
data: {
id: id,
name: name,
icon: icon,
displayType: displayType,
jumpType: jumpType,
roomUid: roomId,
link: link,
gameId: gameId,
seqNo: seqNo,
routerType: routerType,
routerValue: routerValue,
activityType: activityType,
minImage: minImage,
appId: appId
},
dataType: 'json',
success: function (json) {
if (json.success == 'true') {
$("#tipMsg").text("保存成功");
$("#tipModal").modal('show');
TableHelper.doRefresh("#table");
$("#discoveryModal").modal('hide');
} else {
$("#tipMsg").text("保存失败." + json.msg);
$("#tipModal").modal('show');
TableHelper.doRefresh("#table");
}
}
});
}
});
});
$("#table").on("click", '.opt-edit', function () {
clearForm("discoveryForm");
// 处理appId
$('#appId').empty();
$('#appId').append('<option value="">请选择...</option>');
for (var key in app) {
$('#appId').append('<option value="' + key + '">' + app[key] + '</option>');
}
var id = $(this).attr("data-id");
$.ajax({
type: "get",
url: "/admin/discovery/getById.action",
data: { id: id },
dataType: "json",
success: function (json) {
if (json) {
$("#editId").val(id);
$("#name").val(json.name);
$("#appId").val(json.appId);
if (json.jumpType == "1") {
$("#linkGroup").show();
$("#roomGroup").hide();
$("#gameGroup").hide();
$("#jumpAppGroup").hide();
$("#link").prop("checked", true);
$("#room").prop("checked", false);
$("#game").prop("checked", false);
$("#app").prop("checked", false);
$("#jumpLink").val(json.link);
} else if (json.jumpType == "2") {
$("#linkGroup").hide();
$("#roomGroup").show();
$("#gameGroup").hide();
$("#jumpAppGroup").hide();
$("#link").prop("checked", false);
$("#room").prop("checked", true);
$("#game").prop("checked", false);
$("#app").prop("checked", false);
$("#jumpRoom").val(json.roomUid);
} else if (json.jumpType == "3") {
$("#linkGroup").hide();
$("#roomGroup").hide();
$("#jumpAppGroup").hide();
$("#gameGroup").show();
$("#link").prop("checked", false);
$("#room").prop("checked", false);
$("#game").prop("checked", true);
$("#app").prop("checked", false);
$("#gameId").val(json.gameId);
getGame();
} else if (json.jumpType == "5") {
$("#linkGroup").hide();
$("#roomGroup").hide();
$("#gameGroup").hide();
$("#jumpAppGroup").show();
$("#link").prop("checked", false);
$("#room").prop("checked", false);
$("#game").prop("checked", false);
$("#app").prop("checked", true);
$("#jumpLink").val(json.link);
$("#routerType").val(json.routerType);
$("#routerValue").val(json.routerValue);
}
if (json.displayType == '1') {
$("#up").prop("checked", true);
$("#down").prop("checked", false);
} else if (json.displayType == '2') {
$("#up").prop("checked", false);
$("#down").prop("checked", true);
}
if (json.activityType == '1') {
$("#sign").prop("checked", true);
$("#task").prop("checked", false);
} else if (json.activityType == '2') {
$("#sign").prop("checked", false);
$("#task").prop("checked", true);
}
$("#seqNo").val(json.seqNo);
// 设置礼物图片
$('#pic').val(json.icon);
$('#picImage').attr("src", json.icon);
if (json.icon != '') {
$("#picInfo").html('已上传');
} else {
$("#picInfo").html('未上传');
}
$('#minPic').val(json.minImage);
$('#picMinImage').attr("src", json.minImage);
if (json.icon != '') {
$("#minPicInfo").html('已上传');
} else {
$("#minPicInfo").html('未上传');
}
// 打开编辑弹窗
$("#discoveryModal").modal('show');
} else {
$("#tipMsg").text("获取菜单信息出错");
$("#tipModal").modal('show');
}
}
});
});
//删除发现(逻辑删除)
$("#table").on("click", ".opt-del", function () {
var id = $(this).attr("data-id");
if (confirm("你确认删除该记录吗?" +
"\r\n删除后再也不能找回请谨慎操作")) {
$.ajax({
type: 'post',
url: "/admin/discovery/saveOrUpdate.action",
data: {
'id': id,
'status': '0'
},
dataType: "json",
success: function (json) {
if (json.success == 'true') {
$("#tipMsg").text("删除成功");
$("#tipModal").modal('show');
TableHelper.doRefresh("#table");
} else {
$("#tipMsg").text("删除失败");
$("#tipModal").modal('show');
}
}
});
}
});
}
},
};
function uploadfile(file, image, path, info) {
$(this).attr('disabled', "true");
$.ajaxFileUpload({
fileElementId: file, //需要上传的文件域的ID即<input type="file">的ID。
url: '/admin/upload/img', //后台方法的路径
type: 'post', //当要提交自定义参数时这个参数要设置成post
dataType: 'json', //服务器返回的数据类型。可以为xml,script,json,html。如果不填写jQuery会自动判断。
secureuri: false, //是否启用安全提交默认为false。
async: true, //是否是异步
success: function (json) { //提交成功后自动执行的处理函数参数data就是服务器返回的数据。
if (json.path) {
$('#' + path).val(json.path);
$('#' + image).attr("src", json.path);
if (json.path != '') {
$("#" + info).html('已上传成功');
} else {
$("#" + info).html('未上传成功');
}
console.log(json.path);
} else {
$("#tipMsg").text(json.msg);
$("#tipModal").modal('show');
}
},
error: function (data, status, e) { //提交失败自动执行的处理函数。
$(this).removeAttr("disabled");
console.error(e);
}
});
}
function clearForm(formId) {
var $form = $('#' + formId);
$form.find('input').val('');
$form.find('select').val('');
$form.find('textarea').val('');
$form.find('input:radio').attr("checked", false);
$form.find('img').attr("src", "");
$form.find('span').html("");
}
function getGame() {
$.ajax({
type: 'get',
url: '/admin/discovery/queryGame.action',
data: {},
dataType: 'json',
success: function (json) {
if (json) {
var gameStr = "";
if (json != null && json.length > 0) {
for (var i = 0; i < json.length; i++) {
gameStr = gameStr + "<option value='" + json[i].id + "'>" + json[i].name + "</option>";
}
}
$("#jumpGame").html(gameStr);
var gameId = $("#gameId").val();
if (gameId != null && gameId != "") {
$("#jumpGame").find("option[value='" + gameId + "']").attr("selected", "selected");
}
$("#gameGroup").show();
} else {
$("#tipMsg").text("查询游戏信息失败." + json.msg);
$("#tipModal").modal('show');
TableHelper.doRefresh("#table");
}
}
});
}
function initTable() {
$('#table').bootstrapTable({
columns: [
{ field: 'tmp', title: 'id', align: 'center', checkbox: true, width: '5%', valign: 'middle' },
{ field: 'id', title: '编号', align: 'center', width: '5%', valign: 'middle' },
{ field: 'name', title: '名称', align: 'center', width: '5%', valign: 'middle' },
{
field: 'icon',
title: '图片',
align: 'center',
width: '5%',
//valign: 'middle',
formatter: function (val, row, index) {
if (val && val.indexOf("https") == 0) {
return "<img src='" + val + "' height='106'>";
} else {
return row.value;
}
}
},
{
field: 'displayType',
title: '上架',
align: 'center',
width: '5%',
valign: 'middle',
formatter: function (val, row, index) {
if (val == "1") {
return "是";
} else if (val == "2") {
return "否";
}
}
},
{
field: 'jumpType',
title: '跳转类型',
align: 'center',
width: '5%',
valign: 'middle',
formatter: function (val, row, index) {
if (val == 1) {
return "跳转链接";
} else if (val == "2") {
return "跳转房间";
} else if (val == "3") {
return "跳转游戏";
} else if (val == "5") {
return "跳转App页面";
} else {
return '-';
}
}
},
{
field: 'roomUid',
title: '跳转房间',
align: 'center',
width: '5%',
valign: 'middle',
formatter: function (val, row, index) {
if (row.jumpType == '2') {
return "ID:" + val;
} else {
return "否";
}
}
},
{
field: 'link',
title: '链接',
align: 'center',
width: '5%',
valign: 'middle',
formatter: function (val, row, index) {
if (row.jumpType == '1') {
return val;
} else {
return "否";
}
}
},
{
field: 'appId',
title: 'app',
align: 'center',
width: '10%',
valign: 'middle',
formatter: function (val, row, index) {
var name = app[val];
if (name != null) {
return name;
}
return val;
}
},
{
field: 'familyGame.name',
title: '游戏',
align: 'center',
width: '5%',
valign: 'middle',
formatter: function (val, row, index) {
if (row.jumpType == '3') {
return val;
} else {
return "否";
}
}
},
{ field: 'seqNo', title: '排序', align: 'center', width: '5%', valign: 'middle' },
{
field: 'pulishTime',
title: '发布时间',
align: 'center',
width: '5%',
valign: 'middle',
formatter: function (val, row, index) {
if (val) {
var date = new Date(val);
return date.format("yyyy-MM-dd hh:mm:ss");
} else {
return '-';
}
}
},
{
field: 'id',
title: '操作',
align: 'center',
width: '5%',
valign: 'middle',
formatter: function (val, row, index) {
return '<button id="btnEdit" name="btnEdit" class="btn btn-sm btn-success opt-edit" data-id=' + val + '>' +
'<i class="glyphicon glyphicon-edit"></i> 编辑</button>&nbsp;&nbsp;' +
'<button id="btnMove" name="btnDel" class="btn btn-sm btn-success opt-del" data-id=' + val + '>' +
'<i class="glyphicon glyphicon-move"></i> 删除</button>&nbsp;&nbsp;';
}
}
],
cache: false,
striped: true,
showRefresh: false,
pageSize: 20,
pagination: true,
pageList: [20, 50, 100, 200, 300, 500],
search: false,
sidePagination: "server", //表示服务端请求
//设置为undefined可以获取pageNumberpageSizesearchTextsortNamesortOrder
//设置为limit可以获取limit, offset, search, sort, order
queryParamsType: "undefined",
queryParams: function queryParams(params) { //设置查询参数
var param = {
page: params.pageNumber,
pageSize: params.pageSize,
name: $('#searchName').val(),
appId: $('#searchAppId').val(),
displayType: $('#searchDisplayType').val(),
jumpType: $('#searchJumpType').val(),
};
return param;
},
toolbar: '#toolbar',
url: '/admin/discovery/list.action',
onLoadSuccess: function () { //加载成功时执行
$(".bs-checkbox").css({ 'text-align': 'center', 'vertical-align': 'middle' });
console.log("load success");
},
onLoadError: function () { //加载失败时执行
console.log("load fail");
}
});
}
</script>
<style scoped></style>

25
src/views/home/index.vue Normal file
View File

@@ -0,0 +1,25 @@
<template>
<div class="wrapper">
<HeaderView/>
<MaintainerView/>
<FooterView/>
</div>
</template>
<script>
import HeaderView from "@/components/header/index.vue";
import MaintainerView from "@/components/maintainer/index.vue";
import FooterView from "@/components/footer/index.vue";
export default {
name: 'HomeView',
components: {
HeaderView,
MaintainerView,
FooterView
}
}
</script>
<style scoped>
</style>

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

@@ -0,0 +1,227 @@
<template>
<div class="page-container">
<div class="main_box" :style="[smsSwitch ? 'height: 350px' : '']">
<div class="login_box">
<div class="login_logo">
<span
style="font-size:24px;color:#222;font-weight:bold;">&nbsp;&nbsp;PIKO&nbsp;&nbsp;&nbsp;&nbsp;</span>
</div>
<div class="login_form">
<form id="loginForm" method="post">
<input type="hidden" id="smsSwitch" :value="smsSwitch" />
<div class="form-group">
<label for="account" class="t">&nbsp;&nbsp;&nbsp;&nbsp;</label>
<input id="account" name="account" type="text" class="form-control x319 in" autocomplete="off">
</div>
<div class="form-group">
<label for="password" class="t">&nbsp;&nbsp;&nbsp;&nbsp;</label>
<input id="password" name="password" type="password" class="password form-control x319 in">
</div>
<div v-if="smsSwitch">
<div class="form-group">
<label for="validateCode" class="t">验证码</label>
<input id="validateCode" name="authCode" type="text" class="form-control x319 in"
style="width: 210px;">
<button type="button" id="validateSend" class="btn btn-primary btn-lg">发送验证码</button>
</div>
</div>
<div class="form-group" style="margin-bottom: 0px;">
<label class="t"></label>
<span class="loginTips"></span>
</div>
<div class="form-group space">
<button type="button" id="loginBtn" class="btn btn-primary btn-lg">&nbsp;&nbsp;</button>
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="reset" value="重&nbsp;&nbsp;置" class="btn btn-default btn-lg">
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script>
import store from '@/store';
var sendFlag = true;
export default {
name: "LoginView",
data() {
return {
smsSwitch: true,
};
},
created() {
this.smsSwitch = !process.env.VUE_APP_DEBUG_MODE;
this.$nextTick(function () {
this.initData();
});
},
methods: {
initData() {
$(function () {
var $ul = $('<ul id="bubble-wrapper"/>');
for (var i = 0; i < 10; i++) {
$ul.append($('<li/>'));
}
$("body").append($ul);
var rememberMe = getCookie("wolfbe.remember");
if (rememberMe == 1) {
var account = getCookie("wolfbe.account");
if (account) $("#account").val(account);
}
$("#loginBtn").click(function () {
var account = $("#account").val();
var password = $("#password").val();
if (account.trim() == '') {
$(".loginTips").html("账号不能为空!");
return;
} else if (password.trim() == '') {
$(".loginTips").html("密码不能为空!");
return;
}
password = $.md5(password);
var param = { 'account': account, 'password': password };
var smsSwitch = $('#smsSwitch').val();
console.info('smsSwitch:' + smsSwitch);
if (smsSwitch && smsSwitch == 'true') {
var authCode = $("#validateCode").val();
if (authCode.trim() == '') {
$(".loginTips").html("验证码不能为空!");
return;
}
authCode = $.md5(authCode);
param = { 'account': account, 'password': password, 'authCode': authCode };
}
$.ajax({
type: "post",
url: "/login/login.action",
data: param,
dataType: "json",
success: function (data) {
console.log("data is: " + data.success);
//刷新验证码
$("#validateImg").click();
if (data.success == 'true') {
console.log('login success!');
var array = new String(data.msg).split('@');
store.commit('updateUser', {
adminId: array[0],
username: array[1]
});
//处理css样式污染问题关闭当前窗口重新渲染
window.close();
window.open('#/home', '_blank');
} else {
if (data.msg == "4003" || data.msg == "404") {
if (data.data == "0") {
data.msg = "输入超过次数限制,请联系管理员";
} else {
data.msg = "密码或验证码错误!你今天还有" + data.data + "次重试机会";
}
}
$(".loginTips").html(data.msg).css("padding", "3px 5px");
return false;
}
}
});
});
$("#loginForm :input").not("#rememberAccount").focus(function () {
$(".loginTips").html("").css("padding", "0");
}).keyup(function (e) {
var keyCode = e.which || e.keyCode;
if (keyCode == 13) {
$("#loginBtn").click();
}
});
$("#validateImg").click(function () {
$(this).attr("src", "/admin/kaptcha?t=" + new Date().getTime());
});
$("#validateSend").click(function () {
var account = $("#account").val();
if (account.trim() == '') {
$(".loginTips").html("账号不能为空!");
return;
}
if (sendFlag) {
$.ajax({
type: "post",
url: "/login/sendSmsCode.action",
data: { 'account': account },
dataType: "json",
success: function (data) {
if (data.code == 200) {
sendSmsSuccess();
} else {
$(".loginTips").html(data.message);
}
}
});
}
});
});
}
},
};
function sendSmsSuccess() {
if (sendFlag) {
sendFlag = false;
var time = 181;
var timer = setInterval(() => {
time--;
$("#validateSend").html(time + ' 秒');
if (time === 0) {
clearInterval(timer);
$("#validateSend").html('重新获取');
sendFlag = true;
}
}, 1000);
}
}
//获取cookie的信息
function getCookie(name) {
var reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
var arr = document.cookie.match(reg);
if (arr) {
return (arr[2]);
} else {
return null;
}
}
</script>
<style scoped>
@import '../../css/supersized.css';
@import '../../css/login.css';
* {
font-family: "Helvetica Neue", Helvetica, STheiti, 微软雅黑, 宋体, Arial, Tahoma, sans-serif, serif;
}
.btn-lg {
padding: 8px 25px;
}
#captcha_img {
margin-left: 5px;
}
.loginTips {
color: red;
font-size: 14px;
margin-left: 10px;
}
#validateSend {
width: 106px;
padding: 6px 17px;
font-size: 14px;
}
</style>

38
vue.config.js Normal file
View File

@@ -0,0 +1,38 @@
const { defineConfig } = require('@vue/cli-service')
var webpack = require('webpack')
module.exports = defineConfig({
transpileDependencies: true,
chainWebpack: config => {
config
.plugin('html')
.tap(args => {
args[0].title = 'PIKO管理后台'
return args
})
},
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'windows.jQuery': 'jquery',
Popper: ['popper.js', 'default']
}),
],
},
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {
'/': {
ws: false,
target: process.env.VUE_APP_API_BASE_URL,
changeOrigin: true,
pathRewrite: {
'^/': ''
}
}
},
},
})