Merge remote-tracking branch 'origin/build' into build

This commit is contained in:
aixianling
2022-10-20 16:35:40 +08:00
15 changed files with 4098 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
<template>
<div class="doc-circulation">
<keep-alive :include="['List']">
<component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import Add from './components/Add'
import List from './components/List'
import Detail from './components/Detail'
export default {
name: 'AppRatingTask',
label: '评分任务',
props: {
instance: Function,
dict: Object
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
Detail,
List,
Add
},
methods: {
onChange (data) {
if (data.type === 'Detail') {
this.component = 'Detail'
this.params = data.params
}
if (data.type === 'Add') {
this.component = 'Add'
this.params = data.params
}
if (data.type === 'List') {
this.component = 'List'
this.params = data.params
this.$nextTick(() => {
if (data.isRefresh) {
this.$refs.component.getList()
}
})
}
}
}
}
</script>
<style lang="scss">
.doc-circulation {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,188 @@
<template>
<ai-detail isHasSidebar v-loading="isLoading">
<template slot="title">
<ai-title title="返乡报备详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<AiSidebar :tabTitle="tabList" v-model="currIndex"></AiSidebar>
<div v-show="currIndex === 0">
<ai-card title="基本信息" v-show="currIndex === 0">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="姓名" :value="info.name"></ai-info-item>
<ai-info-item label="填报时间" :value="info.createTime"></ai-info-item>
<ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item>
<ai-info-item label="手机号码" :value="info.phone"></ai-info-item>
<ai-info-item label="人员类别" :value="dict.getLabel('EP_registerPersonType', info.type)"></ai-info-item>
<ai-info-item label="是否从事高危行业" :value="dict.getLabel('EP_highRiskIndustries', info.highRiskIndustries)"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="行程信息">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="出发时间" :value="info.startTime"></ai-info-item>
<ai-info-item label="出发地" >
<span :style="{color: info.denger == 1 ? '#FF4466' : '#333'}">{{info.startAreaName}} </span>
</ai-info-item>
<ai-info-item label="出发地详址" isLine :value="info.startAddress"></ai-info-item>
<ai-info-item label="出行方式" :value="info.travelTypeNames"></ai-info-item>
<ai-info-item label="车次/航班" isLine :value="info.trainNo"></ai-info-item>
<ai-info-item label="行程描述" :value="info.description"></ai-info-item>
<ai-info-item label="抵平卡口" :value="info.gatewayName"></ai-info-item>
<ai-info-item label="抵平时间" :value="info.arriveTime"></ai-info-item>
<ai-info-item label="目的地" :value="info.arriveAreaName"></ai-info-item>
<ai-info-item label="目的地详址" isLine :value="info.arriveAddress"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="健康状况">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="是否有风险旅居史" v-if="info.fromHighRiskArea === '1'">
<span style="color: red">{{ info.highRiskAreaName }}</span>
</ai-info-item>
<ai-info-item label="是否有风险旅居史" v-else value="否"></ai-info-item>
<ai-info-item label="7天内是否接触新冠确诊或疑似患者">
<span :style="{color: info.contactPatients === '1' ? 'red' : ''}">{{ dict.getLabel('yesOrNo', info.contactPatients) }}</span>
</ai-info-item>
<ai-info-item label="当前健康状况" v-if="info.abnormalHealth === '1'">
<span style="color: red">{{ dict.getLabel('EP_abnormalType', info.abnormalType) }}</span>
</ai-info-item>
<ai-info-item label="当前健康状况" v-else value="否"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
</div>
<div v-show="currIndex === 1">
<ai-card title="风险处置">
<template #right>
<el-button type="primary" v-if="info.status === '0'" @click="release">解除异常</el-button>
</template>
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="姓名" :value="info.name"></ai-info-item>
<ai-info-item label="填报时间" :value="info.createTime"></ai-info-item>
<ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item>
<ai-info-item label="手机号码" :value="info.phone"></ai-info-item>
<ai-info-item label="人员类别" :value="dict.getLabel('EP_registerPersonType', info.type)"></ai-info-item>
<ai-info-item label="是否从事高危行业" :value="dict.getLabel('EP_highRiskIndustries', info.highRiskIndustries)"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="风险处理意见">
<template #content>
<ai-table
:isShowPagination="false"
tableSize="small"
border
:tableData="info.riskDisposalList"
:col-configs="colConfigs"
@getList="() => {}">
</ai-table>
</template>
</ai-card>
<ai-card title="社区初排" v-if="info.cmAdvanceDisposal">
<template #content>
<ai-wrapper
style="margin-bottom: 20px;"
label-width="120px">
<ai-info-item label="处置意见" :value="dict.getLabel('EP_communityHandleType', info.cmAdvanceDisposal.communityHandleType)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="居家状态" :value="dict.getLabel('EP_homeStatus2', info.cmAdvanceDisposal.homeStatus)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="隔离时间" :value="info.cmAdvanceDisposal.quarantineBeginTime + ' - ' + info.cmAdvanceDisposal.quarantineEndTime"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="隔离策略" :value="dict.getLabel('EP_quarantineStrategy', info.cmAdvanceDisposal.quarantineStrategy)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="管控方式" :value="dict.getLabel('EP_controlMethod', info.cmAdvanceDisposal.controlMethod)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="图片" isLine>
<ai-uploader
:instance="instance"
:value="info.cmAdvanceDisposal.fileList"
disabled
:limit="9">
</ai-uploader>
</ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '2'" label="风险解除证明文件" isLine>
<ai-uploader
:instance="instance"
:value="info.cmAdvanceDisposal.proveFileList"
disabled
:limit="9">
</ai-uploader>
</ai-info-item>
</ai-wrapper>
<ai-empty style="margin-bottom: 60px;" v-if="!info.cmAdvanceDisposal"></ai-empty>
</template>
</ai-card>
</div>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Detail',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
info: {},
isShow: false,
currIndex: 0,
isLoading: false,
tableData: [],
colConfigs: [
{prop: 'remarks', label: '异常记录', align: 'center' },
{prop: 'createTime', label: '创建时间', align: 'center'},
{prop: 'createUserName', label: '记录人', align: 'center' }
],
tabList: ['基本信息', '风险处置']
}
},
created () {
this.isLoading = true
if (this.params && this.params.id) {
this.id = this.params.id
this.$dict.load(['EP_registerPersonType', 'EP_communityHandleType', 'EP_highRiskIndustries', 'EP_travelType', 'yesOrNo', 'EP_homeStatus2', 'EP_quarantineStrategy', 'EP_controlMethod', 'EP_abnormalType']).then(() => {
this.getInfo(this.params.id)
})
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appepidemicpreventionregisterinfo/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.info = res.data
this.info.travelTypeNames = res.data.travelType.split(',').map(v => {
return this.dict.getLabel('EP_travelType', v)
}).join('')
}
this.isLoading = false
}).catch(() => {
this.isLoading = false
})
},
cancel () {
this.$emit('change', {
type: 'List',
isRefresh: true
})
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,125 @@
<template>
<ai-list class="list">
<ai-title slot="title" title="评分任务" isShowBottomBorder :instance="instance"></ai-title>
<template slot="content">
<div class="content">
<ai-search-bar bottomBorder>
<template #left>
<el-button type="primary" @click="toAdd">新建评分任务</el-button>
</template>
<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
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
v-loading="loading"
style="margin-top: 16px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column slot="options" width="140px" fixed="right" label="操作" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</div>
</template>
</ai-list>
</template>
<script>
export default {
name: 'List',
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
name: ''
},
colConfigs: [
{ prop: 'name', label: '任务名称' },
{ prop: 'phone', align: 'center', label: '创建人' },
{ prop: 'startTime', align: 'center', label: '更新时间' },
{ prop: 'startAreaName', align: 'center', label: '任务状态' }
],
ids: [],
tableData: [],
total: 0,
loading: false
}
},
created () {
this.loading = true
this.dict.load(['EP_riskLevel', 'EP_handleType', 'modeType', 'EP_riskStatus', 'EP_source']).then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appepidemicpreventionregisterinfo/list`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
this.loading = false
} else {
this.loading = false
}
}).catch(() => {
this.loading = false
})
},
toDetail (id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || ''
}
})
},
remove(id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appepidemicpreventionregisterinfo/delete?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,70 @@
<template>
<div class="doc-circulation">
<keep-alive :include="['List']">
<component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import Add from './components/Add'
import List from './components/List'
import Detail from './components/Detail'
export default {
name: 'AppScoringTemplate',
label: '评分模板',
props: {
instance: Function,
dict: Object
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
Detail,
List,
Add
},
methods: {
onChange (data) {
if (data.type === 'Detail') {
this.component = 'Detail'
this.params = data.params
}
if (data.type === 'Add') {
this.component = 'Add'
this.params = data.params
}
if (data.type === 'List') {
this.component = 'List'
this.params = data.params
this.$nextTick(() => {
if (data.isRefresh) {
this.$refs.component.getList()
}
})
}
}
}
}
</script>
<style lang="scss">
.doc-circulation {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
<template>
<ai-detail isHasSidebar v-loading="isLoading">
<template slot="title">
<ai-title title="返乡报备详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<AiSidebar :tabTitle="tabList" v-model="currIndex"></AiSidebar>
<div v-show="currIndex === 0">
<ai-card title="基本信息" v-show="currIndex === 0">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="姓名" :value="info.name"></ai-info-item>
<ai-info-item label="填报时间" :value="info.createTime"></ai-info-item>
<ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item>
<ai-info-item label="手机号码" :value="info.phone"></ai-info-item>
<ai-info-item label="人员类别" :value="dict.getLabel('EP_registerPersonType', info.type)"></ai-info-item>
<ai-info-item label="是否从事高危行业" :value="dict.getLabel('EP_highRiskIndustries', info.highRiskIndustries)"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="行程信息">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="出发时间" :value="info.startTime"></ai-info-item>
<ai-info-item label="出发地" >
<span :style="{color: info.denger == 1 ? '#FF4466' : '#333'}">{{info.startAreaName}} </span>
</ai-info-item>
<ai-info-item label="出发地详址" isLine :value="info.startAddress"></ai-info-item>
<ai-info-item label="出行方式" :value="info.travelTypeNames"></ai-info-item>
<ai-info-item label="车次/航班" isLine :value="info.trainNo"></ai-info-item>
<ai-info-item label="行程描述" :value="info.description"></ai-info-item>
<ai-info-item label="抵平卡口" :value="info.gatewayName"></ai-info-item>
<ai-info-item label="抵平时间" :value="info.arriveTime"></ai-info-item>
<ai-info-item label="目的地" :value="info.arriveAreaName"></ai-info-item>
<ai-info-item label="目的地详址" isLine :value="info.arriveAddress"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="健康状况">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="是否有风险旅居史" v-if="info.fromHighRiskArea === '1'">
<span style="color: red">{{ info.highRiskAreaName }}</span>
</ai-info-item>
<ai-info-item label="是否有风险旅居史" v-else value="否"></ai-info-item>
<ai-info-item label="7天内是否接触新冠确诊或疑似患者">
<span :style="{color: info.contactPatients === '1' ? 'red' : ''}">{{ dict.getLabel('yesOrNo', info.contactPatients) }}</span>
</ai-info-item>
<ai-info-item label="当前健康状况" v-if="info.abnormalHealth === '1'">
<span style="color: red">{{ dict.getLabel('EP_abnormalType', info.abnormalType) }}</span>
</ai-info-item>
<ai-info-item label="当前健康状况" v-else value="否"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
</div>
<div v-show="currIndex === 1">
<ai-card title="风险处置">
<template #right>
<el-button type="primary" v-if="info.status === '0'" @click="release">解除异常</el-button>
</template>
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="姓名" :value="info.name"></ai-info-item>
<ai-info-item label="填报时间" :value="info.createTime"></ai-info-item>
<ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item>
<ai-info-item label="手机号码" :value="info.phone"></ai-info-item>
<ai-info-item label="人员类别" :value="dict.getLabel('EP_registerPersonType', info.type)"></ai-info-item>
<ai-info-item label="是否从事高危行业" :value="dict.getLabel('EP_highRiskIndustries', info.highRiskIndustries)"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="风险处理意见">
<template #content>
<ai-table
:isShowPagination="false"
tableSize="small"
border
:tableData="info.riskDisposalList"
:col-configs="colConfigs"
@getList="() => {}">
</ai-table>
</template>
</ai-card>
<ai-card title="社区初排" v-if="info.cmAdvanceDisposal">
<template #content>
<ai-wrapper
style="margin-bottom: 20px;"
label-width="120px">
<ai-info-item label="处置意见" :value="dict.getLabel('EP_communityHandleType', info.cmAdvanceDisposal.communityHandleType)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="居家状态" :value="dict.getLabel('EP_homeStatus2', info.cmAdvanceDisposal.homeStatus)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="隔离时间" :value="info.cmAdvanceDisposal.quarantineBeginTime + ' - ' + info.cmAdvanceDisposal.quarantineEndTime"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="隔离策略" :value="dict.getLabel('EP_quarantineStrategy', info.cmAdvanceDisposal.quarantineStrategy)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="管控方式" :value="dict.getLabel('EP_controlMethod', info.cmAdvanceDisposal.controlMethod)"></ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '1'" label="图片" isLine>
<ai-uploader
:instance="instance"
:value="info.cmAdvanceDisposal.fileList"
disabled
:limit="9">
</ai-uploader>
</ai-info-item>
<ai-info-item v-if="info.cmAdvanceDisposal.communityHandleType === '2'" label="风险解除证明文件" isLine>
<ai-uploader
:instance="instance"
:value="info.cmAdvanceDisposal.proveFileList"
disabled
:limit="9">
</ai-uploader>
</ai-info-item>
</ai-wrapper>
<ai-empty style="margin-bottom: 60px;" v-if="!info.cmAdvanceDisposal"></ai-empty>
</template>
</ai-card>
</div>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Detail',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
info: {},
isShow: false,
currIndex: 0,
isLoading: false,
tableData: [],
colConfigs: [
{prop: 'remarks', label: '异常记录', align: 'center' },
{prop: 'createTime', label: '创建时间', align: 'center'},
{prop: 'createUserName', label: '记录人', align: 'center' }
],
tabList: ['基本信息', '风险处置']
}
},
created () {
this.isLoading = true
if (this.params && this.params.id) {
this.id = this.params.id
this.$dict.load(['EP_registerPersonType', 'EP_communityHandleType', 'EP_highRiskIndustries', 'EP_travelType', 'yesOrNo', 'EP_homeStatus2', 'EP_quarantineStrategy', 'EP_controlMethod', 'EP_abnormalType']).then(() => {
this.getInfo(this.params.id)
})
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appepidemicpreventionregisterinfo/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.info = res.data
this.info.travelTypeNames = res.data.travelType.split(',').map(v => {
return this.dict.getLabel('EP_travelType', v)
}).join('')
}
this.isLoading = false
}).catch(() => {
this.isLoading = false
})
},
cancel () {
this.$emit('change', {
type: 'List',
isRefresh: true
})
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,133 @@
<template>
<ai-list class="list">
<ai-title slot="title" title="评分模板" isShowBottomBorder :instance="instance"></ai-title>
<template slot="content">
<div class="content">
<ai-search-bar bottomBorder>
<template #left>
<el-button type="primary" @click="toAdd">新建模板</el-button>
</template>
<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
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
v-loading="loading"
style="margin-top: 16px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column slot="options" width="140px" fixed="right" label="操作" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</div>
</template>
</ai-list>
</template>
<script>
export default {
name: 'List',
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
name: ''
},
colConfigs: [
{ prop: 'name', label: '模板名称' },
{ prop: 'phone', align: 'center', label: '创建人' },
{ prop: 'startTime', align: 'center', label: '更新时间' }
],
ids: [],
tableData: [],
total: 0,
loading: false
}
},
created () {
this.loading = true
this.dict.load(['EP_riskLevel', 'EP_handleType', 'modeType', 'EP_riskStatus', 'EP_source']).then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appepidemicpreventionregisterinfo/list`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
this.loading = false
} else {
this.loading = false
}
}).catch(() => {
this.loading = false
})
},
toDetail (id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || ''
}
})
},
toAdd (id) {
this.$emit('change', {
type: 'Add',
params: {
id: id || ''
}
})
},
remove(id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appepidemicpreventionregisterinfo/delete?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,181 @@
export const components = [
{
type: 'options',
tips: '(可重复添加)',
label: '选项',
children: [
{
type: 'radio',
label: '单选',
fixedLabel: '单选',
value: '',
points: '',
icon: 'iconradio',
isShowPoints: false,
required: true,
hasAnswer: false,
answer: '',
pointType: '0',
pointDict: [
{
dictName: '此题有唯一答案和分值',
dictValue: '0'
},
{
dictName: '每个选项都有对应分值',
dictValue: '1'
}
],
options: [
{
label: '选项1',
value: '',
point: '',
img: []
},
{
label: '选项2',
value: '',
point: '',
img: []
}
],
title: ''
},
{
type: 'checkbox',
label: '多选',
fixedLabel: '多选',
points: '',
icon: 'iconcheck_box',
isShowPoints: false,
required: true,
hasAnswer: false,
answer: [],
value: [],
pointType: '0',
pointDict: [
{
dictName: '此题有唯一答案和分值',
dictValue: '0'
},
{
dictName: '每个选项都有对应分值',
dictValue: '1'
},
{
dictName: '答对几项得几分,答错不得分',
dictValue: '2'
}
],
options: [
{
label: '选项1',
value: '',
point: '',
img: []
},
{
label: '选项2',
point: '',
value: '',
img: []
}
],
title: ''
},
{
type: 'select',
label: '单下拉框',
fixedLabel: '单下拉框',
value: '',
points: '',
icon: 'iconSelect',
isShowPoints: false,
required: true,
hasAnswer: false,
answer: '',
pointType: '0',
pointDict: [
{
dictName: '此题有唯一答案和分值',
dictValue: '0'
},
{
dictName: '每个选项都有对应分值',
dictValue: '1'
}
],
options: [
{
label: '选项1',
value: '',
point: '',
img: []
},
{
label: '选项2',
value: '',
point: '',
img: []
}
],
title: ''
}
]
},
{
type: 'input',
tips: '(可重复添加)',
label: '填空',
children: [
{
type: 'input',
label: '单行填空',
fixedLabel: '单行填空',
value: '',
pointType: '0',
icon: 'icontext_box',
isShowPoints: false,
points: '',
required: true,
hasAnswer: false,
placeholder: '请输入...',
answer: ''
},
{
type: 'textarea',
label: '多行填空',
fixedLabel: '多行填空',
pointType: '0',
icon: 'icontext_area',
points: '',
isShowPoints: false,
required: true,
hasAnswer: false,
answer: '',
placeholder: '请输入...',
value: ''
}
]
},
{
type: 'annex',
tips: '(可重复添加)',
label: '附件',
children: [
{
type: 'upload',
label: '上传图片',
fixedLabel: '上传图片',
value: '',
icon: 'iconpic',
isShowPoints: false,
points: '',
required: true,
hasAnswer: false,
answer: ''
}
]
}
];

View File

@@ -0,0 +1,195 @@
<template>
<ai-detail class="Add">
<ai-title slot="title" title="发起活动" isShowBack isShowBottomBorder @onBackClick="cancel"/>
<template slot="content">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="right">
<ai-card title="活动信息">
<template #content>
<div class="ai-form">
<el-form-item label="发布地区" prop="areaId" style="width: 100%">
<ai-area-select
clearable
:instance="instance"
v-model="form.areaId"
@fullname="v => form.areaName = v"
always-show
area-level="5"
:disabledLevel="disabledLevel"
/>
</el-form-item>
<el-form-item label="标题" prop="title" style="width: 100%">
<el-input
size="small"
v-model="form.title"
placeholder="请输入..."
clearabel
:maxLength="60"
></el-input>
</el-form-item>
<el-form-item label="活动地点" prop="address" style="width: 100%">
<el-input
size="small"
v-model="form.address"
placeholder="请输入..."
clearabel
:maxLength="20"
></el-input>
</el-form-item>
<el-form-item label="参与名额" prop="total" style="width: 100%">
<el-input-number v-model="form.total" :min="0" :max="1000" size="small"></el-input-number>
<span class="text">*0表示不限制活动报名人数</span>
</el-form-item>
<el-form-item label="活动时间" prop="activeTimeList" style="width: 50%">
<el-date-picker size="small" :picker-options="pickerOptions"
v-model="form.activeTimeList"
type="datetimerange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item label="截至时间" prop="stopSignupTime" style="width: 50%">
<el-date-picker size="small" :picker-options="pickerOptions"
v-model="form.stopSignupTime"
type="date"
style="width: 100%"
placeholder="选择日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item label="联系人" prop="contactPerson" style="width: 50%">
<el-input
size="small"
v-model="form.contactPerson"
placeholder="请输入..."
clearabel
:maxLength="10"
></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="contactPhone" style="width: 50%">
<el-input
size="small"
v-model="form.contactPhone"
placeholder="请输入..."
clearabel
:maxLength="11"
></el-input>
</el-form-item>
<el-form-item label="活动介绍" prop="content" style="width: 100%">
<ai-editor v-model="form.content" :instance="instance" @validate="v=>valid=!v"/>
</el-form-item>
</div>
</template>
</ai-card>
</el-form>
</template>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">提交</el-button>
</template>
</ai-detail>
</template>
<script>
import {mapState} from "vuex";
export default {
name: "Add",
props: {
instance: Function,
dict: Object,
selected: Object,
},
computed: {
...mapState(["user"]),
},
data() {
return {
disabledLevel: 0,
pickerOptions: {
disabledDate(time) {
return time.getTime() < Date.now();
},
},
rules: {
areaId: [{ required: true, message: '请选择发布地区', trigger: 'change' },
{
validator: (r, v, cb) => {
if (/.+0{3}$/.test(v)) {
cb("发布地区必须选到村级")
} else cb()
}, trigger: "blur"
}
],
title: [{ required: true, message: '请输入标题', trigger: 'change' }],
address: [{ required: true, message: '请选输入活动地点', trigger: 'change' }],
total: [{ required: true, message: '请输入参与人员', trigger: 'change' }],
activeTimeList: [{ required: true, message: '请选择活动时间', trigger: 'change' }],
stopSignupTime: [{ required: true, message: '请选择截止时间', trigger: 'change' }],
contactPerson: [{ required: true, message: '请输入联系人', trigger: 'change' }],
contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'change' }],
content: [{ required: true, message: '请输入活动介绍', trigger: 'change' }],
},
form: {
areaId: '',
areaName: '',
title: '',
address: '',
total: '',
activeTimeList: [],
stopSignupTime: '',
contactPerson: '',
contactPhone: '',
content: ''
}
};
},
created() {
console.log(this.user)
this.dict.load("education", "sex", "nation", "developStatus")
this.disabledLevel = this.user.info.areaList.length - 1
this.form.areaId = this.user.info.areaId
},
methods: {
confirm() {
this.$refs.form.validate((valid) => {
if (valid) {
this.form.beginTime = this.form.activeTimeList[0]
this.form.endTime = this.form.activeTimeList[1]
this.instance.post(`/app/apppartyreport/addOrUpdate`, {...this.form}).then((res) => {
if (res.code == 0) {
this.$message.success('发起活动成功')
setTimeout(() => {
this.cancel()
}, 600);
}
});
}
});
},
cancel() {
this.$emit("goBack")
},
}
};
</script>
<style lang="scss" scoped>
.Add {
.ai-form {
display: flex;
}
.text {
display: inline-block;
padding-left: 8px;
color: #999;
}
::v-deep .el-range-separator {
width: 28px!important;
}
::v-deep .el-date-editor {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<div class="AppCommunityMember">
<ai-list v-show="!detailShow">
<template slot="title">
<ai-title title="报到服务" :isShowBottomBorder="false" :instance="instance" :isShowArea="currIndex == 0 ? true : false" v-model="areaId" @change="changeArea"></ai-title>
</template>
<template slot="tabs">
<el-tabs v-model="currIndex">
<el-tab-pane v-for="(tab,i) in tabs" :key="i" :name="String(i)" :label="tab.label">
<component
:is="tab.comp"
v-if="currIndex === String(i)"
:areaId="areaId"
:ref="tab.name"
@showDetail="showDetail"
:instance="instance"
:dict="dict"
:permissions="permissions" />
</el-tab-pane>
</el-tabs>
</template>
</ai-list>
<component v-if="detailShow" :is="currDet" :areaId="areaId" :id="id" @goBack="goBack" :instance="instance" :dict="dict" :permissions="permissions"/>
</div>
</template>
<script>
import List from './List'
import Statistics from './Statistics'
import Organization from './Organization'
import Add from './Add'
import Detail from './Detail'
import {mapState} from 'vuex'
export default {
name: "AppCommunityMember",
label: "在职党员社区报到",
components: {List, Statistics, Organization, Add, Detail},
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
tabs() {
return [
{
label: "社区活动管理",
name: "List",
comp: List,
},
{
label: "报到数据",
name: "Statistics",
comp: Statistics,
},
{
label: "报到组织管理",
name: "Organization",
comp: Organization,
},
]
},
...mapState(['user']),
currDet() {
return this.id ? Detail : Add
}
},
created() {
this.areaId = this.user.info.areaId;
},
data() {
return {
activeName: "List",
currIndex: 0,
areaId: '',
detailShow: false,
id: ''
}
},
methods: {
goBack() {
this.detailShow = false;
this.$nextTick(() => {
this.$refs.List[0].getListInit()
})
},
showDetail(id) {
this.id = id || ''
this.detailShow = true
},
changeArea() {
this.$nextTick(() => {
this.$refs.List[0].getListInit()
})
}
}
}
</script>
<style lang="scss" scoped>
.AppCommunityMember {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,130 @@
<template>
<ai-detail class="party-detail">
<template slot="title">
<ai-title
title="活动详情"
isShowBack
isShowBottomBorder
@onBackClick="cancel()"
></ai-title>
</template>
<template slot="content">
<div>
<ai-card title="活动信息">
<template #content>
<ai-wrapper label-width="120px">
<ai-info-item isLine label="发布地区">{{ info.areaName }}</ai-info-item>
<ai-info-item isLine label="标题">{{ info.title }}</ai-info-item>
<ai-info-item isLine label="活动地点">{{ info.address }}</ai-info-item>
<ai-info-item isLine label="参与名额">{{ info.total }}</ai-info-item>
<ai-info-item label="报名状态">{{ dict.getLabel('partyReportSignupStatus', info.signupStatus) }}</ai-info-item>
<ai-info-item label="活动状态">{{ dict.getLabel('activityStatus', info.actionStatus) }}</ai-info-item>
<ai-info-item label="活动时间">{{ info.beginTime.substring(0, 10) }} {{ info.endTime.substring(0, 10) }}</ai-info-item>
<ai-info-item label="截至时间">{{ info.stopSignupTime }}</ai-info-item>
<ai-info-item label="联系人">{{ info.contactPerson }}</ai-info-item>
<ai-info-item label="联系电话">{{ info.contactPhone }}</ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="活动介绍">
<template #content>
<p v-html="info.content"></p>
</template>
</ai-card>
<ai-card title="报名情况">
<template #content>
<ai-table
:border="true"
:tableData="info.introducerList"
:isShowPagination="false"
:col-configs="colConfigs"
>
<el-table-column slot="user" width="300px" label="报名人员资料" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<p>{{row.partyName}}-{{row.phone}}</p>
<p class="color-999">{{partyOrgName}}</p>
</div>
</template>
</el-table-column>
<el-table-column slot="options" width="120px" fixed="right" label="操作" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="toDetail(row.id)">查看日志</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
</div>
</template>
</ai-detail>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "detail",
props: {
instance: Function,
dict: Object,
params: Object,
id: String
},
data() {
return {
info: {},
colConfigs: [
{prop: "signupTime", label: "报名时间", align: "center"},
{slot: "user"},
{prop: "reportType", label: "活动报到类型", align: "center", dict: 'partyReportSignupReportType'},
{prop: "remark", label: "报名备注", align: "center"},
{prop: "status", label: "报名状态", align: "center", dict: 'partyReportPersonSignupStatus'},
{prop: "logStatus", label: "活动日志", align: "center", dict: 'partyReportSignupLogStatus'},
{slot: "options"},
],
userList: []
}
},
computed: {
...mapState(["user"]),
},
created() {
this.dict.load('activityStatus', 'partyReportSignupStatus', 'partyReportSignupReportType', 'partyReportPersonSignupStatus', 'partyReportSignupLogStatus').then(() => {
this.getInfo()
this.getList()
})
},
methods: {
getInfo() {
this.instance.post(`/app/apppartyreport/queryDetailById?id=${this.id}`).then((res) => {
if (res?.data) {
this.info = res.data;
if (this.info.birthday) {
this.info.birthday = this.info.birthday.substring(0, 10);
}
}
});
},
getList() {
this.instance.post(`/app/apppartyreport/signup-info?id=${this.id}`).then((res) => {
if (res?.data) {
res.data.map((item) => {
item.signupTime = item.signupTime.substring(0, 10)
})
}
});
},
cancel() {
this.$emit("goBack")
},
},
};
</script>

View File

@@ -0,0 +1,155 @@
<template>
<ai-list class="partyList">
<template slot="content">
<ai-search-bar>
<template #left>
<el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd()">发起活动</el-button>
<ai-select v-model="search.actionStatus" placeholder="请选择活动状态" :selectList="dict.getDict('activityStatus')"
@change="getListInit"></ai-select>
<ai-select v-model="search.signupStatus" placeholder="请选择报名状态" :selectList="dict.getDict('partyReportSignupStatus')"
@change="getListInit"></ai-select>
</template>
<template slot="right">
<el-input
v-model="search.con"
size="small"
placeholder="请输入活动名称或发布地区"
clearable
@change="search.current=1,getList()"
suffix-icon="iconfont iconSearch"/>
</template>
</ai-search-bar>
<ai-table
:dict="dict"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
v-loading="loading"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column slot="activeTime" width="220px" label="活动时间" align="center">
<template slot-scope="{ row }">
<div class="table-options">{{row.beginTime.substring(0, 10)}}{{row.endTime.substring(0, 10)}}</div>
</template>
</el-table-column>
<el-table-column slot="options" width="180px" fixed="right" label="操作" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'List',
props: {
instance: Function,
permissions: Function,
dict: Object,
selected: Object,
areaId: String
},
data() {
return {
search: {
current: 1,
size: 10,
con: '',
actionStatus: '',
signupStatus: ''
},
orgName: '',
loading: false,
total: 0,
colConfigs: [
{prop: 'title', label: '活动名称', align: 'center'},
{prop: 'areaName', label: '发布地区', align: 'center'},
{slot: 'activeTime'},
{prop: 'total', label: '报名人数', align: 'center', width: 120},
{prop: 'signupStatus', label: '报名状态', align: 'center', dict: 'partyReportSignupStatus', width: 120},
{prop: 'actionStatus', label: '活动状态', align: 'center', dict: 'activityStatus', width: 120},
{slot: 'option'},
],
tableData: [],
ids: '',
}
},
computed: {
...mapState(['user']),
},
created() {
this.dict.load('activityStatus', 'partyReportSignupStatus').then(() => {
this.getList()
})
},
methods: {
getListInit() {
this.current = 1
this.getList()
},
getList() {
this.instance.post(`/app/apppartyreport/list`, null, {
params: {...this.search, areaId: this.areaId}
}).then(res => {
this.loading = false
if (res?.data) {
this.tableData = res.data.records
this.total = res.data.total
}
}).catch(() => {
this.loading = false
})
},
toDetail(id) {
this.$emit("showDetail", id)
},
toAdd() {
this.$emit("showDetail")
}
}
}
</script>
<style lang="scss" scoped>
.partyList {
.party-table__btns {
display: flex;
align-items: center;
}
::v-deep .audit-0 {
color: #FF8822 !important;
}
::v-deep .audit-1 {
color: #2EA222 !important;
}
::v-deep .ai-list__content--right {
flex: 1;
min-width: 0;
margin-left: 1px;
box-shadow: none;
.ai-list__content--right-wrapper {
width: 100%;
}
}
::v-deep .is-current>.el-tree-node__content{
width: 100%!important;
padding-right: 16px!important;
}
}
</style>

