This commit is contained in:
David 2019-05-29 12:27:56 +08:00
parent 107afc89bc
commit a6773358f5
39 changed files with 1890 additions and 1883 deletions

View File

@ -94,7 +94,11 @@
{
"ignoreReadBeforeAssign": false
}
]
],
"camelcase": 0,
"new-cap": 0,
"vue/no-unused-vars": 0,
"vue/require-default-prop": 0
}
},
"postcss": {

View File

@ -22,4 +22,4 @@ export default {
checkUsername (username) {
return fetchAPI('user/checkUsername/', 'get', null, { username })
}
}
}

View File

@ -16,4 +16,4 @@ export default {
getStatusList () {
return fetchAPI('booking/statusList/', 'get')
}
}
}

View File

@ -7,4 +7,4 @@ export default {
getRoomBookingStatusDetail (id, params) {
return fetchAPI(`booking/room/${id}/bookingStatus/`, 'get', null, params)
}
}
}

View File

@ -25,4 +25,4 @@ export default {
deleteSeat (id) {
return fetchAPI(`booking/seat/${id}`, 'delete')
}
}
}

View File

@ -7,4 +7,4 @@ export default {
updateSettingDetail (setting) {
return fetchAPI('booking/setting/1/', 'put', setting)
}
}
}

View File

