Skip to content
  • P
    Projects
  • G
    Groups
  • S
    Snippets
  • Help

杨树贤 / kefu_server

  • This project
    • Loading...
  • Sign in
Go to a project
  • Project
  • Repository
  • Issues 0
  • Merge Requests 0
  • Pipelines
  • Wiki
  • Snippets
  • Settings
  • Activity
  • Graph
  • Charts
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • Files
  • Commits
  • Branches
  • Tags
  • Contributors
  • Graph
  • Compare
  • Charts
Commit 9541c36a authored 5 years ago by chenxianqi's avatar chenxianqi
Browse files
Options
  • _('Browse Files')
  • Download
  • Email Patches
  • Plain Diff

add create workorder

parent 1142582d master
Hide whitespace changes
Inline Side-by-side
Showing with 611 additions and 252 deletions
  • ui/kefu_client/src/App.vue
  • ui/kefu_client/src/assets/arrow.png
  • ui/kefu_client/src/views/workorder_create.vue
  • ui/kefu_client/src/views/workorder_detail.vue
ui/kefu_client/src/App.vue
View file @ 9541c36a
......@@ -281,4 +281,7 @@ body {
margin: auto !important;
}
}
.workorder-create-picker .picker-item{
font-size 15px
}
</style>
This diff is collapsed. Click to expand it.
ui/kefu_client/src/assets/arrow.png 0 → 100644
View file @ 9541c36a
ui/kefu_client/src/assets/arrow.png

1.32 KB