View File

@@ -0,0 +1,324 @@
<template>
<section class="Organization">
<div class="organizations_content">
<header>
<i class="iconfont iconModal_Warning"></i>
<p>
并非所有单位都需要参加社区活动对于需要参加社区活动的单位请在下方应报到单位名单中进行修改无需参加活动的单位请勿勾选
</p>
</header>
<div class="organizations_tree">
<p class="top">
<span class="word">应报到单位名单</span>
<el-button
type="text"
icon="iconfont iconEdit"
@click="editDialog = true"
>修改</el-button
>
</p>
<el-input
size="mini"
v-model.trim="selectFilterText"
style="margin-top: 8px"
placeholder="请输入单位..."
suffix-icon="iconfont iconSearch"
@keyup.native="$refs.selectTree.filter(selectFilterText)"
clearable
@clear="$refs.selectTree.filter(selectFilterText)"
/>
<div class="left_cont">
<el-tree
:data="selectTreeData"
:props="defaultProps"
ref="selectTree"
node-key="partyOrgId"
:default-expanded-keys="[user.info.organizationId]"
empty-text="搜索不到相关内容"
:filter-node-method="filterNode"
:highlight-current="true"
v-if="selectTreeData.length > 0"
/>
<div v-else class="no-data" style="height: 300px"></div>
</div>
</div>
</div>
<el-dialog
class="mask"
:visible.sync="editDialog"
width="520px"
title="修改应报到单位"
:close-on-click-modal="false"
>
<div class="tree">
<el-input
size="mini"
v-model="filterText"
placeholder="请输入单位..."
suffix-icon="iconfont iconSearch"
clearable
/>
<div class="left_cont">
<el-tree
v-if="editDialog"
:data="originTreeData"
:props="defaultProps"
ref="tree_edit"
node-key="partyOrgId"
show-checkbox
empty-text="搜索不到相关内容"
:filter-node-method="filterNode"
:default-expanded-keys="[user.info.organizationId]"
:default-checked-keys="defaultKeys"
:current-node-key="user.info.organizationId"
:highlight-current="true"
/>
<div v-else class="no-data" style="height: 300px"></div>
</div>
</div>
<div slot="footer" style="text-align: center">
<el-button
style="width: 92px"
size="small"
class="delete-btn"
@click="editDialog = false"
>取消</el-button
>
<el-button
style="width: 92px"
size="small"
type="primary"
@click="selectTree()"
>确认</el-button
>
</div>
</el-dialog>
</section>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "Organization",
props: {
instance: Function,
dict: Object,
permissions: Function,
},
data() {
return {
treeData: [],
editDialog: false,
filterText: "",
selectFilterText: "",
};
},
computed: {
...mapState(["user"]),
defaultProps() {
return {
children: "children",
label: "partyOrgName",
};
},
defaultKeys() {
return this.selectTreeData.map((e) => e.partyOrgId) || [];
},
originTreeData() {
let data = [];
if (this.editDialog) {
let origin = JSON.parse(JSON.stringify(this.treeData)) || [];
data = origin.filter(
(e) => e.partyOrgId == this.user.info.organizationId
);
data.map((t) => this.addChildParty(t, origin));
}
return data;
},
selectTreeData() {
let treeMeta = [],
arr = JSON.parse(
JSON.stringify(this.treeData.filter((e) => e.checked))
);
treeMeta = this.findParent(arr, {
id: "partyOrgId",
parent: "partyOrgParentId",
});
treeMeta.map((t) => this.addChildParty(t, arr));
return treeMeta;
},
},
watch: {
filterText(val) {
this.$refs.tree_edit.filter(val);
},
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.partyOrgName.indexOf(value) !== -1;
},
// 根据登录用户单位查 树形结构
searchSysAll() {
this.treeData = [];
this.instance.post("/app/apppartyreportconfig/list").then((res) => {
if (res?.data) {
this.treeData = res.data;
}
});
},
selectTree() {
let selectArr = this.$refs.tree_edit.getCheckedNodes();
this.updateTree(selectArr);
},
updateTree(arr) {
arr = arr.map((e) => {
delete e.children;
return { ...e, checked: true };
});
this.instance.post("/app/apppartyreportconfig/addOrUpdate", arr).then((res) => {
if (res?.code == 0) {
this.$message.success("修改成功");
this.searchSysAll();
this.editDialog = false;
}
});
},
findParent(data, ops) {
ops = {
id: "self",
parent: "parentId",
parents: {},
pending: [],
...ops,
};
let parentsObj = {};
data
.filter(
(e) => ops.pending.length == 0 || ops.pending.includes(e[ops.id])
)
.map((e) => {
if (e[ops.parent]) {
parentsObj[e[ops.parent]] = 0;
} else {
ops.parents[e[ops.id]] = e;
}
});
let pending = {};
Object.keys(parentsObj).map((p) => {
let item = data.find((e) => e[ops.id] == p);
if (item) {
item[ops.parent] && (pending[p] = item[ops.parent]);
} else {
if (!ops.parents[p]) {
data
.filter((e) => e[ops.parent] == p)
.map((e) => {
ops.parents[e[ops.id]] = e;
});
}
}
});
ops.pending = Object.values(pending);
if (ops.pending.length) {
return this.findParent(data, ops);
} else return Object.values(ops.parents);
},
},
mounted() {
this.$nextTick(() => this.searchSysAll());
},
};
</script>
<style lang="scss" scoped>
.Organization {
width: 100%;
height: 100%;
padding: 16px 0;
overflow: auto;
display: flex;
justify-content: center;
align-items: flex-start;
.organizations_content {
width: 1200px;
height: 100%;
overflow-y: hidden;
header {
width: 100%;
background: rgba(255, 243, 232, 1);
border-radius: 4px;
border: 1px solid rgba(255, 136, 34, 1);
padding: 8px;
box-sizing: border-box;
overflow: hidden;
.iconfont.iconModal_Warning {
color: #ff8822;
height: 100%;
float: left;
width: 20px;
}
p {
float: left;
width: 800px;
font-size: 12px;
color: #ff8822;
}
}
.organizations_tree {
margin-top: 16px;
width: 100%;
min-height: 500px;
background: rgba(255, 255, 255, 1);
border-radius: 4px;
padding: 0 16px 16px 16px;
box-sizing: border-box;
margin-bottom: 48px;
.top {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
border-bottom: 1px solid #eee;
.word {
font-size: 16px;
color: #333333;
font-weight: bold;
}
::v-deep .el-button--text [class*="iconfont"] {
color: #5088ff;
}
}
}
}
::v-deep .el-dialog {
.el-dialog__body {
padding: 16px;
}
}
::v-deep .el-tree {
margin-top: 16px;
min-height: 300px;
max-height: 600px;
overflow: auto;
.el-tree-node.is-focusable.is-checked {
.el-tree-node__label {
color: #5088ff;
background: rgba(239, 246, 255, 1);
}
}
}
}
</style>