@ -26,25 +26,25 @@
</template>
<script>
import RouteView from '@/components/layouts/RouteView'
import { mixinDevice } from '@/utils/mixin.js'
import RouteView from '@/components/layouts/RouteView'
import { mixinDevice } from '@/utils/mixin.js'
export default {
name: 'UserLayout',
components: { RouteView },
mixins: [mixinDevice],
data () {
return {
year: (new Date()).getFullYear()
}
},
mounted () {
document.body.classList.add('userLayout')
},
beforeDestroy () {
document.body.classList.remove('userLayout')
export default {
name: 'UserLayout',
components: { RouteView },
mixins: [mixinDevice],
data () {
return {
year: (new Date()).getFullYear()
}
},
mounted () {
document.body.classList.add('userLayout')
},
beforeDestroy () {
document.body.classList.remove('userLayout')
}
}
</script>
<style lang="less" scoped>

View File

@ -18,45 +18,45 @@
</template>
<script>
import ALayoutSider from 'ant-design-vue/es/layout/Sider'
import Logo from '../tools/Logo'
import SMenu from './index'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import ALayoutSider from 'ant-design-vue/es/layout/Sider'
import Logo from '../tools/Logo'
import SMenu from './index'
import { mixin, mixinDevice } from '@/utils/mixin.js'
export default {
name: 'SideMenu',
components: { ALayoutSider, Logo, SMenu },
mixins: [mixin, mixinDevice],
props: {
mode: {
type: String,
required: false,
default: 'inline'
},
theme: {
type: String,
required: false,
default: 'dark'
},
collapsible: {
type: Boolean,
required: false,
default: false
},
collapsed: {
type: Boolean,
required: false,
default: false
},
menus: {
type: Array,
required: true
}
export default {
name: 'SideMenu',
components: { ALayoutSider, Logo, SMenu },
mixins: [mixin, mixinDevice],
props: {
mode: {
type: String,
required: false,
default: 'inline'
},
methods: {
onSelect (obj) {
this.$emit('menuSelect', obj)
}
theme: {
type: String,
required: false,
default: 'dark'
},
collapsible: {
type: Boolean,
required: false,
default: false
},
collapsed: {
type: Boolean,
required: false,
default: false
},
menus: {
type: Array,
required: true
}
},
methods: {
onSelect (obj) {
this.$emit('menuSelect', obj)
}
}
}
</script>

View File

@ -1,8 +1,9 @@
<template>
<!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%' -->
<a-layout-header v-if="!headerBarFixed"
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
:style="{ padding: '0' }">
<a-layout-header
v-if="!headerBarFixed"
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
:style="{ padding: '0' }">
<div v-if="mode === 'sidemenu'" class="header">
<a-icon
v-if="device==='mobile'"
@ -41,72 +42,72 @@
</template>
<script>
import UserMenu from '../tools/UserMenu'
import SMenu from '../menu/'
import Logo from '../tools/Logo'
import UserMenu from '../tools/UserMenu'
import SMenu from '../menu/'
import Logo from '../tools/Logo'
import { mixin } from '@/utils/mixin.js'
import { mixin } from '@/utils/mixin.js'
export default {
name: 'GlobalHeader',
components: {
UserMenu,
SMenu,
Logo
export default {
name: 'GlobalHeader',
components: {
UserMenu,
SMenu,
Logo
},
mixins: [mixin],
props: {
mode: {
type: String,
// sidemenu, topmenu
default: 'sidemenu'
},
mixins: [mixin],
props: {
mode: {
type: String,
// sidemenu, topmenu
default: 'sidemenu'
},
menus: {
type: Array,
required: true
},
theme: {
type: String,
required: false,
default: 'dark'
},
collapsed: {
type: Boolean,
required: false,
default: false
},
device: {
type: String,
required: false,
default: 'desktop'
}
menus: {
type: Array,
required: true
},
data () {
return {
headerBarFixed: false
}
theme: {
type: String,
required: false,
default: 'dark'
},
mounted () {
window.addEventListener('scroll', this.handleScroll)
collapsed: {
type: Boolean,
required: false,
default: false
},
methods: {
handleScroll () {
if (this.autoHideHeader) {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
if (scrollTop > 100) {
this.headerBarFixed = true
} else {
this.headerBarFixed = false
}
device: {
type: String,
required: false,
default: 'desktop'
}
},
data () {
return {
headerBarFixed: false
}
},
mounted () {
window.addEventListener('scroll', this.handleScroll)
},
methods: {
handleScroll () {
if (this.autoHideHeader) {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
if (scrollTop > 100) {
this.headerBarFixed = true
} else {
this.headerBarFixed = false
}
},
toggle () {
this.$emit('toggle')
} else {
this.headerBarFixed = false
}
},
toggle () {
this.$emit('toggle')
}
}
}
</script>
<style lang="less" scoped>

View File

@ -46,8 +46,9 @@
</a-drawer>
</template>
<a-layout :class="[layoutMode, `content-width-${contentWidth}`]"
:style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
<a-layout
:class="[layoutMode, `content-width-${contentWidth}`]"
:style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
<!-- layout header -->
<global-header
:mode="layoutMode"
@ -72,75 +73,75 @@
</template>
<script>
import SideMenu from '@/components/menu/SideMenu'
import GlobalHeader from '@/components/page/GlobalHeader'
import GlobalFooter from '@/components/page/GlobalFooter'
import { triggerWindowResizeEvent } from '@/utils/util'
import { mapActions, mapState } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import SideMenu from '@/components/menu/SideMenu'
import GlobalHeader from '@/components/page/GlobalHeader'
import GlobalFooter from '@/components/page/GlobalFooter'
import { triggerWindowResizeEvent } from '@/utils/util'
import { mapActions, mapState } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin.js'
export default {
name: 'GlobalLayout',
components: {
SideMenu,
GlobalHeader,
GlobalFooter
},
mixins: [mixin, mixinDevice],
data () {
return {
collapsed: false,
menus: []
export default {
name: 'GlobalLayout',
components: {
SideMenu,
GlobalHeader,
GlobalFooter
},
mixins: [mixin, mixinDevice],
data () {
return {
collapsed: false,
menus: []
}
},
computed: {
...mapState({
//
mainMenu: state => state.permission.addRouters
}),
contentPaddingLeft () {
if (!this.fixSidebar || this.isMobile()) {
return '0'
}
},
computed: {
...mapState({
//
mainMenu: state => state.permission.addRouters
}),
contentPaddingLeft () {
if (!this.fixSidebar || this.isMobile()) {
return '0'
}
if (this.sidebarOpened) {
return '256px'
}
return '80px'
if (this.sidebarOpened) {
return '256px'
}
return '80px'
}
},
watch: {
sidebarOpened (val) {
this.collapsed = !val
}
},
created () {
this.menus = this.mainMenu.find((item) => item.path === '/').children
this.collapsed = !this.sidebarOpened
},
methods: {
...mapActions(['setSidebar']),
toggle () {
this.collapsed = !this.collapsed
this.setSidebar(!this.collapsed)
triggerWindowResizeEvent()
},
watch: {
sidebarOpened (val) {
this.collapsed = !val
paddingCalc () {
let left = ''
if (this.sidebarOpened) {
left = this.isDesktop() ? '256px' : '80px'
} else {
left = this.isMobile() && '0' || (this.fixSidebar && '80px' || '0')
}
console.log('left', left)
return left
},
created () {
this.menus = this.mainMenu.find((item) => item.path === '/').children
this.collapsed = !this.sidebarOpened
},
methods: {
...mapActions(['setSidebar']),
toggle () {
this.collapsed = !this.collapsed
this.setSidebar(!this.collapsed)
triggerWindowResizeEvent()
},
paddingCalc () {
let left = ''
if (this.sidebarOpened) {
left = this.isDesktop() ? '256px' : '80px'
} else {
left = this.isMobile() && '0' || (this.fixSidebar && '80px' || '0')
}
console.log('left', left)
return left
},
menuSelect () {
if (!this.isDesktop()) {
this.collapsed = false
}
menuSelect () {
if (!this.isDesktop()) {
this.collapsed = false
}
}
}
}
</script>
<style lang="less">

View File

@ -1,14 +1,14 @@
<template>
<div class="page-header">
<div class="page-header-index-wide">
<!-- <a-breadcrumb class="breadcrumb">-->
<!-- <a-breadcrumb-item v-for="(item, index) in breadList" :key="index">-->
<!-- <router-link v-if="item.name !== name" :to="{ path: item.path }">-->
<!-- {{ item.meta.title }}-->
<!-- </router-link>-->
<!-- <span v-else>{{ item.meta.title }}</span>-->
<!-- </a-breadcrumb-item>-->
<!-- </a-breadcrumb>-->
<!-- <a-breadcrumb class="breadcrumb">-->
<!-- <a-breadcrumb-item v-for="(item, index) in breadList" :key="index">-->
<!-- <router-link v-if="item.name !== name" :to="{ path: item.path }">-->
<!-- {{ item.meta.title }}-->
<!-- </router-link>-->
<!-- <span v-else>{{ item.meta.title }}</span>-->
<!-- </a-breadcrumb-item>-->
<!-- </a-breadcrumb>-->
<div class="detail">
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
@ -41,62 +41,62 @@
</template>
<script>
// import Breadcrumb from '@/components/tools/Breadcrumb'
// import Breadcrumb from '@/components/tools/Breadcrumb'
export default {
name: 'PageHeader',
components: {
// 's-breadcrumb': Breadcrumb
export default {
name: 'PageHeader',
components: {
// 's-breadcrumb': Breadcrumb
},
props: {
title: {
type: String,
default: '',
required: false
},
props: {
title: {
type: String,
default: '',
required: false
},
// breadcrumb: {
// type: Array,
// default: null,
// required: false
// },
logo: {
type: String,
default: '',
required: false
},
avatar: {
type: String,
default: '',
required: false
}
// breadcrumb: {
// type: Array,
// default: null,
// required: false
// },
logo: {
type: String,
default: '',
required: false
},
data () {
return {
name: '',
// breadList: []
}
},
created () {
// this.getBreadcrumb()
},
methods: {
// getBreadcrumb () {
// this.breadList = []
// // this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: ''}})
//
// this.name = this.$route.name
// this.$route.matched.forEach((item) => {
// // item.name !== 'index' && this.breadList.push(item)
// this.breadList.push(item)
// })
// }
},
watch: {
// $route () {
// this.getBreadcrumb()
// }
avatar: {
type: String,
default: '',
required: false
}
},
data () {
return {
name: ''
// breadList: []
}
},
created () {
// this.getBreadcrumb()
},
methods: {
// getBreadcrumb () {
// this.breadList = []
// // this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: ''}})
//
// this.name = this.$route.name
// this.$route.matched.forEach((item) => {
// // item.name !== 'index' && this.breadList.push(item)
// this.breadList.push(item)
// })
// }
},
watch: {
// $route () {
// this.getBreadcrumb()
// }
}
}
</script>
<style lang="less" scoped>

View File

@ -18,33 +18,33 @@
</template>
<script>
export default {
name: 'HeaderNotice',
props: {
tabs: {
type: Array,
default: null,
required: true
}
},
data () {
return {
loadding: false
}
},
methods: {
fetchNotice () {
if (this.loadding) {
this.loadding = false
return
}
this.loadding = true
setTimeout(() => {
this.loadding = false
}, 2000)
export default {
name: 'HeaderNotice',
props: {
tabs: {
type: Array,
default: null,
required: true
}
},
data () {
return {
loadding: false
}
},
methods: {
fetchNotice () {
if (this.loadding) {
this.loadding = false
return
}
this.loadding = true
setTimeout(() => {
this.loadding = false
}, 2000)
}
}
}
</script>
<style lang="less" scoped>

View File

@ -8,19 +8,19 @@
</template>
<script>
export default {
name: 'Logo',
props: {
title: {
type: String,
default: 'Booking Admin',
required: false
},
showTitle: {
type: Boolean,
default: true,
required: false
}
export default {
name: 'Logo',
props: {
title: {
type: String,
default: 'Booking Admin',
required: false
},
showTitle: {
type: Boolean,
default: true,
required: false
}
}
}
</script>

View File

@ -1,9 +1,9 @@
<template>
<div class="user-wrapper">
<router-link :to="{ name: 'help' }">
<span class="action">
<span class="action">
<a-icon type="question-circle-o"></a-icon>
</span>
</span>
</router-link>
<a-dropdown>
<span class="action ant-dropdown-link user-dropdown-menu">
@ -29,28 +29,28 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'UserMenu',
methods: {
...mapActions(['Logout']),
...mapGetters(['username']),
handleLogout () {
const that = this
this.$confirm({
title: '提示',
content: '确定要注销登录吗?',
onOk () {
return that.Logout({})
.then(() => {
that.$router.push({ name: 'login' })
})
},
onCancel () {
}
})
}
export default {
name: 'UserMenu',
methods: {
...mapActions(['Logout']),
...mapGetters(['username']),
handleLogout () {
const that = this
this.$confirm({
title: '提示',
content: '确定要注销登录吗?',
onOk () {
return that.Logout({})
.then(() => {
that.$router.push({ name: 'login' })
})
},
onCancel () {
}
})
}
}
}
</script>

View File

@ -25,14 +25,14 @@ export const routerMap = [
path: '/booking',
name: 'booking',
component: () => import(/* webpackChunkName: "booking" */ '../views/booking/Booking'),
meta: { title: '预约', icon: 'book', permission: ['ADMIN', 'TEACHER'] },
meta: { title: '预约', icon: 'book', permission: ['ADMIN', 'TEACHER'] }
},
{
path: '/booking/new',
name: 'bookingNew',
hidden: true,
component: () => import(/* webpackChunkName: "booking" */ '../views/booking/BookingNew'),
meta: { title: '新建预约', icon: 'book', permission: ['ADMIN', 'TEACHER'] },
meta: { title: '新建预约', icon: 'book', permission: ['ADMIN', 'TEACHER'] }
},
{
path: '/booking/create',
@ -40,7 +40,7 @@ export const routerMap = [
props: true,
hidden: true,
component: () => import(/* webpackChunkName: "booking" */ '../views/booking/BookingDetail'),
meta: { title: '新建预约', permission: ['ADMIN', 'TEACHER'] },
meta: { title: '新建预约', permission: ['ADMIN', 'TEACHER'] }
},
{
path: '/booking/:id/edit',
@ -48,7 +48,7 @@ export const routerMap = [
props: true,
hidden: true,
component: () => import(/* webpackChunkName: "booking" */ '../views/booking/BookingDetail'),
meta: { title: '编辑预约', permission: ['ADMIN', 'TEACHER'] },
meta: { title: '编辑预约', permission: ['ADMIN', 'TEACHER'] }
},
{
path: '/room',
@ -122,7 +122,7 @@ export const routerMap = [
name: 'settingBooking',
component: () => import(/* webpackChunkName: "setting" */ '../views/setting/BookingSetting'),
meta: { title: '预约设置', permission: ['ADMIN'] }
},
}
]
},
{
@ -130,7 +130,7 @@ export const routerMap = [
name: 'help',
component: () => import(/* webpackChunkName: "help" */ '../views/help/Help'),
meta: { title: '帮助', icon: 'question-circle', permission: ['ADMIN', 'TEACHER'] }
},
}
]
},
{
@ -163,4 +163,4 @@ export const basicRouterMap = [
component:
() => import(/* webpackChunkName: "exception" */ '@/views/exception/404')
}
]
]

View File

@ -11,7 +11,7 @@ const whiteList = ['login'] // no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
let isLogin = cookie.get('sessionid')
const isLogin = cookie.get('sessionid')
if (isLogin) {
if (to.path === '/user/login') {
next({ name: 'dashboard' })
@ -52,4 +52,4 @@ router.beforeEach((to, from, next) => {
router.afterEach(() => {
NProgress.done()
})
})

View File

@ -6,7 +6,7 @@ const getters = {
username: state => state.user.username,
userid: state => state.user.id,
role: state => state.user.role,
addRouters: state => state.permission.addRouters,
addRouters: state => state.permission.addRouters
}
export default getters

View File

@ -12,7 +12,7 @@ export default new Vuex.Store({
modules: {
app,
user,
permission,
permission
},
state: {

View File

@ -52,4 +52,4 @@ const permission = {
}
}
export default permission
export default permission

View File

@ -31,7 +31,7 @@ const user = {
key: 'ERROR'
})
dispatch('Logout').then(() => {})
reject('Not a admin or teacher account')
reject(new Error('Not a admin or teacher account'))
} else {
resolve(data)
}
@ -51,7 +51,7 @@ const user = {
key: 'ERROR'
})
dispatch('Logout').then(() => {})
reject('Not a admin or teacher account')
reject(new Error('Not a admin or teacher account'))
} else {
commit('SET_ID', data.id)
commit('SET_USERNAME', data.username)

View File

@ -42,7 +42,7 @@ export default function fetchAPI (url, method, data = null, params = null) {
} else if (res.ok) {
return res.json()
} else {
throw `${res.status}, ${res.statusText}`
throw new Error(`${res.status}, ${res.statusText}`)
}
})
.then(data => {
@ -61,7 +61,7 @@ export default function fetchAPI (url, method, data = null, params = null) {
export function fetchAPIBase (url, method, data = null, params = null) {
let body = null
// csrf
let headers = {
const headers = {
'Content-Type': 'application/json'
}
if (!/^(GET|HEAD|OPTIONS|TRACE)$/.test(method.toUpperCase())) {
@ -79,4 +79,4 @@ export function fetchAPIBase (url, method, data = null, params = null) {
method: method,
body: body
})
}
}

View File

@ -40,10 +40,10 @@ export function removeLoadingAnimate (id = '', timeout = 1500) {
* @param fileName
*/
export function downloadFile (blob, fileName) {
let aLink = document.createElement('a')
let evt = document.createEvent('HTMLEvents')
evt.initEvent('click', true, true) //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
const aLink = document.createElement('a')
const evt = document.createEvent('HTMLEvents')
evt.initEvent('click', true, true) // initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
aLink.download = fileName
aLink.href = URL.createObjectURL(blob)
aLink.click()
}
}

View File

@ -10,7 +10,7 @@
style="width: 100%;"
@change="getData()">
<a-select-option value="">角色筛选</a-select-option>
<a-select-option :value="item.id" v-for="item in roleList">{{item.name}}</a-select-option>
<a-select-option :value="item.id" v-for="item in roleList" :key="item.id">{{ item.name }}</a-select-option>
</a-select>
</a-col>
<a-col
@ -84,10 +84,10 @@
:dataSource="userList"
:pagination="false">
<template slot="role" slot-scope="text, record, index">
{{roleList.find(item=> item.id === text).name}}
{{ roleList.find(item=> item.id === text).name }}
</template>
<template slot="is_active" slot-scope="text, record, index">
{{text ? '有效': '无效'}}
{{ text ? '有效': '无效' }}
</template>
<template slot="operation" slot-scope="text, record, index">
<a-button @click="$router.push({name: 'accountEdit', params: {id: record.id.toString()}})">编辑</a-button>
@ -97,7 +97,7 @@
</a-row>
<a-row>
<a-col class="item">
<span style="vertical-align: -5px"> {{count}} </span>
<span style="vertical-align: -5px"> {{ count }} </span>
<a-pagination
v-model="params.page"
@change="handelePage"
@ -111,127 +111,127 @@
</template>
<script>
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '8%'
},
{
title: '用户名',
dataIndex: 'username',
width: '14%'
},
{
title: '邮箱',
dataIndex: 'email',
width: '12%'
},
{
title: '角色',
dataIndex: 'role',
width: '12%',
scopedSlots: { customRender: 'role' }
},
{
title: '积分',
dataIndex: 'point',
width: '12%'
},
{
title: '上次登陆时间',
dataIndex: 'last_login',
width: '14%'
},
{
title: '状态',
dataIndex: 'is_active',
width: '10%',
scopedSlots: { customRender: 'is_active' }
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' }
}
]
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/account'
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/account'
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '8%'
},
{
title: '用户名',
dataIndex: 'username',
width: '14%'
},
{
title: '邮箱',
dataIndex: 'email',
width: '12%'
},
{
title: '角色',
dataIndex: 'role',
width: '12%',
scopedSlots: { customRender: 'role' }
},
{
title: '积分',
dataIndex: 'point',
width: '12%'
},
{
title: '上次登陆时间',
dataIndex: 'last_login',
width: '14%'
},
{
title: '状态',
dataIndex: 'is_active',
width: '10%',
scopedSlots: { customRender: 'is_active' }
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' }
}
]
export default {
name: 'Account',
components: {
PageLayout
},
data () {
return {
columns,
layout1: {
xs: { span: 24 },
sm: { span: 8 },
xl: { span: 8 },
},
layout2: {
xs: { span: 12 },
sm: { span: 8 },
xl: { span: 4 },
},
params: {
search: '',
page: 1,
ordering: '-last_login',
role: '',
},
state: {
loading: false,
},
roleList: [],
count: 0,
userList: [],
setting: {}
export default {
name: 'Account',
components: {
PageLayout
},
data () {
return {
columns,
layout1: {
xs: { span: 24 },
sm: { span: 8 },
xl: { span: 8 }
},
layout2: {
xs: { span: 12 },
sm: { span: 8 },
xl: { span: 4 }
},
params: {
search: '',
page: 1,
ordering: '-last_login',
role: ''
},
state: {
loading: false
},
roleList: [],
count: 0,
userList: [],
setting: {}
}
},
mounted () {
api.getRoleList()
.then(data => {
this.roleList = data
this.getData()
})
},
methods: {
getData () {
this.state.loading = true
if (this.$store.getters.role === 'TEACHER') {
this.params.teacher = this.$store.getters.userid
}
},
mounted () {
api.getRoleList()
api.getUserList(this.params)
.then(data => {
this.roleList = data
this.getData()
this.userList = data.results
this.count = data.count
this.state.loading = false
})
.catch(() => {
this.state.loading = false
})
},
methods: {
getData () {
this.state.loading = true
if (this.$store.getters.role === 'TEACHER') {
this.params.teacher = this.$store.getters.userid
}
api.getUserList(this.params)
.then(data => {
this.userList = data.results
this.count = data.count
this.state.loading = false
})
.catch(() => {
this.state.loading = false
})
},
handleClearParams () {
this.params = {
search: '',
page: 1,
ordering: '-last_login',
role: '',
}
this.state = {
loading: false
}
this.getData()
},
handelePage (page) {
this.params.page = page
this.getData()
},
handleClearParams () {
this.params = {
search: '',
page: 1,
ordering: '-last_login',
role: ''
}
this.state = {
loading: false
}
this.getData()
},
handelePage (page) {
this.params.page = page
this.getData()
}
}
}
</script>
<style scoped lang="less">

View File

@ -13,19 +13,19 @@
v-if="isEdit"
label="ID"
v-bind="layout">
{{id}}
{{ id }}
</a-form-item>
<a-form-item
v-if="isEdit"
label="加入日期"
v-bind="layout">
{{account.date_joined}}
{{ account.date_joined }}
</a-form-item>
<a-form-item
v-if="isEdit"
label="上次登录时间"
v-bind="layout">
{{account.last_login || '无'}}
{{ account.last_login || '无' }}
</a-form-item>
<a-divider v-if="isEdit">修改信息</a-divider>
<a-form-item
@ -42,7 +42,7 @@
],
validateTrigger: 'blur',
initialValue: isEdit ? account.username : ''
}
}
]">
</a-input>
</a-form-item>
@ -53,16 +53,16 @@
<a-input
type="password"
v-decorator="[
'password',
{
rules: [
{required: !isEdit, message: '请输入密码'},
{min: 8, message: '密码长度在8个字符以上'},
{validator: notAllNumberValidator, message: '密码不能全为数字'},
],
validateTrigger: 'blur'
}
]">
'password',
{
rules: [
{required: !isEdit, message: '请输入密码'},
{min: 8, message: '密码长度在8个字符以上'},
{validator: notAllNumberValidator, message: '密码不能全为数字'},
],
validateTrigger: 'blur'
}
]">
</a-input>
</a-tooltip>
<a-input
@ -77,7 +77,7 @@
{validator: notAllNumberValidator, message: '密码不能全为数字'},
],
validateTrigger: 'blur'
}
}
]">
</a-input>
</a-form-item>
@ -95,7 +95,7 @@
{validator: samePasswordValidator, message: '密码不能全为数字'},
],
validateTrigger: 'blur'
}
}
]">
</a-input>
</a-form-item>
@ -115,7 +115,7 @@
initialValue: isEdit ? account.role : (isTeacher ? 'STUDENT' : '')
}
]">
<a-select-option v-for="item in roleList" :value="item.id">{{item.name}}</a-select-option>
<a-select-option v-for="item in roleList" :value="item.id" :key="item.id">{{ item.name }}</a-select-option>
</a-select>
</a-tooltip>
</a-form-item>
@ -132,12 +132,12 @@
:disabled="form.getFieldValue('role') !== 'STUDENT'"
@search="handleSearch"
v-decorator="[
'teacher',
{
initialValue: isEdit ? account.teacher : []
}
]">
<a-select-option v-for="item in teacherList" :value="item.id">{{item.username}} {{item.email}}
'teacher',
{
initialValue: isEdit ? account.teacher : []
}
]">
<a-select-option v-for="item in teacherList" :value="item.id" :key="item.id">{{ item.username }} {{ item.email }}
</a-select-option>
</a-select>
</a-form-item>
@ -219,141 +219,137 @@
</template>
<script>
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/account'
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/account'
export default {
name: 'AccountDetail',
components: {
PageLayout
},
props: {
id: String,
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
form: this.$form.createForm(this),
roleList: [],
teacherList: [],
params: {
search: '',
role: 'TEACHER'
},
account: {}
}
},
mounted () {
api.getRoleList()
export default {
name: 'AccountDetail',
components: {
PageLayout
},
props: {
id: String
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
form: this.$form.createForm(this),
roleList: [],
teacherList: [],
params: {
search: '',
role: 'TEACHER'
},
account: {}
}
},
mounted () {
api.getRoleList()
.then(data => {
this.roleList = data
this.getData()
if (this.isEdit) {
api.getUser(this.id)
.then(data => {
this.account = data
})
}
})
},
methods: {
getData () {
api.getUserList(this.params)
.then(data => {
this.roleList = data
this.getData()
this.teacherList = data.results
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
if (this.isEdit) {
api.getUser(this.id)
api.updateUser(this.account.id, data)
.then(data => {
this.account = data
this.$notification.success({
message: '成功',
description: `更新用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
})
} else {
api.createUser(data)
.then(data => {
this.$notification.success({
message: '成功',
description: `新建用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
})
}
}
})
},
handleDelete () {
api.deleteUser(this.id)
.then(data => {
this.$notification.success({
message: '成功',
description: `删除用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
})
},
methods: {
getData () {
api.getUserList(this.params)
usernameValidator (rule, value, callback) {
if ((value && !this.isEdit) || (value && this.isEdit && (value !== this.account.username))) {
api.checkUsername(value)
.then(data => {
this.teacherList = data.results
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
if (this.isEdit) {
api.updateUser(this.account.id, data)
.then(data => {
this.$notification.success({
message: '成功',
description: `更新用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
})
if (data) {
const msg = 'username is duplicated'
callback(msg)
} else {
api.createUser(data)
.then(data => {
this.$notification.success({
message: '成功',
description: `新建用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
})
callback()
}
}
})
},
handleDelete () {
api.deleteUser(this.id)
.then(data => {
this.$notification.success({
message: '成功',
description: `删除用户成功`,
key: 'SUCCESS'
})
this.$router.push({ name: 'account' })
})
},
usernameValidator (rule, value, callback) {
if ((value && !this.isEdit) || (value && this.isEdit && (value !== this.account.username))) {
api.checkUsername(value)
.then(data => {
if (data) {
callback('username is duplicated')
return false
} else {
callback()
return true
}
})
} else {
callback()
return true
}
},
notAllNumberValidator (rule, value, callback) {
if (/^[0-9]+.?[0-9]*$/.test(value)) {
callback('password cant be all numbers')
return false
}
} else {
callback()
return true
},
samePasswordValidator (rule, value, callback) {
if (value && (value !== this.form.getFieldValue('password'))) {
callback('two passwords are different')
return false
}
callback()
return true
},
handleSearch (value) {
this.params.search = value
this.getData()
}
},
computed: {
isEdit () {
return this.$route.path.split('/').pop() === 'edit'
},
isTeacher () {
return this.$store.getters.role === 'TEACHER'
notAllNumberValidator (rule, value, callback) {
if (/^[0-9]+.?[0-9]*$/.test(value)) {
const msg = 'password cant be all numbers'
callback(msg)
}
callback()
},
samePasswordValidator (rule, value, callback) {
if (value && (value !== this.form.getFieldValue('password'))) {
const msg = 'two passwords are different'
callback(msg)
}
callback()
},
handleSearch (value) {
this.params.search = value
this.getData()
}
},
computed: {
isEdit () {
return this.$route.path.split('/').pop() === 'edit'
},
isTeacher () {
return this.$store.getters.role === 'TEACHER'
}
}
}
</script>
<style scoped lang="less">
</style>
</style>

View File

@ -56,7 +56,7 @@
style="width: 100%;"
@change="getData">
<a-select-option value="">房间筛选</a-select-option>
<a-select-option :value="item.id" v-for="item in roomList">{{item.name}}</a-select-option>
<a-select-option :value="item.id" v-for="item in roomList" :key="item.id">{{ item.name }}</a-select-option>
</a-select>
</a-col>
<a-col
@ -67,7 +67,7 @@
style="width: 100%;"
@change="getData">
<a-select-option value="">状态筛选</a-select-option>
<a-select-option :value="item.id" v-for="item in statusList">{{item.name}}</a-select-option>
<a-select-option :value="item.id" v-for="item in statusList" :key="item.id">{{ item.name }}</a-select-option>
</a-select>
</a-col>
<a-col
@ -141,7 +141,7 @@
:dataSource="bookingList"
:pagination="false">
<template slot="status" slot-scope="text, record, index">
{{statusList.find(item=>item.id === text).name}}
{{ statusList.find(item=>item.id === text).name }}
</template>
<template slot="operation" slot-scope="text, record, index">
<a-button @click="$router.push({name: 'bookingEdit', params: {id: record.id}})">编辑</a-button>
@ -151,7 +151,7 @@
</a-row>
<a-row>
<a-col class="item">
<span style="vertical-align: -5px"> {{count}} </span>
<span style="vertical-align: -5px"> {{ count }} </span>
<a-pagination
v-model="params.page"
@change="handelePage"
@ -165,194 +165,194 @@
</template>
<script>
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '8%'
},
{
title: '房间',
dataIndex: 'room.name',
width: '14%'
},
{
title: '日期',
dataIndex: 'date',
width: '12%'
},
{
title: '开始时间',
dataIndex: 'start_time',
width: '12%'
},
{
title: '结束时间',
dataIndex: 'end_time',
width: '12%'
},
{
title: '用户',
dataIndex: 'user.username',
width: '14%'
},
{
title: '状态',
dataIndex: 'status',
width: '14%',
scopedSlots: { customRender: 'status' }
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' }
}
]
import moment from 'moment'
import bookingAPI from '../../api/booking'
import roomAPI from '../../api/room'
import settingAPI from '../../api/setting'
import PageLayout from '../../components/page/PageLayout'
import moment from 'moment'
import bookingAPI from '../../api/booking'
import roomAPI from '../../api/room'
import settingAPI from '../../api/setting'
import PageLayout from '../../components/page/PageLayout'
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '8%'
},
{
title: '房间',
dataIndex: 'room.name',
width: '14%'
},
{
title: '日期',
dataIndex: 'date',
width: '12%'
},
{
title: '开始时间',
dataIndex: 'start_time',
width: '12%'
},
{
title: '结束时间',
dataIndex: 'end_time',
width: '12%'
},
{
title: '用户',
dataIndex: 'user.username',
width: '14%'
},
{
title: '状态',
dataIndex: 'status',
width: '14%',
scopedSlots: { customRender: 'status' }
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' }
}
]
export default {
name: 'Booking',
components: {
PageLayout
export default {
name: 'Booking',
components: {
PageLayout
},
data () {
return {
columns,
layout1: {
xs: { span: 24 },
sm: { span: 8 },
xl: { span: 8 }
},
layout2: {
xs: { span: 12 },
sm: { span: 8 },
xl: { span: 6 }
},
params: {
search: '',
page: 1,
ordering: 'date',
room: '',
status: '',
min_date: this.getDateRangeDefaultValue()[0].format('YYYY-MM-DD'),
max_date: this.getDateRangeDefaultValue()[1].format('YYYY-MM-DD'),
min_time: '',
max_time: ''
},
state: {
loading: false,
dateRange: this.getDateRangeDefaultValue(),
startTime: new moment(),
endTime: new moment()
},
roomList: [],
count: 0,
bookingList: [],
statusList: [],
setting: {}
}
},
mounted () {
roomAPI.getRoomList()
.then(data => {
this.roomList = data
})
settingAPI.getSettingDetail()
.then(data => {
this.params.min_time = data.start_time
this.params.max_time = data.end_time
data.start_time = new moment(data.start_time, 'HH:mm:ss')
this.state.startTime = data.start_time
data.end_time = new moment(data.end_time, 'HH:mm:ss')
this.state.endTime = data.end_time
this.setting = data
})
bookingAPI.getStatusList()
.then(data => {
this.statusList = data
})
this.getData()
},
methods: {
getData () {
this.state.loading = true
bookingAPI.getBookingList(this.params)
.then(data => {
this.bookingList = data.results
this.count = data.count
this.state.loading = false
})
},
data () {
return {
columns,
layout1: {
xs: { span: 24 },
sm: { span: 8 },
xl: { span: 8 },
},
layout2: {
xs: { span: 12 },
sm: { span: 8 },
xl: { span: 6 },
},
params: {
search: '',
page: 1,
ordering: 'date',
room: '',
status: '',
min_date: this.getDateRangeDefaultValue()[0].format('YYYY-MM-DD'),
max_date: this.getDateRangeDefaultValue()[1].format('YYYY-MM-DD'),
min_time: '',
max_time: ''
},
state: {
loading: false,
dateRange: this.getDateRangeDefaultValue(),
startTime: new moment(),
endTime: new moment()
},
roomList: [],
count: 0,
bookingList: [],
statusList: [],
setting: {}
handleClearParams () {
this.params = {
search: '',
page: 1,
ordering: 'date',
room: '',
status: '',
min_date: this.getDateRangeDefaultValue()[0].format('YYYY-MM-DD'),
max_date: this.getDateRangeDefaultValue()[1].format('YYYY-MM-DD'),
min_time: this.setting.start_time.format('HH:mm:ss'),
max_time: this.setting.end_time.format('HH:mm:ss')
}
this.state = {
loading: false,
dateRange: this.getDateRangeDefaultValue(),
startTime: this.setting.start_time,
endTime: this.setting.end_time
}
},
mounted () {
roomAPI.getRoomList()
.then(data => {
this.roomList = data
})
settingAPI.getSettingDetail()
.then(data => {
this.params.min_time = data.start_time
this.params.max_time = data.end_time
data.start_time = new moment(data.start_time, 'HH:mm:ss')
this.state.startTime = data.start_time
data.end_time = new moment(data.end_time, 'HH:mm:ss')
this.state.endTime = data.end_time
this.setting = data
})
bookingAPI.getStatusList()
.then(data=>{
this.statusList = data
})
this.getData()
},
methods: {
getData () {
this.state.loading = true
bookingAPI.getBookingList(this.params)
.then(data => {
this.bookingList = data.results
this.count = data.count
this.state.loading = false
})
},
handleClearParams () {
this.params = {
search: '',
page: 1,
ordering: 'date',
room: '',
status: '',
min_date: this.getDateRangeDefaultValue()[0].format('YYYY-MM-DD'),
max_date: this.getDateRangeDefaultValue()[1].format('YYYY-MM-DD'),
min_time: this.setting.start_time.format('HH:mm:ss'),
max_time: this.setting.end_time.format('HH:mm:ss')
handelePage (page) {
this.params.page = page
this.getData()
},
hangdleDateRangeChange (dates, dateStrings) {
this.params.min_date = dateStrings[0]
this.params.max_date = dateStrings[1]
this.getData()
},
handleStartTimeChange (time, timeString) {
this.params.min_time = timeString
this.getData()
},
handleEndTimeChange (time, timeString) {
this.params.max_time = timeString
this.getData()
},
getDateRangeDefaultValue () {
const now = new moment()
const aWeekLater = (new moment()).add(7, 'days')
return [now, aWeekLater]
},
getDisabledHours () {
const ret = []
const start_hour = this.setting.start_time.hour()
const end_hour = this.setting.end_time.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour || i > end_hour) {
ret.push(i)
}
this.state = {
loading: false,
dateRange: this.getDateRangeDefaultValue(),
startTime: this.setting.start_time,
endTime: this.setting.end_time
}
this.getData()
},
handelePage (page) {
this.params.page = page
this.getData()
},
hangdleDateRangeChange (dates, dateStrings) {
this.params.min_date = dateStrings[0]
this.params.max_date = dateStrings[1]
this.getData()
},
handleStartTimeChange (time, timeString) {
this.params.min_time = timeString
this.getData()
},
handleEndTimeChange (time, timeString) {
this.params.max_time = timeString
this.getData()
},
getDateRangeDefaultValue () {
let now = new moment()
let aWeekLater = (new moment()).add(7, 'days')
return [now, aWeekLater]
},
getDisabledHours () {
let ret = []
let start_hour = this.setting.start_time.hour()
let end_hour = this.setting.end_time.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour || i > end_hour) {
ret.push(i)
}
}
return ret
},
getDisabledMinutes (selectedHour) {
let ret = []
if (selectedHour === this.setting.end_time.hour()) {
ret.push(30)
}
return ret
}
return ret
},
getDisabledMinutes (selectedHour) {
const ret = []
if (selectedHour === this.setting.end_time.hour()) {
ret.push(30)
}
return ret
}
}
}
</script>
<style scoped lang="less">
.item {
margin: 8px 0;
}
</style>
</style>

View File

@ -11,47 +11,49 @@
label="ID"
v-if="isEdit"
v-bind="layout">
{{bookingEdit.id}}
{{ bookingEdit.id }}
</a-form-item>
<a-form-item
label="房间"
v-bind="layout">
<div v-if="isTeacher">{{isEdit ? bookingEdit.room.name : bookingCreate.room.name}}</div>
<div v-if="isTeacher">{{ isEdit ? bookingEdit.room.name : bookingCreate.room.name }}</div>
<router-link
v-else
:to="{ name: 'roomEdit', params: { id: bookingEdit.room.id} }">
{{isEdit ? bookingEdit.room.name : bookingCreate.room.name}}
{{ isEdit ? bookingEdit.room.name : bookingCreate.room.name }}
</router-link>
</a-form-item>
<a-form-item
label="日期"
v-bind="layout">
{{isEdit ? bookingEdit.date : bookingCreate.date}}
{{ isEdit ? bookingEdit.date : bookingCreate.date }}
</a-form-item>
<a-form-item
label="开始时间"
v-bind="layout">
{{isEdit ? bookingEdit.start_time : bookingCreate.start_time}}
{{ isEdit ? bookingEdit.start_time : bookingCreate.start_time }}
</a-form-item>
<a-form-item
label="结束时间"
v-bind="layout">
{{isEdit ? bookingEdit.end_time : bookingCreate.end_time}}
{{ isEdit ? bookingEdit.end_time : bookingCreate.end_time }}
</a-form-item>
<div v-if="bookingEdit">
<a-form-item
v-for="(item, index) in bookingEdit.seats" :key="index"
v-for="(item, index) in bookingEdit.seats"
:key="index"
:label="'座位' + (index + 1).toString()"
v-bind="layout">
<div>{{item.name}}</div>
<div>{{ item.name }}</div>
</a-form-item>
</div>
<div v-if="bookingCreate">
<a-form-item
v-for="(item, index) in bookingCreate.seats" :key="index"
v-for="(item, index) in bookingCreate.seats"
:key="index"
:label="'座位' + (index + 1).toString()"
v-bind="layout">
<div>{{item.name}}</div>
<div>{{ item.name }}</div>
</a-form-item>
</div>
<a-form-item
@ -66,38 +68,38 @@
@search="handleSearch"
v-if="!isEdit"
v-model="createBookingForm.user">
<a-select-option v-for="item in userList" :value="item.id">{{item.username}} {{item.email}}
<a-select-option v-for="item in userList" :value="item.id" :key="item.id">{{ item.username }} {{ item.email }}
</a-select-option>
</a-select>
<router-link
:to="{ name: 'accountEdit', params: { id: bookingEdit.user.id} }"
v-else>
{{bookingEdit.user.username}} {{bookingEdit.user.email}}
{{ bookingEdit.user.username }} {{ bookingEdit.user.email }}
</router-link>
</a-form-item>
<a-form-item
label="申请时间"
v-if="isEdit"
v-bind="layout">
{{bookingEdit.created_datetime}}
{{ bookingEdit.created_datetime }}
</a-form-item>
<a-form-item
label="到达时间"
v-if="isEdit"
v-bind="layout">
{{bookingEdit.arrive_time || '未签到'}}
{{ bookingEdit.arrive_time || '未签到' }}
</a-form-item>
<a-form-item
label="离开时间"
v-if="isEdit"
v-bind="layout">
{{bookingEdit.leave_time || '未签离'}}
{{ bookingEdit.leave_time || '未签离' }}
</a-form-item>
<a-form-item
label="状态"
v-if="isEdit"
v-bind="layout">
{{statusName}}
{{ statusName }}
</a-form-item>
<a-form-item v-if="!isEdit">
<a-button type="primary" @click="handleCreateSubmit" style="float: right">
@ -115,12 +117,12 @@
<a-form-item
label="取消人"
v-bind="layout">
{{bookingEdit.cancel_by ? bookingEdit.cancel_by.username : ''}}
{{ bookingEdit.cancel_by ? bookingEdit.cancel_by.username : '' }}
</a-form-item>
<a-form-item
label="取消时间"
v-bind="layout">
{{bookingEdit.cancel_datetime}}
{{ bookingEdit.cancel_datetime }}
</a-form-item>
<a-form-item
label="取消原因"
@ -130,9 +132,9 @@
v-decorator="[
'cancel_reason',
{rules: [
{max: 512, message: '最多512个字符'},
], validateTrigger: 'blur',
initialValue: bookingEdit.cancel_reason}
{max: 512, message: '最多512个字符'},
], validateTrigger: 'blur',
initialValue: bookingEdit.cancel_reason}
]">
</a-textarea>
</a-form-item>
@ -169,140 +171,140 @@
</template>
<script>
import PageLayout from '../../components/page/PageLayout'
import accountAPI from '../../api/account'
import bookingAPI from '../../api/booking'
import PageLayout from '../../components/page/PageLayout'
import accountAPI from '../../api/account'
import bookingAPI from '../../api/booking'
export default {
name: 'BookingDetail',
components: {
PageLayout
},
props: {
id: Number,
bookingCreate: Object
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
export default {
name: 'BookingDetail',
components: {
PageLayout
},
props: {
id: Number,
bookingCreate: Object
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
userList: [],
bookingEdit: {
room: {
id: '0',
name: ''
},
userList: [],
bookingEdit: {
room: {
id: '0',
name: ''
},
user: {
id: 0,
name: '',
email: ''
}
},
createBookingForm: {
user: this.$store.getters.userid,
},
cancelBookingForm: this.$form.createForm(this),
params: {
search: ''
},
statusList: []
}
},
beforeRouteEnter (to, from, next) {
if (from.path !== '/') {
next()
user: {
id: 0,
name: '',
email: ''
}
},
createBookingForm: {
user: this.$store.getters.userid
},
cancelBookingForm: this.$form.createForm(this),
params: {
search: ''
},
statusList: []
}
},
beforeRouteEnter (to, from, next) {
if (from.path !== '/') {
next()
} else {
next({ name: 'dashboard' })
}
},
mounted () {
this.getData()
},
methods: {
getData () {
if (this.isEdit) {
bookingAPI.getBooking(this.id)
.then(data => {
this.bookingEdit = data
})
bookingAPI.getStatusList()
.then(data => {
this.statusList = data
})
} else {
next({ name: 'dashboard' })
accountAPI.getUserList(this.getParams())
.then(data => {
this.userList = data.results
if (this.isTeacher) {
this.userList.push({
id: this.$store.getters.userid,
username: this.$store.getters.username,
email: ''
})
}
})
}
},
mounted () {
handleCreateSubmit () {
const form = {
user: this.createBookingForm.user,
room: this.bookingCreate.room.id,
seats: this.bookingCreate.seats.map((item) => {
return item.id
}),
date: this.bookingCreate.date,
start_time: this.bookingCreate.start_time,
end_time: this.bookingCreate.end_time
}
bookingAPI.createBooking(form)
.then(data => {
this.$notification.success({ message: '成功', description: '提交申请成功', key: 'SUCCESS' })
this.$router.push({ name: 'dashboard' })
})
},
handleCancelSubmit () {
this.cancelBookingForm.validateFields((error, data) => {
if (!error) {
bookingAPI.cancelBooking(this.bookingEdit.id, data)
.then(data => {
this.$notification.success({ message: '成功', description: '取消预约成功', key: 'SUCCESS' })
this.getData()
})
}
})
},
handleSearch (value) {
this.params.search = value
this.getData()
},
methods: {
getData () {
if (this.isEdit) {
bookingAPI.getBooking(this.id)
.then(data => {
this.bookingEdit = data
})
bookingAPI.getStatusList()
.then(data => {
this.statusList = data
})
} else {
accountAPI.getUserList(this.getParams)
.then(data => {
this.userList = data.results
if (this.isTeacher) {
this.userList.push({
id: this.$store.getters.userid,
username: this.$store.getters.username,
email: ''
})
}
})
}
},
handleCreateSubmit () {
let form = {
user: this.createBookingForm.user,
room: this.bookingCreate.room.id,
seats: this.bookingCreate.seats.map((item) => {
return item.id
}),
date: this.bookingCreate.date,
start_time: this.bookingCreate.start_time,
end_time: this.bookingCreate.end_time
}
bookingAPI.createBooking(form)
.then(data => {
this.$notification.success({ message: '成功', description: '提交申请成功', key: 'SUCCESS' })
this.$router.push({ name: 'dashboard' })
})
},
handleCancelSubmit () {
this.cancelBookingForm.validateFields((error, data) => {
if (!error) {
bookingAPI.cancelBooking(this.bookingEdit.id, data)
.then(data => {
this.$notification.success({ message: '成功', description: '取消预约成功', key: 'SUCCESS' })
this.getData()
})
}
})
},
handleSearch (value) {
this.params.search = value
this.getData()
getParams () {
if (this.isTeacher) {
this.params.teacher = this.$store.getters.userid
}
return this.params
}
},
computed: {
isEdit () {
return this.$route.path.split('/').pop() === 'edit'
},
computed: {
isEdit () {
return this.$route.path.split('/').pop() === 'edit'
},
isTeacher () {
return this.$store.getters.role === 'TEACHER'
},
statusName () {
let ret = ''
let result = this.statusList.find(item => item.id === this.bookingEdit.status)
if (result) {
ret = result.name
}
return ret
},
getParams () {
if (this.isTeacher) {
this.params.teacher = this.$store.getters.userid
}
return this.params
isTeacher () {
return this.$store.getters.role === 'TEACHER'
},
statusName () {
let ret = ''
const result = this.statusList.find(item => item.id === this.bookingEdit.status)
if (result) {
ret = result.name
}
return ret
}
}
}
</script>
<style scoped>
</style>
</style>

View File

@ -12,7 +12,7 @@
<a-select
v-model="form.room"
@change="handleRoomChange">
<a-select-option v-for="item in roomList" :key="item.id">{{item.name}}</a-select-option>
<a-select-option v-for="item in roomList" :key="item.id">{{ item.name }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item
@ -66,11 +66,13 @@
v-bind="layout">
<a-spin :spinning="this.state.loading">
<div class="seat" v-if="bookingStatusList">
<div class="seat-card"
v-for="item in bookingStatusList.seat_list"
@click="handleClick(item)"
:class="[{unselectable: item.is_booked}, {selected: seatSelected.indexOf(item.id) !== -1}]">
{{item.name}}
<div
class="seat-card"
v-for="item in bookingStatusList.seat_list"
:key="item.id"
@click="handleClick(item)"
:class="[{unselectable: item.is_booked}, {selected: seatSelected.indexOf(item.id) !== -1}]">
{{ item.name }}
</div>
<div>
<span style="vertical-align: -6px;">提示:
@ -94,14 +96,14 @@
:notFoundContent="null"
@search="handleSearch"
v-model="form.user">
<a-select-option v-for="item in userList" :value="item.id">{{item.username}} {{item.email}}
<a-select-option v-for="item in userList" :value="item.id" :key="item.id">{{ item.username }} {{ item.email }}
</a-select-option>
</a-select>
<a-select
v-else
:disabled="true"
v-model="form.user">
<a-select-option :value="form.user">{{$store.getters.username}}</a-select-option>
<a-select-option :value="form.user">{{ $store.getters.username }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
@ -119,192 +121,191 @@
</template>
<script>
import PageLayout from '../../components/page/PageLayout'
import DatePickerBar from '../dashboard/components/DatePickerBar'
import moment from 'moment'
import settingAPI from '../../api/setting'
import roomAPI from '../../api/room'
import dashboardAPI from '../../api/dashboard'
import accountAPI from '../../api/account'
import bookingAPI from '../../api/booking'
import PageLayout from '../../components/page/PageLayout'
import DatePickerBar from '../dashboard/components/DatePickerBar'
import moment from 'moment'
import settingAPI from '../../api/setting'
import roomAPI from '../../api/room'
import dashboardAPI from '../../api/dashboard'
import accountAPI from '../../api/account'
import bookingAPI from '../../api/booking'
export default {
name: 'BookingNew',
components: {
PageLayout,
DatePickerBar
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
state: {
loading: false,
},
form: {
date: new moment(),
startTime: new moment(),
endTime: new moment(),
room: null,
user: this.isTeacher ? this.$store.getters.userid : null
},
setting: {},
roomList: [],
bookingStatusList: [],
seatSelected: [],
userList: []
}
},
mounted () {
settingAPI.getSettingDetail()
export default {
name: 'BookingNew',
components: {
PageLayout,
DatePickerBar
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
state: {
loading: false
},
form: {
date: new moment(),
startTime: new moment(),
endTime: new moment(),
room: null,
user: this.isTeacher ? this.$store.getters.userid : null
},
setting: {},
roomList: [],
bookingStatusList: [],
seatSelected: [],
userList: []
}
},
mounted () {
settingAPI.getSettingDetail()
.then(data => {
data.start_time = new moment(data.start_time, 'HH:mm:ss')
data.end_time = new moment(data.end_time, 'HH:mm:ss')
this.form.startTime = data.start_time
this.form.endTime = data.end_time
this.setting = data
roomAPI.getRoomList()
.then(data => {
this.roomList = data
this.form.room = data[0].id
this.getData()
})
})
},
methods: {
getData () {
this.state.loading = true
dashboardAPI.getRoomBookingStatusDetail(this.form.room, {
date: this.form.date.format('YYYY-MM-DD'),
start_time: this.form.startTime.format('HH:mm:ss'),
end_time: this.form.endTime.format('HH:mm:ss')
})
.then(data => {
data.start_time = new moment(data.start_time, 'HH:mm:ss')
data.end_time = new moment(data.end_time, 'HH:mm:ss')
this.form.startTime = data.start_time
this.form.endTime = data.end_time
this.setting = data
roomAPI.getRoomList()
.then(data => {
this.roomList = data
this.form.room = data[0].id
this.getData()
})
this.seatSelected = []
this.bookingStatusList = data
if (!this.isTeacher) {
this.handleSearch('')
}
this.state.loading = false
})
},
methods: {
getData () {
this.state.loading = true
dashboardAPI.getRoomBookingStatusDetail(this.form.room, {
handleSubmit () {
if (this.form.user) {
const form = {
user: this.form.user,
room: this.form.room,
seats: this.seatSelected,
date: this.form.date.format('YYYY-MM-DD'),
start_time: this.form.startTime.format('HH:mm:ss'),
end_time: this.form.endTime.format('HH:mm:ss')
})
}
bookingAPI.createBooking(form)
.then(data => {
this.$notification.success({ message: '成功', description: '提交申请成功', key: 'SUCCESS' })
this.seatSelected = []
this.bookingStatusList = data
if (!this.isTeacher) {
this.handleSearch('')
}
this.state.loading = false
})
},
handleSubmit () {
if (this.form.user) {
let form = {
user: this.form.user,
room: this.form.room,
seats: this.seatSelected,
date: this.form.date.format('YYYY-MM-DD'),
start_time: this.form.startTime.format('HH:mm:ss'),
end_time: this.form.endTime.format('HH:mm:ss')
}
bookingAPI.createBooking(form)
.then(data => {
this.$notification.success({ message: '成功', description: '提交申请成功', key: 'SUCCESS' })
this.seatSelected = []
this.getData()
})
} else {
this.$notification.error({ message: '错误', description: '请选择预约人', key: 'ERROR' })
}
},
handleRoomChange (room) {
this.getData()
},
handleDateChange (date) {
this.getData()
},
handleStartTimeChange (time, timeString) {
this.getData()
},
handleEndTimeChange (time, timeString) {
this.getData()
},
getDisabledDate (currentDate) {
return currentDate > this.maxDate || currentDate <= new moment().subtract(1, 'days')
},
getStartTimeDisabledHours () {
let ret = []
let start_hour = this.setting.start_time.hour()
let end_hour = this.form.endTime.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour || i > end_hour) {
ret.push(i)
}
}
if (this.form.endTime.minute() === 0) {
ret.push(end_hour)
}
return ret
},
getStartTimeDisabledMinutes (selectedHour) {
let ret = []
if (selectedHour === this.form.endTime.hour()) {
ret.push(30)
}
return ret
},
getEndTimeDisabledHours () {
let ret = []
let start_hour = this.form.startTime.hour()
let end_hour = this.setting.end_time.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour || i > end_hour) {
ret.push(i)
}
}
if (this.form.startTime.minute() === 30 || this.setting.booking_interval === 60) {
ret.push(start_hour)
}
return ret
},
getEndTimeDisabledMinutes (selectedHour) {
let ret = []
if (selectedHour === this.form.startTime.hour()) {
ret.push(0)
}
if (selectedHour === this.setting.end_time.hour()) {
ret.push(30)
}
return ret
},
handleClick (item) {
if (!item.is_booked) {
let index = this.seatSelected.indexOf(item.id)
if (index === -1) {
this.seatSelected.push(item.id)
} else {
this.seatSelected.splice(index, 1)
}
}
},
handleSearch (value) {
accountAPI.getUserList({
search: value,
role: 'TEACHER'
})
.then(data => {
this.userList = data.results
this.getData()
})
} else {
this.$notification.error({ message: '错误', description: '请选择预约人', key: 'ERROR' })
}
},
computed: {
maxDate () {
return new moment().add(this.setting.pre_booking_interval_day, 'days')
},
isTeacher () {
return this.$store.getters.role === 'TEACHER'
handleRoomChange (room) {
this.getData()
},
handleDateChange (date) {
this.getData()
},
handleStartTimeChange (time, timeString) {
this.getData()
},
handleEndTimeChange (time, timeString) {
this.getData()
},
getDisabledDate (currentDate) {
return currentDate > this.maxDate || currentDate <= new moment().subtract(1, 'days')
},
getStartTimeDisabledHours () {
const ret = []
const start_hour = this.setting.start_time.hour()
const end_hour = this.form.endTime.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour || i > end_hour) {
ret.push(i)
}
}
if (this.form.endTime.minute() === 0) {
ret.push(end_hour)
}
return ret
},
getStartTimeDisabledMinutes (selectedHour) {
const ret = []
if (selectedHour === this.form.endTime.hour()) {
ret.push(30)
}
return ret
},
getEndTimeDisabledHours () {
const ret = []
const start_hour = this.form.startTime.hour()
const end_hour = this.setting.end_time.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour || i > end_hour) {
ret.push(i)
}
}
if (this.form.startTime.minute() === 30 || this.setting.booking_interval === 60) {
ret.push(start_hour)
}
return ret
},
getEndTimeDisabledMinutes (selectedHour) {
const ret = []
if (selectedHour === this.form.startTime.hour()) {
ret.push(0)
}
if (selectedHour === this.setting.end_time.hour()) {
ret.push(30)
}
return ret
},
handleClick (item) {
if (!item.is_booked) {
const index = this.seatSelected.indexOf(item.id)
if (index === -1) {
this.seatSelected.push(item.id)
} else {
this.seatSelected.splice(index, 1)
}
}
},
handleSearch (value) {
accountAPI.getUserList({
search: value,
role: 'TEACHER'
})
.then(data => {
this.userList = data.results
})
}
},
computed: {
maxDate () {
return new moment().add(this.setting.pre_booking_interval_day, 'days')
},
isTeacher () {
return this.$store.getters.role === 'TEACHER'
}
}
}
</script>
<style scoped lang="less">
@import "../../../public/color";
.seat {
.seat-card {
text-align: center;
@ -354,4 +355,4 @@
background-color: #fff;
}
}
</style>
</style>

View File

@ -13,11 +13,11 @@
:key="item.id"
@click="handleJump(item)">
<a-card class="room-status-card">
<span class="name">{{item.name}}</span>
<span class="name">{{ item.name }}</span>
<div v-if="item.count">
<span class="big">{{item.booking_count}}</span>
<span class="big">{{ item.booking_count }}</span>
<span> ,</span>
<span> {{((1 - item.booked_count/item.count)*100).toFixed(2)}} % 剩余</span>
<span> {{ ((1 - item.booked_count/item.count)*100).toFixed(2) }} % 剩余</span>
</div>
<div v-else>
<span class="big">无座位</span>
@ -27,10 +27,10 @@
</a-row>
</a-spin>
<a-row>
<span> {{bookingCountTotal}} , {{((1 - bookedCountTotal/countTotal)*100).toFixed(2)}} % 剩余</span>
<span> {{ bookingCountTotal }} , {{ ((1 - bookedCountTotal/countTotal)*100).toFixed(2) }} % 剩余</span>
<a-tooltip placement="top" title="每分钟自动">
<div style="float: right;">
<span style="vertical-align: -2px;">更新时间: {{updateTime}} 自动更新 </span>
<span style="vertical-align: -2px;">更新时间: {{ updateTime }} 自动更新 </span>
<a-switch checkedChildren="开" unCheckedChildren="关" defaultChecked @change="handleAutoChange"></a-switch>
</div>
</a-tooltip>
@ -40,76 +40,76 @@
</template>
<script>
import PageLayout from '../../components/page/PageLayout'
import moment from 'moment'
import api from '../../api/dashboard'
import DatePickerBar from './components/DatePickerBar'
import PageLayout from '../../components/page/PageLayout'
import moment from 'moment'
import api from '../../api/dashboard'
import DatePickerBar from './components/DatePickerBar'
export default {
name: 'Dashboard',
components: {
PageLayout,
DatePickerBar
export default {
name: 'Dashboard',
components: {
PageLayout,
DatePickerBar
},
data () {
return {
params: {
date: new moment().format('YYYY-MM-DD')
},
status: {
loading: false
},
updateTime: '',
roomBookingStatusList: [],
countTotal: 0,
bookedCountTotal: 0,
bookingCountTotal: 0,
intervalID: null
}
},
mounted () {
this.getData()
this.handleAutoChange(true)
},
destroyed () {
this.handleAutoChange(false)
},
methods: {
getData () {
this.status.loading = true
api.getRoomBookingStatus(this.params)
.then(data => {
this.roomBookingStatusList = data.results
this.countTotal = data.count_total
this.bookedCountTotal = data.booked_count_total
this.bookingCountTotal = data.booking_count_total
this.updateTime = new moment().format('YYYY-MM-DD hh:mm:ss')
this.status.loading = false
})
},
data () {
return {
params: {
date: new moment().format('YYYY-MM-DD')
},
status: {
loading: false
},
updateTime: '',
roomBookingStatusList: [],
countTotal: 0,
bookedCountTotal: 0,
bookingCountTotal: 0,
intervalID: null
}
},
mounted () {
handleDateChange (date) {
this.params.date = date.format('YYYY-MM-DD')
this.getData()
this.handleAutoChange(true)
},
destroyed () {
this.handleAutoChange(false)
},
methods: {
getData () {
this.status.loading = true
api.getRoomBookingStatus(this.params)
.then(data => {
this.roomBookingStatusList = data.results
this.countTotal = data.count_total
this.bookedCountTotal = data.booked_count_total
this.bookingCountTotal = data.booking_count_total
this.updateTime = new moment().format('YYYY-MM-DD hh:mm:ss')
this.status.loading = false
})
},
handleDateChange (date) {
this.params.date = date.format('YYYY-MM-DD')
this.getData()
},
handleAutoChange (checked) {
if (checked) {
this.intervalID = setInterval(this.getData, 1000 * 60)
} else {
clearInterval(this.intervalID)
}
},
getDefaultDate () {
return new moment()
},
handleJump (item) {
if (item.count) {
this.$router.push({ name: 'dashboardDetail', params: { id: item.id, date: this.params.date } })
} else {
this.$router.push({ name: 'seat', params: { id: item.id } })
}
handleAutoChange (checked) {
if (checked) {
this.intervalID = setInterval(this.getData, 1000 * 60)
} else {
clearInterval(this.intervalID)
}
},
getDefaultDate () {
return new moment()
},
handleJump (item) {
if (item.count) {
this.$router.push({ name: 'dashboardDetail', params: { id: item.id, date: this.params.date } })
} else {
this.$router.push({ name: 'seat', params: { id: item.id } })
}
}
}
}
</script>
<style scoped lang="less">

View File

@ -11,33 +11,33 @@
<th
v-for="(item, index) in room.seat_list[0].booking_status_list"
:key="index">
{{item.start_time.slice(0, 5)}} {{item.end_time.slice(0, 5)}}
{{ item.start_time.slice(0, 5) }} {{ item.end_time.slice(0, 5) }}
</th>
</tr>
<tr
v-for="(seat, seatIndex) in room.seat_list"
:key="seat.id">
<td>
{{seat.name}}
{{ seat.name }}
</td>
<td
v-for="(item, itemIndex) in seat.booking_status_list"
:key="itemIndex"
:class="[
{
'booked-cell': item.booking,
'selected-cell': selected[seatIndex.toString() + ' ' + itemIndex.toString()],
},
'cell'
]">
{
'booked-cell': item.booking,
'selected-cell': selected[seatIndex.toString() + ' ' + itemIndex.toString()],
},
'cell'
]">
<a-popover placement="top" v-if="item.booking">
<div style="width: 100%; height: 20px;"></div>
<template slot="title">预约 {{item.booking.id}}</template>
<template slot="title">预约 {{ item.booking.id }}</template>
<template slot="content">
<div>用户: {{item.booking.user.username}}</div>
<div>预约时间: {{item.booking.start_time}}-{{item.booking.end_time}}</div>
<div>到达时间: {{item.booking.arrive_time}}</div>
<div>离开时间: {{item.booking.leave_time}}</div>
<div>用户: {{ item.booking.user.username }}</div>
<div>预约时间: {{ item.booking.start_time }}-{{ item.booking.end_time }}</div>
<div>到达时间: {{ item.booking.arrive_time }}</div>
<div>离开时间: {{ item.booking.leave_time }}</div>
<router-link :to="{name: 'bookingEdit', params:{id: item.booking.id}}">查看</router-link>
</template>
</a-popover>
@ -68,10 +68,10 @@
</a-button>
</a-row>
<a-row>
<span>总计: {{room.booked_count_total}} / {{room.count_total}}</span>
<span>总计: {{ room.booked_count_total }} / {{ room.count_total }}</span>
<a-tooltip placement="top" title="每分钟自动">
<div style="float: right;">
<span style="vertical-align: -2px;">更新时间: {{updateTime}} 自动更新 </span>
<span style="vertical-align: -2px;">更新时间: {{ updateTime }} 自动更新 </span>
<a-switch
checkedChildren="开"
unCheckedChildren="关"
@ -86,142 +86,142 @@
</template>
<script>
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/dashboard'
import DatePickerBar from './components/DatePickerBar'
import moment from 'moment'
import _ from 'lodash'
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/dashboard'
import DatePickerBar from './components/DatePickerBar'
import moment from 'moment'
import _ from 'lodash'
export default {
name: 'DashboardDetail',
components: {
PageLayout,
DatePickerBar
export default {
name: 'DashboardDetail',
components: {
PageLayout,
DatePickerBar
},
props: {
id: String,
date: String
},
data () {
return {
params: {
date: this.date ? new moment(this.date).format('YYYY-MM-DD') : new moment().format('YYYY-MM-DD')
},
status: {
loading: false
},
room: {},
updateTime: '',
intervalID: null,
selected: {},
firstSelected: null
}
},
mounted () {
this.getData()
},
methods: {
getData () {
this.status.loading = true
api.getRoomBookingStatusDetail(this.id, this.params)
.then(data => {
this.room = data
this.updateTime = new moment().format('YYYY-MM-DD hh:mm:ss')
this.selected = {}
this.firstSelected = null
this.status.loading = false
})
},
props: {
id: String,
date: String
},
data () {
return {
params: {
date: this.date ? new moment(this.date).format('YYYY-MM-DD') : new moment().format('YYYY-MM-DD')
},
status: {
loading: false
},
room: {},
updateTime: '',
intervalID: null,
selected: {},
firstSelected: null
}
},
mounted () {
handleDateChange (date) {
this.params.date = date.format('YYYY-MM-DD')
this.getData()
},
methods: {
getData () {
this.status.loading = true
api.getRoomBookingStatusDetail(this.id, this.params)
.then(data => {
this.room = data
this.updateTime = new moment().format('YYYY-MM-DD hh:mm:ss')
this.selected = {}
this.firstSelected = null
this.status.loading = false
})
},
handleDateChange (date) {
this.params.date = date.format('YYYY-MM-DD')
this.getData()
},
getDefaultDate () {
return new moment(this.date)
},
handleAutoChange (checked) {
if (checked) {
this.intervalID = setInterval(this.getData, 1000 * 60)
getDefaultDate () {
return new moment(this.date)
},
handleAutoChange (checked) {
if (checked) {
this.intervalID = setInterval(this.getData, 1000 * 60)
} else {
clearInterval(this.intervalID)
}
},
handleSelect (x, y) {
if (this.isSelectable) {
let newData = {}
if (!this.firstSelected) {
this.firstSelected = { x, y }
newData[x.toString() + ' ' + y.toString()] = true
} else {
clearInterval(this.intervalID)
}
},
handleSelect (x, y) {
if (this.isSelectable) {
let newData = {}
if (!this.firstSelected) {
this.firstSelected = { x, y }
newData[x.toString() + ' ' + y.toString()] = true
} else {
if (this.firstSelected.x === x) {
newData = _.cloneDeep(this.selected)
if (this.firstSelected.y === y) {
this.selected = {}
newData = {}
} else if (this.firstSelected.y < y) {
for (let i = this.firstSelected.y; i <= y; i++) {
if (!this.room.seat_list[x].booking_status_list[i].booking) {
newData[x.toString() + ' ' + i.toString()] = true
} else {
this.selected = {}
newData = {}
break
}
}
} else {
for (let i = y; i <= this.firstSelected.y; i++) {
if (!this.room.seat_list[x].booking_status_list[i].booking) {
newData[x.toString() + ' ' + i.toString()] = true
} else {
this.selected = {}
newData = {}
break
}
if (this.firstSelected.x === x) {
newData = _.cloneDeep(this.selected)
if (this.firstSelected.y === y) {
this.selected = {}
newData = {}
} else if (this.firstSelected.y < y) {
for (let i = this.firstSelected.y; i <= y; i++) {
if (!this.room.seat_list[x].booking_status_list[i].booking) {
newData[x.toString() + ' ' + i.toString()] = true
} else {
this.selected = {}
newData = {}
break
}
}
} else {
this.selected = {}
for (let i = y; i <= this.firstSelected.y; i++) {
if (!this.room.seat_list[x].booking_status_list[i].booking) {
newData[x.toString() + ' ' + i.toString()] = true
} else {
this.selected = {}
newData = {}
break
}
}
}
this.firstSelected = null
} else {
this.selected = {}
}
this.selected = newData
this.firstSelected = null
}
},
handleCreate () {
let key_list = Object.keys(this.selected)
let x = key_list[0].split(' ')[0]
let key_y_list = key_list.map((item) => {
return item.split(' ')[1]
})
let y1 = Math.min.apply(null, key_y_list)
let y2 = Math.max.apply(null, key_y_list)
let seats = []
let seat = this.room.seat_list[x]
let start_time = this.room.seat_list[x].booking_status_list[y1].start_time
let end_time = this.room.seat_list[x].booking_status_list[y2].end_time
seats.push({
id: seat.id,
name: seat.name
})
let bookingCreate = {
date: this.params.date,
room: {
id: this.room.id,
name: this.room.name
},
seats,
start_time,
end_time
}
this.$router.push({ name: 'bookingCreate', params: { bookingCreate } })
this.selected = newData
}
},
computed: {
isSelectable () {
return new moment().format('YYYY-MM-DD') <= this.params.date
handleCreate () {
const key_list = Object.keys(this.selected)
const x = key_list[0].split(' ')[0]
const key_y_list = key_list.map((item) => {
return item.split(' ')[1]
})
const y1 = Math.min.apply(null, key_y_list)
const y2 = Math.max.apply(null, key_y_list)
const seats = []
const seat = this.room.seat_list[x]
const start_time = this.room.seat_list[x].booking_status_list[y1].start_time
const end_time = this.room.seat_list[x].booking_status_list[y2].end_time
seats.push({
id: seat.id,
name: seat.name
})
const bookingCreate = {
date: this.params.date,
room: {
id: this.room.id,
name: this.room.name
},
seats,
start_time,
end_time
}
this.$router.push({ name: 'bookingCreate', params: { bookingCreate } })
}
},
computed: {
isSelectable () {
return new moment().format('YYYY-MM-DD') <= this.params.date
}
}
}
</script>
<style scoped lang="less">
@ -233,7 +233,6 @@
.booking-status-timetable {
th, td {
border: solid 1px rgba(128, 128, 128, 0.3);
padding: 5px 2px;
@ -282,4 +281,4 @@
background-color: #fff;
}
}
</style>
</style>

View File

@ -5,8 +5,8 @@
class="date"
v-if="!status.showDatePicker"
@click="status.showDatePicker = true">
{{dateString}}
</span>
{{ dateString }}
</span>
<a-date-picker
v-if="status.showDatePicker"
class="date-picker"
@ -28,63 +28,63 @@
</template>
<script>
import settingAPI from '../../../api/setting'
import moment from 'moment'
import settingAPI from '../../../api/setting'
import moment from 'moment'
export default {
name: 'DatePickerBar',
props: {
defaultDate: Object
export default {
name: 'DatePickerBar',
props: {
defaultDate: Object
},
data () {
return {
status: {
showDatePicker: false
},
date: this.defaultDate,
dateString: '',
setting: {}
}
},
mounted () {
settingAPI.getSettingDetail()
.then(data => {
this.setting = data
})
this.getDateString()
},
methods: {
getDateString () {
this.dateString = this.date.format('YYYY 年 MM 月 DD 日')
},
data () {
return {
status: {
showDatePicker: false
},
date: this.defaultDate,
dateString: '',
setting: {}
}
},
mounted () {
settingAPI.getSettingDetail()
.then(data => {
this.setting = data
})
handleDateChange (date) {
this.date = date
this.getDateString()
this.$emit('change', this.date)
},
methods: {
getDateString () {
this.dateString = this.date.format('YYYY 年 MM 月 DD 日')
},
handleDateChange (date) {
this.date = date
this.getDateString()
this.$emit('change', this.date)
},
handleDatePickerClose (status) {
if (!status) this.status.showDatePicker = false
},
handlePrevious () {
this.date.subtract(1, 'days')
this.getDateString()
this.$emit('change', this.date)
},
handleNext () {
this.date.add(1, 'days')
this.getDateString()
this.$emit('change', this.date)
},
disabledDate (currentDate) {
return currentDate > this.maxDate
}
handleDatePickerClose (status) {
if (!status) this.status.showDatePicker = false
},
computed: {
maxDate () {
return new moment().add(this.setting.pre_booking_interval_day, 'days')
}
handlePrevious () {
this.date.subtract(1, 'days')
this.getDateString()
this.$emit('change', this.date)
},
handleNext () {
this.date.add(1, 'days')
this.getDateString()
this.$emit('change', this.date)
},
disabledDate (currentDate) {
return currentDate > this.maxDate
}
},
computed: {
maxDate () {
return new moment().add(this.setting.pre_booking_interval_day, 'days')
}
}
}
</script>
<style scoped lang="less">
@ -108,4 +108,4 @@
float: right;
}
}
</style>
</style>

View File

@ -28,7 +28,7 @@ export default {
return {
config: types
}
},
}
}
</script>

View File

@ -4,14 +4,14 @@
</template>
<script>
import PageLayout from '../../components/page/PageLayout'
import PageLayout from '../../components/page/PageLayout'
export default {
name: 'Help',
components: {
PageLayout
}
export default {
name: 'Help',
components: {
PageLayout
}
}
</script>
<style scoped lang="less">

View File

@ -41,92 +41,92 @@
</template>
<script>
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '30%'
},
{
title: '名称',
dataIndex: 'name',
width: '10%'
},
{
title: '座位数量',
dataIndex: 'seat_count',
width: '10%'
},
{
title: '说明',
dataIndex: 'description',
width: '20%'
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' },
width: '30%'
}
]
import api from '../../api/room'
import PageLayout from '../../components/page/PageLayout'
import { fetchAPIBase } from '../../utils/fetch'
import { downloadFile } from '../../utils/util'
import api from '../../api/room'
import PageLayout from '../../components/page/PageLayout'
import { fetchAPIBase } from '../../utils/fetch'
import { downloadFile } from '../../utils/util'
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '30%'
},
{
title: '名称',
dataIndex: 'name',
width: '10%'
},
{
title: '座位数量',
dataIndex: 'seat_count',
width: '10%'
},
{
title: '说明',
dataIndex: 'description',
width: '20%'
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' },
width: '30%'
}
]
export default {
name: 'Room',
components: {
PageLayout
},
data () {
return {
columns,
state: {
loading: false,
},
roomList: [],
}
},
mounted () {
this.getData()
},
methods: {
getData () {
this.state.loading = true
api.getRoomList()
.then(data => {
this.roomList = data
this.state.loading = false
})
.catch(() => {
this.state.loading = false
})
export default {
name: 'Room',
components: {
PageLayout
},
data () {
return {
columns,
state: {
loading: false
},
handleDownload (record) {
const qrcode = {
type: 'room',
name: record.name,
id: record.id
}
fetchAPIBase('qrcode/', 'get', null, qrcode)
.then(res => {
return res.blob()
})
.then(data => {
downloadFile(data, `房间-${record.name}.png`)
})
.catch(error => {
this.$notification({ message: '下载失败', description: error, key: 'ERROR' })
})
roomList: []
}
},
mounted () {
this.getData()
},
methods: {
getData () {
this.state.loading = true
api.getRoomList()
.then(data => {
this.roomList = data
this.state.loading = false
})
.catch(() => {
this.state.loading = false
})
},
handleDownload (record) {
const qrcode = {
type: 'room',
name: record.name,
id: record.id
}
fetchAPIBase('qrcode/', 'get', null, qrcode)
.then(res => {
return res.blob()
})
.then(data => {
downloadFile(data, `房间-${record.name}.png`)
})
.catch(error => {
this.$notification({ message: '下载失败', description: error, key: 'ERROR' })
})
}
}
}
</script>
<style scoped lang="less">
.item {
margin-bottom: 16px;
}
</style>
</style>

View File

@ -14,7 +14,7 @@
v-if="isEdit"
label="ID"
v-bind="layout">
{{room.id}}
{{ room.id }}
</a-form-item>
<a-form-item
label="名称"
@ -28,7 +28,7 @@
validateTrigger: 'blur',
initialValue: isEdit ? room.name : null
}
]">
]">
</a-input>
</a-form-item>
<a-form-item
@ -46,7 +46,7 @@
],
validateTrigger: 'blur'
}
]">
]">
</a-input>
</a-form-item>
<a-form-item
@ -58,7 +58,7 @@
v-decorator="[
'description',
{initialValue: isEdit ? room.description: ''}
]">
]">
</a-textarea>
</a-form-item>
<a-form-item>
@ -85,76 +85,76 @@
</template>
<script>
import api from '../../api/room'
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/room'
import PageLayout from '../../components/page/PageLayout'
export default {
name: 'RoomDetail',
components: {
PageLayout
},
props: {
id: String,
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
form: this.$form.createForm(this),
room: {}
}
},
mounted () {
if (this.isEdit) {
this.getData()
}
},
methods: {
getData () {
api.getRoom(this.id)
.then(data => {
this.room = data
})
export default {
name: 'RoomDetail',
components: {
PageLayout
},
props: {
id: String
},
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
if (this.isEdit) {
api.updateRoom(this.id, data)
.then(data => {
this.$notification.success({ message: '成功', description: '成功修改房间', key: 'SUCCESS' })
this.$router.push({ name: 'room' })
})
} else {
api.createRoom(data)
.then(data => {
this.$notification.success({ message: '成功', description: '成功新建房间', key: 'SUCCESS' })
this.$router.push({ name: 'room' })
})
}
}
form: this.$form.createForm(this),
room: {}
}
},
mounted () {
if (this.isEdit) {
this.getData()
}
},
methods: {
getData () {
api.getRoom(this.id)
.then(data => {
this.room = data
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
if (this.isEdit) {
api.updateRoom(this.id, data)
.then(data => {
this.$notification.success({ message: '成功', description: '成功修改房间', key: 'SUCCESS' })
this.$router.push({ name: 'room' })
})
} else {
api.createRoom(data)
.then(data => {
this.$notification.success({ message: '成功', description: '成功新建房间', key: 'SUCCESS' })
this.$router.push({ name: 'room' })
})
}
)
},
handleDelete () {
api.deleteRoom(this.id)
.then(data => {
this.$notification.success({ message: '成功', description: '成功删除房间', key: 'SUCCESS' })
this.$router.push({ name: 'room' })
})
},
},
computed: {
isEdit () {
return this.$route.path.split('/').pop() === 'edit'
}
}
)
},
handleDelete () {
api.deleteRoom(this.id)
.then(data => {
this.$notification.success({ message: '成功', description: '成功删除房间', key: 'SUCCESS' })
this.$router.push({ name: 'room' })
})
}
},
computed: {
isEdit () {
return this.$route.path.split('/').pop() === 'edit'
}
}
}
</script>
<style scoped lang="less">
</style>
</style>

View File

@ -7,7 +7,7 @@
:xs="{span: 12}"
:sm="{span: 6}"
:xl="{span: 4}">
<span style="vertical-align: -5px"> {{count||0}} </span>
<span style="vertical-align: -5px"> {{ count||0 }} </span>
</a-col>
<a-col
class="item"
@ -70,130 +70,130 @@
</template>
<script>
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '40%'
},
{
title: '名称',
dataIndex: 'name',
width: '20%'
},
{
title: '创建时间',
dataIndex: 'created_datetime',
width: '20%'
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' },
width: '20%'
}
]
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/room'
import { fetchAPIBase } from '../../utils/fetch'
import { downloadFile } from '../../utils/util'
import PageLayout from '../../components/page/PageLayout'
import api from '../../api/room'
import { fetchAPIBase } from '../../utils/fetch'
import { downloadFile } from '../../utils/util'
const columns = [
{
title: '编号',
dataIndex: 'id',
width: '40%'
},
{
title: '名称',
dataIndex: 'name',
width: '20%'
},
{
title: '创建时间',
dataIndex: 'created_datetime',
width: '20%'
},
{
title: '操作',
dataIndex: 'operation',
scopedSlots: { customRender: 'operation' },
width: '20%'
}
]
export default {
name: 'Seat',
components: {
PageLayout
},
props: {
id: String
},
data () {
return {
columns,
params: {
room__id: this.id
},
state: {
loading: false,
},
room: {},
seatList: [],
count: 0,
}
},
mounted () {
this.getData()
},
methods: {
getData () {
this.state.loading = true
api.getRoom(this.id)
.then(data => {
this.room = data
api.getSeatList(this.params)
.then(data => {
this.seatList = data
this.count = data.length
this.state.loading = false
})
.catch(() => {
this.state.loading = false
})
})
.catch(() => {
this.state.loading = false
})
export default {
name: 'Seat',
components: {
PageLayout
},
props: {
id: String
},
data () {
return {
columns,
params: {
room__id: this.id
},
handleCreate () {
let new_seat = {
name: (this.count + 1).toString(),
room: this.id
}
api.createSeat(new_seat)
.then(data => {
this.$notification.success({ message: '成功', description: `成功新建座位`, key: 'SUCCESS' })
state: {
loading: false
},
room: {},
seatList: [],
count: 0
}
},
mounted () {
this.getData()
},
methods: {
getData () {
this.state.loading = true
api.getRoom(this.id)
.then(data => {
this.room = data
api.getSeatList(this.params)
.then(data => {
this.seatList = data
this.count = data.length
this.state.loading = false
})
.catch(() => {
this.state.loading = false
})
})
.catch(() => {
this.state.loading = false
})
},
handleCreate () {
const new_seat = {
name: (this.count + 1).toString(),
room: this.id
}
api.createSeat(new_seat)
.then(data => {
this.$notification.success({ message: '成功', description: `成功新建座位`, key: 'SUCCESS' })
this.getData()
})
},
handleDelete () {
if (this.count) {
const last = this.seatList[0]
api.deleteSeat(last.id)
.then(() => {
this.$notification.success({ message: '成功', description: `成功删除座位`, key: 'SUCCESS' })
this.getData()
})
},
handleDelete () {
if (this.count) {
let last = this.seatList[0]
api.deleteSeat(last.id)
.then(() => {
this.$notification.success({ message: '成功', description: `成功删除座位`, key: 'SUCCESS' })
this.getData()
})
} else {
this.$notification.error({ message: '错误', description: '暂无座位', key: 'ERROR' })
}
},
handleDownload (record) {
const qrcode = {
type: 'seat',
name: record.name,
id: record.id
}
fetchAPIBase('qrcode/', 'get', null, qrcode)
.then(res => {
return res.blob()
})
.then(data => {
downloadFile(data, `座位-${record.name}.png`)
})
},
handleDownloadAll () {
fetchAPIBase('qrcode/all/', 'get', null, { room: this.room.id })
.then(res => {
return res.blob()
})
.then(data => {
downloadFile(data, `房间-${this.room.name}.zip`)
})
.catch(error => {
this.$notification({ message: '下载失败', description: error, key: 'ERROR' })
})
} else {
this.$notification.error({ message: '错误', description: '暂无座位', key: 'ERROR' })
}
},
handleDownload (record) {
const qrcode = {
type: 'seat',
name: record.name,
id: record.id
}
fetchAPIBase('qrcode/', 'get', null, qrcode)
.then(res => {
return res.blob()
})
.then(data => {
downloadFile(data, `座位-${record.name}.png`)
})
},
handleDownloadAll () {
fetchAPIBase('qrcode/all/', 'get', null, { room: this.room.id })
.then(res => {
return res.blob()
})
.then(data => {
downloadFile(data, `房间-${this.room.name}.zip`)
})
.catch(error => {
this.$notification({ message: '下载失败', description: error, key: 'ERROR' })
})
}
}
}
</script>
<style scoped lang="less">
@ -201,4 +201,4 @@
margin-top: 8px;
margin-bottom: 8px;
}
</style>
</style>

View File

@ -14,14 +14,14 @@
type="number"
addonAfter="天"
v-decorator="[
'pre_booking_interval_day',
{rules: [
{required: true, message: '请输入提前预约间隔'},
{pattern: /^[1-9]\d*$/, message: '请输入大于1的正整数'},
],
validateTrigger: 'blur',
initialValue: setting.pre_booking_interval_day}
]">
'pre_booking_interval_day',
{rules: [
{required: true, message: '请输入提前预约间隔'},
{pattern: /^[1-9]\d*$/, message: '请输入大于1的正整数'},
],
validateTrigger: 'blur',
initialValue: setting.pre_booking_interval_day}
]">
</a-input>
</a-form-item>
<a-form-item
@ -29,13 +29,13 @@
v-bind="layout">
<a-radio-group
v-decorator="[
'booking_interval',
{rules: [
{required: true, message: '请选择预约间隔'},
{validator: validateInterval, message: '请确保预约间隔和开始结束时间匹配'}
], validateTrigger: 'blur',
initialValue: setting.booking_interval}
]">
'booking_interval',
{rules: [
{required: true, message: '请选择预约间隔'},
{validator: validateInterval, message: '请确保预约间隔和开始结束时间匹配'}
], validateTrigger: 'blur',
initialValue: setting.booking_interval}
]">
<a-radio :value="30">30分钟</a-radio>
<a-radio :value="60">60分钟</a-radio>
</a-radio-group>
@ -53,10 +53,10 @@
:allowEmpty="false"
style="width: 100%;"
v-decorator="[
'start_time',{
rules: [{ type: 'object', required: true, message: '请输入开始时间' }],
initialValue: setting.start_time
}]"></a-time-picker>
'start_time',{
rules: [{ type: 'object', required: true, message: '请输入开始时间' }],
initialValue: setting.start_time
}]"></a-time-picker>
</a-form-item>
<a-form-item
label="结束时间"
@ -71,10 +71,10 @@
:allowEmpty="false"
style="width: 100%;"
v-decorator="[
'end_time',{
rules: [{ type: 'object', required: true, message: '请选择结束时间' }],
initialValue: setting.end_time
}]"></a-time-picker>
'end_time',{
rules: [{ type: 'object', required: true, message: '请选择结束时间' }],
initialValue: setting.end_time
}]"></a-time-picker>
</a-form-item>
<a-form-item
label="惩罚积分数"
@ -83,13 +83,13 @@
type="number"
addonAfter="分"
v-decorator="[
'punish_point',
{rules: [
{required: true, message: '请输入惩罚积分数'},
{pattern: /(^[1-9]\d*$)/, message: '请输入大于1的正整数'},
], validateTrigger: 'blur',
initialValue: setting.punish_point}
]">
'punish_point',
{rules: [
{required: true, message: '请输入惩罚积分数'},
{pattern: /(^[1-9]\d*$)/, message: '请输入大于1的正整数'},
], validateTrigger: 'blur',
initialValue: setting.punish_point}
]">
</a-input>
</a-form-item>
<a-form-item
@ -99,13 +99,13 @@
type="number"
addonAfter="分"
v-decorator="[
'reward_point',
{rules: [
{required: true, message: '请输入奖励积分数'},
{pattern: /(^[1-9]\d*$)/, message: '请输入大于1的正整数'},
], validateTrigger: 'blur',
initialValue: setting.reward_point}
]">
'reward_point',
{rules: [
{required: true, message: '请输入奖励积分数'},
{pattern: /(^[1-9]\d*$)/, message: '请输入大于1的正整数'},
], validateTrigger: 'blur',
initialValue: setting.reward_point}
]">
</a-input>
</a-form-item>
<a-form-item>
@ -119,105 +119,106 @@
</template>
<script>
import api from '../../api/setting'
import moment from 'moment'
import api from '../../api/setting'
import moment from 'moment'
export default {
name: 'BookingSetting',
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
setting: {},
form: this.$form.createForm(this),
export default {
name: 'BookingSetting',
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
setting: {},
form: this.$form.createForm(this)
}
},
mounted () {
this.getData()
},
methods: {
getData () {
api.getSettingDetail()
.then(data => {
data.start_time = new moment(data.start_time, 'HH:mm:ss')
data.end_time = new moment(data.end_time, 'HH:mm:ss')
this.setting = data
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
data.start_time = data.start_time.format('HH:mm:ss')
data.end_time = data.end_time.format('HH:mm:ss')
api.updateSettingDetail(data)
.then(data => {
this.$notification.success({ message: '成功', description: '保存成功', key: 'SUCCESS' })
this.getData()
})
}
}
)
},
mounted () {
this.getData()
getStartTimeDisabledHours () {
const ret = []
const end_time = this.form.getFieldValue('end_time')
const end_hour = end_time.hour()
for (let i = 0; i < 24; i++) {
if (i > end_hour) {
ret.push(i)
}
}
if (end_time.minute() === 0 || this.setting.booking_interval === 60) {
ret.push(end_hour)
}
return ret
},
methods: {
getData () {
api.getSettingDetail()
.then(data => {
data.start_time = new moment(data.start_time, 'HH:mm:ss')
data.end_time = new moment(data.end_time, 'HH:mm:ss')
this.setting = data
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
data.start_time = data.start_time.format('HH:mm:ss')
data.end_time = data.end_time.format('HH:mm:ss')
api.updateSettingDetail(data)
.then(data => {
this.$notification.success({ message: '成功', description: '保存成功', key: 'SUCCESS' })
this.getData()
})
}
}
)
},
getStartTimeDisabledHours () {
let ret = []
let end_time = this.form.getFieldValue('end_time')
let end_hour = end_time.hour()
for (let i = 0; i < 24; i++) {
if (i > end_hour) {
ret.push(i)
}
}
if (end_time.minute() === 0 || this.setting.booking_interval === 60) {
ret.push(end_hour)
}
return ret
},
getStartTimeDisabledMinutes (selectedHour) {
let ret = []
let end_time = this.form.getFieldValue('end_time')
if (selectedHour === end_time.hour()) {
ret.push(30)
}
return ret
},
getEndTimeDisabledHours () {
let ret = []
let start_time = this.form.getFieldValue('start_time')
let start_hour = start_time.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour) {
ret.push(i)
}
}
if (start_time.minute() === 30 || this.setting.booking_interval === 60) {
ret.push(start_hour)
}
return ret
},
getEndTimeDisabledMinutes (selectedHour) {
let ret = []
let start_time = this.form.getFieldValue('start_time')
if (selectedHour === start_time.hour()) {
ret.push(0)
}
return ret
},
validateInterval (rule, value, callback) {
let start_time = this.form.getFieldValue('start_time')
let end_time = this.form.getFieldValue('end_time')
if (end_time.diff(start_time) / 1000 / 60 % value === 0) {
callback()
} else {
callback('can by divided by interval')
getStartTimeDisabledMinutes (selectedHour) {
const ret = []
const end_time = this.form.getFieldValue('end_time')
if (selectedHour === end_time.hour()) {
ret.push(30)
}
return ret
},
getEndTimeDisabledHours () {
const ret = []
const start_time = this.form.getFieldValue('start_time')
const start_hour = start_time.hour()
for (let i = 0; i < 24; i++) {
if (i < start_hour) {
ret.push(i)
}
}
if (start_time.minute() === 30 || this.setting.booking_interval === 60) {
ret.push(start_hour)
}
return ret
},
getEndTimeDisabledMinutes (selectedHour) {
const ret = []
const start_time = this.form.getFieldValue('start_time')
if (selectedHour === start_time.hour()) {
ret.push(0)
}
return ret
},
validateInterval (rule, value, callback) {
const start_time = this.form.getFieldValue('start_time')
const end_time = this.form.getFieldValue('end_time')
if (end_time.diff(start_time) / 1000 / 60 % value === 0) {
callback()
} else {
const msg = 'can by divided by interval'
callback(msg)
}
}
}
}
</script>
<style scoped lang="less">
</style>
</style>

View File

@ -23,23 +23,23 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'Setting',
data () {
return {}
},
methods: {
...mapActions(['ToggleTheme']),
handleThemeChange (e) {
this.ToggleTheme(e.target.value)
}
},
computed: {
...mapGetters(['theme'])
export default {
name: 'Setting',
data () {
return {}
},
methods: {
...mapActions(['ToggleTheme']),
handleThemeChange (e) {
this.ToggleTheme(e.target.value)
}
},
computed: {
...mapGetters(['theme'])
}
}
</script>
<style lang="less" scoped>

View File

@ -73,109 +73,111 @@
</template>
<script>
import api from '../../api/user'
import { getCookie } from '../../utils/fetch'
import api from '../../api/user'
import { getCookie } from '../../utils/fetch'
export default {
name: 'UserSetting',
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
reset: {
form: this.$form.createForm(this),
},
status: {
image: {
loading: false
}
},
export default {
name: 'UserSetting',
data () {
return {
layout: {
'label-col': { span: 6 },
'wrapper-col': { span: 18 }
},
reset: {
form: this.$form.createForm(this)
},
status: {
image: {
url: '',
id: ''
},
headers: {
'X-CSRFToken': getCookie('csrftoken')
loading: false
}
},
image: {
url: '',
id: ''
},
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
}
},
mounted () {
api.getUserAvatar()
.then(data => {
this.image = data
})
},
methods: {
handleSubmit (e) {
e.preventDefault()
this.reset.form.validateFields((error, data) => {
if (!error) {
api.resetPassword(data.password)
.then(() => {
this.$notification.success({
message: '成功',
description: '修改密码成功,请重新登陆'
})
this.$router.push({ name: 'login' })
})
}
}
)
},
mounted () {
api.getUserAvatar()
.then(data => {
this.image = data
})
notAllNumberValidator (rule, value, callback) {
if (/^[0-9]+.?[0-9]*$/.test(value)) {
const msg = 'password cant be all numbers'
callback(msg)
return false
}
callback()
return true
},
methods: {
handleSubmit (e) {
e.preventDefault()
this.reset.form.validateFields((error, data) => {
if (!error) {
api.resetPassword(data.password)
.then(() => {
this.$notification.success({
message: '成功',
description: '修改密码成功,请重新登陆',
})
this.$router.push({ name: 'login' })
})
}
}
)
},
notAllNumberValidator (rule, value, callback) {
if (/^[0-9]+.?[0-9]*$/.test(value)) {
callback('password cant be all numbers')
return false
}
callback()
return true
},
samePasswordValidator (rule, value, callback) {
if (value !== this.reset.form.getFieldValue('password')) {
callback('two passwords are different')
return false
}
callback()
return true
},
handleImageChange (info) {
if (info.file.status === 'uploading') {
this.loading = true
} else if (info.file.status === 'done') {
this.image.id = info.file.response.id
this.image.url = info.file.response.url.toString()
this.loading = false
api.updateUserAvatar({ id: this.image.id })
.then(data => {
this.$notification.success({ message: '成功', description: '上传头像成功', key: 'SUCCESS' })
})
} else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} 上传失败.`)
this.loading = false
}
},
beforeUpload (file) {
const isImage = /image\/*/.test(file.type)
if (!isImage) {
this.$message.error('图片格式不正确')
}
const isLt10M = file.size / 1024 / 1024 <= 10
if (!isLt10M) {
this.$message.error('图片大小请小于10MB')
}
const isNameLt256 = file.name.length < 256
if (!isNameLt256) {
this.$message.error('图片名称请小于256个字符')
}
return isImage && isLt10M && isNameLt256
},
samePasswordValidator (rule, value, callback) {
if (value !== this.reset.form.getFieldValue('password')) {
const msg = 'two passwords are different'
callback(msg)
return false
}
callback()
return true
},
handleImageChange (info) {
if (info.file.status === 'uploading') {
this.loading = true
} else if (info.file.status === 'done') {
this.image.id = info.file.response.id
this.image.url = info.file.response.url.toString()
this.loading = false
api.updateUserAvatar({ id: this.image.id })
.then(data => {
this.$notification.success({ message: '成功', description: '上传头像成功', key: 'SUCCESS' })
})
} else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} 上传失败.`)
this.loading = false
}
},
beforeUpload (file) {
const isImage = /image\/*/.test(file.type)
if (!isImage) {
this.$message.error('图片格式不正确')
}
const isLt10M = file.size / 1024 / 1024 <= 10
if (!isLt10M) {
this.$message.error('图片大小请小于10MB')
}
const isNameLt256 = file.name.length < 256
if (!isNameLt256) {
this.$message.error('图片名称请小于256个字符')
}
return isImage && isLt10M && isNameLt256
}
}
}
</script>
<style scoped lang="less">
</style>
</style>

View File

@ -16,9 +16,9 @@
autocomplete="true"
placeholder="用户名"
v-decorator="[
'username',
{rules: [{ required: true, message: '请输入用户名' }], validateTrigger: 'blur'}
]">
'username',
{rules: [{ required: true, message: '请输入用户名' }], validateTrigger: 'blur'}
]">
<a-icon slot="prefix" type="user" style="color: rgba(0, 0, 0, .25)"></a-icon>
</a-input>
</a-form-item>
@ -29,9 +29,9 @@
autocomplete="true"
placeholder="密码"
v-decorator="[
'password',
{rules: [{ required: true, message: '请输入密码' }], validateTrigger: 'blur'}
]">
'password',
{rules: [{ required: true, message: '请输入密码' }], validateTrigger: 'blur'}
]">
<a-icon slot="prefix" type="lock" style="color: rgba(0, 0, 0, .25)"></a-icon>
</a-input>
</a-form-item>
@ -51,53 +51,53 @@
</template>
<script>
import { mapActions } from 'vuex'
import { timeFix } from '@/utils/util'
import { mapActions } from 'vuex'
import { timeFix } from '@/utils/util'
export default {
name: 'Login',
data () {
return {
form: this.$form.createForm(this),
state: {
loginBtn: false
}
}
},
methods: {
...mapActions(['Login', 'Logout']),
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
this.Login(data)
.then((data) => {
this.state.loginBtn = true
this.loginSuccess(data)
})
.catch(() => {
this.state.loginBtn = false
})
} else {
this.state.loginBtn = true
setTimeout(() => {
this.state.loginBtn = false
}, 500)
}
})
},
loginSuccess (data) {
this.$router.push({ path: '/' })
setTimeout(() => {
this.$notification.success({
message: `欢迎`,
description: `${timeFix()} ${this.$store.getters.username},欢迎回来`,
key: 'SUCCESS'
})
}, 1000)
export default {
name: 'Login',
data () {
return {
form: this.$form.createForm(this),
state: {
loginBtn: false
}
}
},
methods: {
...mapActions(['Login', 'Logout']),
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((error, data) => {
if (!error) {
this.Login(data)
.then((data) => {
this.state.loginBtn = true
this.loginSuccess(data)
})
.catch(() => {
this.state.loginBtn = false
})
} else {
this.state.loginBtn = true
setTimeout(() => {
this.state.loginBtn = false
}, 500)
}
})
},
loginSuccess (data) {
this.$router.push({ path: '/' })
setTimeout(() => {
this.$notification.success({
message: `欢迎`,
description: `${timeFix()} ${this.$store.getters.username},欢迎回来`,
key: 'SUCCESS'
})
}, 1000)
}
}
}
</script>
<style lang="less" scoped>