init
3
VUE-WEB/lions-agent/.browserslistrc
Normal file
@@ -0,0 +1,3 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
17
VUE-WEB/lions-agent/.eslintrc.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||
}
|
||||
}
|
||||
21
VUE-WEB/lions-agent/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
29
VUE-WEB/lions-agent/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# lions
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run your tests
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
5
VUE-WEB/lions-agent/babel.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
11975
VUE-WEB/lions-agent/package-lock.json
generated
Normal file
30
VUE-WEB/lions-agent/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "lions-agent",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"core-js": "^3.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-router": "^3.1.6",
|
||||
"vuelidate": "^0.7.5",
|
||||
"vuetify": "^2.2.26",
|
||||
"vuex": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mdi/font": "^5.1.45",
|
||||
"@vue/cli-plugin-babel": "^4.3.0",
|
||||
"@vue/cli-plugin-eslint": "^4.3.0",
|
||||
"@vue/cli-service": "^4.3.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
}
|
||||
}
|
||||
BIN
VUE-WEB/lions-agent/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
17
VUE-WEB/lions-agent/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
23
VUE-WEB/lions-agent/src/App.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<router-view></router-view>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'app',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fdfdfd;
|
||||
}
|
||||
|
||||
.hasFooter {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
</style>
|
||||
56
VUE-WEB/lions-agent/src/api/axios.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import axios from 'axios'
|
||||
import store from "@/store"
|
||||
import router from "@/router";
|
||||
// axios 配置
|
||||
axios.defaults.timeout = 3000;
|
||||
axios.defaults.baseURL = 'http://lions-vote.funnyzhibo.com/api/';
|
||||
|
||||
const axiosConf = (config) => {
|
||||
// config.headers.Authorization = store.state.accessToken;
|
||||
config.headers.Authorization = localStorage.getItem('accessToken')
|
||||
return config;
|
||||
};
|
||||
|
||||
axios.interceptors.request.use(axiosConf, err => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(async (response) => {
|
||||
let data = {};
|
||||
let code = Number(response.data.status_code);
|
||||
if (code === 401 || code == 0) {
|
||||
if (response.data.status == 'EXCEPTION') {
|
||||
return Promise.reject(response.data)
|
||||
}
|
||||
if (response.headers.authorization) {
|
||||
await store.commit('login', response.headers.authorization);
|
||||
|
||||
response.config.headers.Authorization = response.headers.authorization;
|
||||
const result = await axios.request(axiosConf(response.config));
|
||||
if (result) {
|
||||
data = result;
|
||||
}
|
||||
} else if (router.currentRoute.name != 'AuthLogin') {
|
||||
setTimeout(() => {
|
||||
router.replace({name: 'AuthLogin', query: {redirect: router.currentRoute.fullPath}});
|
||||
}, 1000);
|
||||
}
|
||||
} else if (code === 200) {
|
||||
data = response.data.data;
|
||||
} else if (code === 403) {
|
||||
await router.replace({path: '/403', query: {redirect: router.currentRoute.fullPath}});
|
||||
} else if (code === 410) {
|
||||
setTimeout(() => {
|
||||
router.replace({name: 'AuthLogin', query: {redirect: router.currentRoute.fullPath}});
|
||||
}, 1200);
|
||||
} else if (code == 404) {
|
||||
return Promise.reject(response.data);
|
||||
} else {
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
return data;
|
||||
}, (error) => {
|
||||
return Promise.reject(error.response.data);
|
||||
});
|
||||
|
||||
export default axios;
|
||||
7
VUE-WEB/lions-agent/src/api/index.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import auth from './interfaces/auth'
|
||||
import index from './interfaces/index'
|
||||
|
||||
export default {
|
||||
index,
|
||||
auth
|
||||
}
|
||||
13
VUE-WEB/lions-agent/src/api/interfaces/auth.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import axios from '../axios'
|
||||
|
||||
const license = () => axios.get('auth/license')
|
||||
|
||||
const code = (data) => axios.post('auth/code/login2', data)
|
||||
|
||||
const loginByCode = (data) => axios.post('auth/loginByCode', data)
|
||||
|
||||
export default {
|
||||
license,
|
||||
code,
|
||||
loginByCode
|
||||
}
|
||||
7
VUE-WEB/lions-agent/src/api/interfaces/index.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import axios from '../axios'
|
||||
|
||||
const index = () => axios.get('agent')
|
||||
|
||||
export default {
|
||||
index
|
||||
}
|
||||
BIN
VUE-WEB/lions-agent/src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
27
VUE-WEB/lions-agent/src/main.js
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import api from './api'
|
||||
import toast from './plugins/toast'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$api = api
|
||||
Vue.prototype.toast = toast;
|
||||
Vue.prototype.jump = (name, params, query) => {
|
||||
router.push({
|
||||
name: name,
|
||||
params: params,
|
||||
query: query
|
||||
})
|
||||
}
|
||||
new Vue({
|
||||
vuetify,
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
174
VUE-WEB/lions-agent/src/pages/auth/login.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<v-container class="login">
|
||||
<div class="body-2 grey--text">
|
||||
中国狮子联会哈尔滨代表处
|
||||
</div>
|
||||
<div class="display-1 pt-3 pb-10">
|
||||
选举监督系统
|
||||
</div>
|
||||
|
||||
<v-form
|
||||
@submit="formSubmit"
|
||||
v-model="valid"
|
||||
>
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<v-text-field
|
||||
:counter="11"
|
||||
:rules="usernameRules"
|
||||
clearable
|
||||
label="手机号"
|
||||
prefix="+86"
|
||||
required
|
||||
type="tel"
|
||||
v-model="username"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col
|
||||
class="caption grey--text"
|
||||
cols="12"
|
||||
md="12"
|
||||
>
|
||||
点击"下一步"即表示已阅读并同意并自愿遵守
|
||||
<span
|
||||
@click="ishow(1)"
|
||||
class="blue--text text--darken-4"
|
||||
>《服务使用协议》</span>以及
|
||||
<span
|
||||
@click="ishow(2)"
|
||||
class="blue--text text--darken-4"
|
||||
>《隐私声明》</span>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn
|
||||
:disabled="!valid"
|
||||
block
|
||||
color="#1f3c84"
|
||||
dark
|
||||
large
|
||||
type="submit"
|
||||
>
|
||||
下一步
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
width="500"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title
|
||||
class="grey lighten-2"
|
||||
>
|
||||
{{dia.title}}
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pt-4" v-html="dia.content">
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-actions>
|
||||
<div class="flex-grow-1"></div>
|
||||
<v-btn
|
||||
@click="dialog = false"
|
||||
color="#1f3c84"
|
||||
text
|
||||
>
|
||||
已阅读
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import router from '@/router'
|
||||
|
||||
export default {
|
||||
name: "login",
|
||||
data() {
|
||||
return {
|
||||
valid: false,
|
||||
username: '',
|
||||
usernameRules: [
|
||||
v => !!v || '手机号必须填写',
|
||||
v => (v && v.length == 11) || '手机号码必须是11位',
|
||||
v => /^1[3-9][0-9]{9}$/.test(v) || '手机号码格式不正确'
|
||||
],
|
||||
dialog: false,
|
||||
dia: {
|
||||
title: '',
|
||||
content: ''
|
||||
},
|
||||
agreement: '',
|
||||
privacy: '',
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$api.auth.license().then(result => {
|
||||
this.agreement = result.agreement;
|
||||
this.privacy = result.privacy
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
formSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.$api.auth.code({
|
||||
username: this.username
|
||||
}).then(() => {
|
||||
router.push({
|
||||
name: 'AuthVerify',
|
||||
params: {
|
||||
username: this.username
|
||||
}
|
||||
})
|
||||
}).catch(err => {
|
||||
this.toast(err.message, 'warning')
|
||||
})
|
||||
},
|
||||
ishow(n) {
|
||||
this.dialog = !this.dialog;
|
||||
if (n == 1) {
|
||||
this.dia = {
|
||||
title: '用户协议',
|
||||
content: this.agreement
|
||||
}
|
||||
} else {
|
||||
this.dia = {
|
||||
title: '隐私声明',
|
||||
content: this.privacy
|
||||
}
|
||||
}
|
||||
},
|
||||
wechatLogin() {
|
||||
let uri = window.location.origin + '/auth/wechat';
|
||||
this.$api.auth.wechat_url({
|
||||
uri: uri,
|
||||
scope: 'snsapi_base'
|
||||
}).then(res => {
|
||||
window.location.href = res.url
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login {
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("../../assets/logo.png");
|
||||
background-size: 200px;
|
||||
background-position: center;
|
||||
|
||||
padding: 15%;
|
||||
}
|
||||
|
||||
.caption span {
|
||||
color: #1f3c84;
|
||||
}
|
||||
</style>
|
||||
186
VUE-WEB/lions-agent/src/pages/auth/verify.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<v-content>
|
||||
<v-container>
|
||||
<input
|
||||
:disabled="disable"
|
||||
@input="getVal"
|
||||
autofocus="autofocus"
|
||||
class="hidden-input"
|
||||
type="number"
|
||||
v-model="verify"
|
||||
>
|
||||
<div class="code-box">
|
||||
<div class="title">
|
||||
输入短信验证码
|
||||
</div>
|
||||
<div class="body-2">
|
||||
验证码已发送至{{username}},请在下方输入框内输入4位数字验证码
|
||||
</div>
|
||||
<div class="d-flex justify-space-around">
|
||||
<template v-for="(item, index) in ranges">
|
||||
<div :class="{active: codeIndex === item}" :key="index" class="item">
|
||||
<div v-if="codeIndex == item">
|
||||
<span class="dot-line"></span>
|
||||
</div>
|
||||
{{ codeArr[index] ? codeArr[index] : ''}}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</v-container>
|
||||
</v-content>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import router from "@/router";
|
||||
import {mapActions} from 'vuex'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
maxlength: 4,
|
||||
codeIndex: 1,
|
||||
codeArr: [],
|
||||
ranges: [1, 2, 3, 4],
|
||||
disable: false,
|
||||
username: '',
|
||||
verify: '',
|
||||
next_path: ''
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
if (from.name != 'AuthLogin' || to.params.username == undefined) {
|
||||
router.push({
|
||||
name: 'AuthLogin'
|
||||
})
|
||||
}
|
||||
next()
|
||||
},
|
||||
mounted() {
|
||||
this.username = this.$route.params.username;
|
||||
this.parent_id = this.$route.params.parent_id;
|
||||
this.next_path = this.$route.params.next_path
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['doLogin']),
|
||||
getVal(e) {
|
||||
let value = e.target.value;
|
||||
let arr = value.split('');
|
||||
this.codeIndex = arr.length + 1;
|
||||
this.codeArr = arr;
|
||||
if (this.codeIndex > Number(this.maxlength)) {
|
||||
this.disable = true;
|
||||
this.sendVerifyToServer()
|
||||
}
|
||||
},
|
||||
sendVerifyToServer() {
|
||||
this.$api.auth.loginByCode({
|
||||
username: this.username,
|
||||
parent_id: this.parent_id,
|
||||
verify: this.verify
|
||||
}).then(result => {
|
||||
this.doLogin(result.access_token);
|
||||
this.toast('登录成功');
|
||||
setTimeout(() => {
|
||||
if (this.next_path) {
|
||||
router.push({
|
||||
path: this.next_path
|
||||
})
|
||||
} else {
|
||||
router.push({
|
||||
name: 'Index'
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
}).catch(err => {
|
||||
this.toast(err.message, 'warning');
|
||||
this.disable = false;
|
||||
this.codeIndex = 1;
|
||||
this.codeArr = [];
|
||||
this.verify = ''
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@-webkit-keyframes twinkling {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes twinkling {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes twinkling {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.v-content {
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("../../assets/logo.png");
|
||||
background-size: 200px;
|
||||
background-position: bottom right;
|
||||
}
|
||||
|
||||
.code-box {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.d-flex {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.hidden-input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 300%;
|
||||
color: #fff;
|
||||
height: 100%;
|
||||
text-align: left;
|
||||
z-index: 9;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.item {
|
||||
border-bottom: 5px solid #BDBDBD;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.dot-line {
|
||||
display: block;
|
||||
height: 20px;
|
||||
width: 2px;
|
||||
background: #BDBDBD;
|
||||
margin-top: 15px;
|
||||
margin-left: 24px;
|
||||
-webkit-animation: twinkling 1s infinite;
|
||||
-moz-animation: twinkling 1s infinite;
|
||||
animation: twinkling 1s infinite;
|
||||
}
|
||||
|
||||
.item.active {
|
||||
border-bottom-color: #1f3c84;
|
||||
}
|
||||
</style>
|
||||
140
VUE-WEB/lions-agent/src/pages/index/index.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div
|
||||
class="pa-4 mbody"
|
||||
>
|
||||
<v-list
|
||||
three-line
|
||||
>
|
||||
<v-list-item>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>尊敬的 {{ user.name }} 狮兄/狮姐:</v-list-item-title>
|
||||
<v-list-item-subtitle>欢迎您参与中国狮子联会哈尔滨代表处第六次联席会议</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="now_vote">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>当前投票轮次</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ now_vote }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-simple-table>
|
||||
<template v-slot:default>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">姓名</th>
|
||||
<th class="text-left">手机号</th>
|
||||
<th class="text-left">签到</th>
|
||||
<th class="text-left">投票</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in desserts" :key="item.name">
|
||||
<td>{{ item.name }}</td>
|
||||
<td><a :href="`tel:` + item.mobile">{{ item.mobile }}</a></td>
|
||||
<td>
|
||||
<v-icon
|
||||
v-if="item.sign"
|
||||
color="green"
|
||||
>
|
||||
mdi-check
|
||||
</v-icon>
|
||||
<v-icon
|
||||
v-else
|
||||
color="red"
|
||||
>
|
||||
mdi-close
|
||||
</v-icon>
|
||||
</td>
|
||||
<td>
|
||||
<v-icon
|
||||
v-if="item.vote"
|
||||
color="green"
|
||||
>
|
||||
mdi-check
|
||||
</v-icon>
|
||||
<v-icon
|
||||
v-else
|
||||
color="red"
|
||||
>
|
||||
mdi-close
|
||||
</v-icon>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
</v-simple-table>
|
||||
<v-btn
|
||||
absolute
|
||||
dark
|
||||
fab
|
||||
right
|
||||
color="#1f3c84"
|
||||
style="bottom: 16px"
|
||||
@click="reload"
|
||||
>
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
<!-- <v-btn-->
|
||||
<!-- color="primary"-->
|
||||
<!-- block-->
|
||||
<!-- @click="logout"-->
|
||||
<!-- >-->
|
||||
<!-- 注销登录-->
|
||||
<!-- </v-btn>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions} from 'vuex'
|
||||
import router from "@/router";
|
||||
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
user: [],
|
||||
desserts: [],
|
||||
now_vote: '当前无投票'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$api.index.index().then(result => {
|
||||
this.user = result.user
|
||||
this.now_vote = result.now_vote
|
||||
this.desserts = result.desserts
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['doLogout']),
|
||||
reload() {
|
||||
this.$api.index.index().then(result => {
|
||||
this.user = result.user
|
||||
this.now_vote = result.now_vote
|
||||
this.desserts = result.desserts
|
||||
});
|
||||
},
|
||||
logout: function () {
|
||||
this.toast('退出成功');
|
||||
this.doLogout();
|
||||
setTimeout(() => {
|
||||
router.push({
|
||||
name: 'AuthLogin'
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.cns {
|
||||
background-image: url("../../assets/logo.png");
|
||||
background-size: 100%;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.v-card__text p {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
</style>
|
||||
13
VUE-WEB/lions-agent/src/pages/layouts/common.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "common.layout"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
34
VUE-WEB/lions-agent/src/plugins/toast/index.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
import Toast from "./toast"
|
||||
import Vue from 'vue'
|
||||
|
||||
let currentToast;
|
||||
|
||||
const index = (message, color) => {
|
||||
if (currentToast) {
|
||||
currentToast.close()
|
||||
}
|
||||
|
||||
currentToast = createToast({
|
||||
Vue,
|
||||
message,
|
||||
color: color || 'success',
|
||||
onClose: () => {
|
||||
currentToast = null
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function createToast({Vue, message, color, onClose}) {
|
||||
const Constructor = Vue.extend(Toast)
|
||||
|
||||
let toast = new Constructor()
|
||||
toast.message = message
|
||||
toast.color = color
|
||||
toast.timeout = 1000
|
||||
toast.$mount();
|
||||
toast.$on('close', onClose)
|
||||
document.body.appendChild(toast.$el)
|
||||
return toast
|
||||
}
|
||||
|
||||
export default index
|
||||
38
VUE-WEB/lions-agent/src/plugins/toast/toast.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<v-snackbar
|
||||
:color="color"
|
||||
:timeout="timeout"
|
||||
:top="true"
|
||||
v-model="show"
|
||||
>
|
||||
{{ message }}
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "toast.vue",
|
||||
data() {
|
||||
return {
|
||||
timeout: 1000,
|
||||
color: 'success',
|
||||
show: true,
|
||||
message: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log(this.color)
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$el.remove();
|
||||
this.$emit("close");
|
||||
this.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
8
VUE-WEB/lions-agent/src/plugins/utils.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
const isWechat = () => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
return (/micromessenger/.test(ua)) ? true : false;
|
||||
}
|
||||
|
||||
export default {
|
||||
isWechat
|
||||
}
|
||||
13
VUE-WEB/lions-agent/src/plugins/vuetify.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import Vuetify from 'vuetify'
|
||||
import 'vuetify/dist/vuetify.min.css'
|
||||
import '@mdi/font/css/materialdesignicons.css'
|
||||
|
||||
Vue.use(Vuetify)
|
||||
|
||||
export default new Vuetify({
|
||||
icons: {
|
||||
iconfont: 'mdi'
|
||||
},
|
||||
theme: {}
|
||||
})
|
||||
79
VUE-WEB/lions-agent/src/router/index.js
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
import VueRouter from "vue-router"
|
||||
import store from "@/store"
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Index',
|
||||
component: () => import(/* webpackChunkName: "index" */ '@/pages/index/index'),
|
||||
meta: {
|
||||
title: '首页',
|
||||
keepAlive: true,
|
||||
requireAuth: true
|
||||
}
|
||||
}, {
|
||||
path: '/auth',
|
||||
component: () => import(/* webpackChunkName: "auth" */ '@/pages/layouts/common'),
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
name: 'AuthLogin',
|
||||
component: () => import(/* webpackChunkName: "auth" */ '@/pages/auth/login'),
|
||||
meta: {
|
||||
title: '登录'
|
||||
}
|
||||
}, {
|
||||
path: 'verify',
|
||||
name: 'AuthVerify',
|
||||
component: () => import(/* webpackChunkName: "auth" */ '@/pages/auth/verify'),
|
||||
meta: {
|
||||
title: '验证手机号'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
let hasLogin = store.state.hasLogin;
|
||||
console.log('ROUTE' + to.name + '||' + hasLogin)
|
||||
if (to.name === 'AuthLogin') {
|
||||
// 如果已经登录,直接跳转到首页去
|
||||
if (hasLogin) {
|
||||
next({
|
||||
name: 'Index'
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
let tk = localStorage.getItem('accessToken')
|
||||
|
||||
if (tk) {
|
||||
store.commit('login', tk);
|
||||
next({
|
||||
name: 'Index'
|
||||
});
|
||||
return
|
||||
}
|
||||
} else if (to.meta.requireAuth === true) {
|
||||
if (hasLogin === false) {
|
||||
next({
|
||||
name: 'AuthLogin',
|
||||
params: {
|
||||
next: to.path
|
||||
}
|
||||
});
|
||||
return
|
||||
}
|
||||
}
|
||||
store.state.showFooter = to.meta.showFooter || false;
|
||||
|
||||
next()
|
||||
});
|
||||
|
||||
export default router
|
||||
49
VUE-WEB/lions-agent/src/store/index.js
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
hasLogin: false,
|
||||
accessToken: '',
|
||||
userInfo: null,
|
||||
},
|
||||
mutations: {
|
||||
login(state, token) {
|
||||
state.hasLogin = true;
|
||||
state.accessToken = token;
|
||||
localStorage.setItem('accessToken', token)
|
||||
},
|
||||
logout(state) {
|
||||
state.hasLogin = false;
|
||||
state.accessToken = "";
|
||||
state.userInfo = null;
|
||||
localStorage.removeItem('accessToken')
|
||||
localStorage.removeItem('userInfo')
|
||||
}
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
/**
|
||||
* 登录并获取用户基础信息
|
||||
* @param dispatch
|
||||
* @param commit
|
||||
* @param token
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async doLogin({commit}, token) {
|
||||
commit('login', token)
|
||||
},
|
||||
/**
|
||||
* 登出方法
|
||||
* @param commit
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async doLogout({commit}) {
|
||||
commit('logout')
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default store
|
||||
3
VUE-WEB/lions-web/.browserslistrc
Normal file
@@ -0,0 +1,3 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
17
VUE-WEB/lions-web/.eslintrc.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||
}
|
||||
}
|
||||
21
VUE-WEB/lions-web/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
29
VUE-WEB/lions-web/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# lions
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run your tests
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
5
VUE-WEB/lions-web/babel.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
11980
VUE-WEB/lions-web/package-lock.json
generated
Normal file
31
VUE-WEB/lions-web/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "lions",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"core-js": "^3.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-router": "^3.1.6",
|
||||
"vue-wechat-title": "^2.0.5",
|
||||
"vuelidate": "^0.7.5",
|
||||
"vuetify": "^2.2.26",
|
||||
"vuex": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mdi/font": "^5.1.45",
|
||||
"@vue/cli-plugin-babel": "^4.3.0",
|
||||
"@vue/cli-plugin-eslint": "^4.3.0",
|
||||
"@vue/cli-service": "^4.3.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
}
|
||||
}
|
||||
BIN
VUE-WEB/lions-web/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
17
VUE-WEB/lions-web/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>推举系统</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
欢迎使用中国狮子联会哈尔滨代表处推举系统
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
40
VUE-WEB/lions-web/src/App.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<router-view
|
||||
v-wechat-title="$route.meta.title"
|
||||
></router-view>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'app',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fdfdfd;
|
||||
}
|
||||
|
||||
.hasFooter {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
/*
|
||||
* 水平居中
|
||||
*/
|
||||
.pack-center {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-pack: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
56
VUE-WEB/lions-web/src/api/axios.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import axios from 'axios'
|
||||
import store from "@/store"
|
||||
import router from "@/router";
|
||||
// axios 配置
|
||||
axios.defaults.timeout = 3000;
|
||||
axios.defaults.baseURL = 'http://api.lions-vote.cnskl.com/api/';
|
||||
|
||||
const axiosConf = (config) => {
|
||||
// config.headers.Authorization = store.state.accessToken;
|
||||
config.headers.Authorization = localStorage.getItem('accessToken')
|
||||
return config;
|
||||
};
|
||||
|
||||
axios.interceptors.request.use(axiosConf, err => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(async (response) => {
|
||||
let data = {};
|
||||
let code = Number(response.data.status_code);
|
||||
if (code === 401 || code == 0) {
|
||||
if (response.data.status == 'EXCEPTION') {
|
||||
return Promise.reject(response.data)
|
||||
}
|
||||
if (response.headers.authorization) {
|
||||
await store.commit('login', response.headers.authorization);
|
||||
|
||||
response.config.headers.Authorization = response.headers.authorization;
|
||||
const result = await axios.request(axiosConf(response.config));
|
||||
if (result) {
|
||||
data = result;
|
||||
}
|
||||
} else if (router.currentRoute.name != 'AuthLogin') {
|
||||
setTimeout(() => {
|
||||
router.replace({name: 'AuthLogin', query: {redirect: router.currentRoute.fullPath}});
|
||||
}, 1000);
|
||||
}
|
||||
} else if (code === 200) {
|
||||
data = response.data.data;
|
||||
} else if (code === 403) {
|
||||
await router.replace({path: '/403', query: {redirect: router.currentRoute.fullPath}});
|
||||
} else if (code === 410) {
|
||||
setTimeout(() => {
|
||||
router.replace({name: 'AuthLogin', query: {redirect: router.currentRoute.fullPath}});
|
||||
}, 1200);
|
||||
} else if (code == 404) {
|
||||
return Promise.reject(response.data);
|
||||
} else {
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
return data;
|
||||
}, (error) => {
|
||||
return Promise.reject(error.response.data);
|
||||
});
|
||||
|
||||
export default axios;
|
||||
11
VUE-WEB/lions-web/src/api/index.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import article from './interfaces/article'
|
||||
import auth from './interfaces/auth'
|
||||
import index from './interfaces/index'
|
||||
import vote from './interfaces/vote'
|
||||
|
||||
export default {
|
||||
article,
|
||||
index,
|
||||
auth,
|
||||
vote
|
||||
}
|
||||
11
VUE-WEB/lions-web/src/api/interfaces/article.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import axios from '../axios'
|
||||
|
||||
const index = () => axios.get('articles')
|
||||
const show = (id) => axios.get('articles/' + id)
|
||||
const audit = (id) => axios.post('articles/' + id)
|
||||
|
||||
export default {
|
||||
index,
|
||||
show,
|
||||
audit
|
||||
}
|
||||
13
VUE-WEB/lions-web/src/api/interfaces/auth.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import axios from '../axios'
|
||||
|
||||
const license = () => axios.get('auth/license')
|
||||
|
||||
const code = (data) => axios.post('auth/code/login', data)
|
||||
|
||||
const loginByCode = (data) => axios.post('auth/loginByCode', data)
|
||||
|
||||
export default {
|
||||
license,
|
||||
code,
|
||||
loginByCode
|
||||
}
|
||||
10
VUE-WEB/lions-web/src/api/interfaces/index.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import axios from '../axios'
|
||||
|
||||
const index = () => axios.get('index')
|
||||
|
||||
const sign = () => axios.post('sign')
|
||||
|
||||
export default {
|
||||
index,
|
||||
sign
|
||||
}
|
||||
12
VUE-WEB/lions-web/src/api/interfaces/vote.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import axios from '../axios'
|
||||
|
||||
const index = () => axios.get('vote')
|
||||
const show = (id) => axios.get('vote/' + id)
|
||||
|
||||
const submit = (id, data) => axios.post('vote/' + id, {data: data})
|
||||
|
||||
export default {
|
||||
index,
|
||||
show,
|
||||
submit
|
||||
}
|
||||
BIN
VUE-WEB/lions-web/src/assets/back.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
VUE-WEB/lions-web/src/assets/back_1.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
VUE-WEB/lions-web/src/assets/back_2.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
VUE-WEB/lions-web/src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
28
VUE-WEB/lions-web/src/main.js
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import api from './api'
|
||||
import toast from './plugins/toast'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
Vue.use(require('vue-wechat-title'))
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$api = api
|
||||
Vue.prototype.toast = toast;
|
||||
Vue.prototype.jump = (name, params, query) => {
|
||||
router.push({
|
||||
name: name,
|
||||
params: params,
|
||||
query: query
|
||||
})
|
||||
}
|
||||
new Vue({
|
||||
vuetify,
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
177
VUE-WEB/lions-web/src/pages/auth/login.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<v-container class="login">
|
||||
<div class="login-title">登录</div>
|
||||
<div class="login-subhead grey--text">
|
||||
心连心服务队-推举系统
|
||||
</div>
|
||||
<v-form
|
||||
@submit="formSubmit"
|
||||
v-model="valid"
|
||||
>
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<v-text-field
|
||||
:counter="11"
|
||||
:rules="usernameRules"
|
||||
clearable
|
||||
label="手机号"
|
||||
prefix="+86"
|
||||
required
|
||||
type="tel"
|
||||
v-model="username"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<!-- <v-col-->
|
||||
<!-- class="caption grey--text"-->
|
||||
<!-- cols="12"-->
|
||||
<!-- md="12"-->
|
||||
<!-- >-->
|
||||
<!-- 点击"下一步"即表示已阅读并同意并自愿遵守-->
|
||||
<!-- <span-->
|
||||
<!-- @click="ishow(1)"-->
|
||||
<!-- class="blue--text text--darken-4"-->
|
||||
<!-- >《服务使用协议》</span>以及-->
|
||||
<!-- <span-->
|
||||
<!-- @click="ishow(2)"-->
|
||||
<!-- class="blue--text text--darken-4"-->
|
||||
<!-- >《隐私声明》</span>-->
|
||||
<!-- </v-col>-->
|
||||
<v-col>
|
||||
<v-btn
|
||||
:disabled="!valid"
|
||||
block
|
||||
style="color: #ffffff"
|
||||
color="#1f3c84"
|
||||
large
|
||||
type="submit"
|
||||
>
|
||||
下一步
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
width="500"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title
|
||||
class="grey lighten-2"
|
||||
>
|
||||
{{dia.title}}
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pt-4" v-html="dia.content">
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-actions>
|
||||
<div class="flex-grow-1"></div>
|
||||
<v-btn
|
||||
@click="dialog = false"
|
||||
color="#1f3c84"
|
||||
text
|
||||
>
|
||||
已阅读
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import router from '@/router'
|
||||
|
||||
export default {
|
||||
name: "login",
|
||||
data() {
|
||||
return {
|
||||
valid: false,
|
||||
username: '',
|
||||
usernameRules: [
|
||||
v => !!v || '手机号必须填写',
|
||||
v => (v && v.length == 11) || '手机号码必须是11位',
|
||||
v => /^1[3-9][0-9]{9}$/.test(v) || '手机号码格式不正确'
|
||||
],
|
||||
dialog: false,
|
||||
dia: {
|
||||
title: '',
|
||||
content: ''
|
||||
},
|
||||
agreement: '',
|
||||
privacy: '',
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$api.auth.license().then(result => {
|
||||
this.agreement = result.agreement;
|
||||
this.privacy = result.privacy
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
formSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.$api.auth.code({
|
||||
username: this.username
|
||||
}).then(() => {
|
||||
router.push({
|
||||
name: 'AuthVerify',
|
||||
params: {
|
||||
username: this.username
|
||||
}
|
||||
})
|
||||
}).catch(err => {
|
||||
this.toast(err.message, 'warning')
|
||||
})
|
||||
},
|
||||
ishow(n) {
|
||||
this.dialog = !this.dialog;
|
||||
if (n == 1) {
|
||||
this.dia = {
|
||||
title: '用户协议',
|
||||
content: this.agreement
|
||||
}
|
||||
} else {
|
||||
this.dia = {
|
||||
title: '隐私声明',
|
||||
content: this.privacy
|
||||
}
|
||||
}
|
||||
},
|
||||
wechatLogin() {
|
||||
let uri = window.location.origin + '/auth/wechat';
|
||||
this.$api.auth.wechat_url({
|
||||
uri: uri,
|
||||
scope: 'snsapi_base'
|
||||
}).then(res => {
|
||||
window.location.href = res.url
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login {
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("../../assets/logo.png");
|
||||
background-size: 200px;
|
||||
background-position: center;
|
||||
padding: 20vh 60px;
|
||||
}
|
||||
|
||||
.login-title{
|
||||
position: relative;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-subhead{
|
||||
padding-bottom: 5vh;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
190
VUE-WEB/lions-web/src/pages/auth/verify.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<v-container class="verify">
|
||||
<input
|
||||
:disabled="disable"
|
||||
@input="getVal"
|
||||
autofocus="autofocus"
|
||||
class="hidden-input"
|
||||
type="number"
|
||||
v-model="verify"
|
||||
>
|
||||
<div class="login-title">验证码</div>
|
||||
<div class="login-subhead grey--text">
|
||||
验证码已发送至{{username}},请在下方输入框内输入4位数字验证码
|
||||
</div>
|
||||
<div class="code-box">
|
||||
<div class="d-flex justify-space-around">
|
||||
<template v-for="(item, index) in ranges">
|
||||
<div :class="{active: codeIndex === item}" :key="index" class="item">
|
||||
<div v-if="codeIndex == item">
|
||||
<span class="dot-line"></span>
|
||||
</div>
|
||||
{{ codeArr[index] ? codeArr[index] : ''}}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import router from "@/router";
|
||||
import {mapActions} from 'vuex'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
maxlength: 4,
|
||||
codeIndex: 1,
|
||||
codeArr: [],
|
||||
ranges: [1, 2, 3, 4],
|
||||
disable: false,
|
||||
username: '',
|
||||
verify: '',
|
||||
next_path: ''
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
if (from.name != 'AuthLogin' || to.params.username == undefined) {
|
||||
router.push({
|
||||
name: 'AuthLogin'
|
||||
})
|
||||
}
|
||||
next()
|
||||
},
|
||||
mounted() {
|
||||
this.username = this.$route.params.username;
|
||||
this.parent_id = this.$route.params.parent_id;
|
||||
this.next_path = this.$route.params.next_path
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['doLogin']),
|
||||
getVal(e) {
|
||||
let value = e.target.value;
|
||||
let arr = value.split('');
|
||||
this.codeIndex = arr.length + 1;
|
||||
this.codeArr = arr;
|
||||
if (this.codeIndex > Number(this.maxlength)) {
|
||||
this.disable = true;
|
||||
this.sendVerifyToServer()
|
||||
}
|
||||
},
|
||||
sendVerifyToServer() {
|
||||
this.$api.auth.loginByCode({
|
||||
username: this.username,
|
||||
parent_id: this.parent_id,
|
||||
verify: this.verify
|
||||
}).then(result => {
|
||||
this.doLogin(result.access_token);
|
||||
this.toast('登录成功');
|
||||
setTimeout(() => {
|
||||
if (this.next_path) {
|
||||
router.push({
|
||||
path: this.next_path
|
||||
})
|
||||
} else {
|
||||
router.push({
|
||||
name: 'Index'
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
}).catch(err => {
|
||||
this.toast(err.message, 'warning');
|
||||
this.disable = false;
|
||||
this.codeIndex = 1;
|
||||
this.codeArr = [];
|
||||
this.verify = ''
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@-webkit-keyframes twinkling {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes twinkling {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes twinkling {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.verify{
|
||||
position: relative;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("../../assets/logo.png");
|
||||
background-size: 200px;
|
||||
background-position: bottom right;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
padding: 20vh 60px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-title{
|
||||
position: relative;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-subhead{
|
||||
padding-bottom: 5vh;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.hidden-input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
color: #fff;
|
||||
text-align: left;
|
||||
z-index: 9;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.item {
|
||||
border-bottom: 5px solid #BDBDBD;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.dot-line {
|
||||
display: block;
|
||||
height: 20px;
|
||||
width: 2px;
|
||||
background: #BDBDBD;
|
||||
margin-top: 15px;
|
||||
margin-left: 24px;
|
||||
-webkit-animation: twinkling 1s infinite;
|
||||
-moz-animation: twinkling 1s infinite;
|
||||
animation: twinkling 1s infinite;
|
||||
}
|
||||
|
||||
.item.active {
|
||||
border-bottom-color: #1f3c84;
|
||||
}
|
||||
</style>
|
||||
65
VUE-WEB/lions-web/src/pages/files/index.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div
|
||||
class="pt-4 pl-4 pr-4"
|
||||
>
|
||||
<v-card
|
||||
class="mb-4"
|
||||
tile
|
||||
>
|
||||
<v-card-title>
|
||||
文件审阅
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-list three-line subheader>
|
||||
<v-list-item
|
||||
v-for="(item, i) in files"
|
||||
:key="i"
|
||||
@click="jump('FileShow', {id: item.id})"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ item.desc }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action v-if="item.is">
|
||||
<v-icon
|
||||
color="red"
|
||||
>
|
||||
mdi-check
|
||||
</v-icon>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
<v-btn
|
||||
absolute
|
||||
dark
|
||||
fab
|
||||
right
|
||||
color="#1f3c84"
|
||||
style="bottom: 16px"
|
||||
@click="jump('Index')"
|
||||
>
|
||||
<v-icon>mdi-home</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$api.article.index().then(result => {
|
||||
this.files = result.files
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
51
VUE-WEB/lions-web/src/pages/files/show.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div
|
||||
class="pa-4"
|
||||
v-if="loaded"
|
||||
>
|
||||
<h2>{{ detail.title }}</h2>
|
||||
<v-subheader class="text-right">{{ detail.desc }}</v-subheader>
|
||||
<div v-html="detail.content"></div>
|
||||
<v-btn
|
||||
color="primary"
|
||||
block
|
||||
@click="auditPost"
|
||||
:disabled="audited"
|
||||
>
|
||||
审阅完毕
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
id: 0,
|
||||
loaded: false,
|
||||
detail: [],
|
||||
audited: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.id = this.$route.params.id;
|
||||
this.$api.article.show(this.id).then(result => {
|
||||
this.detail = result.article
|
||||
this.audited = result.audited
|
||||
this.loaded = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
auditPost: function () {
|
||||
this.$api.article.audit(this.id).then(() => {
|
||||
this.$router.back()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
189
VUE-WEB/lions-web/src/pages/index/index.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
|
||||
<div class="mian">
|
||||
<div class="footer">
|
||||
<div class="footer-title">尊敬的 {{ user.name }} 狮友</div>
|
||||
<div class="footer-subtitle">欢迎您参加中国狮子联会哈尔滨心连心服务队2020-2021年度队长、副队长、秘书和队长团队成员推举会议</div>
|
||||
<div v-if="dested">
|
||||
<v-btn
|
||||
block
|
||||
>
|
||||
投票已结束,您的投票记录已销毁
|
||||
</v-btn>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="vote">
|
||||
<v-btn
|
||||
block
|
||||
x-large
|
||||
class="footer-btn"
|
||||
@click="jump('VoteIndex')"
|
||||
>
|
||||
开始投票
|
||||
</v-btn>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-btn
|
||||
block
|
||||
x-large
|
||||
class="footer-btn"
|
||||
@click="reload"
|
||||
>
|
||||
等待开始
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
width="500"
|
||||
persistent
|
||||
>
|
||||
<v-card
|
||||
class="cns"
|
||||
tile
|
||||
>
|
||||
<v-card-title>
|
||||
<div class="flex-grow-1"></div>
|
||||
签署承诺书及签到环节
|
||||
<div class="flex-grow-1"></div>
|
||||
</v-card-title>
|
||||
<v-card-text v-html="html">
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-actions>
|
||||
<div class="flex-grow-1"></div>
|
||||
<v-btn
|
||||
@click="signPost"
|
||||
color="primary"
|
||||
text
|
||||
>
|
||||
我同意并签到
|
||||
</v-btn>
|
||||
<div class="flex-grow-1"></div>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<!-- <v-btn-->
|
||||
<!-- color="primary"-->
|
||||
<!-- block-->
|
||||
<!-- @click="logout"-->
|
||||
<!-- >-->
|
||||
<!-- 注销登录,正式版本会取消-->
|
||||
<!-- </v-btn>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions} from 'vuex'
|
||||
import router from "@/router";
|
||||
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
user: [],
|
||||
dialog: false,
|
||||
vote: false,
|
||||
dested: false,
|
||||
html: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$api.index.index().then(result => {
|
||||
this.user = result.user
|
||||
this.html = result.license
|
||||
this.dialog = !result.sign
|
||||
this.vote = result.vote
|
||||
this.dested = result.dested
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['doLogout']),
|
||||
reload() {
|
||||
this.$api.index.index().then(result => {
|
||||
this.user = result.user
|
||||
this.html = result.license
|
||||
this.dialog = !result.sign
|
||||
this.vote = result.vote
|
||||
this.dested = result.dested
|
||||
});
|
||||
},
|
||||
signPost: function () {
|
||||
this.$api.index.sign().then(() => {
|
||||
this.dialog = false
|
||||
})
|
||||
},
|
||||
logout: function () {
|
||||
this.toast('退出成功');
|
||||
this.doLogout();
|
||||
setTimeout(() => {
|
||||
router.push({
|
||||
name: 'AuthLogin'
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mian {
|
||||
background-color: #1f3c84;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background-image: url("../../assets/back_2.png");
|
||||
background-position: top center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
top: 40vh;
|
||||
width: 100vw;
|
||||
padding: 30px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.footer-title,
|
||||
.footer-subtitle {
|
||||
text-align: center;
|
||||
text-shadow: 0 2px 2px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.footer-title {
|
||||
font-weight: bold;
|
||||
padding-bottom: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.footer-subtitle {
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.footer-btn {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
background: white;
|
||||
color: #1f3c84;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.cns {
|
||||
background-image: url("../../assets/logo.png");
|
||||
background-size: 100%;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.v-card__text p {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
</style>
|
||||
13
VUE-WEB/lions-web/src/pages/layouts/common.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "common.layout"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
331
VUE-WEB/lions-web/src/pages/vote/diff.vue
Normal file
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<div v-if="!loaded">
|
||||
<v-dialog
|
||||
v-model="lock"
|
||||
persistent
|
||||
width="300"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card-text>
|
||||
<p class="pt-4">数据加载中,请稍后</p>
|
||||
<v-progress-linear
|
||||
indeterminate
|
||||
color="white"
|
||||
class="mb-0"
|
||||
></v-progress-linear>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
<v-container class="mian" v-else>
|
||||
<!-- 已投票提示信息 -->
|
||||
<div class="mian-shoeToast" v-if="lock">
|
||||
<div class="mian-shoeToast-icon">
|
||||
<v-icon color="#1f3c84" size="58">{{ message.icon }}</v-icon>
|
||||
</div>
|
||||
<div class="mian-shoeToast-title">{{ message.msg }}</div>
|
||||
<div class="mian-shoeToast-content">{{ message.tips }}</div>
|
||||
<div class="mian-shoeToast-btn" @click="jump('Index')">返回主页</div>
|
||||
</div>
|
||||
<!-- 投票列表 -->
|
||||
<div v-else>
|
||||
<v-dialog
|
||||
v-model="loading"
|
||||
persistent
|
||||
width="300"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card-text>
|
||||
<p class="pt-4">数据提交中,请稍后</p>
|
||||
<v-progress-linear
|
||||
indeterminate
|
||||
color="white"
|
||||
class="mb-0"
|
||||
></v-progress-linear>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<div class="list">
|
||||
<div class="vote-title">
|
||||
中国狮子联会哈尔滨心连心服务队
|
||||
</div>
|
||||
<div style="color: #ffffff">
|
||||
2020-2021年度
|
||||
</div>
|
||||
<div class="vote-title">{{ info.title }}</div>
|
||||
|
||||
<div class="list-card" v-for="(item, index) in list" :key="index">
|
||||
<v-img
|
||||
:src="item.cover"
|
||||
width="110"
|
||||
height="160"
|
||||
position="top center"
|
||||
style="position: absolute"
|
||||
>
|
||||
</v-img>
|
||||
<div class="list-card-content">
|
||||
<div class="list-card-content-name">{{ item.name }}</div>
|
||||
<div class="list-card-content-item"><label>参选职务:</label>{{ item.desc }}</div>
|
||||
<!-- <div class="list-card-content-item"><label>服务队:</label>{{ item.desc2 }}</div>-->
|
||||
<!-- :disabled="tomax && !(item.id in selected)"-->
|
||||
|
||||
<v-checkbox
|
||||
v-model="selected"
|
||||
:value="item.id"
|
||||
class="radioGroup"
|
||||
:disabled="tomax && !inArray(item.id)"
|
||||
>
|
||||
<template v-slot:label>
|
||||
<div class="list-radio-text">赞成</div>
|
||||
</template>
|
||||
</v-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-dialog v-model="sure" persistent>
|
||||
<v-card>
|
||||
<v-card-title>确认提交</v-card-title>
|
||||
<!-- <v-card-text>-->
|
||||
<!-- 确认您选择无误,立即提交么?-->
|
||||
<!-- </v-card-text>-->
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" text @click="sure=false">我再想想</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" @click="submitForm">确认提交</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<div class="list-footer">
|
||||
<div class="list-footer-text">最多可选{{ info.max }}项,您已经选择{{ selected.length }}项</div>
|
||||
<v-btn
|
||||
class="list-footer-btn"
|
||||
:disabled="disable"
|
||||
@click="sure = true"
|
||||
>
|
||||
提交推举票
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
id: 0,
|
||||
info: [],
|
||||
list: [],
|
||||
submitData: [],
|
||||
lock: true,
|
||||
selected: [],
|
||||
disable: true,
|
||||
loading: false,
|
||||
message: '',
|
||||
tomax: false,
|
||||
sure: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selected: function () {
|
||||
if (this.selected.length >= this.info.max) {
|
||||
this.tomax = true
|
||||
} else {
|
||||
this.tomax = false
|
||||
}
|
||||
if (this.selected.length > 0 && this.selected.length <= this.info.max) {
|
||||
this.disable = false;
|
||||
} else {
|
||||
this.disable = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.id = this.$route.params.id;
|
||||
this.$api.vote.show(this.id).then(result => {
|
||||
this.lock = result.lock
|
||||
this.info = result.info
|
||||
this.list = result.list
|
||||
this.loaded = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
inArray: function (id) {
|
||||
for (let i in this.selected) {
|
||||
if (this.selected[i] == id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
submitForm: function (e) {
|
||||
e.preventDefault()
|
||||
this.loading = true
|
||||
this.$api.vote.submit(this.id, this.selected).then((res) => {
|
||||
this.message = res
|
||||
this.loading = false
|
||||
this.lock = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vote-title {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
padding-top: 20px;
|
||||
text-shadow: 0 2px 2px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.mian {
|
||||
min-height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: #1f3c84;
|
||||
background-image: url("../../assets/back_1.png");
|
||||
background-position: top center;
|
||||
background-size: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 提示信息 */
|
||||
.mian-shoeToast {
|
||||
margin-top: 10vh;
|
||||
margin-left: 30px;
|
||||
height: 80vh;
|
||||
width: calc(100vw - 60px);
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-pack: center;
|
||||
padding: 30px 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mian-shoeToast-icon {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-title {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #1f3c84;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-content {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-btn {
|
||||
height: 45px;
|
||||
background: #1f3c84;
|
||||
color: white;
|
||||
line-height: 45px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
/* 列表 */
|
||||
.list-header b {
|
||||
color: #1f3c84;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.list-footer {
|
||||
background: #1f3c84;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.list-footer-text {
|
||||
text-align: center;
|
||||
color: white;
|
||||
line-height: 20px;
|
||||
padding-bottom: 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.list-footer-btn {
|
||||
font-size: 1rem;
|
||||
height: 45px !important;
|
||||
line-height: 45px;
|
||||
width: 100%;
|
||||
background: white;
|
||||
color: #1f3c84;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 0px 15px 110px;
|
||||
}
|
||||
|
||||
.list-card {
|
||||
background: white;
|
||||
margin-top: 15px;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.list-card-content {
|
||||
margin-left: 130px;
|
||||
height: 160px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.radioGroup {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -20px;
|
||||
}
|
||||
|
||||
.list-card-content-name {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #1f3c84;
|
||||
}
|
||||
|
||||
.list-card-content-item {
|
||||
padding-top: 5px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
padding-left: 80px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list-card-content-item label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.list-radio-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
315
VUE-WEB/lions-web/src/pages/vote/equal.vue
Normal file
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<div v-if="!loaded">
|
||||
<v-dialog
|
||||
v-model="lock"
|
||||
persistent
|
||||
width="300"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card-text>
|
||||
<p class="pt-4">数据加载中,请稍后</p>
|
||||
<v-progress-linear
|
||||
indeterminate
|
||||
color="white"
|
||||
class="mb-0"
|
||||
></v-progress-linear>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
<v-container class="mian" v-else>
|
||||
<!-- 已投票提示信息 -->
|
||||
<div class="mian-shoeToast" v-if="lock">
|
||||
<div class="mian-shoeToast-icon">
|
||||
<v-icon color="#1f3c84" size="58">{{ message.icon }}</v-icon>
|
||||
</div>
|
||||
<div class="mian-shoeToast-title">{{ message.msg }}</div>
|
||||
<div class="mian-shoeToast-content">{{ message.tips }}</div>
|
||||
<div class="mian-shoeToast-btn" @click="jump('Index')">返回主页</div>
|
||||
</div>
|
||||
<!-- 投票列表 -->
|
||||
<div v-else>
|
||||
<v-dialog
|
||||
v-model="loading"
|
||||
persistent
|
||||
width="300"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card-text>
|
||||
<p class="pt-4">数据提交中,请稍后</p>
|
||||
<v-progress-linear
|
||||
indeterminate
|
||||
color="white"
|
||||
class="mb-0"
|
||||
></v-progress-linear>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<div class="list">
|
||||
<div class="vote-title">
|
||||
中国狮子联会哈尔滨心连心服务队
|
||||
</div>
|
||||
<div style="color: #ffffff">
|
||||
2020-2021年度
|
||||
</div>
|
||||
<div class="vote-title">{{ info.title }}</div>
|
||||
|
||||
<div class="list-card" v-for="(item, index) in list" :key="index">
|
||||
<v-img
|
||||
:src="item.cover"
|
||||
width="110"
|
||||
height="160"
|
||||
position="top center"
|
||||
style="position: absolute"
|
||||
>
|
||||
</v-img>
|
||||
<div class="list-card-content">
|
||||
<div class="list-card-content-name">{{ item.name }}</div>
|
||||
<div class="list-card-content-item"><label>参选职务:</label>{{ item.desc }}</div>
|
||||
<!-- <div class="list-card-content-item"><label>服 务 队:</label>{{ item.desc2 }}</div>-->
|
||||
<v-radio-group
|
||||
class="radioGroup"
|
||||
row
|
||||
v-model="item.radio"
|
||||
>
|
||||
<v-radio
|
||||
color="#1f3c84"
|
||||
value="0"
|
||||
>
|
||||
<template v-slot:label>
|
||||
<div class="list-radio-text">不赞成</div>
|
||||
</template>
|
||||
</v-radio>
|
||||
<v-radio
|
||||
color="#1f3c84"
|
||||
value="1"
|
||||
>
|
||||
<template v-slot:label>
|
||||
<div class="list-radio-text">赞成</div>
|
||||
</template>
|
||||
</v-radio>
|
||||
</v-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-dialog v-model="sure" persistent>
|
||||
<v-card>
|
||||
<v-card-title>确认提交</v-card-title>
|
||||
<!-- <v-card-text>-->
|
||||
<!-- 确认您选择无误,立即提交么?-->
|
||||
<!-- </v-card-text>-->
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" text @click="sure=false">我再想想</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" @click="submitForm">确认提交</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<div class="list-footer">
|
||||
<button
|
||||
class="list-footer-btn"
|
||||
:disabled="lock"
|
||||
@click="sure = true"
|
||||
>
|
||||
提交推举票
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
id: 0,
|
||||
info: [],
|
||||
list: [],
|
||||
submitData: [],
|
||||
lock: true,
|
||||
loading: false,
|
||||
message: '',
|
||||
sure: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.id = this.$route.params.id;
|
||||
this.$api.vote.show(this.id).then(result => {
|
||||
this.lock = result.lock
|
||||
this.info = result.info
|
||||
this.list = result.list
|
||||
this.loaded = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
submitForm: function (e) {
|
||||
this.loading = true
|
||||
e.preventDefault()
|
||||
this.submitData = []
|
||||
this.list.forEach((item) => {
|
||||
this.submitData.push({
|
||||
id: item.id,
|
||||
result: item.radio
|
||||
})
|
||||
});
|
||||
|
||||
this.$api.vote.submit(this.id, this.submitData).then((res) => {
|
||||
this.message = res
|
||||
this.loading = false
|
||||
this.lock = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vote-title {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
padding-top: 20px;
|
||||
text-shadow: 0 2px 2px rgba(0, 0, 0, .5);
|
||||
}
|
||||
.mian {
|
||||
min-height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: #1f3c84;
|
||||
background-image: url("../../assets/back_1.png");
|
||||
background-position: top center;
|
||||
background-size: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 提示信息 */
|
||||
.mian-shoeToast {
|
||||
margin-top: 10vh;
|
||||
margin-left: 30px;
|
||||
height: 80vh;
|
||||
width: calc(100vw - 60px);
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-pack: center;
|
||||
padding: 30px 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mian-shoeToast-icon {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-title {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #1f3c84;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-content {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-btn {
|
||||
height: 45px;
|
||||
background: #1f3c84;
|
||||
color: white;
|
||||
line-height: 45px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
/* 列表 */
|
||||
.list-header b {
|
||||
color: #1f3c84;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.list-footer {
|
||||
background: #1f3c84;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.list-footer-btn {
|
||||
font-size: 1rem;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
width: 100%;
|
||||
background: white;
|
||||
color: #1f3c84;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 0px 15px 75px;
|
||||
}
|
||||
|
||||
.list-card {
|
||||
background: white;
|
||||
margin-top: 15px;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.list-card-content {
|
||||
margin-left: 130px;
|
||||
height: 160px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.radioGroup {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -20px;
|
||||
}
|
||||
|
||||
.list-card-content-name {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #1f3c84;
|
||||
}
|
||||
|
||||
.list-card-content-item {
|
||||
padding-top: 5px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
padding-left: 80px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list-card-content-item label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.list-radio-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
179
VUE-WEB/lions-web/src/pages/vote/index.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-container class="mian" v-if="loaded">
|
||||
<div v-if="!lock">
|
||||
<div class="vote-title">
|
||||
中国狮子联会哈尔滨心连心服务队
|
||||
</div>
|
||||
<div style="color: #ffffff">
|
||||
2020-2021年度
|
||||
</div>
|
||||
<div class="vote-title">{{ vote.title }}</div>
|
||||
<div class="vote-block">
|
||||
<div class="vote-block-title">
|
||||
{{ vote.type == 'diff' ? '差额推举' : '等额推举' }}规则说明
|
||||
</div>
|
||||
<div class="vote-block-html" v-html="vote.rules"></div>
|
||||
</div>
|
||||
<div class="vote-block-btn">
|
||||
<span @click="jump('Vote_' + vote.type, {id: vote.id})">继续</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mian-shoeToast" v-else>
|
||||
<div class="mian-shoeToast-icon">
|
||||
<v-icon color="#1f3c84" size="58">mdi-checkbox-marked-circle</v-icon>
|
||||
</div>
|
||||
<div class="mian-shoeToast-title">投票成功</div>
|
||||
<div class="mian-shoeToast-content">您已提交本轮次选票,为保密起见,已隐藏您的投票结果,其他人也无法查询</div>
|
||||
<div class="mian-shoeToast-btn" @click="jump('Index')">返回主页</div>
|
||||
</div>
|
||||
</v-container>
|
||||
<div v-else>
|
||||
<v-dialog
|
||||
v-model="loading"
|
||||
persistent
|
||||
width="300"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card-text>
|
||||
<p class="pt-4">数据加载中,请稍后</p>
|
||||
<v-progress-linear
|
||||
indeterminate
|
||||
color="white"
|
||||
class="mb-0"
|
||||
></v-progress-linear>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
vote: [],
|
||||
lock: true,
|
||||
loaded: false,
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$api.vote.index().then(result => {
|
||||
this.vote = result.vote
|
||||
this.lock = result.lock
|
||||
|
||||
this.loaded = true
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mian {
|
||||
min-height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: #1f3c84;
|
||||
background-image: url("../../assets/back_1.png");
|
||||
background-position: top center;
|
||||
background-size: 100%;
|
||||
padding: 0 30px 130px 30px;
|
||||
}
|
||||
|
||||
/* 提示信息 */
|
||||
.mian-shoeToast {
|
||||
margin-top: 10vh;
|
||||
height: 80vh;
|
||||
width: calc(100vw - 60px);
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-pack: center;
|
||||
padding: 30px 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mian-shoeToast-icon {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-title {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #1f3c84;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-content {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mian-shoeToast-btn {
|
||||
height: 45px;
|
||||
background: #1f3c84;
|
||||
color: white;
|
||||
line-height: 45px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
|
||||
.vote-block {
|
||||
background: white;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.vote-block-title {
|
||||
line-height: 40px;
|
||||
padding-bottom: 10px;
|
||||
font-size: 18px;
|
||||
color: #1f3c84;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vote-title {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
padding-bottom: 10px;
|
||||
color: white;
|
||||
padding-top: 20px;
|
||||
text-shadow: 0 2px 2px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.vote-block-btn {
|
||||
background: #1f3c84;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding: 30px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vote-block-btn span {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
background: white;
|
||||
color: #1f3c84;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
34
VUE-WEB/lions-web/src/plugins/toast/index.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
import Toast from "./toast"
|
||||
import Vue from 'vue'
|
||||
|
||||
let currentToast;
|
||||
|
||||
const index = (message, color) => {
|
||||
if (currentToast) {
|
||||
currentToast.close()
|
||||
}
|
||||
|
||||
currentToast = createToast({
|
||||
Vue,
|
||||
message,
|
||||
color: color || 'success',
|
||||
onClose: () => {
|
||||
currentToast = null
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function createToast({Vue, message, color, onClose}) {
|
||||
const Constructor = Vue.extend(Toast)
|
||||
|
||||
let toast = new Constructor()
|
||||
toast.message = message
|
||||
toast.color = color
|
||||
toast.timeout = 1000
|
||||
toast.$mount();
|
||||
toast.$on('close', onClose)
|
||||
document.body.appendChild(toast.$el)
|
||||
return toast
|
||||
}
|
||||
|
||||
export default index
|
||||
38
VUE-WEB/lions-web/src/plugins/toast/toast.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<v-snackbar
|
||||
:color="color"
|
||||
:timeout="timeout"
|
||||
:top="true"
|
||||
v-model="show"
|
||||
>
|
||||
{{ message }}
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "toast.vue",
|
||||
data() {
|
||||
return {
|
||||
timeout: 1000,
|
||||
color: 'success',
|
||||
show: true,
|
||||
message: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log(this.color)
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$el.remove();
|
||||
this.$emit("close");
|
||||
this.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
8
VUE-WEB/lions-web/src/plugins/utils.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
const isWechat = () => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
return (/micromessenger/.test(ua)) ? true : false;
|
||||
}
|
||||
|
||||
export default {
|
||||
isWechat
|
||||
}
|
||||
13
VUE-WEB/lions-web/src/plugins/vuetify.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import Vuetify from 'vuetify'
|
||||
import 'vuetify/dist/vuetify.min.css'
|
||||
import '@mdi/font/css/materialdesignicons.css'
|
||||
|
||||
Vue.use(Vuetify)
|
||||
|
||||
export default new Vuetify({
|
||||
icons: {
|
||||
iconfont: 'mdi'
|
||||
},
|
||||
theme: {}
|
||||
})
|
||||
123
VUE-WEB/lions-web/src/router/index.js
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
import VueRouter from "vue-router"
|
||||
import store from "@/store"
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Index',
|
||||
component: () => import(/* webpackChunkName: "index" */ '@/pages/index/index'),
|
||||
meta: {
|
||||
title: '首页',
|
||||
keepAlive: true,
|
||||
requireAuth: true
|
||||
}
|
||||
}, {
|
||||
path: '/vote',
|
||||
name: 'VoteIndex',
|
||||
component: () => import(/* webpackChunkName: "index" */ '@/pages/vote/index'),
|
||||
meta: {
|
||||
title: '推举规则',
|
||||
keepAlive: true,
|
||||
requireAuth: true
|
||||
}
|
||||
}, {
|
||||
path: '/vote_equal/:id',
|
||||
name: 'Vote_equal',
|
||||
component: () => import(/* webpackChunkName: "index" */ '@/pages/vote/equal'),
|
||||
meta: {
|
||||
title: '等额推举',
|
||||
keepAlive: true,
|
||||
requireAuth: true
|
||||
}
|
||||
}, {
|
||||
path: '/vote_diff/:id',
|
||||
name: 'Vote_diff',
|
||||
component: () => import(/* webpackChunkName: "index" */ '@/pages/vote/diff'),
|
||||
meta: {
|
||||
title: '差额推举',
|
||||
keepAlive: true,
|
||||
requireAuth: true
|
||||
}
|
||||
}, {
|
||||
path: '/files',
|
||||
name: 'FileIndex',
|
||||
component: () => import(/* webpackChunkName: "index" */ '@/pages/files/index'),
|
||||
meta: {
|
||||
title: '文件审阅',
|
||||
keepAlive: true,
|
||||
requireAuth: true
|
||||
}
|
||||
}, {
|
||||
path: '/files/:id',
|
||||
name: 'FileShow',
|
||||
component: () => import(/* webpackChunkName: "index" */ '@/pages/files/show'),
|
||||
meta: {
|
||||
title: '审阅',
|
||||
keepAlive: true,
|
||||
requireAuth: true
|
||||
}
|
||||
}, {
|
||||
path: '/auth',
|
||||
component: () => import(/* webpackChunkName: "auth" */ '@/pages/layouts/common'),
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
name: 'AuthLogin',
|
||||
component: () => import(/* webpackChunkName: "auth" */ '@/pages/auth/login'),
|
||||
meta: {
|
||||
title: '推举系统登录'
|
||||
}
|
||||
}, {
|
||||
path: 'verify',
|
||||
name: 'AuthVerify',
|
||||
component: () => import(/* webpackChunkName: "auth" */ '@/pages/auth/verify'),
|
||||
meta: {
|
||||
title: '验证手机号'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
let hasLogin = store.state.hasLogin;
|
||||
if (to.name === 'AuthLogin') {
|
||||
// 如果已经登录,直接跳转到首页去
|
||||
if (hasLogin) {
|
||||
next({
|
||||
name: 'Index'
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
let tk = localStorage.getItem('accessToken')
|
||||
|
||||
if (tk) {
|
||||
store.commit('login', tk);
|
||||
next({
|
||||
name: 'Index'
|
||||
});
|
||||
return
|
||||
}
|
||||
} else if (to.meta.requireAuth === true) {
|
||||
if (hasLogin === false) {
|
||||
next({
|
||||
name: 'AuthLogin',
|
||||
params: {
|
||||
next: to.path
|
||||
}
|
||||
});
|
||||
return
|
||||
}
|
||||
}
|
||||
store.state.showFooter = to.meta.showFooter || false;
|
||||
|
||||
next()
|
||||
});
|
||||
|
||||
export default router
|
||||
49
VUE-WEB/lions-web/src/store/index.js
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
hasLogin: false,
|
||||
accessToken: '',
|
||||
userInfo: null,
|
||||
},
|
||||
mutations: {
|
||||
login(state, token) {
|
||||
state.hasLogin = true;
|
||||
state.accessToken = token;
|
||||
localStorage.setItem('accessToken', token)
|
||||
},
|
||||
logout(state) {
|
||||
state.hasLogin = false;
|
||||
state.accessToken = "";
|
||||
state.userInfo = null;
|
||||
localStorage.removeItem('accessToken')
|
||||
localStorage.removeItem('userInfo')
|
||||
}
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
/**
|
||||
* 登录并获取用户基础信息
|
||||
* @param dispatch
|
||||
* @param commit
|
||||
* @param token
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async doLogin({commit}, token) {
|
||||
commit('login', token)
|
||||
},
|
||||
/**
|
||||
* 登出方法
|
||||
* @param commit
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async doLogout({commit}) {
|
||||
commit('logout')
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default store
|
||||