<template>
|
<div>
|
<el-dialog :close-on-click-modal="false" :close-on-press-escape="false" v-model="formDia" title="客户满意度调查表"
|
width="70%" @close="closeFormDia">
|
<div class="survey-intro">
|
尊敬的客户:为了使本实验室的服务水平得以不断提高,特设此客户满意度调查表,烦请您拨冗填写。我们十分注重您的反馈,您的意见和建议,将为本实验室的持续改进提供重要参考,从而达到您期望的水平。请以传真或邮件方式将此表回复本实验室,多谢合作!在此非常感谢您对我们工作的支持!
|
</div>
|
<table border="1" cellspacing="10" class="tables">
|
<colgroup>
|
<col style="width: 12%" />
|
<col style="width: 12%" />
|
<col style="width: 12%" />
|
<col style="width: 10.66%" />
|
<col style="width: 10.66%" />
|
<col style="width: 10.66%" />
|
<col style="width: 10.66%" />
|
<col style="width: 10.66%" />
|
<col style="width: 10.66%" />
|
</colgroup>
|
<tr>
|
<td class="section-title" colspan="9">
|
<p>客 户 填 写</p>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title">
|
<p>客户:</p>
|
</td>
|
<td class="td-info" colspan="8">
|
<el-select v-model="form.orderId" filterable clearable size="small" style="width: 100%"
|
popper-class="customer-select-popper" placeholder="请选择客户(已完成委托单)" @change="onCustomerChange">
|
<el-option v-for="opt in customerOptions" :key="opt.id" :label="opt.company" :value="opt.id">
|
<div class="customer-card">
|
<div>客户:{{ opt.company }}</div>
|
<div class="customer-card-sub">委托单编号:{{ opt.entrustCode || '--' }}</div>
|
<div class="customer-card-sub">样品名称:{{ opt.sampleName || '--' }}</div>
|
</div>
|
</el-option>
|
</el-select>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title">
|
<p>地址:</p>
|
</td>
|
<td class="td-info" colspan="8">
|
<el-input v-model="form.address" placeholder="请输入内容" size="small">
|
</el-input>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title">
|
<p>联系人:</p>
|
</td>
|
<td class="td-info" colspan="3">
|
<el-input v-model="form.contactPerson" placeholder="请输入内容" size="small">
|
</el-input>
|
</td>
|
<td class="td-title">
|
<p>填表日期:</p>
|
</td>
|
<td class="td-info" colspan="4">
|
<el-date-picker v-model="form.fillDate" format="YYYY-MM-DD" placeholder="选择日期" size="small" type="date"
|
value-format="YYYY-MM-DD" style="width: 100%">
|
</el-date-picker>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title" colspan="3">
|
<p>评分项</p>
|
</td>
|
<td class="score-head" colspan="2">优秀</td>
|
<td class="score-head" colspan="2">一般</td>
|
<td class="score-head" colspan="2">差</td>
|
</tr>
|
<tr>
|
<td class="td-title" colspan="3">
|
<p>(分值:10—5分)</p>
|
</td>
|
<td class="score-head" v-for="s in scoreOptions" :key="'head-' + s">{{ s }}</td>
|
</tr>
|
<tr v-for="(q, qi) in questions" :key="q.key">
|
<td class="td-title question-title" colspan="3">
|
<p>{{ q.text }}</p>
|
</td>
|
<td class="score-cell" v-for="s in scoreOptions" :key="s">
|
<el-radio v-model="form[q.key]" :value="s" class="score-radio"></el-radio>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title" colspan="3">
|
<p>*您认为本实验室还有哪些不足,或对我们的工作还有哪些意见和建议?请说明:</p>
|
</td>
|
<td class="td-info" colspan="6">
|
<el-input v-model="form.remark" :rows="4" placeholder="请输入内容" size="small" type="textarea">
|
</el-input>
|
</td>
|
</tr>
|
<tr>
|
<td class="section-title" colspan="9">
|
<p>实 验 室 填 写</p>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title">
|
<p>总分:</p>
|
</td>
|
<td class="td-info">
|
<span class="score-result">{{ scoreComplete ? totalScore : '--' }}</span> 分
|
</td>
|
<td class="td-title" colspan="2">
|
<p>满意度评定:</p>
|
</td>
|
<td class="td-info" colspan="5">
|
{{ scoreComplete ? satisfactionText : '填写评分后自动计算' }}
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title">
|
<p>意见:</p>
|
</td>
|
<td class="td-info" colspan="8">
|
<el-input v-model="form.labOpinion" :rows="3" placeholder="请输入内容" size="small" type="textarea">
|
</el-input>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title">
|
<p>质量负责人:</p>
|
</td>
|
<td class="td-info" colspan="3">
|
<el-input v-model="form.qualityManager" placeholder="请输入内容" size="small">
|
</el-input>
|
</td>
|
<td class="td-title">
|
<p>日期:</p>
|
</td>
|
<td class="td-info" colspan="4">
|
<el-date-picker v-model="form.labDate" format="YYYY-MM-DD" placeholder="选择日期" size="small" type="date"
|
value-format="YYYY-MM-DD" style="width: 100%">
|
</el-date-picker>
|
</td>
|
</tr>
|
<tr>
|
<td class="td-title">
|
<p>照片:</p>
|
</td>
|
<td class="td-info" colspan="8">
|
<lims-attachment ref="photoAttachment" inline defer-upload accept=".jpg,.jpeg,.png,.gif"
|
:main-id="form.clientSatisfactionId || ''" upload-url="/clientSatisfaction/uploadAttachment"
|
select-url="/clientSatisfaction/selectAttachments" delete-url="/clientSatisfaction/deleteAttachment"
|
id-param="clientSatisfactionId" tip="支持 jpg / png / gif 照片,可预览,提交时一并上传"></lims-attachment>
|
</td>
|
</tr>
|
</table>
|
<template #footer><span class="dialog-footer">
|
<el-button @click="closeFormDia">取 消</el-button>
|
<el-button :loading="editLoad" type="primary" @click="handleEdit">提 交</el-button>
|
</span></template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import {
|
addClientSatisfaction,
|
updateClientSatisfaction
|
} from '@/api/cnas/systemManagement/customerSatisfaction.js'
|
import { selectCompletedOrders } from '@/api/business/productOrder'
|
import limsAttachment from '@/components/Attachment/lims-attachment.vue'
|
|
const QUESTIONS = [
|
{ key: 'score1', text: '1.业务/接待人员的服务态度' },
|
{ key: 'score2', text: '2.业务/接待人员的沟通能力' },
|
{ key: 'score3', text: '3.业务/接待人员的响应速度或回复及时性' },
|
{ key: 'score4', text: '4.出具检测报告的及时性' },
|
{ key: 'score5', text: '5.检测结果的准确性' },
|
{ key: 'score6', text: '6.检测报告内容表达清晰,编排合理并易于理解' },
|
{ key: 'score7', text: '7.处理投诉抱怨的及时性' },
|
{ key: 'score8', text: '8.对投诉抱怨处理的结果' },
|
{ key: 'score9', text: '9.对我司服务的专业性评价' },
|
{ key: 'score10', text: '10.对我司服务的总体满意程度评价' }
|
]
|
|
function createEmptyForm() {
|
const form = {
|
clientSatisfactionId: '',
|
orderId: '',
|
unitName: '',
|
userName: '',
|
address: '',
|
contactPerson: '',
|
fillDate: '',
|
remark: '',
|
labOpinion: '',
|
qualityManager: '',
|
labDate: null,
|
entrustCode: '',
|
sampleName: ''
|
}
|
QUESTIONS.forEach(q => {
|
form[q.key] = ''
|
})
|
return form
|
}
|
|
export default {
|
name: 'formDialog',
|
components: { limsAttachment },
|
data() {
|
return {
|
formDia: false,
|
form: createEmptyForm(),
|
questions: QUESTIONS,
|
scoreOptions: [10, 9, 8, 7, 6, 5],
|
customerOptions: [],
|
operationType: '',
|
editLoad: false,
|
};
|
},
|
computed: {
|
scoreComplete() {
|
return this.questions.every(q => this.form[q.key] !== '' && this.form[q.key] !== null && this.form[q.key] !== undefined)
|
},
|
totalScore() {
|
return this.questions.reduce((sum, q) => sum + (Number(this.form[q.key]) || 0), 0)
|
},
|
satisfactionText() {
|
if (!this.scoreComplete) {
|
return ''
|
}
|
return this.totalScore >= 80 ? '满意(80分(含)以上)' : '不满意(80分以下)'
|
}
|
},
|
methods: {
|
async openDia(type, row) {
|
this.formDia = true;
|
this.operationType = type
|
await this.loadCustomerOptions()
|
if (this.operationType === 'edit') {
|
this.form = { ...createEmptyForm(), ...row }
|
if (!this.form.unitName) {
|
this.form.unitName = this.form.userName || ''
|
}
|
// 老数据没有关联委托单:合成一个选项把原来的值回显出来,不强制重选
|
if (row.orderId == null && row.userName) {
|
this.customerOptions.unshift({
|
id: 0,
|
company: row.userName,
|
entrustCode: row.entrustCode || '--',
|
sampleName: row.sampleName || '--'
|
})
|
this.form.orderId = 0
|
} else if (row.orderId && !this.customerOptions.some(o => o.id === row.orderId)) {
|
// 关联的委托单已不是"已完成"状态,用记录里的信息兜底回显
|
this.customerOptions.unshift({
|
id: row.orderId,
|
company: row.userName || '未知客户',
|
entrustCode: row.entrustCode || '--',
|
sampleName: row.sampleName || '--'
|
})
|
}
|
} else {
|
this.resetForm()
|
}
|
},
|
resetForm() {
|
this.form = createEmptyForm()
|
},
|
// 下拉数据:后端只返回"已检验"的委托单(state=4)
|
loadCustomerOptions() {
|
this.customerOptions = []
|
return selectCompletedOrders().then(res => {
|
if (res.code === 200 && res.data) {
|
this.customerOptions = res.data.map(o => ({
|
id: o.id,
|
company: o.company,
|
entrustCode: o.entrustCode,
|
sampleName: o.sampleView
|
}))
|
}
|
}).catch(err => {
|
console.log('err---', err)
|
})
|
},
|
// 选中客户后:回填客户名并自动带出单位名称、委托单编号、样品名称
|
onCustomerChange(val) {
|
const opt = this.customerOptions.find(o => o.id === val)
|
if (!opt || opt.id === 0) {
|
return
|
}
|
this.form.userName = opt.company
|
this.form.entrustCode = opt.entrustCode
|
this.form.sampleName = opt.sampleName
|
this.form.unitName = opt.company
|
},
|
handleEdit() {
|
const orderIdEmpty = this.form.orderId === '' || this.form.orderId === null || this.form.orderId === undefined
|
if (orderIdEmpty) {
|
this.$message.warning('请选择客户')
|
return
|
}
|
if (!this.form.fillDate) {
|
this.$message.warning('请选择填表日期')
|
return
|
}
|
for (let i = 0; i < this.questions.length; i++) {
|
const score = this.form[this.questions[i].key]
|
if (score === '' || score === null || score === undefined) {
|
this.$message.warning('请完成第' + (i + 1) + '题评分')
|
return
|
}
|
}
|
this.editLoad = true
|
const payload = { ...this.form }
|
// 老数据合成的回显选项(id=0)不算真实关联,保存时仍保持未关联
|
if (payload.orderId === 0) {
|
payload.orderId = null
|
}
|
if (this.operationType === 'add') {
|
addClientSatisfaction(payload).then(res => {
|
// 后端返回保存后的记录(含自增id),用于把新增时暂存的照片传上去
|
const savedId = res.data && res.data.clientSatisfactionId
|
this.finishSave(savedId)
|
}).catch(err => {
|
console.log('err---', err);
|
this.editLoad = false
|
})
|
} else {
|
updateClientSatisfaction(payload).then(() => {
|
this.finishSave(this.form.clientSatisfactionId)
|
}).catch(err => {
|
console.log('err---', err);
|
this.editLoad = false
|
})
|
}
|
},
|
// 记录保存成功后上传暂存照片;照片上传失败不影响记录本身
|
finishSave(clientSatisfactionId) {
|
this.uploadPendingPhotos(clientSatisfactionId).then(() => {
|
this.editLoad = false
|
this.$message.success('提交成功')
|
this.closeFormDia()
|
}).catch(err => {
|
console.log('照片上传失败', err)
|
this.editLoad = false
|
this.$message.warning('记录已保存,但照片上传失败,请回到列表用“附件”补充上传')
|
this.closeFormDia()
|
})
|
},
|
uploadPendingPhotos(clientSatisfactionId) {
|
const attachment = this.$refs.photoAttachment
|
if (!attachment || !attachment.pendingFiles.length) {
|
return Promise.resolve()
|
}
|
if (!clientSatisfactionId) {
|
return Promise.reject(new Error('未获取到记录id,无法上传照片'))
|
}
|
return attachment.flushPending(clientSatisfactionId)
|
},
|
closeFormDia() {
|
this.formDia = false;
|
this.$emit('closeFormDia')
|
},
|
}
|
};
|
</script>
|
|
<style scoped>
|
:deep(.el-dialog) {
|
margin: 10vh auto 50px !important;
|
}
|
|
.survey-intro {
|
margin-top: 10px;
|
font-size: 13px;
|
line-height: 22px;
|
color: #303133;
|
}
|
|
.tables {
|
table-layout: fixed;
|
width: 100%;
|
margin-top: 10px;
|
}
|
|
.section-title {
|
height: 34px;
|
text-align: center;
|
font-size: 14px;
|
font-weight: bold;
|
background: #f5f7fa;
|
padding: 6px;
|
}
|
|
.td-title {
|
height: 40px;
|
text-align: center;
|
font-size: 14px;
|
word-wrap: break-word;
|
white-space: normal;
|
padding: 6px;
|
}
|
|
.question-title {
|
text-align: left;
|
}
|
|
.td-info {
|
padding: 6px;
|
}
|
|
.score-head {
|
text-align: center;
|
font-size: 14px;
|
padding: 6px;
|
}
|
|
.score-cell {
|
text-align: center;
|
height: 38px;
|
}
|
|
.score-cell :deep(.el-radio) {
|
margin-right: 0;
|
}
|
|
.score-result {
|
font-weight: bold;
|
color: #409eff;
|
}
|
|
.customer-card-sub {
|
color: #909399;
|
font-size: 12px;
|
}
|
</style>
|
|
<style>
|
/* 下拉卡片三行需要自适应高度,弹层 teleport 到 body,用自定义 popper-class 命中 */
|
.customer-select-popper .el-select-dropdown__item {
|
height: auto;
|
line-height: 1.7;
|
padding: 6px 12px;
|
}
|
</style>
|