Commit 9b4dfdfd authored by huai.li's avatar huai.li

工单管理

parent 5b6a0192
...@@ -43,6 +43,12 @@ ...@@ -43,6 +43,12 @@
"title": "文档管理", "title": "文档管理",
"appName": 'document', "appName": 'document',
"url": "/document" "url": "/document"
}, {
"icon": "fa-home",
"isRouteShow": 1,
"title": "工单管理",
"appName": 'case',
"url": "/case"
}], }],
homePage: { homePage: {
appName: 'reimbursement', appName: 'reimbursement',
......
...@@ -2,6 +2,7 @@ import ScheduleApi from './apis/schedule.js' ...@@ -2,6 +2,7 @@ import ScheduleApi from './apis/schedule.js'
import ReimburrsementApi from './apis/reimbursement.js' import ReimburrsementApi from './apis/reimbursement.js'
import TaskApi from './apis/task.js' import TaskApi from './apis/task.js'
import DocumentApi from './apis/document.js' import DocumentApi from './apis/document.js'
import CaseApi from './apis/case.js'
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
...@@ -30,6 +31,8 @@ apis = Object.assign(apis, ScheduleApi) ...@@ -30,6 +31,8 @@ apis = Object.assign(apis, ScheduleApi)
apis = Object.assign(apis, ReimburrsementApi) apis = Object.assign(apis, ReimburrsementApi)
apis = Object.assign(apis, TaskApi) apis = Object.assign(apis, TaskApi)
apis = Object.assign(apis, DocumentApi) apis = Object.assign(apis, DocumentApi)
apis = Object.assign(apis, CaseApi)
export default { export default {
option, option,
...apis ...apis
......
export default {
getCaseFilter: {
url: '/vue/case/get-filter'
},
getCaseList: {
url: '/vue/case/list'
},
getCaseNew: {
url: '/vue/case/get-new'
},
saveCaseNew: {
url: '/vue/case/save-new'
},
getCaseEdit: {
url: '/vue/case/get-edit'
},
saveCaseEdit: {
url: '/vue/case/save-edit'
},
deleteCase: {
url: '/vue/case/delete'
}
}
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
@import './reim-app.scss'; @import './reim-app.scss';
@import './task-app.scss'; @import './task-app.scss';
@import './document-app.scss'; @import './document-app.scss';
@import './case-app.scss';
.case-app {
a {
cursor: pointer;
color: white;
}
.border-17a2b8 {
border-color: #17a2b8;
color: #17a2b8;
}
}
<template>
<section class="ec-page-wrapper" style="overflow: hidden; padding-bottom: 0px;">
<el-form label-width="120px" ref="form" :rules="rules" :model="model" class="ec-create-form">
<el-form-item label="标题:" prop="title" class="ec-form-item ec-clear-left is-required">
<el-input
class="ec-input-normal"
size="mini"
v-model.trim="model.title">
</el-input>
</el-form-item>
<el-form-item label="备注:" prop="description" class="ec-clear-left ec-form-item-lg">
<el-input
class="ec-input-normal"
type="textarea"
:rows="3"
v-model.trim="model.description">
</el-input>
</el-form-item>
<el-form-item label="是否为模版:" prop="is_template" class="ec-clear-left is-required">
<single-radio
:form-item="model.is_template"
:options-list="docIsTemplateArray"
@update:item="val => {model.is_template = val}">
</single-radio>
</el-form-item>
<el-form-item label="文档类型:" prop="documentTypeTag" class="ec-clear-left is-required">
<single-radio
:form-item="model.documentTypeTag"
:options-list="docTypeTagsArray"
@update:item="val => {model.documentTypeTag = val}">
</single-radio>
</el-form-item>
<el-form-item label="文档部门:" prop="department_id" class="ec-clear-left is-required">
<single-radio
:form-item="model.department_id"
:options-list="docDepartmentIdArray"
@update:item="val => {model.department_id = val}">
</single-radio>
</el-form-item>
<el-form-item label="文档分类:" prop="documentType" class="ec-clear-left ec-form-item-lg is-required">
<el-select v-model="model.documentType" size="small" placeholder="请选择">
<el-option v-for="(option, optKey) in options.documentTypeArray"
:key="optKey"
:label="option.name"
:value="option.key"></el-option>
</el-select>
</el-form-item>
<el-form-item label="文档:" prop="attachment_id" class="ec-clear-left ec-form-item-lg is-required">
<el-input
class="ec-input-normal"
size="mini"
v-model.trim="model.attachment_id">
</el-input>
</el-form-item>
</el-form>
</section>
</template>
<script>
import singleRadio from '../common/singleRadio'
import settingMixin from '../common/settingMixin'
import {
requestAPI,
api
} from '@/lib/commonMixin'
export default {
name: 'documentForm',
mixins: [settingMixin],
props: {
model: Object
},
components: {
singleRadio
},
data () {
return {
options: {
documentTypeArray: []
},
rules: {
'title': [
{required: true, message: '标题不能为空', trigger: 'blur'}
],
'documentTypeTag': [
{required: true, message: '文档类型不能为空', trigger: 'change'}
],
'department_id': [
{required: true, message: '部门不能为空', trigger: 'change'}
],
'documentType': [
{required: true, message: '文档分类不能为空', trigger: 'blur'}
],
'attachment_id': [
{required: true, message: '未上传 ', trigger: 'blur'}
]
},
query: {}
}
},
created () {
this.$watch('model.department_id', function (val) {
this.getDocTypeArray(val)
})
},
mounted () {
},
computed: {
},
methods: {
getNew () {
this.initSetting(['getDocNew'])
},
getEdit (id) {
return this.initEditSetting('getDocEdit', id)
},
_validate (cb) {
this.$refs['form'].validate((valid) => {
if (valid) {
cb && cb()
}
})
},
_resetFields () {
this.$refs['form'].resetFields()
},
getDocTypeArray (id) {
requestAPI(api.getDocType, {
id: id
}).then(res => {
this.options.documentTypeArray = res
})
}
}
}
</script>
<style lang="scss" scoped>
.ec-create-form .el-form-item {
width: 70%;
}
// .ec-create-form .el-form-item {
// width: 100%;
// }
</style>
<template>
<section>
<sidePopup ref="sidePopup" title="新建文档" :width="50" @update:close="() => btnClose()">
<CaseForm
ref="docForm"
:model="model">
</CaseForm>
<div class="mt20" style="margin-left: 150px;">
<el-button type="cancel" @click.stop.prevent="handleClose" size="mini">取 消</el-button>
<el-button type="confirm" @click.stop.prevent="save" size="mini">确 定</el-button>
</div>
</sidePopup>
</section>
</template>
<script>
import {
requestAPI,
api,
UTIL
} from '@/lib/commonMixin'
import CaseForm from './caseForm'
import { setModule } from '@/lib/viewHelper'
export default {
components: {
CaseForm
},
data () {
return {
diaVis: false,
model: {
id: '',
title: '',
description: '',
is_template: 0,
documentTypeTag: '',
department_id: '',
documentType: '',
attachment_id: ''
}
}
},
methods: {
show (model) {
if (model) {
this.$refs.docForm.getEdit(model.id)
.then((_model) => {
UTIL.flatten(this.model, _model)
this.model.documentTypeTag = _model.documentTypeTag.key
})
} else {
this.$refs.docForm.getNew()
}
this.$refs.sidePopup.show()
},
handleClose () {
this.$refs.sidePopup.close(() => {
this.$refs.docForm._resetFields()
})
},
btnClose () {
this.$refs.docForm._resetFields()
},
save () {
let _params = Object.assign({}, setModule(this.model, 'Documents'))
let _apiUrl = !this.model.id ? api.saveDocNew : api.saveDocEdit
!this.model.id && delete _params['Documents[id]']
this.$refs['docForm']._validate(() => {
requestAPI(Object.assign(_apiUrl, { method: 'POST' }), _params)
.then((res) => {
this.$refs.sidePopup.close()
this.$message.success('操作成功')
this.$parent.reload()
}) // save
}) // validate
}
}
}
</script>
<style lang="scss" scoped>
</style>
<template>
<section>
<div class="content">
<search-header
ref="searchHeader"
:title="'工单管理'"
:search-key="'ClientSearch'"
:add-title="'新建Case'"
@update:headerSearch="search => searchKeyword(search)"
@update:headerAdd="() => addSch()"
@update:headerClear="() => searchClear()">
</search-header>
<search-form
ref="clientForm"
:filter="filter"
@update:clientList="form =>{ updateForm(form) }">
</search-form>
<div class="page-body-content leaveMessage">
<Item
v-for="item in result.list"
:item="item"
:key="item.id">
<span slot="opearate" class="obear-opearate-button">
<el-button
type="primary"
size="mini"
@click.prevent.stop="leaveMessageSch(item)">
<span class="badge" v-if="item.commentCount.comment">
{{ item.commentCount.comment ? item.commentCount.comment : '' }}
</span>
<i class="fa fa-commenting"></i>留言
</el-button>
<el-button
type="primary"
size="mini"
@click.prevent.stop="editSch(item)"
:disabled="item.can_update">
<i class="fa fa-edit animated"></i>编辑
</el-button>
<el-button
type="primary"
size="mini"
@click.prevent.stop="deleteSch(item.id)"
:disabled="!item.can_delete">
<i class="fa fa-trash-o animated-hove"></i>删除
</el-button>
<el-button
type="primary"
size="mini"
@click.prevent.stop="deleteSch(item.id)"
:disabled="!item.can_delete">
<i class="fa fa-fw fa-download"></i>下载
</el-button>
</span>
</Item>
<Pagenation
@update:pager="pager => {updatePage(pager)}"
:total="result.pagenation.totalcount">
</Pagenation>
<leave-message
v-click-outside="lmClose"
ref="leaveMessage"
:lmTemplate="lmTemplate"
:type="9">
</leave-message>
</div>
</div>
<FormModal ref="formModal"></FormModal>
</section>
</template>
<script>
import SearchHeader from '../common/searchHeader'
import SearchForm from '../common/SearchForm'
import Item from './caseItem'
import FormModal from '../case/caseModal'
import LeaveMessage from '../common/leaveMessage'
import Pagenation from './casePagenation'
import clickOutside from '@/lib/bind'
import SetParams from '../common/setParams'
import {
requestAPI,
api
} from '@/lib/commonMixin'
export default {
name: 'documentHome',
mixins: [SetParams],
components: {
SearchHeader,
SearchForm,
Item,
FormModal,
LeaveMessage,
Pagenation
},
directives: {
clickOutside
},
data () {
return {
lmTemplate: [
{
name: '客户名称',
value: 'client.name',
default: '未设置'
},
{
name: '内容',
value: 'description'
},
{
name: '待办时间',
value: 'schedule_week_display'
},
{
name: '状态',
value: 'schedule_status_display'
},
{
name: '开始时间',
value: 'start_at'
},
{
name: '结束时间',
value: 'end_at'
},
{
name: '重复',
value: 'repeat_type_display'
},
{
name: '负责人',
value: 'scheduleCreator.name'
},
{
name: '创建时间',
value: 'created_at'
}
],
filter: [],
form: {
'ClientSearch[keyword]': ''
},
pagenation: {
thispage: 1,
pagesize: 10
},
result: {
list: [],
pagenation: {
totalcount: 1,
thispage: 1,
pagesize: 10
},
thisUser: {
id: '',
name: '',
sex: ''
}
}
}
},
mounted () {
this.init()
this.getList({})
},
methods: {
init () {
this.getFilter()
},
// 1.查询条件
getFilter () {
requestAPI(api.getCaseFilter).then(res => {
this.filter = res
})
},
// 2.列表数据
getList (ret) {
requestAPI(api.getCaseList, ret).then((res) => {
const {
list = [],
pagenation = {},
thisUser = {}
} = res
this.result.list = list
this.result.pagenation = pagenation
this.result.thisUser = thisUser
})
},
// 3.1关键字搜索
searchKeyword (search) {
let ret = this.setParams({
...this.form,
page: this.pagenation.thispage
}, search)
this.getList(ret)
},
// 3.2关键字后面的重置
searchClear () {
this.init()
this._reload()
},
// 4.重新加载
_reload () {
let ret = this.setParams({
...this.form,
page: this.pagenation.thispage
}, {})
this.getList(ret)
},
// 5.1查询条件
updateForm (search) {
let ret = this.setParams({
...this.form,
page: this.pagenation.thispage
}, search)
this.getList(ret)
},
// 5.2分页查询
updatePage (pager) {
let ret = this.setParams({
...this.form,
page: this.pagenation.thispage
}, {page: pager.thispage})
this.getList(ret)
},
// 6.1新增
addSch () {
this.$refs.formModal.show()
},
// 6.2编辑
editSch (item) {
this.$refs.formModal.show(item)
},
// 7.删除
deleteSch (id) {
this.$confirm('删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
requestAPI(api.deleteDoc, { id })
.then((res) => {
this.$message.success('删除成功')
this._reload()
})
}).catch(() => {
this.$message.info('取消删除')
})
},
// 8.留言
leaveMessageSch (item) {
this.$refs.leaveMessage.isShow(item)
},
lmClose () {
this.$refs.leaveMessage &&
this.$refs.leaveMessage.isClose()
}
}
}
</script>
<style scoped lang="scss">
@include c('opearate-button') {
> .el-button {
margin-right:0px;
}
& .btn-primary .badge {
color: #333744;
background-color: #fff;
}
& .badge {
position: relative;
top: 0px;
left: -5px;
display: inline-block;
padding: 0px 5px;
font-size: 12px;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25rem;
background:white;
color:#333744;
}
i {
display: inline-block;
padding-right: 3px;
}
}
</style>
<template>
<section class="obear-schedule-item">
<el-row class="obear-schedule-row">
<el-col :span="4" :xs="24" class="obear-schedule-left" :style="{background: item.bgcolor}">
<el-row :gutter="10">
<el-col :span="24" style="padding-left: 3px;">
Case编号:<span>{{ item.case_no }}</span>
</el-col>
<el-col :span="24">
<a>
<i class="fa fa-star" aria-hidden="true"></i>{{ item.client.name }}
</a>
</el-col>
<el-col :span="24" style="padding-left: 5px;">
<span>
<i class="fa fa-fw fa-th-list" aria-hidden="true"></i>{{ isNullProject ? '(未设置)' : item.project.name }}
</span>
</el-col>
</el-row>
</el-col>
<el-col :span="20" :xs="24" class="obear-schedule-right">
<el-row :gutter="10">
<el-col :span="6" :xs="24">
<span>Case类型:</span><span class="obear-schedule-right__content border-17a2b8">{{ item.case_type }}</span>
</el-col>
<el-col :span="6" :xs="24">
<span>Case状态:</span><span class="obear-schedule-right__content" :style="{color: item.bgcolor, 'border-color': item.bgcolor }">{{ item.case_status_display }}</span>
</el-col>
<el-col :span="6" :xs="24">
<span>后续工作:</span><span>{{ item.uncompleteTickets ? '是' : '否' }}</span>
</el-col>
<el-col :span="6" :xs="24">
<span>合计分工:{{ item.points }}</span>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="24" :xs="24">
<span>服务目录:</span><span class="obear-schedule-right__content">{{ item.documentType }}</span>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="6" :xs="24">
<span>计划时间:</span><span class="obear-schedule-right__content">{{ item.start_at }}</span>
</el-col>
<el-col :span="6" :xs="24">
<span>关单时间:</span><span class="obear-schedule-right__content">{{ isEmptyCloseAt ? '(未设置)' : item.close_at }}</span>
</el-col>
<el-col :span="6" :xs="24">
<span>创建时间:</span><span>{{ item.created_at }}</span>
</el-col>
<el-col :span="6" :xs="24">
<span>创建人:{{ item.createdBy }}</span>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="6" :xs="24">
<span>回访内容:</span><span class="obear-schedule-right__content">{{ item.start_at }}</span>
</el-col>
<el-col :span="6" :xs="24">
<div v-if="item">
<span>回访尚未完成</span>
</div>
<div v-else>
<span>服务满意度得分:</span><span class="obear-schedule-right__content">{{ item.close_at }}</span>
<span>工程师技能得分:</span><span class="obear-schedule-right__content">{{ item.close_at }}</span>
</div>
</el-col>
<el-col :span="6" :xs="24">
<span>客户经理:</span><span>{{ item.created_at }}</span>
</el-col>
<el-col :span="6" :xs="24">
<span>工程师:{{ item.createdBy }}</span>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="6" :xs="24">
<span class="obear-schedule-right__content">Case说明</span><span>{{ item.description }}</span>
</el-col>
<el-col :span="6" :xs="24">
</el-col>
<el-col :span="6" :xs="24">
</el-col>
<el-col :span="6" :xs="24">
<slot name="opearate">
</slot>
</el-col>
</el-row>
</el-col>
</el-row>
</section>
</template>
<script>
export default {
name: 'documentItem',
props: {
item: Object
},
data () {
return {
}
},
computed: {
isNullProject () {
return !!this.item.Project
},
isEmptyCloseAt () {
return !!this.item.close_at
}
},
mounted () {
},
methods: {
}
}
</script>
<style scoped lang="scss">
$lightBlue: #20a0ff;
$lightRed: #dc3545;
.colRed {
color: red;
border: 1px solid red;
}
.colGreen {
color: green;
border: 1px solid green;
}
.colWhite {
color: white;
}
.collightBlue {
color: #649FD7;
}
.colBlue {
color: #0056b3;
}
.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') {
.obear-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: #EB7567;
color: white;
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{
color: white;
}
@include e('checkbox') {
display:block;
font-size: 12px;
background:white;
border-radius:2px;
color: $lightBlue;
padding: 0 2px 0 2px;
}
.el-checkbox {
margin-right: 10px;
}
span {
// display: inline-block;
// padding-left: 5px;
}
i {
display:inline-block;
padding-right:5px;
}
img {
display:inline-block;
margin-right:3px;
}
@include e('private') {
display: inline-block;
padding-left: 5px;
border-radius:5px;
border: 1px solid white;
width:35px;
height:17px;
}
}
@include c('schedule-right') {
height:100%;
padding: 10px 15px 6px;
> .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
}y: flex;
@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-radius:2px;
border: 1px solid;
padding: 0 2px 0 2px;
}
}
</style>
<template>
<section class="pull-right">
<el-pagination v-if="total > 0" class="mb20 mt20"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pager.thispage"
:page-sizes="[10, 20, 40, 60, 80, 100]"
:page-size="pager.pagesize"
layout="total, prev, pager, next, jumper"
:total="total">
</el-pagination>
</section>
</template>
<script>
export default {
props: {
total: {
type: Number,
default: 0
}
},
data () {
return {
pager: {
thispage: 1,
pagesize: 20,
totalcount: 0
}
}
},
created () {
// this.$emit('update:pager', this.pager)
},
methods: {
handleSizeChange (val) {
this.pager.thispage = 1
this.pager.pagesize = val
this.pager.totalcount = this.total
this.$emit('update:pager', this.pager)
},
handleCurrentChange (val) {
this.pager.thispage = val
this.pager.totalcount = this.total
this.$emit('update:pager', this.pager)
}
}
}
</script>
<style scoped>
</style>
...@@ -4,11 +4,13 @@ import ScheduleRoute from './routes/schedule' ...@@ -4,11 +4,13 @@ import ScheduleRoute from './routes/schedule'
import ReimbursementRoute from './routes/reimbursement' import ReimbursementRoute from './routes/reimbursement'
import TaskRoute from './routes/task' import TaskRoute from './routes/task'
import DocumentRoute from './routes/document' import DocumentRoute from './routes/document'
import CaseRoute from './routes/case'
let routes = [] let routes = []
let reimRoutes = [] let reimRoutes = []
let taskRoutes = [] let taskRoutes = []
let documentRoutes = [] let documentRoutes = []
let caseRoutes = []
const appName = 'schedule' const appName = 'schedule'
...@@ -16,6 +18,7 @@ routes = [].concat(ScheduleRoute) ...@@ -16,6 +18,7 @@ routes = [].concat(ScheduleRoute)
reimRoutes = [].concat(ReimbursementRoute) reimRoutes = [].concat(ReimbursementRoute)
taskRoutes = [].concat(TaskRoute) taskRoutes = [].concat(TaskRoute)
documentRoutes = [].concat(DocumentRoute) documentRoutes = [].concat(DocumentRoute)
caseRoutes = [].concat(CaseRoute)
let RouterInit = () => { let RouterInit = () => {
portal.createApp(appName, {}, app => { portal.createApp(appName, {}, app => {
...@@ -30,6 +33,9 @@ let RouterInit = () => { ...@@ -30,6 +33,9 @@ let RouterInit = () => {
portal.createApp('document', {}, app => { portal.createApp('document', {}, app => {
app.mapRoute(documentRoutes) app.mapRoute(documentRoutes)
}) })
portal.createApp('case', {}, app => {
app.mapRoute(caseRoutes)
})
// portal.createApp('client', {}, app => { // portal.createApp('client', {}, app => {
// app.mapRoute([ // app.mapRoute([
// { // {
......
import CaseHome from '../components/case_list/caseHome'
const routes = [{
path: '/case',
name: 'caseHome',
component: CaseHome
}]
export default routes
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