This diff is collapsed. Click to expand it.
ui/kefu_client/src/views/workorder_create.vue
View file @ 9541c36a
<template>
<div class="container">
workorder_create
<mt-header v-if="isShowHeader" fixed title="创建工单">
<div slot="left">
<mt-button @click="$router.go(-1)" icon="back"></mt-button>
</div>
</mt-header>
<div class="content" :class="{'hide-header': !isShowHeader}">
<div class="field-line arrow-right" @click="isShowTypesPicker = true">
<span>类型:</span>
<span>{{selectTyped || '选择工单分类'}}</span>
</div>
<div class="field-line algin-left">
<span>标题:</span>
<input type="text" v-model="request.title" placeholder="请输入工单标题~">
</div>
<div class="field-line algin-left">
<span>手机:</span>
<input type="number" v-model="request.phone" placeholder="请输入您的手机~">
</div>
<div class="tip">必填,预留手机号方便客服联系到您~</div>
<div class="field-line algin-left">
<span>邮箱:</span>
<input type="email" v-model="request.email" placeholder="请输入您的电子邮箱~">
</div>
<div class="tip">非必填,预留邮箱后若工单回复后会通过邮箱通知您~</div>
<div class="field-line textarea">
<span>内容:</span>
<textarea v-model="request.content" placeholder="请输入您的工单内容~"></textarea>
</div>
<div class="field-line arrow-right file">
<span>附件:</span>
<span>{{file || '上传附件'}}</span>
<input type="file" />
</div>
<span class="sub-btn" @click="submit()">提交</span>
</div>
<!-- types-sheet -->
<div class="types-sheet" v-if="isShowTypesPicker">
<!-- <div class="mask" @click="isShowTypesPicker = false"></div> -->
<div class="picker-box">
<div class="title">
<span>选择工单类型</span>
<span class="sub-btn" @click="isShowTypesPicker = false">确定</span>
</div>
<mt-picker :slots="types" @change="onValuesChange"></mt-picker>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import { Toast } from "mint-ui";
import axios from "axios";
export default {
name: "workorder_create",
components: {},
data() {
return {};
return {
isSubmit: false,
request: {
"tid": 0,
"title": "",
"content": "",
"phone": "",
"email": ""
},
selectTyped: "",
file: "",
isShowTypesPicker: false
};
},
computed: {
...mapGetters([
"isShowHeader",
"workorders",
"userInfo",
"workorderTypes",
"uploadToken",
"workorderTypes"
]),
types() {
var values = [];
var slot = [
{
flex: 1,
values: [],
className: "workorder-create-picker",
textAlign: "center"
}
];
for (var i = 0; i < this.workorderTypes.length; i++) {
values.push(this.workorderTypes[i].title);
}
slot[0].values = values;
return slot;
}
},
mounted() {
},
mounted() {},
methods: {}
methods: {
onValuesChange(_, values) {
this.selectTyped = values[0]
for(var i=0; i<this.workorderTypes.length; i++){
if(values[0] == this.workorderTypes[i].title){
this.request.tid = this.workorderTypes[i].id
break
}
}
console.log(_)
},
submit(){
if(this.request.tid == 0){
Toast({
message: "请选择工单类型!"
});
return
}
if(this.request.title.trim() == ""){
Toast({
message: "工单标题不能为空!"
});
return
}
if(this.request.content.trim() == ""){
Toast({
message: "工单内容不能为空!"
});
return
}
if(this.isSubmit) return
this.isSubmit = true
axios
.post("/public/workorder/create", this.request)
.then(response => {
this.isSubmit = false
Toast({
message: "工单创建成功~"
});
setTimeout(()=>this.$router.replace("/workorder/detail/"+response.data.data), 500)
})
.catch(error => {
this.isSubmit = false
Toast({
message: error.response.data.message
});
console.log(error);
});
}
}
};
</script>
<style lang="stylus" scoped>
.content {
padding 50px 10px
.field-line{
display flex
justify-content space-between
box-sizing border-box
height 45px
border-bottom 1px solid #ddd
align-content center
align-items center
font-size 14px
color #333
span:first-child{
width 35px
}
input{
flex-grow 1
padding-left 10px
height 100%
background none
border 0
color #333
font-size 14px
border-radius 0
}
&.algin-left{
align-content left
align-items left
}
&.arrow-right{
background url(./../assets/arrow.png) right center no-repeat
background-size 18px
padding-right 25px
}
&.file{
position relative
overflow hidden
margin-top 20px
border-top 1px solid #ddd
input{
font-size 100px
opacity 0
position absolute
top 0
right 0
}
}
&.textarea{
align-items start
align-content start
border-bottom 0
padding-top 10px
height 100px
textarea{
flex-grow 1
border 0
height 100%
resize none
color #333
font-size 14px
padding 3px 10px
background-color rgba(0, 0, 0, 0.03);
border-radius 3px
}
}
}
.tip{
font-size 11px
color #ff9800
}
&.hide-header {
padding-top: 0;
}
.sub-btn {
display: block;
width: 100%;
height: 45px;
color: #fff;
margin-top 30px
line-height: 45px;
text-align: center;
border-radius: 3px;
border: none;
font-size: 14px;
background: linear-gradient(to right, #26a2ff, #736cde);
flex-shrink: 0;
&:active {
opacity: 0.8;
}
}
}
.types-sheet{
width 100vw;
height 100vh
position fixed
top 0
left 0
right 0
bottom 0
margin auto
background-color rgba(0,0,0,.5)
// .mask{
// width 100vw;
// height 100vh
// background-color rgba(0,0,0,.5)
// }
.picker-box{
height 250px
width 100vw
position absolute
bottom 0
left 0
right 0
margin 0 auto
background-color #fff
}
.title{
height 35px
border-bottom 1px solid #f3f3f3
display flex
justify-content space-between
padding 0 10px
box-sizing border-box
align-content center
align-items center
span{
font-size 14px
color #333
}
.sub-btn {
display: block;
width: 55px;
height: 30px;
color: #26a2ff
line-height: 30px;
text-align: right;
font-size: 14px;
font-weight 900
&:active {
opacity: 0.8;
}
}
}
}
</style>
This diff is collapsed. Click to expand it.
ui/kefu_client/src/views/workorder_detail.vue
View file @ 9541c36a
......@@ -7,8 +7,11 @@
<mt-button @click="close()" v-if="workorder.status != 3" slot="right">
<span>关闭工单</span>
</mt-button>
<mt-button @click="del()" v-else slot="right">
<span>删除</span>
</mt-button>
</mt-header>
<div class="content" :class="{'hide-header': !isShowHeader}">
<div class="content" :class="{'hide-header': !isShowHeader}">
<div class="head">
<div class="con">
<span>标题:</span>
......@@ -45,8 +48,12 @@
<template v-else v-for="(item,index) in comments">
<div :key="index" class="item">
<div class="avatar">
<img v-if="item.aid == '0'" :src="userInfo.avatar || 'http://qiniu.cmp520.com/avatar_degault_3.png'" alt="">
<img v-else :src="item.avatar || 'http://qiniu.cmp520.com/avatar_degault_3.png'" alt="">
<img
v-if="item.aid == '0'"
:src="userInfo.avatar || 'http://qiniu.cmp520.com/avatar_degault_3.png'"
alt
/>
<img v-else :src="item.avatar || 'http://qiniu.cmp520.com/avatar_degault_3.png'" alt />
</div>
<div class="right">
<div class="nickname" v-if="item.aid == '0'">我</div>
......@@ -56,17 +63,15 @@
</div>
</div>
</template>
<div class="workorder-close" v-if="workorder.status == 3">
工单已结束~
</div>
<div class="workorder-close" v-if="workorder.status == 3">工单已结束~</div>
</div>
<div class="file-view" v-if="request.file != '' || isShowUploadLoading">
<span v-if="isShowUploadLoading">
<img src="./../assets/loading.gif" alt="">
<img src="./../assets/loading.gif" alt />
<i>上传中~</i>
</span>
<span v-else>
<img src="./../assets/fujian1.png" alt="">
<img src="./../assets/fujian1.png" alt />
<i>你已成功添加附件,重新上传可替换~</i>
</span>
</div>
......@@ -81,8 +86,8 @@
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { Toast,MessageBox } from 'mint-ui';
import { mapGetters } from "vuex";
import { Toast, MessageBox } from "mint-ui";
import axios from "axios";
export default {
name: "workorder_detail",
......@@ -92,298 +97,365 @@ export default {
isShowUploadLoading: false,
isSubmit: false,
workorder: {},
comments:[],
comments: [],
fileType: "",
request:{
request: {
file: "",
content: "",
content: ""
}
};
},
computed: {
...mapGetters([
'isShowHeader',
'workorders',
'userInfo',
'workorderTypes',
"isShowHeader",
"workorders",
"userInfo",
"workorderTypes",
"uploadToken"
])
},
created() {
document.title = "工单详细"
const id = this.$route.params.id
this.$store.commit("updateState", {isShowPageLoading: true});
axios.all([this.getWorkOrder(id), this.getComments(id)])
.then(axios.spread(() => {
this.$store.commit("updateState", {isShowPageLoading: false});
}));
document.title = "工单详细";
const id = this.$route.params.id;
this.$store.commit("updateState", { isShowPageLoading: true });
axios.all([this.getWorkOrder(id), this.getComments(id)]).then(
axios.spread(() => {
this.$store.commit("updateState", { isShowPageLoading: false });
})
);
},
methods: {
commentView(comment){
var res = comment.match(/\[file=(.*)\]/)
if(res == null) return comment
var fileType = res[1].substr(res[1].lastIndexOf('.')+1)
if("jpg,jpeg,png,JPG,JPEG,PNG".indexOf(fileType) != -1){
comment = comment.replace(res[0], "<br><img style='max-width:60%' preview='1' src='"+res[1]+"' />")
commentView(comment) {
var res = comment.match(/\[file=(.*)\]/);
if (res == null) return comment;
var fileType = res[1].substr(res[1].lastIndexOf(".") + 1);
if ("jpg,jpeg,png,JPG,JPEG,PNG".indexOf(fileType) != -1) {
comment = comment.replace(
res[0],
"<br><img style='max-width:60%' preview='1' src='" + res[1] + "' />"
);
this.$previewRefresh();
}
return comment
return comment;
},
getWorkOrder(id){
return axios.get("/public/workorder/"+id).then(response => {
this.workorder = response.data.data
}).catch(error => {
console.log(error)
});
getWorkOrder(id) {
return axios
.get("/public/workorder/" + id)
.then(response => {
this.workorder = response.data.data;
})
.catch(error => {
console.log(error);
});
},
getComments(id){
return axios.get("/public/workorder/comments/"+id).then(response => {
if(response.data.data == null) return;
this.comments = response.data.data
}).catch(error => {
console.log(error)
});
getComments(id) {
return axios
.get("/public/workorder/comments/" + id)
.then(response => {
if (response.data.data == null) return;
this.comments = response.data.data;
})
.catch(error => {
console.log(error);
});
},
reply(){
const content = this.request.content + this.request.file
if(content.trim() == ''){
reply() {
const content = this.request.content + this.request.file;
if (content.trim() == "") {
Toast({
message: "请输入内容~"
})
return
});
return;
}
if(this.isSubmit) return
this.isSubmit = true
const wid = this.workorder.id
axios.post("/public/workorder/reply", {wid, content}).then(response => {
this.getComments(wid)
this.request = {
file: "",
content: "",
}
}).catch(error => {
Toast({
message: "提交失败~"
if (this.isSubmit) return;
this.isSubmit = true;
const wid = this.workorder.id;
axios
.post("/public/workorder/reply", { wid, content })
.then(response => {
this.isSubmit = false
console.log(response);
this.getComments(wid);
this.request = {
file: "",
content: ""
};
})
});
.catch(error => {
this.isSubmit = false
console.log(error);
Toast({
message: "提交失败~"
});
});
},
inputBlur(){
  setTimeout(() => { 
  document.body.scrollTo = 0;
window.scrollTo(0, 0);  
}, 100);
inputBlur() {
setTimeout(() => {
document.body.scrollTo = 0;
window.scrollTo(0, 0);
}, 100);
},
uploadFile(e){
uploadFile(e) {
var fileDom = e.target;
var file = fileDom.files[0];
this.isShowUploadLoading = true
const self = this
this.isShowUploadLoading = true;
const self = this;
this.$uploadFile({
file,
mode: this.uploadToken.mode,
// 七牛才会执行
percent(res) {},
success(src) {
self.request.file = '[file=' + self.uploadToken.host + '/' + src + ']'
self.isShowUploadLoading = false
},
fail(e) {
self.isShowUploadLoading = false
if(e.response && e.response.data){
Toast({
message: e.response.data.message
})
return
}
file,
mode: this.uploadToken.mode,
// 七牛才会执行
percent() {},
success(src) {
self.request.file =
"[file=" + self.uploadToken.host + "/" + src + "]";
self.isShowUploadLoading = false;
},
fail(e) {
self.isShowUploadLoading = false;
if (e.response && e.response.data) {
Toast({
message: e.response.data.message
});
return;
}
});
}
});
},
close(){
var wid = this.workorder.id
MessageBox.confirm('您确定关闭该工单吗?').then(action => {
axios.put("/public/workorder/close/"+wid).then(response => {
close() {
var wid = this.workorder.id;
MessageBox.confirm("您确定关闭该工单吗?").then(() => {
axios
.put("/public/workorder/close/" + wid)
.then(response => {
console.log(response);
Toast({
message: "工单已关闭~"
})
this.getWorkOrder(wid)
}).catch(error => {
Toast({
message: "工单关闭失败~"
});
this.getWorkOrder(wid);
})
console.log(error)
});
.catch(error => {
Toast({
message: "工单关闭失败~"
});
console.log(error);
});
});
},
del() {
var wid = this.workorder.id;
MessageBox.confirm("您确定删除该工单吗?").then(() => {
axios
.delete("/public/workorder/" + wid)
.then(response => {
console.log(response);
Toast({
message: "工单已删除~"
});
setTimeout(() => this.$router.go(-1));
})
.catch(error => {
Toast({
message: "工单关闭失败~"
});
console.log(error);
});
});
}
}
};
</script>
<style lang="stylus" scoped>
.container{
height 100vh
overflow hidden
overflow-y auto
.container {
height: 100vh;
overflow: hidden;
overflow-y: auto;
}
.content {
padding-top: 50px;
padding-bottom: 90px;
.no-data {
color: #666;
font-size: 14px;
}
.content{
padding-top 50px
padding-bottom: 90px;
.no-data{
color #666
font-size 14px
}
.workorder-close{
text-align center
color #666
font-size 14px
padding 10px
}
&.hide-header{
padding-top 0
.workorder-close {
text-align: center;
color: #666;
font-size: 14px;
padding: 10px;
}
&.hide-header {
padding-top: 0;
}
.head {
margin: 0 10px;
padding: 10px 0;
border-bottom: 1px solid rgba(158, 158, 158, 0.13);
.con {
font-size: 15px;
color: #333;
display: flex;
margin-bottom: 8px;
span {
flex-flow: 1;
}
span:first-child {
flex-flow: 0;
flex-shrink: 0;
width: 45px;
}
i {
font-style: normal;
}
}
.head{
margin 0 10px
padding 10px 0
border-bottom 1px solid rgba(158, 158, 158, 0.13);
.con{
font-size 15px
color #333
display flex
margin-bottom 8px
span{
flex-flow 1
}
span:first-child{
flex-flow 0
flex-shrink 0
width 45px
}
i{
font-style normal
}
.comments {
padding: 10px;
.item {
display: flex;
.avatar {
padding-top: 10px;
img {
width: 30px;
height: 30px;
border-radius: 100px;
display: block;
}
border-bottom: 1px solid rgba(158, 158, 158, 0.13);
}
}
.comments{
padding 10px
.item{
display flex
.avatar{
padding-top 10px
img{
width 35px
height 35px
border-radius 100px
display block
}
border-bottom 1px solid rgba(158, 158, 158, 0.13);
.right {
padding: 10px 5px;
flex-grow: 1;
border-bottom: 1px solid rgba(158, 158, 158, 0.13);
.nickname {
font-size: 15px;
color: #333;
}
.right{
padding 10px 5px
flex-grow 1
border-bottom 1px solid rgba(158, 158, 158, 0.13);
.nickname{
font-size 15px
color #333
}
.detail{
font-size 15px
color #333
margin-top 5px
}
.date{
color #999
font-size 14px
margin-top 5px
}
.detail {
font-size: 15px;
color: #333;
margin-top: 5px;
}
&:last-child{
.right,.avatar{
border-bottom 0
}
.date {
color: #999;
font-size: 14px;
margin-top: 5px;
}
}
}
.file-view{
position fixed
bottom 80px
left 0
right 0
padding 5px 10px
margin 0 auto
font-size 13px
color #8bc34a;
span {
display flex
align-content center
align-items center
img{
width 20px
height 20px
}
i{
font-style normal
margin-left 5px
&:last-child {
.right, .avatar {
border-bottom: 0;
}
}
}
.input-form{
position fixed
bottom 0
left 0
right 0
margin 0 auto
width 100%
height 80px
background-color #fff
border-top 1px solid rgba(158, 158, 158, 0.13);
display flex
justify-content space-between
padding 0 10px
box-sizing border-box
align-content center
align-items center
textarea{
height 45px
flex-grow 1
border-radius 0
border 0
color #333
font-size 14px
resize none
}
.file-view {
position: fixed;
bottom: 80px;
left: 0;
right: 0;
padding: 5px 10px;
margin: 0 auto;
font-size: 13px;
color: #8bc34a;
span {
display: flex;
align-content: center;
align-items: center;
img {
width: 20px;
height: 20px;
}
.icon-btn{
background url(./../assets/upload.png) center center no-repeat
background-size 30px
width: 55px;
height 55px
overflow hidden
input{
display block
width 100%
height 100%
font-size 100px
opacity 0
}
i {
font-style: normal;
margin-left: 5px;
}
.sub-btn {
display block
width: 55px;
height: 30px;
color: #fff;
line-height: 30px;
text-align: center;
border-radius: 3px;
border: none;
font-size: 14px;
background: linear-gradient(to right, #26a2ff, #736cde);
flex-shrink: 0;
}
}
&:active {
opacity: 0.8;
}
.input-form {
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: 0 auto;
width: 100%;
height: 80px;
background-color: #fff;
border-top: 1px solid rgba(158, 158, 158, 0.13);
display: flex;
justify-content: space-between;
padding: 0 10px;
box-sizing: border-box;
align-content: center;
align-items: center;
textarea {
height: 45px;
flex-grow: 1;
border-radius: 0;
border: 0;
color: #333;
font-size: 14px;
resize: none;
}
.icon-btn {
background: url('./../assets/upload.png') center center no-repeat;
background-size: 30px;
width: 55px;
height: 55px;
overflow: hidden;
input {
display: block;
width: 100%;
height: 100%;
font-size: 100px;
opacity: 0;
}
}
.sub-btn {
display: block;
width: 55px;
height: 30px;
color: #fff;
line-height: 30px;
text-align: center;
border-radius: 3px;
border: none;
font-size: 14px;
background: linear-gradient(to right, #26a2ff, #736cde);
flex-shrink: 0;
&:active {
opacity: 0.8;
}
}
}
}
</style>
This diff is collapsed. Click to expand it.
  • Write
  • Preview
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 sign in to comment