762 lines
22 KiB
Vue
762 lines
22 KiB
Vue
<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',
|
||
format: 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 {
|
||
:deep( .el-textarea.is-disabled), :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>
|