Files
dvcp_v2_webapp/packages/work/AppAskForm/components/Statistics.vue
yanran200730 aeb4fcd452 bug
2022-10-25 16:31:16 +08:00

762 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<ai-detail class="statistics">
<template slot="title">
<ai-title title="数据统计" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title>
</template>
<template slot="content">
<ai-card title="表单信息">
<template #content>
<ai-wrapper label-width="90px">
<ai-info-item label="表单名称" :value="info.title" isLine></ai-info-item>
<ai-info-item label="发布状态"><span
:style="{color: dict.getColor('questionnaireStatus', info.status)}">已发布</span></ai-info-item>
<ai-info-item label="创建人" :value="info.createUserName"></ai-info-item>
<ai-info-item label="创建时间" :value="info.createTime"></ai-info-item>
<ai-info-item label="截止时间"
:value="info.periodValidityEndTime ? info.periodValidityEndTime : '永久有效'"></ai-info-item>
<ai-info-item label="提交次数限制" :value="info.commitType === '1' ? '限提交一次' : '不限次数' "></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<div class="statistics-wrapper">
<div class="statistics-wrapper__title">
<span :class="[currIndex === 0 ? 'active' : '']" @click="currIndex = 0">表单统计</span>
<span :class="[currIndex === 1 ? 'active' : '']" @click="currIndex = 1">居民统计</span>
</div>
<div class="statistics-wrapper__body">
<div v-show="currIndex === 0">
<div class="statistics-wrapper__body--info">
<span></span>
<i>{{ subjectList.length }}</i>
<span>其中</span>
<span v-for="(item, index) in fieldTypeCount" :key="index">
{{ mapType(item.field_type) }}<i>{{ item.c }}</i>{{ fieldTypeCount.length - 1 === index ? '' : '' }}
</span>
</div>
<div class="statistics-wrapper__body--list">
<div class="statistics-wrapper__body--item" v-for="(item, index) in subjectList" :key="index">
<div class="statistics-wrapper__body--top"
:style="{borderBottom: ['input', 'textarea', 'upload'].indexOf(item.type) > -1 ? 'none' : '1px solid #DDDDDD'}">
<div class="left">
<h2>{{ item.fieldName }}{{ item.fixedLabel }}</h2>
</div>
<div class="right">
<span></span>
<i>{{ fieldDataCount[`field_${index}`] }}</i>
<span>条数据</span>
</div>
</div>
<div class="statistics-wrapper__body--bottom"
v-if="['radio', 'checkbox', 'select'].indexOf(item.type) > -1">
<div class="statistics-wrapper__body--select" v-for="(item, i) in item.options" :key="i">
<div class="left">
<h2>{{ item.label }}</h2>
<span>{{ item.c || 0 }}</span>
</div>
<div class="right">
<div class="progress">
<div
:style="{width: `${(((item.c || 0) / fieldDataCount[`field_${index}`]) * 100).toFixed(2)}%`}"></div>
</div>
<i>{{ (((item.c || 0) / fieldDataCount[`field_${index}`]) * 100).toFixed(2) }}%</i>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-show="currIndex === 1" style="padding: 16px;">
<ai-search-bar>
<template #right>
<el-input
v-model="search.name"
size="small"
placeholder="请输入居民名称或真实姓名"
clearable
v-throttle="() => {search.current = 1, getList()}"
@clear="search.current = 1, search.name = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
class="detail-table__table"
:border="true"
style="margin-top: 4px;"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:stripe="false"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column slot="userinfo" label="居民" width="260px" align="left">
<template slot-scope="{ row }">
<div class="userinfo">
<img :src="row.avatarUrl || 'https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png'">
<!-- <h3>{{ row.nickName }}</h3> -->
<el-tooltip effect="dark"
:content="row.corpName ? row.nickName + '@' + row.corpFullName : row.nickName + ''"
placement="top">
<div class="userinfo-right__top">
<h3>{{ row.corpName ? row.nickName : row.nickName }}</h3>
<span class="ellipsis">{{ row.corpName ? '@' + row.corpName : '' }}</span>
</div>
</el-tooltip>
</div>
</template>
</el-table-column>
<el-table-column slot="tags" label="标签" align="center" width="240px">
<template slot-scope="{ row }">
<div class="table-tags" v-if="row.tagNames">
<el-tag type="info" v-for="(item, index) in row.tagNames.split(',')" size="medium" :key="index">
{{ item }}
</el-tag>
</div>
<span v-else></span>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="showForm(row.id)">查看表单</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</div>
</div>
<ai-dialog
customFooter
:visible.sync="isShowForm"
@onConfirm="isShowForm = false"
width="800px"
title="表单">
<div class="middle-content form">
<div class="middle-content__wrapper">
<div>
<div class="left-item__item left-item__item--banner" key="banner" v-if="info.headPicture">
<img :src="info.headPicture">
</div>
<div class="left-item__item left-item__item--formname" key="title">
<h2>{{ info.title }}</h2>
</div>
<div class="left-item__item left-item__item--text" key="text">
<p>{{ info.tableExplain }}</p>
</div>
</div>
<div
class="left-item__item components-item"
v-for="(item, i) in targetList"
:key="i">
<div class="left-item__item--title">
<i :style="{opacity: item.required ? 1 : 0}">*</i>
<span>{{ i + 1 }}.</span>
<h2>{{ item.label }}</h2>
</div>
<div class="left-item__item--wrapper">
<template v-if="(item.type === 'radio')">
<div class="radio-item" v-for="(field, index) in item.options" :key="index">
<input type="radio" disabled :value="field.label" v-model="formInfo[`field_${i}`]"/>
<img :src="field.img[0].url" v-if="field.img.length">
<label>{{ field.label }}</label>
</div>
</template>
<template v-if="item.type === 'upload'">
<img style="width: 100%; height: 100%;" :src="formInfo[`field_${i}`]" v-if="formInfo[`field_${i}`]">
<div class="left-item__item--upload" v-else>
<span>图片</span>
</div>
</template>
<template v-if="item.type === 'select'">
<el-input resize="none" class="preview" type="textarea" style="color: #333" :placeholder="item.placeholder" v-model="formInfo[`field_${i}`]" disabled></el-input>
<!-- <span>{{ formInfo[`field_${i}`] }}</span> -->
<!-- <textarea :placeholder="item.placeholder" v-model="formInfo[`field_${i}`]" disabled></textarea> -->
<!-- <el-select placeholder="请选择" disabled v-model="formInfo[`field_${i}`]" style="width: 100%;">
<el-option
v-for="(item, index) in item.options"
:key="index"
:label="item.label"
:value="item.label">
</el-option>
</el-select> -->
</template>
<template v-if="(item.type === 'checkbox')">
<div class="radio-item" v-for="(field, index) in item.options" :key="index">
<input type="checkbox" disabled :value="field.label" v-model="formInfo[`field_${i}`]"/>
<img :src="field.img[0].url" v-if="field.img.length">
<label>{{ field.label }}</label>
</div>
</template>
<template v-if="(item.type === 'input')">
<div class="text-item">
<input :placeholder="item.placeholder" v-model="formInfo[`field_${i}`]" disabled>
</div>
</template>
<template v-if="(item.type === 'textarea')">
<div class="textarea-item" resize="none">
<textarea :placeholder="item.placeholder" v-model="formInfo[`field_${i}`]" disabled></textarea>
</div>
</template>
</div>
</div>
</div>
</div>
<template #footer>
<el-button @click="isShowForm = false">关闭</el-button>
</template>
</ai-dialog>
</div>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Statistics',
props: {
instance: Function,
dict: Object,
params: Object
},
data() {
return {
currIndex: 0,
search: {
name: '',
size: 10,
current: 1
},
info: {},
subjectList: [],
tableData: [],
total: 0,
form: {},
fieldTypeCount: [],
fieldValueDistribution: [],
fieldDataCount: {},
isShowForm: false,
targetList: [],
formInfo: {},
colConfigs: [
{slot: 'userinfo'},
{prop: 'residentName', label: '真实姓名', align: 'center', width: '100px'},
{prop: 'commitTime', label: '提交时间', align: 'center', width: '160px'},
{
prop: 'userType',
label: '微信类型',
align: 'center',
width: '100px',
formart: v => this.dict.getLabel('wxUserType', v)
},
{prop: 'wxUserNames', label: '所属员工', align: 'center', width: '100px'},
{slot: 'tags', label: '标签', align: 'center', width: '268px'},
{prop: 'totalScore', label: '分值', align: 'center'}
]
}
},
mounted() {
this.getInfo()
this.getFormInfo()
this.dict.load(['wxUserType']).then(() => {
this.getList()
})
},
methods: {
getList() {
this.instance.post(`/app/appquestionnairetemplate/statisticsResident?id=${this.params.id}`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
showForm(id) {
this.instance.post(`/app/appquestionnairetemplate/queryDataInfoById?id=${this.params.id}&dataId=${id}`).then(res => {
if (res.code == 0) {
this.formInfo = res.data
this.targetList.forEach((item, index) => {
if (item.type === 'checkbox' && this.formInfo[`field_${index}`]) {
this.formInfo[`field_${index}`] = this.formInfo[`field_${index}`].split(',')
}
})
this.isShowForm = true
}
})
},
mapType(type) {
return {
upload: '上传图片',
input: '单行填空',
textarea: '多行填空',
radio: '单选',
checkbox: '多选',
select: '单下拉框'
}[type]
},
getFormInfo() {
this.instance.post(`/app/appquestionnairetemplate/statisticsTable?id=${this.params.id}`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.fieldDataCount = res.data.fieldDataCount
this.fieldTypeCount = res.data.fieldTypeCount
this.fieldValueDistribution = res.data.fieldValueDistribution
this.subjectList = res.data.appQuestionnaireTemplate.fields.map((item, index) => {
const fieldInfo = JSON.parse(item.fieldInfo)
let options = fieldInfo.options
if (['radio', 'checkbox', 'select'].indexOf(item.fieldType) > -1) {
options = fieldInfo.options.map(v => {
res.data.fieldValueDistribution[`field_${index}`].forEach(info => {
if (info.fieldValue === v.label) {
v.c = info.c
}
})
return v
})
}
return {
...item,
...fieldInfo,
options
}
})
}
})
},
getInfo() {
this.instance.post(`/app/appquestionnairetemplate/queryDetailById?id=${this.params.id}`).then(res => {
if (res.code == 0) {
this.info = res.data
this.targetList = res.data.fields.map(item => {
return JSON.parse(item.fieldInfo)
})
}
})
},
cancel(isRefresh) {
this.$emit('change', {
type: 'list',
isRefresh: !!isRefresh
})
}
}
}
</script>
<style scoped lang="scss">
.statistics {
* {
box-sizing: border-box;
font-weight: normal;
font-style: normal;
}
.preview {
::v-deep .el-textarea.is-disabled, ::v-deep .el-textarea__inner {
color: #666 !important;
}
}
.form {
.left-item__item--banner {
img {
width: 100%;
height: 235px;
}
.config-item__banner {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 100%;
height: 235px;
line-height: 1;
border: 1px dashed #bbb;
span {
margin-top: 4px;
color: #555555;
font-size: 14px;
}
i {
font-size: 30px;
color: #8899bb;
}
}
}
.left-item__item--formname {
margin: 16px 0 32px;
padding: 0 12px;
color: #333333;
font-size: 15px;
font-weight: normal;
text-align: center;
word-break: break-all;
}
.left-item__item--text {
line-height: 20px;
// margin-bottom: 48px;
padding: 0 12px 20px;
text-align: justify;
color: #666;
font-size: 14px;
word-break: break-all;
}
.components-item {
position: relative;
padding: 16px 16px;
.left-item__item--wrapper {
& > img {
max-width: 300px;
}
}
.left-item__item--upload {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 120px;
height: 120px;
border-radius: 6px;
border: 1px dashed #bbb;
i {
font-size: 24px;
color: #8899bb;
}
span {
margin-top: 4px;
font-size: 12px;
color: #555;
}
}
.text-item {
input {
display: block;
width: 100%;
height: 40px;
border: none;
border-bottom: 1px solid #ddd;
&:focus {
outline: none;
}
&:disabled {
background: #fff;
}
}
}
.textarea-item {
textarea {
width: 100%;
height: 120px;
resize: none;
border: 1px solid #ddd;
padding: 10px;
&:focus {
outline: none;
}
&:disabled {
background: #fff;
}
}
}
.radio-item {
display: flex;
margin-bottom: 10px;
input {
position: relative;
top: 2px;
}
&:last-child {
margin-bottom: 0;
}
label {
margin-left: 10px;
}
img {
width: 60px;
margin-left: 10px;
}
}
}
.left-item__item--title {
display: flex;
margin-bottom: 10px;
i {
position: relative;
top: 3px;
margin-right: 5px;
color: #E22120;
}
span {
position: relative;
top: 3px;
}
h2 {
color: #333333;
font-size: 15px;
}
}
}
.table-tags {
.el-tag {
margin-right: 8px;
margin-bottom: 8px;
border: 1px solid #D0D4DC;
background: #F3F4F7;
border-radius: 4px;
font-size: 13px;
color: #222222;
&:last-child {
margin-right: 0;
}
}
}
h2, h3 {
margin: 0;
}
.userinfo {
display: flex;
align-items: center;
img {
width: 40px;
height: 40px;
margin-right: 8px;
border-radius: 2px;
}
h3 {
font-size: 14px;
font-weight: normal;
color: #222222;
}
.userinfo-right__top {
display: flex;
align-items: center;
margin-bottom: 10px;
cursor: pointer;
white-space: nowrap;
}
span {
padding-left: 8px;
color: #2EA222;
font-size: 14px;
white-space: nowrap;
}
}
.statistics-wrapper {
background: #FFFFFF;
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
border-radius: 2px;
.statistics-wrapper__title {
display: flex;
align-items: center;
height: 56px;
padding: 0 16px;
border-bottom: 1px solid #EEEEEE;
span {
height: 56px;
line-height: 56px;
color: #888888;
font-size: 16px;
font-weight: 600;
cursor: pointer;
user-select: none;
border-bottom: 3px solid transparent;
&:first-child {
margin-right: 32px;
}
&.active {
color: #222222;
border-color: #2266FF;
}
}
}
.statistics-wrapper__body--list {
padding: 0 40px 20px;
.statistics-wrapper__body--item {
margin-bottom: 20px;
background: #FFFFFF;
border-radius: 4px;
border: 1px solid #DDDDDD;
}
.statistics-wrapper__body--bottom {
padding: 20px;
.statistics-wrapper__body--select {
display: flex;
align-items: center;
justify-content: space-between;
height: 48px;
& > div {
display: flex;
align-items: center;
}
.left {
h2 {
max-width: 384px;
margin-right: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
color: #222222;
}
span {
color: #888888;
font-size: 12px;
}
}
.right {
i {
width: 50px;
text-align: right;
color: #2266FF;
font-size: 12px;
}
.progress {
position: relative;
width: 500px;
height: 8px;
margin-right: 10px;
background: #EEEEEE;
border-radius: 5px;
div {
position: absolute;
top: 0;
left: 0;
height: 8px;
background: #2266FF;
border-radius: 5px;
}
}
}
}
}
.statistics-wrapper__body--top {
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
padding: 0 20px;
background: #FFFFFF;
border-radius: 4px 4px 0 0;
border-bottom: 1px solid #DDDDDD;
& > div {
display: flex;
align-items: center;
font-size: 14px;
}
.left {
flex: 1;
h2 {
color: #222222;
font-size: 14px;
}
}
.right {
span {
color: #888888;
}
i {
color: #2266FF;
padding: 0 4px;
}
}
}
}
.statistics-wrapper__body--info {
display: flex;
align-items: center;
height: 70px;
line-height: 1;
padding: 0 40px;
color: #555555;
font-size: 14px;
i {
padding: 0 4px;
font-weight: 600;
font-style: normal;
color: #2266FF;
font-size: 14px;
}
}
}
}
</style>