View File

@@ -0,0 +1,652 @@
<template>
<ai-list class="Statistics">
<template #left>
<ai-tree-menu
title="应报到单位名单"
searchPlaceholder="请输入单位..."
@search="onSearch"
>
<ai-party-tree
:filter-node-method="filterNode"
ref="tree"
:instance="instance"
:root="user.info.organizationId"
:current-node-key="selected.id"
@select="handleNodeClick"
/>
</ai-tree-menu>
</template>
<template slot="content">
<div class="top">
<header>近12个月学员活动参与情况统计</header>
<div class="month" id="month"></div>
</div>
<div class="middle">
<header>
<el-date-picker
v-model="selectMonth"
type="month"
@change="typeScale()"
size="mini"
:clearable="false"
value-format="yyyy-MM-dd"
placeholder="选择年月"
>
</el-date-picker>
</header>
<div class="content">
<div class="middle-left">
<ul>
<li>
<p>应报到学员人数</p>
<div class="num">
<span>{{ numObj.party_num }}</span>
<span></span>
</div>
</li>
<li>
<p>本月报名活动人数</p>
<div class="num">
<span>{{ numObj.registered_num }}</span>
<span></span>
</div>
</li>
<li>
<p>实际参与活动人数</p>
<div class="num">
<span>{{ numObj.actual_num }}</span>
<span></span>
</div>
</li>
<li>
<p>活动实际参与率</p>
<div class="num">
<span>{{ numObj.proportion }}</span>
</div>
</li>
</ul>
</div>
<div class="middle-right" id="middle-right"></div>
</div>
</div>
<div class="bottom">
<header>
<ul>
<li
v-for="(e, index) in table"
:key="index"
@click="handleClick(e, index)"
:class="{ activeNav: e.id == activeId }"
>
{{ e.label }}
</li>
<span ref="border"></span>
</ul>
</header>
<ai-search-bar>
<template #left>
<el-date-picker
v-model="searchObj.ymd"
type="month"
@change="(page.current = 1), searchList()"
size="small"
:clearable="false"
value-format="yyyy-MM-01"
placeholder="选择年月"
>
</el-date-picker>
<el-button
icon="iconfont iconExported"
size="mini"
style="margin-left: 8px"
@click="outPut()"
:disabled="!Boolean(tableData.length)"
>导出
</el-button>
</template>
<template #right>
<el-input
v-model="searchObj.name"
size="small"
placeholder="请输入姓名"
clearable
@change="(search.current = 1), searchList()"
suffix-icon="iconfont iconSearch"
/>
</template>
</ai-search-bar>
<ai-table
:dict="dict"
:tableData="tableData"
:col-configs="colConfigs"
:total="page.total"
style="margin-top: 6px"
:current.sync="page.current"
:size.sync="page.size"
@getList="searchList"
>
</ai-table>
</div>
</template>
</ai-list>
</template>
<script>
import { mapState } from "vuex";
import * as echarts from "echarts";
export default {
name: "Statistics",
props: {
instance: Function,
dict: Object,
permissions: Function,
},
data() {
return {
filterText: "",
defaultArr: "",
partyOrgId: "", //当前选中的id
data12: {},
selectMonth: "",
activeId: "0",
numObj: {},
searchObj: {
name: "",
listType: "0",
ymd: "",
},
tableData: [],
page: {
size: 5,
current: 1,
total: 0,
},
selected: {},
colConfigs: [
{ prop: "name", label: "姓名", align: "center" },
{ prop: "sex", label: "性别", align: "center", dict: "sex" },
{ prop: "partyOrg", label: "所属组织", align: "center" },
{ prop: "signupCount", label: "报名次数", align: "center", width: 150 },
{
prop: "partakeCount",
label: "参与次数",
align: "center",
width: 150,
},
],
};
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
},
computed: {
...mapState(["user"]),
table() {
return [
{ label: "学员活动记录", id: "0" },
{ label: "未参与活动名单", id: "1" },
];
},
defaultProps() {
return {
children: "children",
label: "label",
};
},
orgTree() {
return this.$refs.tree?.$refs?.partyTree;
},
},
methods: {
handleNodeClick(data) {
this.selected = data;
this.partyOrgId = data.id;
this.activeId = "0";
this.searchObj.name = "";
this.searchObj.listType = "0";
this.$nextTick(() => {
this.month12();
this.typeScale();
this.searchList();
});
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
month12() {
this.instance
.post("/app/apppartyreport/trend", null, {
params: {
partyOrgId: this.partyOrgId,
},
})
.then((res) => {
if (res.code == 0) {
this.data12 = res.data;
this.showMonth12(res.data);
}
});
},
typeScale() {
this.instance
.post("/app/apppartyreport/type-scale", null, {
params: {
partyOrgId: this.partyOrgId,
ymd: this.selectMonth,
},
})
.then((res) => {
if (res.code == 0) {
this.numObj = res.data;
this.showPartyScale();
}
});
},
handleClick(e, index) {
this.activeId = e.id;
this.searchObj.listType = e.id;
this.searchObj.name = "";
this.page.current = 1;
this.page.size = 5;
this.searchList();
this.$refs.border.style.left = index * 160 + "px";
},
searchList() {
this.instance
.post(`/app/apppartyreport/list-signup`, null, {
params: {
...this.searchObj,
partyOrgId: this.partyOrgId,
...this.page,
},
})
.then((res) => {
if (res?.data) {
this.tableData = res.data.records;
this.page.total = res.data.total;
}
});
},
outPut() {
this.instance
.post("/app/apppartyreport/export-signup", null, {
responseType: "blob",
params: {
partyOrgId: this.partyOrgId,
...this.searchObj,
},
})
.then((res) => {
const link = document.createElement("a");
let blob = new Blob([res], { type: "application/vnd.ms-excel" });
link.style.display = "none";
link.href = URL.createObjectURL(blob);
let fileName = "";
this.activeId == 0
? (fileName = "学员活动记录")
: (fileName = "未参与活动名单");
link.setAttribute("download", fileName + ".xls");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
},
showMonth12(data) {
let myChart2 = echarts.init(document.getElementById("month"));
let months = [];
let arr1 = [];
let arr2 = [];
let arr3 = [];
data.forEach((e) => {
months.push(e.cycle);
arr1.push(e.partyNum);
arr2.push(e.registeredNumrc);
arr3.push(e.actualNumrc);
});
let option = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
label: {
backgroundColor: "#6a7985",
},
},
},
dataset: {
source: [
["product", ...months],
["应报到学员人次", ...arr1],
["报名活动人次", ...arr2],
["实际参与活动人次", ...arr3],
],
},
legend: {
data: ["应报到学员人次", "报名活动人次", "实际参与活动人次"],
},
toolbox: {
feature: {
saveAsImage: {},
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
boundaryGap: false,
type: "category",
},
yAxis: { gridIndex: 0 },
series: [
{ type: "line", smooth: true, seriesLayoutBy: "row" },
{ type: "line", smooth: true, seriesLayoutBy: "row" },
{ type: "line", smooth: true, seriesLayoutBy: "row" },
],
};
myChart2.setOption(option);
window.onresize = function () {
myChart2.resize();
};
},
showPartyScale() {
let myChart = echarts.init(document.getElementById("middle-right"));
let legendData = [];
let seriesData = [];
if (JSON.stringify(this.numObj.reportTypeMap) != "[]") {
this.numObj.reportTypeMap.forEach((e) => {
legendData.push(
this.dict.getLabel("partyReportSignupReportType", e.report_type)
);
seriesData.push({
value: e.count,
name: this.dict.getLabel(
"partyReportSignupReportType",
e.report_type
),
});
});
} else {
legendData = [
"居住地社区报到服务",
"单位联系社区报到服务",
"其他社区报到服务",
];
seriesData = [
{ value: 0, name: "居住地社区报到服务" },
{ value: 0, name: "单位联系社区报到服务" },
{ value: 0, name: "其他社区报到服务" },
];
}
let option = {
backgroundColor: "rgba(249,249,249,1)",
title: {
text: "学员报到类型比例",
x: "left",
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
legend: {
orient: "vertical",
right: 50,
top: 70,
bottom: 20,
width: "auto",
data: legendData,
},
series: [
{
name: "学员报到类型比例",
type: "pie",
radius: ["45%", "75%"],
center: ["30%", "55%"],
avoidLabelOverlap: false,
label: {
position: "inside",
formatter: "{d}%",
},
data: seriesData,
itemStyle: {
normal: {
color: function (params) {
//自定义颜色
var colorList = [
"#4B87FE",
"#FFAA44",
"#FF4466",
"#FFAA44",
"#2EA222",
];
return colorList[params.dataIndex];
},
},
},
},
],
};
myChart.setOption(option);
window.onresize = function () {
myChart.resize();
};
},
getNowDate() {
let mydate = new Date();
let mymonth = mydate.getMonth() + 1;
mymonth < 10 ? (mymonth = `0${mymonth}`) : mymonth;
let year = mydate.getFullYear();
return `${year}-${mymonth}-01`;
},
onSearch(v) {
this.orgTree.filter(v);
},
},
mounted() {
this.partyOrgId = this.user.info.organizationId;
this.dict.load("partyReportSignupReportType", "sex");
this.selectMonth = this.getNowDate();
this.searchObj.ymd = this.getNowDate();
this.$nextTick(() => {
this.month12();
this.typeScale();
this.searchList();
});
},
};
</script>
<style lang="scss" scoped>
.Statistics {
width: 100%;
height: 100%;
padding: 16px;
box-sizing: border-box;
overflow: auto;
.top {
width: 100%;
height: 288px;
background-color: #fff;
border-radius: 4px;
header {
color: #333333;
padding: 0 16px;
box-sizing: border-box;
height: 47px;
font-size: 16px;
font-weight: bold;
line-height: 47px;
background: rgba(255, 255, 255, 1);
border-bottom: 1px solid #eee;
}
.month {
width: 100%;
padding: 16px;
box-sizing: border-box;
height: calc(100% - 47px);
}
}
.middle {
width: 100%;
height: 320px;
margin-top: 16px;
background-color: #fff;
border-radius: 4px;
header {
padding: 0 16px;
box-sizing: border-box;
height: 48px;
display: flex;
align-items: center;
}
.content {
width: 100%;
padding: 16px;
box-sizing: border-box;
height: calc(100% - 48px);
.middle-left {
width: 50%;
height: 100%;
float: left;
ul {
overflow: hidden;
width: 95%;
height: 100%;
display: flex;
flex-wrap: wrap;
li {
width: 48%;
height: 112px;
padding: 8px;
box-sizing: border-box;
background: rgba(249, 249, 249, 1);
border-radius: 2px;
margin-bottom: 16px;
margin-left: 4%;
&:nth-of-type(2n - 1) {
margin-left: 0;
}
p {
color: #333333;
font-size: 16px;
height: 32px;
font-weight: bold;
}
.num {
height: 80px;
line-height: 60px;
span:nth-of-type(1) {
color: #2266ff;
font-size: 32px;
}
span:nth-of-type(2) {
font-size: 16px;
color: rgba(153, 153, 153, 1);
}
}
}
}
}
.middle-right {
width: 50%;
height: 100%;
float: left;
padding: 4px;
box-sizing: border-box;
background: rgba(249, 249, 249, 1);
border-radius: 2px;
}
}
}
.bottom {
width: 100%;
height: 464px;
margin-top: 16px;
background-color: #fff;
border-radius: 4px;
header {
width: 100%;
height: 56px;
padding: 0 16px;
box-sizing: border-box;
border-bottom: 1px solid rgb(241, 237, 237);
margin-bottom: 16px;
ul {
width: 100%;
height: 100%;
position: relative;
li {
width: 160px;
height: 56px;
line-height: 56px;
float: left;
text-align: center;
font-size: 16px;
color: #333333;
position: relative;
transition: opacity 1s;
cursor: pointer;
opacity: 0.7;
&:hover {
color: rgba(34, 102, 255, 1);
}
}
.activeNav {
opacity: 1;
font-weight: bold;
color: rgba(34, 102, 255, 1);
}
span {
width: 160px;
height: 3px;
display: block;
background-color: rgba(34, 102, 255, 1);
transition: left 0.5s;
position: absolute;
bottom: 0;
left: 0;
}
}
}
}
::v-deep .el-col-24 {
width: auto;
}
}
</style>