-
前言
最近用vue写过一个用于提交反馈的小组件的前端,可以嵌入到页面中,让用户可以直接对产品问题提交工单。同时还带有实时搜索功能,让用户在输入问题时,智能检索是否有相似问题。
效果
代码
Features:
- 使用了vuetify
- 对实时搜索的请求进行深度优化,在几乎不影响用户体验的情况下,有效降低了对后台接口的请求数量。核心代码如下:
watch: {
getContent () { //使用了锁来控制实时搜索的频率
if (this.form.Content.length >= 10 && this.form.Content !== this.nowStr && !this.lock) {
this.lock = true
this.sleep(200).then(() => {
this.nowStr = this.form.Content
this.getList()
})
}
}
}
- 食用方法:
- 创建FeedBack.vue组件,代码在最后。
- 在父组件中引用,注册,传入相应props即可。
- PS:父组件中实现悬浮在页面中的效果即可
// FeedBack.vue
<template>
<div v-if="showSelf" class="dialog" :style="{'z-index': zIndex}">
<div class="dialog-mark" :style="{'z-index': zIndex + 1}" />
<transition name="dialog">
<div class="dialog-sprite" :style="{'z-index': zIndex + 2}">
<!-- 标题 -->
<section v-if="title" class="header">
{{ title }}
</section>
<div class="dialog-body">
<v-row>
<v-col :cols="12">
<v-card>
<v-card-title>{{ `xxx` }}</v-card-title>
<v-card-content>
<v-col>
<v-form v-model="valid">
<v-container>
<v-row>
<v-col
cols="12"
md="4"
>
<v-select v-model="form.Category" :items="sortNames" sm="3" md="3" label="请选择反馈类别" />
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field v-model="form.Name" label="请填写您的姓名" :rules="nameRules" required />
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="form.Email"
:rules="emailRules"
label="请留下您的邮箱"
required
/>
</v-col>
</v-row>
<v-row>
<v-col :cols="6">
<v-textarea v-model="form.Content" outlined label="请输入要反馈的问题(至少20字)" />
</v-col>
<v-col :cols="6">
<h3>
猜你想找:
</h3>
<p v-if="fbList.length === 0">
请输入你想反馈的问题
</p>
<v-chip v-for="list in fbList" :key="list.FeedbackID" class="ma-2" color="primary">
{{ `${list.FeedbackContent.length >= 30 ? list.FeedbackContent.substr(0,30) + '...' : list.FeedbackContent}` }}
<!-- {{ list.FeedbackContent.substr(0,20) }} -->
</v-chip>
</v-col>
</v-row>
</v-container>
</v-form>
<v-row>
<v-spacer />
<v-btn medium color="primary" @click="sumbit()">
提交
</v-btn>
<v-spacer />
</v-row>
<!-- {{ this.nowStr }}
{{ this.form.Content }} -->
</v-col>
</v-card-content>
</v-card>
</v-col>
</v-row>
</div>
<section class="dialog-footer">
<div v-if="showCancel" class="btn btn-refuse" @click="cancel">
{{ cancelText }}
</div>
</section>
</div>
</transition>
</div>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false,
required: true
},
showCancel: {
typs: Boolean,
default: false,
required: false
},
title: {
type: String,
required: true
},
cancelText: {
type: String,
default: '取消',
required: false
},
confirmText: {
type: String,
default: '确定',
required: false
}
},
data () {
return {
nameRules: [
v => !!v || 'Name is required',
v => v.length <= 10 || 'Name must be less than 10 characters'
],
emailRules: [
v => !!v || 'E-mail is required',
v => /.+@.+/.test(v) || 'E-mail must be valid'
],
name: 'dialog',
valid: false,
showSelf: false,
zIndex: this.getZIndex(),
bodyOverflow: '',
detail: {},
sort: [],
sortNames: ['无(默认类别)'],
form: {
Category: '',
Crop: '',
Name: '',
Email: '',
Content: ''
},
fbList: [],
nowStr: '',
lock: false
}
},
computed: {
getContent () {
return this.form.Content
}
},
watch: {
show (val) {
if (!val) {
this.closeMyself()
} else {
this.showSelf = val
}
},
//添加了锁来控制实时搜索的请求频率
getContent () {
if (this.form.Content.length >= 10 && this.form.Content !== this.nowStr && !this.lock) {
this.lock = true
this.sleep(200).then(() => {
this.nowStr = this.form.Content
this.getList()
})
}
}
},
async created () {
await this.getName()
await this.getSort()
this.showSelf = this.show
},
mounted () {
this.forbidScroll()
},
methods: {
async getName () {
const { data: name } = await this.$axios.get(
'获取反馈title的接口', {}
)
this.detail = name.data.detail
},
async getSort () {
const { data: sort } = await this.$axios.get(
'获取反馈的分类接口', {}
)
this.sort = sort.data.detail
for (const i of this.sort) {
this.sortNames.push(String(i.Name))
}
},
async sumbit () {
this.form.Crop = this.detail.UID
if (this.form.Category === '无(默认类别)') {
this.form.Category = ''
} else {
for (const i of this.sort) {
if (this.form.Category === i.Name) {
this.form.Category = i.UID
}
}
} //验证表单
if (!this.valid) {
alert('您填写的表单有误,请检查后再提交')
} else if (this.form.Content.length < 20 || this.form.Content.length > 500) {
alert('您的反馈内容字数不合规,请检查后再提交')
} else {
// console.log(this.form)
await this.$axios.post(
'发送反馈接口', this.form
).then((res) => {
if (res.status === 200) {
this.form = {
Category: '',
Crop: '',
Name: '',
Email: '',
Content: ''
}
alert('提交成功!我们通过邮件回复您的反馈')
this.cancel()
} else {
alert('提交失败,请联系管理员')
}
})
}
},
sleep (time) {
return new Promise(resolve => setTimeout(resolve, time))
},
async getList () {
let url = ''
await this.sleep(300).then(() => {
this.lock = false
})
url = `` //实时搜索请求的url
const { data: list } = await this.$axios.get(
url, {}
)
// console.log(list)
this.fbList = list.data.detail.ResultSet
// this.fbList.push()
},
forbidScroll () {
this.bodyOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
},
getZIndex () {
let zIndexInit = 20190315
return zIndexInit++
},
cancel () {
this.$emit('cancel', false)
},
confirm () {
this.$emit('confirm', false)
},
closeMyself () {
this.showSelf = false
this.sloveBodyOverflow()
},
sloveBodyOverflow () {
document.body.style.overflow = this.bodyOverflow
}
}
}
</script>
<style scoped lang="scss">
.dialog-enter-active,
.dialog-leave-active {
transition: opacity .5s;
}
.dialog-enter,
.dialog-leave-to {
opacity: 0;
}
.dialog {
position: fixed;
top: 0;
right: 0;
width: 100%;
height: 100%;
.dialog-mark {
position: absolute;
top: 0;
height: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .6);
}
}
.dialog-sprite {
position: absolute;
top: 10%;
left: 15%;
right: 15%;
bottom: 25%;
display: flex;
flex-direction: column;
max-height: 75%;
min-height: 180px;
overflow: hidden;
z-index: 23456765435;
background: #fff;
border-radius: 8px;
.header {
padding: 15px;
text-align: center;
font-size: 18px;
font-weight: 700;
color: #333;
}
.dialog-body {
flex: 1;
overflow-x: hidden;
overflow-y: scroll;
padding: 0 15px 20px 15px;
}
.dialog-footer {
position: relative;
display: flex;
width: 100%;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 1px;
background: #ddd;
transform: scaleY(.5);
}
.btn {
flex: 1;
text-align: center;
padding: 15px;
font-size: 17px;
&:nth-child(2) {
position: relative;
&::after {
content: '';
position: absolute;
left: 0;
top: 0;
width: 1px;
height: 100%;
background: #ddd;
transform: scaleX(.5);
}
}
}
.btn-confirm {
color: #43ac43;
}
}
}
</style>
// 父组件
<the-dialog
:show="showDialog"
:title="'提交反馈'"
:show-cancel="true"
:confirm-text="`提交`"
:cancel-text="`关闭`"
@confirm="confirm"
@cancel="cancel"
/>
<v-btn
small
color="primary"
style="
position: fixed;
right: 0;
top: 85%;"
@click="() => showDialog = true"
>
提交反馈
</v-btn>
Comments | 0 条评论