Commit ef160a3d authored by 高宇's avatar 高宇

新建日程管理项目;

parent 9f17d823
...@@ -28,11 +28,25 @@ ...@@ -28,11 +28,25 @@
"url": "/worklog" "url": "/worklog"
}, },
{ {
"icon": "fa-book", "icon": "fa-calendar",
"isRouteShow": 1, "isRouteShow": 1,
"title": "test", "title": "日程管理",
"appName": portalName, "appName": 'calendar',
"url": "/test" "url": "/calendar"
},
{
"icon": "fa-calendar",
"isRouteShow": 1,
"title": "日程管理表格",
"appName": 'calendar',
"url": "/fullCalendarGrid"
},
{
"icon": "fa-calendar",
"isRouteShow": 1,
"title": "日程管理单行",
"appName": 'calendar',
"url": "/fullCalendarLine"
} }
], ],
homePage: { homePage: {
......
...@@ -16,7 +16,18 @@ ...@@ -16,7 +16,18 @@
"moco": "cd test/data && java -jar moco.jar start -g settings.json -p 13009 && cd -" "moco": "cd test/data && java -jar moco.jar start -g settings.json -p 13009 && cd -"
}, },
"dependencies": { "dependencies": {
"@fullcalendar/bootstrap": "^4.1.0",
"@fullcalendar/core": "^4.1.0",
"@fullcalendar/daygrid": "^4.1.0",
"@fullcalendar/interaction": "^4.1.0",
"@fullcalendar/resource-common": "^4.1.0",
"@fullcalendar/resource-timeline": "^4.1.0",
"@fullcalendar/timegrid": "^4.1.0",
"@fullcalendar/vue": "^4.1.0",
"bootstrap": "^4.3.1",
"express-proxy-mock": "^1.2.12", "express-proxy-mock": "^1.2.12",
"popper": "^1.0.1",
"popper.js": "^1.15.0",
"sass-bem": "^2.6.5", "sass-bem": "^2.6.5",
"sass-resources-loader": "^2.0.0", "sass-resources-loader": "^2.0.0",
"viewerjs": "^1.3.2", "viewerjs": "^1.3.2",
...@@ -32,6 +43,7 @@ ...@@ -32,6 +43,7 @@
"babel-preset-env": "^1.3.2", "babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0", "babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0", "babel-register": "^6.22.0",
"bootstrap": "^4.3.1",
"chalk": "^1.1.3", "chalk": "^1.1.3",
"connect-history-api-fallback": "^1.3.0", "connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^4.0.1", "copy-webpack-plugin": "^4.0.1",
...@@ -50,16 +62,17 @@ ...@@ -50,16 +62,17 @@
"extract-text-webpack-plugin": "^2.0.0", "extract-text-webpack-plugin": "^2.0.0",
"file-loader": "^0.11.1", "file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.1.3", "friendly-errors-webpack-plugin": "^1.1.3",
"jquery": "^2.2.4",
"html-webpack-plugin": "^2.28.0", "html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.3", "http-proxy-middleware": "^0.17.3",
"inject-loader": "^3.0.0", "inject-loader": "^3.0.0",
"jquery": "^2.2.4",
"lolex": "^1.5.2", "lolex": "^1.5.2",
"node-sass": "^4.5.3", "node-sass": "^4.5.3",
"opn": "^4.0.2", "opn": "^4.0.2",
"optimize-css-assets-webpack-plugin": "^1.3.0", "optimize-css-assets-webpack-plugin": "^1.3.0",
"ora": "^1.2.0", "ora": "^1.2.0",
"phantomjs-prebuilt": "^2.1.14", "phantomjs-prebuilt": "^2.1.14",
"popper": "^1.0.1",
"rimraf": "^2.6.0", "rimraf": "^2.6.0",
"sass-loader": "^4.0.2", "sass-loader": "^4.0.2",
"semver": "^5.3.0", "semver": "^5.3.0",
......
import ElementApi from './apis/workLog' import ElementApi from './apis/workLog'
import Calendar from './apis/calendar'
const usingJSONP = process.env.NODE_ENV !== 'production' const usingJSONP = process.env.NODE_ENV !== 'production'
const API_HOST = process.env.API_HOST const API_HOST = process.env.API_HOST
const API_PORT = process.env.API_PORT const API_PORT = process.env.API_PORT
...@@ -23,7 +24,7 @@ const option = { ...@@ -23,7 +24,7 @@ const option = {
} }
let apis = {} let apis = {}
apis = Object.assign(apis, ElementApi) apis = Object.assign(apis, ElementApi, Calendar)
export default { export default {
option, option,
......
export default {
getFilter: {
url: '/vue/calendar/get-filter'
},
getCalendarList: {
url: '/vue/calendar/list'
},
getCalendarNewOptions: {
url: '/vue/calendar/get-new'
},
getCalendarEdit: {
url: '/vue/calendar/get-edit'
},
saveCalendarAdd: {
url: '/vue/calendar/save-new'
},
saveCalendarEdit: {
url: '/vue/calendar/save-edit'
},
delCalendar: {
url: '/vue/calendar/delete'
},
getCalendarEvents: {
url: '/vue/calendar/ajax-data'
}
}
.tooltip {
position: absolute;
z-index: 1070;
display: block;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-style: normal;
font-weight: 400;
line-height: 1.5;
text-align: left;
text-align: start;
text-decoration: none;
text-shadow: none;
text-transform: none;
letter-spacing: normal;
word-break: normal;
word-spacing: normal;
white-space: normal;
line-break: auto;
font-size: 0.875rem;
word-wrap: break-word;
opacity: 0;
}
.tooltip .arrow {
position: absolute;
display: block;
width: 0.8rem;
height: 0.4rem;
}
.tooltip .arrow::before {
position: absolute;
content: "";
border-color: transparent;
border-style: solid;
}
.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] {
padding: 0.4rem 0;
}
.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow {
bottom: 0;
}
.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before {
top: 0;
border-width: 0.4rem 0.4rem 0;
border-top-color: #000;
}
.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] {
padding: 0 0.4rem;
}
.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow {
left: 0;
width: 0.4rem;
height: 0.8rem;
}
.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before {
right: 0;
border-width: 0.4rem 0.4rem 0.4rem 0;
border-right-color: #000;
}
.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] {
padding: 0.4rem 0;
}
.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow {
top: 0;
}
.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before {
bottom: 0;
border-width: 0 0.4rem 0.4rem;
border-bottom-color: #000;
}
.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] {
padding: 0 0.4rem;
}
.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow {
right: 0;
width: 0.4rem;
height: 0.8rem;
}
.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before {
left: 0;
border-width: 0.4rem 0 0.4rem 0.4rem;
border-left-color: #000;
}
.tooltip-inner {
max-width: 200px;
padding: 0.25rem 0.5rem;
color: #fff;
text-align: center;
background-color: #000;
border-radius: 0.25rem;
}
.tooltip.show {
opacity: 0.9;
}
<template>
<section>
<operation-calendar
ref="operationCalendar"
type="add"
:error-data="errorData">
<span slot="operationBtn">
<el-button type="primary" @click="saveCalendar" size="mini">保存</el-button>
<el-button @click="close()" size="mini">关闭</el-button>
</span>
</operation-calendar>
</section>
</template>
<script>
import operationCalendar from './operationCalendar'
export default {
name: '',
components: {
operationCalendar
},
data () {
return {
errorData: {}
}
},
methods: {
init () {
this.$refs.operationCalendar.initAdd()
},
saveCalendar () {
console.log(this.$refs.operationCalendar.getForm())
},
close (getList) {
this.$emit('close', getList)
}
},
created () {
}
}
</script>
<style scoped>
</style>
<template>
<section>
<operation-calendar
ref="operationCalendar"
type="add"
:error-data="errorData">
<span slot="operationBtn">
<el-button type="primary" @click="saveCalendar" size="mini">保存</el-button>
<el-button @click="close()" size="mini">关闭</el-button>
</span>
</operation-calendar>
</section>
</template>
<script>
import operationCalendar from './operationCalendar'
export default {
name: '',
components: {
operationCalendar
},
data () {
return {
errorData: {}
}
},
methods: {
init (id) {
this.$refs.operationCalendar.initEdit(id)
},
saveCalendar () {
console.log(this.$refs.operationCalendar.getForm())
},
close (getList) {
this.$emit('close', getList)
}
},
created () {
}
}
</script>
<style scoped>
</style>
<template>
<div class="calendar-add-edit-form auto-template-form">
<el-form :model="Calendars">
<el-form-item :show-message="false" :error="errorData['client_id'] ? errorData['client_id'] : ''">
<single-search-input
:form-serch-item="Calendars.client_id"
:form-show-text="searchText"
label="客户名称"
@update:searchItem="val => {Calendars.client_id = val}">
<span slot="formError" class="el-form-item__error">
{{errorData['client_id']}}
</span>
</single-search-input>
</el-form-item>
<el-form-item :show-message="false" :error="errorData['title'] ? errorData['title'] : ''">
<single-input
label="日程内容"
:required="true"
type="textarea"
:rows="4"
:form-item="Calendars.title"
@update:item="val => {Calendars.title = val}">
<span slot="formError" class="el-form-item__error">
{{errorData['title']}}
</span>
</single-input>
</el-form-item>
<el-form-item :show-message="false" :error="errorData['start_at'] ? errorData['start_at'] : ''">
<single-date-time-picker
label="开始时间"
:required="true"
:form-time-item="Calendars.start_at"
@update:item="val => {Calendars.start_at = val}">
<span slot="formError" class="el-form-item__error">
{{errorData['start_at']}}
</span>
</single-date-time-picker>
</el-form-item>
<el-form-item :show-message="false" :error="errorData['end_at'] ? errorData['end_at'] : ''">
<single-date-time-picker
label="结束时间"
:required="true"
:form-time-item="Calendars.end_at"
@update:item="val => {Calendars.end_at = val}">
<span slot="formError" class="el-form-item__error">
{{errorData['end_at']}}
</span>
</single-date-time-picker>
</el-form-item>
<el-form-item :show-message="false" :error="errorData['alarm_before'] ? errorData['alarm_before'] : ''">
<single-select
label="提醒时间"
:form-item="Calendars.alarm_before"
:options-list="getOptions('Calendars[alarm_before]')"
@update:item="val => {Calendars.alarm_before = val}">
<span slot="formError" class="el-form-item__error">
{{errorData['alarm_before']}}
</span>
</single-select>
</el-form-item>
<el-form-item :show-message="false" :error="errorData['is_public'] ? errorData['is_public'] : ''">
<single-radio
label="是否共享"
:form-item="Calendars.is_public"
:options-list="getOptions('Calendars[is_public]')"
@update:item="val => {Calendars.is_public = val}">
</single-radio>
</el-form-item>
<el-form-item :show-message="false" :error="errorData['description'] ? errorData['description'] : ''">
<single-input
label="备注"
:required="true"
type="textarea"
:rows="4"
:form-item="Calendars.description"
@update:item="val => {Calendars.description = val}">
<span slot="formError" class="el-form-item__error">
{{errorData['description']}}
</span>
</single-input>
</el-form-item>
<el-form-item>
<el-row :gutter="10">
<el-col :offset="4" :span="14">
<slot name="operationBtn">
</slot>
</el-col>
</el-row>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {
requestAPI,
api
} from '@/lib/commonMixin'
import singleSearchInput from '../../common/singleSearchInput'
import singleInput from '../../common/singleInput'
import singleDateTimePicker from '../../common/singleDateTimePicker'
import singleSelect from '../../common/singleSelect'
import singleRadio from '../../common/singleRadio'
export default {
name: '',
components: {
singleDateTimePicker,
singleInput,
singleSearchInput,
singleSelect,
singleRadio
},
data () {
return {
Calendars: {
id: '',
client_id: '',
title: '',
description: '',
start_at: '',
end_at: '',
alarm_before: '',
is_public: ''
},
options: {
'Calendars[alarm_before]': {
value: []
},
'Calendars[is_public]': {
value: []
}
},
client: {},
searchText: '',
optionsKey: 'Calendars'
}
},
methods: {
getOptions (key) {
let option = {}
if (key.indexOf('[') !== -1 && key.indexOf(']') !== -1) {
option = this.options[key]
} else {
option = this.options[this.optionsKey + '[' + key + ']']
}
if (option) {
return option.value
}
return []
},
getForm () {
return {Calendars: this.Calendars}
},
initAdd () {
requestAPI(api.getCalendarNewOptions).then(res => {
Object.keys(this.options).forEach(item => {
this.options[item] = res.options[item]
})
})
},
initEdit (id) {
requestAPI(api.getCalendarEdit, {
data: {
id
}
}).then(res => {
Object.keys(this.Calendars).forEach(item => {
this.Calendars[item] = res.model[item]
})
if (Object.keys(res.model.client).length > 0) {
this.searchText = res.model.client.name
}
Object.keys(this.options).forEach(item => {
this.options[item] = res.options[item]
})
})
}
},
props: ['errorData']
}
</script>
<style lang="scss">
.auto-template-form {
.el-form-item {
margin-bottom: 15px;
}
.client-label {
position: relative;
font-size: 12px;
}
.client-label span {
padding-right: 10px;
}
.client-label.required:after {
content: "*";
color: #eb7567;
position: absolute;
top: 3px;
right: 6px;
}
.el-form-item__error {
position: static;
}
}
</style>
<template>
<div>
<el-row class="form-content">
<el-form size="mini" :model="clientForm" label-width="0" label-position="top">
<el-col :span="4" v-for="(item, key) in filter" :key="key">
<el-form-item>
<span slot="label" v-if="item.key !== 'Filter[sort]'">
<a @click.prevent="timeSort" v-if="item.key === 'Filter[name]'">
{{item.name}}
<i :class="['fa', setSortIcon()]"></i>
</a>
<span v-else>
{{item.name}}
</span>
</span>
<el-select clearable
v-model="clientForm[item.key]"
placeholder="请选择"
v-if="!item.itemKey && item.key !== 'Filter[sort]'" size="mini">
<el-option v-for="(option, optKey) in item.value"
:key="optKey"
:label="option.name"
:value="option.key">
<span style="font-size: 12px;">{{option.name}}</span>
</el-option>
</el-select>
<el-cascader
:ref="'elCasc' + key"
v-model="item.cascader"
:props="props"
change-on-select
v-if="item.itemKey"
:options="item.value"
@change="setCascader(item)"
></el-cascader>
</el-form-item>
</el-col>
</el-form>
</el-row>
</div>
</template>
<script>
import {findWhere} from '../../lib/viewHelper'
import $ from 'jquery'
export default {
name: 'client-form',
data () {
return {
clientForm: {},
props: {
label: 'name',
value: 'key',
children: 'child'
}
}
},
props: ['filter'],
methods: {
hideCascader (key) {
// this.$refs['elCasc' + key][0].menuVisible = false
},
setCascader (item, key) {
if (item.cascader.length === 1) {
this.clientForm[item.key] = item.cascader[0]
this.clientForm[item.itemKey] = ''
} else if (item.cascader.length === 2) {
this.clientForm[item.itemKey] = item.cascader[1]
this.clientForm[item.key] = item.cascader[0]
} else {
this.clientForm[item.itemKey] = ''
this.clientForm[item.key] = ''
}
},
setDefault (filter) {
filter.forEach(item => {
if (item.default) {
let keys = findWhere(item.value, item.default, 'key')
if (keys.node) {
if (keys.parentNode) {
this.clientForm[item.itemKey] = keys.node.key
this.clientForm[item.key] = keys.parentNode.key
item.cascader = [keys.parentNode.key, keys.node.key]
} else {
this.clientForm[item.itemKey] = ''
this.clientForm[item.key] = keys.node.key
item.cascader = [keys.node.key]
}
}
}
})
},
init () {
this.$watch('clientForm', (val) => {
this.$emit('update:clientList', val)
}, {deep: true})
let _this = this
$(document).ready(() => {
$(document).on('mouseleave', '.el-cascader-menus', () => {
Object.keys(_this.$refs).forEach(item => {
if (_this.$refs[item]) {
_this.$refs[item][0].menuVisible = false
}
})
})
})
},
setSortIcon () {
if (this.clientForm['Filter[sort]'] === '') {
return 'fa-sort-amount-desc'
} else if (this.clientForm['Filter[sort]'] === 'DESC') {
return 'fa-sort-amount-desc'
} else {
return 'fa-sort-amount-asc'
}
},
timeSort () {
if (this.clientForm['Filter[sort]'] === '') {
this.clientForm['Filter[sort]'] = 'ASC'
} else if (this.clientForm['Filter[sort]'] === 'ASC') {
this.clientForm['Filter[sort]'] = 'DESC'
} else {
this.clientForm['Filter[sort]'] = 'ASC'
}
},
resetForm () {
Object.keys(this.clientForm).forEach(item => {
this.clientForm[item] = ''
})
this.filter.forEach(item => {
if (item.cascader) {
item.cascader = []
}
})
}
},
mounted () {
},
watch: {
'filter' (val) {
val.forEach(item => {
if (item.itemKey) {
this.$set(this.clientForm, item.itemKey, '')
this.$set(item, 'cascader', [])
}
this.$set(this.clientForm, item.key, '')
})
this.$nextTick(() => {
this.init()
this.setDefault(val)
})
}
}
}
</script>
<style scoped>
</style>
<template>
<section>
<FullCalendar
class='demo-app-calendar'
ref="fullCalendar"
:header="{
left: 'prev,next today',
center: 'title',
right: 'timeGridWeek,dayGridMonth'
}"
:default-date="defaultDate"
:content-height="500"
:locales="[cnLocale]"
:column-header-text="false"
locale="zh-cn"
slot-duration="01:00"
:slot-label-format="{
hour: 'numeric',
omitZeroMinute: true
}"
:plugins="calendarPlugins"
:weekends="calendarWeekends"
:events="calendarEvents"
:theme-system="'bootstrap'"
:selectable="true"
scheduler-license-key="GPL-My-Project-Is-Open-Source"
next-day-threshold="00:00:00"
@select="selectDate"
@dateClick="handleDateClick"
@eventRender="eventRender"
@eventMouseEnter="eventMouseEnter"
@eventClick="eventClick"
@datesRender="datesRender"
/>
</section>
</template>
<script>
// import FullCalendar from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import bootstrapPlugin from '@fullcalendar/bootstrap'
import cnLocale from '@fullcalendar/core/locales/zh-cn'
import {
requestAPI,
api
} from '@/lib/commonMixin'
import $ from 'jquery'
require('bootstrap')
export default {
name: 'full-calendar-grid',
components: {
// FullCalendar
},
data () {
return {
cnLocale,
bootstrapPlugin,
filter: [],
operationTitle: '',
operationPage: '',
calendarPlugins: [ // plugins must be defined in the JS
dayGridPlugin,
timeGridPlugin,
interactionPlugin, // needed for dateClick
bootstrapPlugin
],
defaultDate: new Date(),
calendarWeekends: true
}
},
methods: {
addNewCalendar () {
},
resetForm () {
},
updateForm () {
},
searchKeyword () {
},
close () {
},
getFilter () {
requestAPI(api.getFilter).then(res => {
this.filter = res
})
},
eventMouseEnter (info) {
// $(info.el)
},
datesRender (info) {
console.log(info)
$('[data-toggle="tooltip"]').tooltip()
},
eventRender (info) {
console.log(info)
// info.el = $('el-tooltip')
info.el.setAttribute('data-id', info.event.id)
info.el.setAttribute('data-model', info.event.extendedProps.nonstandard.model)
info.el.setAttribute('data-toggle', 'tooltip')
info.el.setAttribute('data-html', true)
info.el.setAttribute('title', info.event.extendedProps.nonstandard.field1)
if (info.event.extendedProps.nonstandard.model) {
$(info.el).addClass('edit-calendar')
}
// console.log($(info.el))
},
selectDate (info) {
console.log(info)
info.jsEvent.preventDefault()
info.jsEvent.stopPropagation()
info.jsEvent.cancelBubble = true
this.$message({
message: '添加'
})
this.$emit('update:add', info)
},
handleDateClick (info) {
info.jsEvent.preventDefault()
this.$message({
message: '添加'
})
this.$emit('update:add', info)
},
eventClick (info) {
info.jsEvent.preventDefault()
this.$message({
message: '编辑'
})
this.$emit('update:edit', info)
},
calendarEvents (info, successCallback, failureCallback) {
console.log(info)
let ids = {}
if (this.userId) {
ids.user_id = this.userId
} else {
if (this.departmentId) {
ids.department_id = this.departmentId
}
}
requestAPI(api.getCalendarEvents, {
data: {
...ids,
start: info.start.valueOf() / 1000,
end: info.end.valueOf() / 1000
}
}).then(res => {
successCallback(res.event)
this.defaultDate = res.defaultDate
}).catch(_ => {
})
}
},
created () {
// this.getFilter()
console.log(this.$refs.fullCalendar)
},
mounted () {
},
props: ['userId', 'departmentId']
}
</script>
<style scoped lang="sass">
@import '~@fullcalendar/core/main.css';
@import '~@fullcalendar/daygrid/main.css';
@import '~@fullcalendar/timegrid/main.css';
@import '~@fullcalendar/timeline/main.css';
@import '~@fullcalendar/resource-timeline/main.css';
@import '../../assets/css/tooltip.css';
</style>
<style>
.tooltip p {
text-align: left;
}
</style>
<template>
<section>
<FullCalendar
class='demo-app-calendar'
ref="fullCalendar"
defaultView="resourceTimelineWeek"
:header="{
left: 'prev,next',
center: 'title',
right: 'resourceTimelineDay,resourceTimelineWeek,resourceTimelineMonth'
}"
:business-hours="{
dow: [1, 2, 3, 4, 5],
start: '09:00',
end: '18:00'
}"
:content-height="500"
:locales="[cnLocale]"
locale="zh-cn"
:event-time-format="{
hour: 'numeric',
minute: '2-digit',
meridiem: false,
hour12: false
}"
resource-area-width="10%"
:resource-columns="[{'labelText':'姓名','field':'name'}]"
:plugins="calendarPlugins"
:resources="resources"
:selectable="true"
scroll-time="08:00:00"
:column-header-format="{
meridiem: false,
hour: 'numeric',
omitZeroMinute: false,
hour12: false
}"
:event-data-transform="eventDataTransform"
:events="events"
:default-date="defaultDate"
scheduler-license-key="GPL-My-Project-Is-Open-Source"
next-day-threshold="00:00:00"
@eventRender="eventRender"
@dateClick="handleDateClick"
@eventClick="eventClick"
@datesRender="datesRender"
@select="selectTime"
/>
</section>
</template>
<script>
import dayGridPlugin from '@fullcalendar/daygrid'
import listPlugin from '@fullcalendar/list'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import resourceCommonPlugin from '@fullcalendar/resource-common'
import resourceTimelinePlugin from '@fullcalendar/resource-timeline'
import cnLocale from '@fullcalendar/core/locales/zh-cn'
import {
requestAPI,
api
} from '@/lib/commonMixin'
import $ from 'jquery'
require('bootstrap')
export default {
name: 'full-calendar-line',
components: {
// FullCalendar
},
data () {
return {
// slotLabel: getSlotLabelFormat('resourceTimelineWeek'),
cnLocale,
form: {
'Calendars[keyword]': ''
},
defaultDate: new Date(),
filter: [],
operationTitle: '',
operationPage: '',
calendarPlugins: [ // plugins must be defined in the JS
dayGridPlugin,
listPlugin,
timeGridPlugin,
interactionPlugin, // needed for dateClick
resourceCommonPlugin,
resourceTimelinePlugin
],
calendarWeekends: true
}
},
methods: {
addNewCalendar () {
},
resetForm () {
},
updateForm () {
},
searchKeyword () {
},
close () {
},
getFilter () {
requestAPI(api.getFilter).then(res => {
this.filter = res
})
},
eventDataTransform (eventData) {
console.log(eventData)
},
datesRender (info) {
// 切换视图
$('[data-toggle="tooltip"]').tooltip()
},
eventRender (info) {
// info.el = $('el-tooltip')
info.el.setAttribute('data-id', info.event.id)
info.el.setAttribute('data-model', info.event.extendedProps.nonstandard.model)
info.el.setAttribute('data-toggle', 'tooltip')
info.el.setAttribute('data-html', true)
info.el.setAttribute('title', info.event.extendedProps.nonstandard.field1)
if (info.event.extendedProps.nonstandard.can_edit) {
$(info.el).addClass('edit-calendar')
}
this.$nextTick(() => {
$(info.el).find('.fc-sticky').removeAttr('style')
})
},
handleDateClick (info) {
console.log(info)
info.jsEvent.preventDefault()
this.$message({
message: '添加'
})
$('.fc-sticky').removeAttr('style')
this.$emit('update:add', info)
},
selectTime (info) {
info.jsEvent.preventDefault()
this.$message({
message: '添加'
})
console.log(info)
$('.fc-sticky').removeAttr('style')
this.$emit('update:add', info)
},
eventClick (info) {
info.jsEvent.preventDefault()
this.$message({
message: '编辑'
})
this.$emit('update:edit', info)
},
events (info, successCallback, failureCallback) {
let ids = {}
if (this.userId) {
ids.user_id = this.userId
} else {
if (this.departmentId) {
ids.department_id = this.departmentId
}
}
requestAPI(api.getCalendarEvents, {
data: {
...ids,
start: info.start.valueOf() / 1000,
end: info.end.valueOf() / 1000
}
}).then(res => {
successCallback(res.event)
this.defaultDate = new Date(res.defaultDate)
}).catch(_ => {
}).finally(() => {
$('[data-toggle="tooltip"]').tooltip()
})
}
},
created () {
// this.getFilter()
},
props: ['userId', 'departmentId', 'resources']
}
</script>
<style scoped lang="sass">
@import '~@fullcalendar/core/main.css';
@import '~@fullcalendar/daygrid/main.css';
@import '~@fullcalendar/timegrid/main.css';
@import '~@fullcalendar/timeline/main.css';
@import '~@fullcalendar/resource-timeline/main.css';
@import '../../assets/css/tooltip.css';
</style>
<style>
.fc-timeline-event .fc-content {
padding: 0 1px;
white-space: nowrap;
overflow: hidden;
}
.tooltip p {
text-align: left;
}
</style>
<template>
<section>
<div class="content">
<client-header
ref="clientHeader"
title="日程管理"
:model="form"
search-key="Calendars"
key-code="keyword"
button-title="新建日程"
:reset-form="resetForm"
:add-new-user="addNewCalendar"
:search-keyword="searchKeyword"
type="keyword"
:title-span="6"></client-header>
<client-form ref="clientForm" :filter="filter" @update:clientList="form =>{ updateForm(form) }"></client-form>
<div class="page-body-content">
<template v-if="listShow">
<div v-loading="loading">
<ScheduleItem
v-for="(item, key) in result.list"
:item="item"
:key="key"
@update:image="imgs => {setImage(imgs)}">
<span slot="opearate">
<el-button class="pull-right"
type="primary"
size="mini"
:disabled="!item.can_delete"
@click="delCalendar(item.id)">
<i class="fa fa-trash-o faa-shake"></i> 删除
</el-button>
<el-button class="pull-right"
type="primary"
size="mini"
:disabled="!item.can_update"
@click.prevent.stop="editCalendar(item)">
<i class="fa fa-edit"></i> 编辑
</el-button>
</span>
</ScheduleItem>
<div v-if="result.list.length === 0">
<el-card class="box-card">
未查询到数据!
</el-card>
</div>
</div>
<Pagenation
@update:pager="pager => {updatePage(pager)}"
:pager.sync="pagenation"
:total="totalcount">
</Pagenation>
</template>
<template v-else>
<component :is="calendarPage" ref="calendarRef"
:user-id="form['CalendarSearch[created_by]']"
:department-id="form['CalendarSearch[department_id]']"
:resources="resources"
@update:add="info => {addItem()}"
@update:edit="info => {editItem(info)}"></component>
</template>
</div>
</div>
<side-popup ref="sidePopup" :title="operationTitle" v-click-outside="popupClose">
<component :is="operationPage" :ref="operationPage" @close="getList => {close(getList)}"></component>
</side-popup>
</section>
</template>
<script>
import ScheduleItem from './scheduleItem'
import ClientHeader from '../common/clientHeader'
import clientForm from './calendarForm'
// import FullCalendar from '@fullcalendar/vue'
import Pagenation from '../common/schedulePagenation'
import clickOutside from '@/lib/bind'
import addCalendar from './calendar/addCalendar'
import editCalendar from './calendar/editCalendar'
import fullCalendarGrid from './fullCalendarGrid'
import fullCalendarLine from './fullCalendarLine'
import {
requestAPI,
api
} from '@/lib/commonMixin'
export default {
name: '',
components: {
ClientHeader,
clientForm,
Pagenation,
ScheduleItem,
addCalendar,
editCalendar,
fullCalendarGrid,
fullCalendarLine
// FullCalendar
},
directives: {
clickOutside
},
data () {
return {
result: {
list: []
},
form: {
'Calendars[keyword]': '',
'CalendarSearch[department_id]': '',
'CalendarSearch[created_by]': ''
},
filter: [],
pagenation: {
page: 1
},
totalcount: 0,
operationTitle: '',
operationPage: '',
loading: false,
calendarPage: '',
calendarRef: '',
resources: [],
listShow: false
}
},
methods: {
editCalendar (row) {
this.operationPage = 'editCalendar'
this.operationTitle = '编辑日程'
// this.$router.push({name: 'editProject', params: {id: row.id}})
this.$nextTick(() => {
this.$refs.sidePopup.show(this.$refs[this.operationPage].init(row.id))
})
},
delCalendar (id) {
this.$confirm('确认删除吗?', '提示').then(() => {
requestAPI(api.delWorkLog, {
data: {
id
}
}).then(() => {
this.$message('删除成功!')
this.getList()
}, error => {
if (Array.isArray(error.msg)) {
error.msg.forEach(item => {
this.$notify.error({
title: '错误',
message: item.error
})
})
}
})
}).catch(_ => {
})
},
addItem (info) {
this.operationPage = 'addCalendar'
this.operationTitle = '添加日程'
// this.$router.push({name: 'editProject', params: {id: row.id}})
this.$nextTick(() => {
this.$refs.sidePopup.show(this.$refs[this.operationPage].init())
})
},
editItem (info) {
console.log(info)
},
addNewCalendar () {
this.operationPage = 'addCalendar'
this.operationTitle = '添加日程'
// this.$router.push({name: 'editProject', params: {id: row.id}})
this.$nextTick(() => {
this.$refs.sidePopup.show(this.$refs[this.operationPage].init())
})
},
resetForm () {
requestAPI(api.getFilter).then(res => {
this.filter = res
})
},
updateForm (form) {
Object.assign(this.form, form)
// console.log(form)
// if (this.form['Calendars[keyword]'] !== '') {
// this.getList()
// }
// this.getList()
},
updatePage (pager) {
Object.assign(this.pagenation, pager)
this.getList(true)
},
searchKeyword () {
this.updateForm()
if (this.form['Calendars[keyword]'] !== '') {
this.getList()
this.listShow = true
} else {
this.listShow = false
}
},
close (getList) {
this.$refs.sidePopup.close()
if (getList) {
this.getList()
}
},
popupClose () {
this.$refs.sidePopup &&
this.$refs.sidePopup.close()
},
getFilter () {
requestAPI(api.getFilter).then(res => {
this.filter = res
})
},
getList (toTop) {
if (this.loading) {
return
}
this.loading = true
requestAPI(api.getCalendarList, {
data: {
...this.form,
page: this.pagenation.page
}
}).then(res => {
this.result.list = res.list
// this.pagenation.page = res.pagenation.thispage
this.totalcount = res.pagenation.totalcount
// this.totalcount = 8
if (toTop) {
this.$el.querySelector('.page-body-content').scrollTo(0, 0)
}
}).finally(_ => {
this.loading = false
})
}
},
created () {
this.getFilter()
},
watch: {
form: {
handler (val) {
if (val['CalendarSearch[created_by]'] !== '') {
this.calendarPage = 'fullCalendarGrid'
this.calendarRef = 'fullCalendarGrid'
} else if (val['CalendarSearch[department_id]]'] !== '') {
this.calendarPage = 'fullCalendarLine'
this.calendarRef = 'fullCalendarLine'
let items = this.filter.find(i => i.key === 'CalendarSearch[department_id]')
if (items) {
let item = items.value.find(i => i.key === val['CalendarSearch[department_id]'])
if (item) {
this.resources = item.child
}
}
}
},
deep: true
}
}
}
</script>
<style scoped lang="scss">
.text-info {
color: #17a2b8 !important;
}
.border-info {
border-color: #17a2b8 !important;
}
.colRed {
color: red;
border: 1px solid red;
}
.colGreen {
color: green;
border: 1px solid green;
}
.colRed, .colGreen, .col {
border-radius: .25rem;
padding: 0 .25rem;
}
.rounded-circle {
border-radius: 50% !important;
}
@include c('schedule-right') {
> .el-row {
> .el-col {
margin-bottom: 4px;
.el-button {
margin: 0 2px;
padding: 7px;
}
.date-time {
color: #649FD7;
}
}
}
.badge {
border-radius: 2px;
color: #333744;
background: #ffffff;
vertical-align: baseline;
display: inline;
padding: 2px 6px;
}
span.badge-unread {
&:hover {
border-color: #de321d
}
color: #fff;
background-color: #e54c3a;
border-color: #a32516
}
}
</style>
<template>
<section class="client-schedule-item">
<el-row class="client-schedule-row">
<el-col :span="4" :xs="24" class="client-schedule-left" :style="{'background-color': item.bgcolor}">
<el-row :gutter="10">
<el-col :span="24" class="entity-name">
<i class="fa fa-star" aria-hidden="true"></i>
<router-link :to="toView(item)" v-if="Object.keys(item.client).length > 0">{{ item.client.name }}</router-link>
<span v-else>客户名称{{noneText}}</span>
</el-col>
<el-col :span="24">
<img class="user-avatar rounded-circle" :src="item.createdBy.avatar.name">
<span>{{ item.createdBy.name }}</span>
</el-col>
<el-col :span="24">
<i class="fa fa-sitemap" aria-hidden="true"></i>
<span>{{ item.createdBy.department.name}}</span>
</el-col>
</el-row>
</el-col>
<el-col :span="20" :xs="24" class="client-schedule-right">
<el-row :gutter="10">
<el-col :span="6" :xs="24">
开始时间: {{item.start_at !== '' ? item.start_at : noneText}}
</el-col>
<el-col :span="6" :xs="24">
结束时间: {{item.end_at !== '' ? item.end_at : noneText}}
</el-col>
<el-col :span="6" :xs="24">
创建时间: {{item.created_at !== '' ? item.created_at : noneText}}
</el-col>
<el-col :span="6" :xs="24">
<span :class="['border', item.is_public === 0 ? 'border-success' : 'border-danger', item.is_public === 0 ? 'text-success' : 'text-danger', 'rounded', 'px-1']">
{{item.is_public === 0 ? '私人' : '共享'}}
</span>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="12" :xs="24">
<span class="border border-danger text-danger rounded px-1">日程内容</span> :
<span v-html="setBr(item)"></span>
</el-col>
<el-col :span="12" :xs="24">
<slot name="opearate">
</slot>
</el-col>
</el-row>
</el-col>
</el-row>
</section>
</template>
<script>
export default {
name: 'scheduleItem',
props: {
item: Object
},
data () {
return {
noneText: '(未设置)'
}
},
methods: {
setBr (item) {
if (item.title === '') {
return this.noneText
} else {
return item.title.replace(/\n/g, '<br />')
}
},
toView (item) {
// console.log(item)
let obj = {
app: 'client',
name: 'viewClient',
params: {
id: item.client_id
}
}
return obj
},
showImageViewer (images) {
this.$emit('update:image', images.map(i => 'https://beta.jinchangxiao.com' + i.attachment.name))
// this.$emit('update:image', ['https://beta.jinchangxiao.com/files/protected/a57be577deb434/2019/03/34cf2b53-3959-33fe-93af-98a50fd18c11.jpg'])
}
},
computed: {
isNullClient () {
return !this.item.client
},
isPublic () {
return !!this.item.is_public
}
},
mounted () {
}
}
</script>
<style scoped lang="scss">
.text-info {
color: #17a2b8 !important;
}
.border-info {
border-color: #17a2b8 !important;
}
.colRed {
color: red;
border: 1px solid red;
}
.colGreen {
color: green;
border: 1px solid green;
}
.colRed, .colGreen, .col {
border-radius: .25rem;
padding: 0 .25rem;
}
.rounded-circle {
border-radius: 50% !important;
}
.user-avatar {
width: 16px;
max-width: 16px;
height: 16px;
max-height: 16px;
}
@include c('schedule-item') {
margin-bottom: 10px;
font-size: 12px;
}
@include c('schedule-item:first-child') {
.client-schedule-left {
border-radius: 5px 0 0 0;
}
}
@include c('schedule-row') {
min-height: 100px;
background-color: white;
display: flex;
flex-wrap: wrap;
}
@include c('schedule-left') {
background-color: #FF6A6A;
min-height: 100%;
padding: 10px 15px 6px;
> .el-row {
> .el-col {
margin-bottom: 4px;
}
}
.el-row div:first-child, .el-row div:first-child a, .entity-name, .entity-name a {
color: white;
}
span {
display: inline-block;
padding-left: 5px;
}
@include e('private') {
display: inline-block;
// border: 1px solid black;
padding-right: 5px;
border-radius: 5px;
width: 40px;
height: 20px;
}
}
@include c('schedule-right') {
height: 100%;
padding: 10px 15px 6px;
.border {
border: 1px solid #dee2e6;
}
.border-success {
border-color: #28a745 !important;
}
.border-danger {
border-color: #dc3545;
}
.text-success {
color: #28a745 !important;
}
.text-danger {
color: #E45744;
}
.rounded {
border-radius: .25rem !important;
}
.px-1 {
padding: 0 .25rem;
}
> .el-row {
> .el-col {
margin-bottom: 4px;
.el-button {
margin: 0 2px;
padding: 7px;
}
.date-time, .show-picture {
color: #649FD7;
}
.show-picture {
cursor: pointer;
}
}
}
.badge {
border-radius: 2px;
color: #333744;
background: #ffffff;
vertical-align: baseline;
display: inline;
padding: 2px 6px;
text-align: center;
}
span.badge-unread {
&:hover {
border-color: #de321d
}
color: #fff;
background-color: #e54c3a;
border-color: #a32516
}
@include e('item') {
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 5px 0 5px 5px;
flex: 1;
}
@include e('status') {
color: red;
}
@include e('content') {
display: inline-block;
border: 1px solid red;
border-radius: 5px;
height: 20px;
padding: 0 5px 0 5px;
color: red;
}
}
</style>
<template> <template>
<el-col :span="12" class="header-search"> <el-col :span="12" class="header-search">
<div class="pull-right search-form"> <div class="pull-right search-form">
<el-button type="primary" size="mini" @click="targetFun"> <el-button type="primary" size="mini" @click.prevent.stop="targetFun">
<i class="fa fa-edit faa-pulse animated"></i> {{buttonTitle}} <i class="fa fa-edit faa-pulse animated"></i> {{buttonTitle}}
</el-button> </el-button>
</div> </div>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="searchKey"> <el-form-item v-if="searchKey">
<el-button type="primary" size="mini" @click="addNewUser"> <el-button type="primary" size="mini" @click.prevent.stop="addNewUser">
<i class="fa fa-plus faa-pulse animated"></i> {{buttonTitle}} <i class="fa fa-plus faa-pulse animated"></i> {{buttonTitle}}
</el-button> </el-button>
</el-form-item> </el-form-item>
......
<template>
<section class="single-date-time-picker">
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{startLabel}}</span>
</el-col>
<el-col :span="14">
<el-row :gutter="10">
<el-col :span="24" class="calc-timer">
<el-row :gutter="2">
<el-col :span="12">
<el-date-picker
size="mini"
:clearable="false"
v-model="startDateTime.date"
type="date"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
placeholder="选择日期">
</el-date-picker>
</el-col>
<el-col :span="12">
<el-time-select
size="mini"
:clearable="false"
v-model="startDateTime.time"
:picker-options="{start: '00:00', step: '00:15', end: '24:00'}"
placeholder="选择时间">
</el-time-select>
</el-col>
</el-row>
</el-col>
</el-row>
<span class="tips"><em>{{tips}}</em></span>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="4" class="client-label text-right">
<span>{{calcLabel}}</span>
</el-col>
<el-col :span="14">
<div class="calc-input">
<div>
<el-button type="primary" size="mini" icon="el-icon-minus" @click="reduceMin"></el-button>
<el-input v-model="calcTime" size="mini" placeholder="间隔时间" @keyup.native="handleInput">
<template slot="append">分钟</template>
</el-input>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="plusMin"></el-button>
</div>
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{endLabel}}</span>
</el-col>
<el-col :span="14">
<el-row :gutter="10">
<el-col :span="24" class="calc-timer">
<el-row :gutter="2">
<el-col :span="12">
<el-date-picker
:clearable="false"
size="mini"
v-model="endDateTime.date"
type="date"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
placeholder="选择日期">
</el-date-picker>
</el-col>
<el-col :span="12">
<el-time-select
size="mini"
:clearable="false"
v-model="endDateTime.time"
:picker-options="{start: '00:00', step: '00:15', end: '24:00'}"
placeholder="选择时间">
</el-time-select>
</el-col>
</el-row>
</el-col>
</el-row>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
<span class="el-form-item__error">{{calcError}}</span>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'single-date-time-picker',
mixins: [itemMixin],
data () {
return {
calcTime: 0,
calcError: ''
}
},
props: ['pickerType'],
methods: {
handleInput (e) {
this.calcTime = e.target.value.replace(/[^\d]/g, 0)
},
reduceMin () {
this.calcTime -= 15
if (this.calcTime < 0) {
this.calcTime = 0
return
}
},
plusMin () {
this.calcTime += 15
if (this.calcTime >= 1440) {
this.calcTime = 1440
}
},
compareTime () {
let start = new Date(this.start_at)
let end = new Date(this.end_at)
let diffTime = end.getTime() - start.getTime()
let days = Math.floor(diffTime / (24 * 3600 * 1000))
let level = diffTime % (24 * 3600 * 1000)
let hours = Math.floor(level / (3600 * 1000))
var leave2 = level % (3600 * 1000) // 计算小时数后剩余的毫秒数
var minutes = Math.floor(leave2 / (60 * 1000))
// console.log(minutes)
this.calcTime = minutes + hours * 60 + days * 24 * 60
if (this.calcTime < 0) {
this.calcTime = 0
}
if (start >= end) {
this.calcError = '结束时间的值必须大于"开始时间"。'
} else {
this.calcError = ''
}
},
updateTime (val) {
if (this.startDateTime.date === '' && this.startDateTime.time === '') {
return
}
if (val > 1440) {
this.calcError = '请检查结束时间,看起来您工作了超过24个小时!'
}
if (val === '') {
this.calcTime = 0
return
}
let start = new Date(this.start_at)
let min = start.getMinutes()
let minutes = (min + parseInt(val)) % 60
let hours = start.getHours() + Math.floor((min + parseInt(val)) / 60)
// let days = start.getDate() + Math.floor(start.getHours() / 24)
// console.log(start.getDate())
start.setMinutes(minutes)
if (hours === 24) {
start.setHours(0)
start.setMinutes(0)
start.setDate(start.getDate() + 1)
} else {
start.setHours(hours)
}
this.endDateTime.date = start.getFullYear() + '-' + ((start.getMonth() + 1) + 100).toString().slice(1, 3) + '-' + (start.getDate() + 100).toString().slice(1, 3)
this.endDateTime.time = (start.getHours() + 100).toString().slice(1, 3) + ':' + (start.getMinutes() + 100).toString().slice(1, 3)
// console.log(this.endDateTime.data)
}
},
watch: {
'startDateTimeItem' (val) {
if (val !== '') {
let t = val.split(' ')
this.startDateTime.date = t[0]
this.startDateTime.time = t[1]
}
},
'endDateTimeItem' (val) {
if (val !== '') {
let t = val.split(' ')
this.endDateTime.date = t[0]
this.endDateTime.time = t[1]
}
},
'startDateTime.date' (val) {
if (this.startDateTime.time !== '') {
this.start_at = val + ' ' + this.startDateTime.time
if (this.endDateTime.date === '' || this.endDateTime.date === '') {
this.endDateTime.date = val
this.endDateTime.time = this.startDateTime.time
}
}
},
'startDateTime.time' (val) {
if (this.startDateTime.date !== '') {
this.start_at = this.startDateTime.date + ' ' + val
if (this.endDateTime.date === '' || this.endDateTime.date === '') {
this.endDateTime.date = this.startDateTime.date
this.endDateTime.time = val
}
}
},
'endDateTime.date' (val) {
if (val) {
if (this.endDateTime.time !== '') {
this.end_at = val + ' ' + this.endDateTime.time
}
}
},
'endDateTime.time' (val) {
// console.log(val)
if (val) {
if (this.endDateTime.date !== '') {
this.end_at = this.endDateTime.date + ' ' + val
}
}
},
'start_at' (val) {
this.compareTime()
this.$emit('update:startItem', val)
},
'end_at' (val) {
if (this.start_at !== '') {
this.compareTime()
}
this.$emit('update:endItem', val)
},
'calcTime' (val) {
this.updateTime(val)
}
}
}
</script>
<style scoped>
.single-date-time-picker > .el-row:not(:last-child) {
margin-bottom: 15px;
}
</style>
<style>
.calc-input {
display: inline-block;
width: 100%;
}
.calc-input > div {
display: flex;
}
.calc-input > div > .el-button {
margin-right: 0;
color: #333;
background-color: #fff;
border-color: #ccc;
padding: 7px 9px;
}
.calc-input > div > .el-button:hover {
color: #333;
background-color: #e6e6e6;
border-color: #adadad;
}
.calc-input > div > .el-input {
border: 1px solid #DCDFE6;
}
.calc-input > div > .el-input input {
text-align: center;
border: none;
}
.calc-input > div > .el-input .el-input-group__append {
border: none;
}
.calc-timer .el-input {
width: 100%;
}
</style>
<template> <template>
<section class="single-date-time-picker"> <section class="single-date-picker">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]"> <el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{startLabel}}</span> <span>{{label}}</span>
</el-col> </el-col>
<el-col :span="14"> <el-col :span="14">
<el-row :gutter="10">
<el-col :span="24" class="calc-timer">
<el-row :gutter="2">
<el-col :span="12">
<el-date-picker <el-date-picker
size="mini" size="mini"
:clearable="false" v-model="timeItem"
v-model="startDateTime.date" type="datetime"
type="date" placeholder="选择日期时间"
format="yyyy-MM-dd" align="right"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:SS"
placeholder="选择日期"> :picker-options="pickerOptions">
</el-date-picker> </el-date-picker>
</el-col>
<el-col :span="12">
<el-time-select
size="mini"
:clearable="false"
v-model="startDateTime.time"
:picker-options="{start: '00:00', step: '00:15', end: '24:00'}"
placeholder="选择时间">
</el-time-select>
</el-col>
</el-row>
</el-col>
</el-row>
<span class="tips"><em>{{tips}}</em></span> <span class="tips"><em>{{tips}}</em></span>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<slot name="formError"></slot> <slot name="formError"></slot>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="10">
<el-col :span="4" class="client-label text-right">
<span>{{calcLabel}}</span>
</el-col>
<el-col :span="14">
<div class="calc-input">
<div>
<el-button type="primary" size="mini" icon="el-icon-minus" @click="reduceMin"></el-button>
<el-input v-model="calcTime" size="mini" placeholder="间隔时间" @keyup.native="handleInput">
<template slot="append">分钟</template>
</el-input>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="plusMin"></el-button>
</div>
</div>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{endLabel}}</span>
</el-col>
<el-col :span="14">
<el-row :gutter="10">
<el-col :span="24" class="calc-timer">
<el-row :gutter="2">
<el-col :span="12">
<el-date-picker
:clearable="false"
size="mini"
v-model="endDateTime.date"
type="date"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
placeholder="选择日期">
</el-date-picker>
</el-col>
<el-col :span="12">
<el-time-select
size="mini"
:clearable="false"
v-model="endDateTime.time"
:picker-options="{start: '00:00', step: '00:15', end: '24:00'}"
placeholder="选择时间">
</el-time-select>
</el-col>
</el-row>
</el-col>
</el-row>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
<span class="el-form-item__error">{{calcError}}</span>
</el-col>
</el-row>
</section> </section>
</template> </template>
...@@ -101,187 +31,35 @@ ...@@ -101,187 +31,35 @@
mixins: [itemMixin], mixins: [itemMixin],
data () { data () {
return { return {
calcTime: 0, pickerOptions: {
calcError: '' shortcuts: [{
} text: '今天',
}, onClick (picker) {
props: ['pickerType'], picker.$emit('pick', new Date())
methods: {
handleInput (e) {
this.calcTime = e.target.value.replace(/[^\d]/g, 0)
},
reduceMin () {
this.calcTime -= 15
if (this.calcTime < 0) {
this.calcTime = 0
return
}
},
plusMin () {
this.calcTime += 15
if (this.calcTime >= 1440) {
this.calcTime = 1440
} }
}, }, {
compareTime () { text: '明天',
let start = new Date(this.start_at) onClick (picker) {
let end = new Date(this.end_at) const date = new Date()
let diffTime = end.getTime() - start.getTime() date.setTime(date.getTime() + 3600 * 1000 * 24)
let days = Math.floor(diffTime / (24 * 3600 * 1000)) picker.$emit('pick', date)
let level = diffTime % (24 * 3600 * 1000)
let hours = Math.floor(level / (3600 * 1000))
var leave2 = level % (3600 * 1000) // 计算小时数后剩余的毫秒数
var minutes = Math.floor(leave2 / (60 * 1000))
// console.log(minutes)
this.calcTime = minutes + hours * 60 + days * 24 * 60
if (this.calcTime < 0) {
this.calcTime = 0
} }
if (start >= end) { }, {
this.calcError = '结束时间的值必须大于"开始时间"。' text: '一周后',
} else { onClick (picker) {
this.calcError = '' const date = new Date()
date.setTime(date.getTime() + 3600 * 1000 * 24 * 7)
picker.$emit('pick', date)
} }
}, }]
updateTime (val) {
if (this.startDateTime.date === '' && this.startDateTime.time === '') {
return
} }
if (val > 1440) {
this.calcError = '请检查结束时间,看起来您工作了超过24个小时!'
}
if (val === '') {
this.calcTime = 0
return
}
let start = new Date(this.start_at)
let min = start.getMinutes()
let minutes = (min + parseInt(val)) % 60
let hours = start.getHours() + Math.floor((min + parseInt(val)) / 60)
// let days = start.getDate() + Math.floor(start.getHours() / 24)
// console.log(start.getDate())
start.setMinutes(minutes)
if (hours === 24) {
start.setHours(0)
start.setMinutes(0)
start.setDate(start.getDate() + 1)
} else {
start.setHours(hours)
}
this.endDateTime.date = start.getFullYear() + '-' + ((start.getMonth() + 1) + 100).toString().slice(1, 3) + '-' + (start.getDate() + 100).toString().slice(1, 3)
this.endDateTime.time = (start.getHours() + 100).toString().slice(1, 3) + ':' + (start.getMinutes() + 100).toString().slice(1, 3)
// console.log(this.endDateTime.data)
}
},
watch: {
'startDateTimeItem' (val) {
if (val !== '') {
let t = val.split(' ')
this.startDateTime.date = t[0]
this.startDateTime.time = t[1]
}
},
'endDateTimeItem' (val) {
if (val !== '') {
let t = val.split(' ')
this.endDateTime.date = t[0]
this.endDateTime.time = t[1]
}
},
'startDateTime.date' (val) {
if (this.startDateTime.time !== '') {
this.start_at = val + ' ' + this.startDateTime.time
if (this.endDateTime.date === '' || this.endDateTime.date === '') {
this.endDateTime.date = val
this.endDateTime.time = this.startDateTime.time
}
}
},
'startDateTime.time' (val) {
if (this.startDateTime.date !== '') {
this.start_at = this.startDateTime.date + ' ' + val
if (this.endDateTime.date === '' || this.endDateTime.date === '') {
this.endDateTime.date = this.startDateTime.date
this.endDateTime.time = val
}
}
},
'endDateTime.date' (val) {
if (val) {
if (this.endDateTime.time !== '') {
this.end_at = val + ' ' + this.endDateTime.time
}
}
},
'endDateTime.time' (val) {
// console.log(val)
if (val) {
if (this.endDateTime.date !== '') {
this.end_at = this.endDateTime.date + ' ' + val
}
}
},
'start_at' (val) {
this.compareTime()
this.$emit('update:startItem', val)
},
'end_at' (val) {
if (this.start_at !== '') {
this.compareTime()
}
this.$emit('update:endItem', val)
},
'calcTime' (val) {
this.updateTime(val)
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.single-date-time-picker > .el-row:not(:last-child) { .el-date-editor.el-input, .el-date-editor.el-input__inner {
margin-bottom: 15px;
}
</style>
<style>
.calc-input {
display: inline-block;
width: 100%;
}
.calc-input > div {
display: flex;
}
.calc-input > div > .el-button {
margin-right: 0;
color: #333;
background-color: #fff;
border-color: #ccc;
padding: 7px 9px;
}
.calc-input > div > .el-button:hover {
color: #333;
background-color: #e6e6e6;
border-color: #adadad;
}
.calc-input > div > .el-input {
border: 1px solid #DCDFE6;
}
.calc-input > div > .el-input input {
text-align: center;
border: none;
}
.calc-input > div > .el-input .el-input-group__append {
border: none;
}
.calc-timer .el-input {
width: 100%; width: 100%;
} }
</style> </style>
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
type="primary" type="primary"
size="mini" size="mini"
:disabled="!item.can_update" :disabled="!item.can_update"
@click="editLog(item)"> @click.prevent.stop="editLog(item)">
<i class="fa fa-edit"></i> 编辑 <i class="fa fa-edit"></i> 编辑
</el-button> </el-button>
<el-button class="pull-right" <el-button class="pull-right"
...@@ -68,15 +68,15 @@ ...@@ -68,15 +68,15 @@
<div class="images clearfix" style="display: none;"> <div class="images clearfix" style="display: none;">
<img v-for="(source, index) in images" :key="index" :src="source" class="image"> <img v-for="(source, index) in images" :key="index" :src="source" class="image">
</div> </div>
<side-popup ref="sidePopup" :title="operationTitle"> <side-popup ref="sidePopup" :title="operationTitle" v-click-outside="popupClose">
<component :is="operationPage" :ref="operationPage" @close="getList => {close(getList)}"></component> <component :is="operationPage" :ref="operationPage" @close="getList => {close(getList)}"></component>
</side-popup> </side-popup>
</section> </section>
</template> </template>
<script> <script>
import ScheduleItem from '../common/scheduleItem' import ScheduleItem from './scheduleItem'
import Pagenation from './schedulePagenation' import Pagenation from '../common/schedulePagenation'
import clientHeader from '../common/clientHeader' import clientHeader from '../common/clientHeader'
import clientForm from './workLogListForm' import clientForm from './workLogListForm'
import clickOutside from '@/lib/bind' import clickOutside from '@/lib/bind'
...@@ -192,6 +192,10 @@ ...@@ -192,6 +192,10 @@
viewer.update() viewer.update()
}) })
}, },
popupClose () {
this.$refs.sidePopup &&
this.$refs.sidePopup.close()
},
editLog (row) { editLog (row) {
// this.$router.push({name: 'workLogEdit', params: {id: row.id}}) // this.$router.push({name: 'workLogEdit', params: {id: row.id}})
this.operationPage = 'editLog' this.operationPage = 'editLog'
......
...@@ -68,9 +68,9 @@ ...@@ -68,9 +68,9 @@
requestAPI, requestAPI,
api api
} from '@/lib/commonMixin' } from '@/lib/commonMixin'
import ScheduleItem from '../../common/scheduleItem' import ScheduleItem from '../scheduleItem'
import clientHeader from '../../common/clientHeader' import clientHeader from '../../common/clientHeader'
import Pagenation from '../schedulePagenation' import Pagenation from '../../common/schedulePagenation'
import clickOutside from '@/lib/bind' import clickOutside from '@/lib/bind'
import addLog from '../work/addLog' import addLog from '../work/addLog'
import editLog from '../work/editLog' import editLog from '../work/editLog'
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
</single-input> </single-input>
</el-form-item> </el-form-item>
<el-form-item :show-message="false" :error="errorData['start_at'] ? errorData['start_at'] : ''"> <el-form-item :show-message="false" :error="errorData['start_at'] ? errorData['start_at'] : ''">
<single-date-time-picker <multiple-date-time-picker
:start-date-time-item="WorkLogs.start_at" :start-date-time-item="WorkLogs.start_at"
:end-date-time-item="WorkLogs.end_at" :end-date-time-item="WorkLogs.end_at"
start-label="开始时间" start-label="开始时间"
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
<span slot="formError" class="el-form-item__error"> <span slot="formError" class="el-form-item__error">
{{errorData['start_at']}} {{errorData['start_at']}}
</span> </span>
</single-date-time-picker> </multiple-date-time-picker>
</el-form-item> </el-form-item>
<el-form-item :show-message="false" :error="errorData['scenario_id'] ? errorData['scenario_id'] : ''"> <el-form-item :show-message="false" :error="errorData['scenario_id'] ? errorData['scenario_id'] : ''">
<single-radio <single-radio
...@@ -117,7 +117,7 @@ ...@@ -117,7 +117,7 @@
requestAPI, requestAPI,
api api
} from '@/lib/commonMixin' } from '@/lib/commonMixin'
import singleDateTimePicker from '../../common/singleDateTimePicker' import multipleDateTimePicker from '../../common/multipleDateTimePicker'
import singleSearchInput from '../../common/singleSearchInput' import singleSearchInput from '../../common/singleSearchInput'
import singleInput from '../../common/singleInput' import singleInput from '../../common/singleInput'
import singleCheckbox from '../../common/singleCheckbox' import singleCheckbox from '../../common/singleCheckbox'
...@@ -162,7 +162,7 @@ ...@@ -162,7 +162,7 @@
}, },
components: { components: {
SingleRadio, SingleRadio,
singleDateTimePicker, multipleDateTimePicker,
singleSearchInput, singleSearchInput,
singleInput, singleInput,
singleCheckbox, singleCheckbox,
......
...@@ -14,4 +14,7 @@ if (process.env.NODE_ENV !== 'production') { ...@@ -14,4 +14,7 @@ if (process.env.NODE_ENV !== 'production') {
import './assets/css/app.scss' import './assets/css/app.scss'
import RouterInit from './route' import RouterInit from './route'
import calendar from '@fullcalendar/vue'
Vue.component('FullCalendar', calendar)
RouterInit() RouterInit()
import portal from 'vis-portal' import portal from 'vis-portal'
import ElementRoute from './routes/workLog' import ElementRoute from './routes/workLog'
import Calendar from './routes/calendar'
let routes = [] let routes = []
const appName = 'worklog' const appName = 'worklog'
...@@ -10,6 +10,9 @@ let RouterInit = () => { ...@@ -10,6 +10,9 @@ let RouterInit = () => {
portal.createApp(appName, {}, app => { portal.createApp(appName, {}, app => {
app.mapRoute(routes) app.mapRoute(routes)
}) })
portal.createApp('calendar', {}, app => {
app.mapRoute(Calendar)
})
} }
export default RouterInit export default RouterInit
import calendar from '../components/calendar/list'
import fullCalendarGrid from '../components/calendar/fullCalendarGrid'
import fullCalendarLine from '../components/calendar/fullCalendarLine'
const projectTitle = '金畅逍BMS - '
const routes = [
{
path: '/calendar',
name: 'calendar',
component: calendar,
meta: {
title: projectTitle + '日程管理'
}
},
{
path: '/fullCalendarGrid',
name: 'fullCalendarGrid',
component: fullCalendarGrid,
meta: {
title: projectTitle + '日程管理'
}
},
{
path: '/fullCalendarLine',
name: 'fullCalendarLine',
component: fullCalendarLine,
meta: {
title: projectTitle + '日程管理'
}
}
]
export default routes
This diff is collapsed.
This diff is collapsed.
[
{
"request": {
"uri": "/vue/calendar/ajax-data"
},
"response": {
"json": {
"code": 2000,
"message": "\u64cd\u4f5c\u6210\u529f",
"result": {
"event": [
{
"id": 25185,
"title": "凤凰花",
"description": null,
"allDay": null,
"start": "2019-04-26 09:44:00",
"end": "2019-04-26 11:00:00",
"ranges": null,
"dow": null,
"url": null,
"className": null,
"editable": null,
"startEditable": null,
"durationEditable": null,
"source": null,
"color": null,
"backgroundColor": "#7cb5ec",
"borderColor": null,
"textColor": null,
"resourceId": 1000,
"nonstandard": {
"field1": "<p align=\"left\">哈哈哈<br/>凤凰花<br/>开始时间:2019-04-26 09:44 <br/> 结束时间:2019-04-26 11:00</p>",
"model": "{\"id\":25185,\"client_id\":1256,\"title\":\"\\u51e4\\u51f0\\u82b1\",\"description\":null,\"start_at\":\"2019-04-26 09:44:00\",\"end_at\":\"2019-04-26 11:00:00\",\"is_public\":0,\"created_by\":1000,\"created_at\":\"2019-04-26 09:45:04\",\"alarm_before\":\"00:00:00\",\"is_remind\":false,\"system_calendar_id\":\"34:D7:12:A3:51:CC_133\",\"is_system\":false,\"is_sync\":true,\"client_name\":\"\\u54c8\\u54c8\\u54c8\",\"can_edit\":true,\"can_delete\":true}",
"can_edit": true,
"can_delete": true
}
},
{
"id": 25805,
"title": "机动车尾号限行 4 和 9",
"description": null,
"allDay": null,
"start": "2019-04-26 07:00:29",
"end": "2019-04-26 20:00:29",
"ranges": null,
"dow": null,
"url": null,
"className": null,
"editable": null,
"startEditable": null,
"durationEditable": null,
"source": null,
"color": null,
"backgroundColor": "#90ed7d",
"borderColor": null,
"textColor": null,
"resourceId": 1000,
"nonstandard": {
"field1": "<p align=\"left\">机动车尾号限行 4 和 9<br/>开始时间:2019-04-26 07:00 <br/> 结束时间:2019-04-26 20:00</p>",
"model": "{\"id\":25805,\"client_id\":null,\"title\":\"\\u673a\\u52a8\\u8f66\\u5c3e\\u53f7\\u9650\\u884c 4 \\u548c 9\",\"description\":null,\"start_at\":\"2019-04-26 07:00:29\",\"end_at\":\"2019-04-26 20:00:29\",\"is_public\":0,\"created_by\":1000,\"created_at\":\"2019-04-26 15:48:32\",\"alarm_before\":\"12:00:00\",\"is_remind\":false,\"system_calendar_id\":\"34:D7:12:A3:51:CC_107\",\"is_system\":true,\"is_sync\":true,\"can_edit\":true,\"can_delete\":true}",
"can_edit": true,
"can_delete": true
}
},
{
"id": 25814,
"title": "机动车尾号限行 5 和 0",
"description": null,
"allDay": null,
"start": "2019-04-29 07:00:29",
"end": "2019-04-29 20:00:29",
"ranges": null,
"dow": null,
"url": null,
"className": null,
"editable": null,
"startEditable": null,
"durationEditable": null,
"source": null,
"color": null,
"backgroundColor": "#f7a35c",
"borderColor": null,
"textColor": null,
"resourceId": 1000,
"nonstandard": {
"field1": "<p align=\"left\">机动车尾号限行 5 和 0<br/>开始时间:2019-04-29 07:00 <br/> 结束时间:2019-04-29 20:00</p>",
"model": "{\"id\":25814,\"client_id\":null,\"title\":\"\\u673a\\u52a8\\u8f66\\u5c3e\\u53f7\\u9650\\u884c 5 \\u548c 0\",\"description\":null,\"start_at\":\"2019-04-29 07:00:29\",\"end_at\":\"2019-04-29 20:00:29\",\"is_public\":0,\"created_by\":1000,\"created_at\":\"2019-04-26 15:48:32\",\"alarm_before\":\"12:00:00\",\"is_remind\":false,\"system_calendar_id\":\"34:D7:12:A3:51:CC_115\",\"is_system\":true,\"is_sync\":true,\"can_edit\":true,\"can_delete\":true}",
"can_edit": true,
"can_delete": true
}
}
],
"default_date": "2019-04-22"
}
}
}
}
]
[ [
{
"include": "options-request.json"
},
{
"request": {
"method": "get"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/test.json"
},
{ {
"request": { "request": {
"method": "post" "method": "post"
...@@ -24,17 +10,6 @@ ...@@ -24,17 +10,6 @@
}, },
"include": "element/getUsers.json" "include": "element/getUsers.json"
}, },
{
"request": {
"method": "post"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/queryContent.json"
},
{ {
"request": { "request": {
"method": "post" "method": "post"
...@@ -66,6 +41,6 @@ ...@@ -66,6 +41,6 @@
"Access-Control-Allow-Origin": "*" "Access-Control-Allow-Origin": "*"
} }
}, },
"include": "element/status.json" "include": "element/events.json"
} }
] ]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment