You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
customAgent/src/components/problem-feedback.vue

500 lines
13 KiB

<template>
<div class="problem-feedback-box" ref="containerRef">
<van-sticky class="problem-feedback-title" :container="containerRef">
<div class="problem-title">
<h2>问题反馈</h2>
<a class="close-popup" href="javascript:;" @click.stop="handleClose"></a>
</div>
</van-sticky>
<div class="problem-feedback-form">
<van-form @submit="submitForm" ref="feedbackformRef">
<div class="problem-feedback-item">
<h2 class="item-block-title">反馈信息录入</h2>
<van-field
readonly
clickable
is-link
:error="false"
name="picker"
:value="dataForm.feedbackSource"
label="移动端渠道"
placeholder="点击选择移动端渠道"
:rules="[{ required: true, message: '移动端渠道未选择,请选择后重新提交' }]"
@click="sourceShowPicker = true"
/>
<van-popup v-model:show="sourceShowPicker" position="bottom" round>
<van-picker
show-toolbar
title="移动端渠道"
:columns="sourceColumns"
@confirm="sourceOnConfirm"
:item-height="32"
:visible-item-count="5"
:loading="feedbackLoading"
@cancel="sourceShowPicker = false"
/>
</van-popup>
<van-field
readonly
clickable
is-link
name="picker"
:error="false"
:value="dataForm.businessType"
label="业务类型"
placeholder="点击选择业务类型"
:rules="[{ required: true, message: '业务类型未选择,请选择后重新提交' }]"
@click="typeShowPicker = true"
/>
<van-popup v-model:show="typeShowPicker" position="bottom" round>
<van-picker
show-toolbar
title="业务类型"
value-key="value"
:columns="typeColumns"
@confirm="typeOnConfirm"
:item-height="32"
:visible-item-count="5"
:loading="businessLoading"
@cancel="typeShowPicker = false"
/>
</van-popup>
<van-field
:error="false"
:rules="[{ required: true, message: '问题表述未填写,请填写后重新提交' }]"
v-model="dataForm.questionDesc"
rows="5"
clearable
label="问题描述"
maxlength="300"
type="textarea"
show-word-limit
:placeholder="feedbackPlaceholder"
/>
</div>
<div class="problem-feedback-item">
<h2 class="item-block-title">
上传图片<span class="title-tips">(上传对应问题的图像,最多上传5张)</span>
</h2>
<van-field
name="uploader"
:error="false"
:rules="[
{
required: true,
validator: validatorImgIds,
message: '图像未上传,请上传图像后重新提交',
},
]"
>
<template #input>
<van-uploader
v-model="dataForm.fileList"
:after-read="onAfterRead"
@oversize="onOversize"
:before-delete="deleteImage"
accept="image/*"
multiple
:max-count="5"
:max-size="5242880"
:preview-size="66"
upload-icon="plus"
/>
</template>
</van-field>
</div>
<div class="footer-btn">
<van-button type="primary" color="#5A9CFF" block round native-type="submitForm"
>确认提交</van-button
>
</div>
</van-form>
</div>
</div>
</template>
<script setup>
import { ref, reactive, inject, onMounted } from 'vue'
import {
showToast,
showLoadingToast,
closeToast,
showDialog,
Field,
Sticky,
Button,
Form,
Popup,
Uploader,
Picker,
} from 'vant'
import { Post } from '@/utils/https'
import { compressImage } from '@/utils/CompressImage'
const emit = defineEmits(['handleClose'])
const store = inject('appStore', null)
const userId = inject('userId', ref(''))
const sessionId = inject('sessionId', ref(''))
const source = inject('source', ref(''))
const qaId = inject('qaId', ref(''))
const mescrollContent = inject('mescrollContent', ref(null))
const containerRef = ref(null)
const feedbackformRef = ref(null)
const hostname = window.location.protocol + '//' + window.location.host
const sourceColumns = ref([])
const typeColumns = ref([])
const feedbackCloseTip = ref('')
const sourceShowPicker = ref(false)
const typeShowPicker = ref(false)
const feedbackLoading = ref(false)
const businessLoading = ref(false)
const feedbackPlaceholder = ref(
'为更好定位并解决问题,问题表述中需包括但不限于问题发生的时间、具体功能模块及问题表现。如2020年8月25日12点30分操作身份认证时,其姓名处无法输入。'
)
const fileUpload = ref(new FormData())
const dataForm = reactive({
feedbackSource: '',
businessType: '',
businessType2: '',
questionDesc: '',
imgIds: [],
fileList: [],
})
onMounted(() => {
getData()
})
function getData() {
feedbackLoading.value = true
businessLoading.value = true
Post('config/feedbackConfig')
.then((res) => {
if (res.code && res.code != 0) {
showToast(res.msg)
return
}
if (res.data.feedbackBusinessType) {
typeColumns.value = JSON.parse(res.data.feedbackBusinessType)
feedbackLoading.value = false
}
if (res.data.feedbackSource) {
sourceColumns.value = JSON.parse(res.data.feedbackSource)
businessLoading.value = false
}
if (res.data.feedbackCloseTip) feedbackCloseTip.value = res.data.feedbackCloseTip
if (res.data.feedbackPlaceholder) feedbackPlaceholder.value = res.data.feedbackPlaceholder
})
.catch((error) => console.log(error))
}
function sourceOnConfirm(value) {
dataForm.feedbackSource = value
sourceShowPicker.value = false
}
function typeOnConfirm(value) {
dataForm.businessType = value.value
dataForm.businessType2 = value.label
typeShowPicker.value = false
}
function onOversize() {
showToast('文件大小不能超过5M')
}
function validatorImgIds() {
if (dataForm.imgIds.length > 0) return true
showToast('请等待图片上传完成,再提交')
return false
}
function asyncForEachFile(file) {
return new Promise((resolve) => {
getDataFiles(0, file.length)
function getDataFiles(i, len) {
file[i].status = 'uploading'
file[i].message = '上传中...'
compressAndUploadFile(file[i], (resultFile) => {
fileUpload.value.append('files', resultFile)
if (++i < len) {
getDataFiles(i, len)
} else {
resolve(fileUpload.value)
}
})
}
})
}
function onAfterRead(file) {
fileUpload.value = new FormData()
if (dataForm.fileList.length > 5) {
showToast('图片上传最多5张!')
dataForm.fileList.splice(5)
return
}
if (Array.isArray(file) && file.length) {
asyncForEachFile(file).then((result) => {
_uploadFile(
result,
(res) => {
if (res && res.length > 0) {
res.forEach((item, index) => {
file[index].status = 'success'
file[index].url = `${hostname}/liics_image${item.url}`
file[index].id = item.id
dataForm.imgIds.push(item.id)
})
}
},
() => {
file.forEach((item) => {
item.status = 'failed'
item.message = '上传失败...'
})
}
)
})
} else {
file.status = 'uploading'
file.message = '上传中...'
compressAndUploadFile(file, (resultFile) => {
fileUpload.value.append('files', resultFile)
_uploadFile(
fileUpload.value,
(res) => {
file.status = 'success'
file.url = `${hostname}/liics_image${res[0].url}`
file.id = res[0].id
dataForm.imgIds.push(res[0].id)
},
() => {
file.status = 'failed'
file.message = '上传失败...'
}
)
})
}
}
function deleteImage(file, name) {
const idx = name.index
if (!file.id) {
dataForm.fileList.splice(idx, 1)
return
}
const ids = [file.id]
showDialog({ title: '提示', message: `确定对[id=${ids.join(',')}]进行删除操作?` })
.then(() => {
const deleteToast = showLoadingToast({
message: '删除中...',
duration: 0,
loadingType: 'spinner',
forbidClick: true,
})
Post('upload/delete', ids)
.then((res) => {
closeToast()
if (res.code && res.code != 0) {
showToast(res.msg)
return
}
dataForm.fileList.splice(idx, 1)
dataForm.imgIds.splice(idx, 1)
})
.catch((error) => {
closeToast()
console.log(error)
})
})
.catch(() => console.log('取消删除'))
}
function compressAndUploadFile(file, callback) {
compressImage(file.content, { file, quality: 0.8 }).then((result) => {
const resultFile = result.blob.size > file.file.size ? file.file : result.blob
callback && callback(resultFile)
})
}
function _uploadFile(files, callback, errorCallback) {
upLoadImg('upload/images', files)
.then((res) => {
if (res.code && res.code != 0) {
showToast(res.msg)
return
}
callback && callback(res.data)
})
.catch((err) => errorCallback && errorCallback(err))
}
function upLoadImg(url, formData) {
return Post(url, formData, { headers: { 'Content-Type': 'multipart/form-data' } })
}
function handleClose() {
showDialog({
title: '关闭提示',
message: feedbackCloseTip.value,
cancelButtonText: '返回填写',
confirmButtonText: '坚持退出',
beforeClose: beforeClose,
}).catch((err) => console.log(err))
}
function beforeClose(action, done) {
if (action === 'confirm') {
closeDialog(
0,
() => {
done()
emit('handleClose', false)
},
() => done()
)
} else {
done()
}
}
function closeDialog(type, callback, errorBack) {
const params = {
userInfoId: userId.value,
sessionId: sessionId.value,
source: source.value,
qaId: qaId.value || '',
closeType: type || 0,
}
const closeToast = showLoadingToast({
message: '退出中...',
duration: 0,
loadingType: 'spinner',
forbidClick: true,
})
Post('question/feedbackClose', params)
.then((res) => {
closeToast()
callback && callback(res)
if (res.code && res.code != 0) {
showToast(res.msg)
return
}
setTimeout(() => {
store?.commit('set_chatList', res.data)
store?.commit('scrollBottom')
}, 200)
})
.catch((error) => {
closeToast()
errorBack && errorBack(error)
console.log(error)
})
}
function submitForm() {
const params = {
userInfoId: userId.value,
sessionId: sessionId.value,
source: source.value,
qaId: qaId.value || '',
feedbackSource: dataForm.feedbackSource,
businessType: dataForm.businessType2,
questionDesc: dataForm.questionDesc,
imgIds: dataForm.imgIds,
closeType: 0,
}
const submitToast = showLoadingToast({
message: '提交中...',
duration: 0,
loadingType: 'spinner',
forbidClick: true,
})
Post('question/feedbackSubmit', params)
.then((res) => {
closeToast()
if (res.code && res.code != 0) {
showToast(res.msg)
return
}
setTimeout(() => {
store?.commit('set_chatList', res.data)
store?.commit('scrollBottom')
}, 200)
emit('handleClose', false)
})
.catch((error) => {
closeToast()
console.log(error)
})
}
</script>
<style scoped>
.problem-feedback-box {
background: #f7f8fa;
min-height: 100vh;
}
.problem-feedback-title .problem-title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: #fff;
}
.problem-feedback-title h2 {
font-size: 18px;
font-weight: 600;
}
.close-popup {
width: 24px;
height: 24px;
background: url('data:image/svg+xml,...') no-repeat center;
background-size: contain;
}
.problem-feedback-form {
padding: 12px 16px;
}
.problem-feedback-item {
background: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
}
.item-block-title {
font-size: 15px;
font-weight: 600;
margin-bottom: 8px;
}
.title-tips {
font-size: 12px;
color: #999;
font-weight: normal;
}
.footer-btn {
margin-top: 24px;
padding-bottom: 24px;
}
</style>