Files
dvcp_v2_webapp/project/fengdu/AppBIBoard/AppBIBoard.vue
2023-10-31 18:03:52 +08:00

1197 lines
39 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>
<section class="AppBIBoard" :class="{fullscreen}">
<ai-fit-view>
<ai-dv-wrapper ref="fddv" title="丰收号-家庭互助" :instance="instance" :mask="false">
<template v-slot:head="head">
<fengdu-head v-model="areaId" v-bind="head" @fullscreen="handleFullScreen" @setting="handleSetting"/>
</template>
<div class="left">
<fd-card label="社群动态概况">
<div class="grid c-2 pad-t14 pad-b20">
<div class="staPanel" v-for="(v,k) in sta" :key="k">
<div v-text="k"/>
<b v-text="v"/>
</div>
<div class="chart">
<div class="title">群活跃率7</div>
<ai-echart :ops="chart" :data="calcProgress(chartData)">
<div class="legend">
<ai-highlight v-for="item in chartData" :key="item.name" :content="`@v${item.value}`"
:value="item.name" color="#9BB7D4" class="flex center mar-b8"/>
</div>
</ai-echart>
</div>
<div class="chart">
<div class="title">人员活跃7</div>
<ai-echart :ops="chart" :data="calcProgress(chartData2)">
<div class="legend">
<ai-highlight v-for="item in chartData2" :key="item.name" :content="`@v${item.value}`"
:value="item.name" color="#9BB7D4" class="flex center mar-b8"/>
</div>
</ai-echart>
</div>
</div>
</fd-card>
<fd-card class="mar-t14" :label="leftBottom">
<div v-if="leftBottom=='志愿者'" class="jumpBtn" slot="right" @click="handleJump">前往志愿者平台
<div class="el-icon-position"/>
</div>
<el-carousel arrow="never" class="mar-t8" @change="v=>leftBottom=['志愿者','互助会'][v]" :interval="6000">
<el-carousel-item name="志愿者">
<div class="staPanel simple flex mar-t10">
<div class="fill" v-for="(v,k) in volunteers" :key="k">
<div v-text="k"/>
<b v-text="v"/>
</div>
</div>
<div class="flex mar-v12">
<b class="fill title">志愿者名单</b>
<ai-select v-model="fraternity" class="areaPicker" placeholder="团队选择" :select-list="fraternities"
:prop="{label:'fraternity_name',value:'fraternity_name'}"
@select="getVolunteerData(areaId)"/>
</div>
<dv-scroll-board :config="volunteerConfig"/>
</el-carousel-item>
<el-carousel-item name="互助会">
<dv-scroll-board class="origin" :config="fraternityTypeConfig" @click="getFraternityData"
style="height:360px"/>
</el-carousel-item>
</el-carousel>
</fd-card>
</div>
<div class="center fill relative">
<fd-map ref="map" class="w100 h100" v-model="map" :root="areaId"/>
<fd-card class="centerBottom pad-b8" label="实时动态">
<dv-scroll-board class="mar-t14" :config="realtimeEvents" style="height: 114px"
@click="handleRealtimeEventDialog"/>
</fd-card>
</div>
<div class="right">
<fd-card label="功德银行">
<template #right>
<div class="shortcut" v-for="cut in shortcuts" :key="cut.k" @click="shortcut=cut.k"
:class="{active:shortcut==cut.k}" v-text="cut.v"/>
</template>
<div class="boxSta flex">
<div class="flex text">
<div>获取总积分</div>
<p v-text="GongdeBank.total"/>
</div>
</div>
<div class="staPanel simple right flex mar-t14">
<div class="fill" v-for="(v,k) in GongdeBank.users" :key="k">
<div v-text="k"/>
<b v-text="v"/>
</div>
</div>
<div class="boxSta box2 flex">
<div class="flex text">
<div>兑换总积分</div>
<p v-text="GongdeBank.useTotal"/>
</div>
</div>
<div class="staPanel simple right flex mar-t14">
<div class="fill" v-for="(v,k) in GongdeBank.stores" :key="k">
<div v-text="k"/>
<b v-text="v"/>
</div>
</div>
<div class="grid c-3 pad-t14 pad-b20">
<div class="staPanel" v-for="(v,k) in GongdeBank.tasks">
<div v-text="k"/>
<b v-text="v"/>
</div>
</div>
</fd-card>
<fd-card label="门户应用统计" class="mar-t14 pad-b20">
<dv-scroll-board class="mar-t14" :config="appSta" style="height: 304px"/>
</fd-card>
</div>
</ai-dv-wrapper>
<fd-dialog v-model="dialog" :title="detail.eventType" :extra-dialog.sync="fraternityExtra"
:extra-title="detail.extraTitle">
<template v-if="detail.mapType=='store'">
<b class="title mar-t8 mar-b16">店铺商品</b>
<carousel autoplay :perPage="3" autoplayHoverPause navigationEnabled :paginationEnabled="false"
class="mar-h32"
navigationNextLabel=" " navigationPrevLabel="">
<slide class="goods" v-for="good in detail.goods" :key="good.id">
<img :src="good.goods.picUrl"/>
<div class="mar-t8" v-text="good.goods.title"/>
</slide>
</carousel>
<b class="title mar-t14 mar-b12">订单列表</b>
<dv-scroll-board :config="goodsConfig" style="height: 152px"/>
</template>
<template v-else-if="detail.mapType=='fraternitySta'">
<el-input placeholder="请输入互助会名称..." class="areaPicker mar-b20" size="small"
v-model="fraternityFilter"/>
<dv-scroll-board v-loading="fraternityLoading" :config="fraternityConfig" style="height: 380px"
element-loading-background="rgba(0, 0, 0, 0.2)" @click="handleFraternityTableClick"/>
<div slot="extra">
<dv-scroll-board class="mar-t10" :config="fraternityExtraConfig" style="height: 380px"
:class="{origin:fraternityExtraConfig.header.length==0}"/>
</div>
</template>
<template v-else-if="detail.mapType=='fraternity'">
<div class="flex normal">
<el-image class="fraternityImg" :src="detail.cover_url"/>
<div class="mar-l16 fill" v-html="detail.introduction"/>
</div>
</template>
<template v-else-if="detail.mapType=='area'">
<div class="staPanel area right mar-t12 mar-b24 grid c-4">
<fd-item v-for="(v,k) in detail.sta" :key="k" :label="k"><b v-text="v"/></fd-item>
</div>
<div class="flex mar-b14">
<div class="shortcut" v-for="cut in areaStaTypes" :key="cut.k" @click="areaStaType=cut.k"
:class="{active:areaStaType==cut.k}" v-text="cut.v"/>
</div>
<dv-scroll-board :config="areaTableConfig" style="height: 152px"/>
</template>
<template v-else>
<div v-if="detail.header" class="contentHead" v-html="detail.header"/>
<el-row type="flex" class="fill">
<el-carousel v-if="detail.imgs" class="fill">
<el-carousel-item v-for="(img,i) in detail.imgs" :key="i">
<el-image :src="img" :preview-src-list="detail.imgs"/>
</el-carousel-item>
</el-carousel>
<fd-scrollbar v-if="detail.form" class="fill mar-l24">
<fd-item v-for="(v,k) in detail.form" :key="k" :label="k" :value="v"/>
</fd-scrollbar>
<fd-scrollbar v-if="detail.content" class="fill mar-l14">
<div v-html="detail.content"/>
</fd-scrollbar>
</el-row>
</template>
</fd-dialog>
</ai-fit-view>
</section>
</template>
<script>
import AiFitView from "dui/packages/layout/AiFitView.vue";
import FengduHead from "./components/fengduHead.vue";
import FdCard from "./components/fdCard.vue";
import AiEchart from "dui/packages/tools/AiEchart.vue";
import AiHighlight from "dui/packages/layout/AiHighlight.vue";
import AiInfoItem from "dui/packages/basic/AiInfoItem.vue";
import AiWrapper from "dui/packages/basic/AiWrapper.vue";
import Vue from "vue";
import {scrollBoard} from "@jiaminghi/data-view"
import FdMap from "./components/fdMap.vue";
import FdDialog from "./components/fdDialog.vue";
import FdItem from "./components/fdItem.vue";
import FdScrollbar from "./components/fdScrollbar.vue";
import {Carousel, Slide} from "vue-carousel"
const tableConfigs = {
headerBGC: 'rgba(33, 180, 253, 0.1)',
headerHeight: 38,
oddRowBGC: 'rgba(112, 112, 112, 0)',
evenRowBGC: 'rgba(112, 112, 112, 0)',
rowNum: 3,
}
const genderDict = {
1: '男', 2: '女', 3: '未知', 0: '未知'
}
const getCluster = (points, options = {big: {}, normal: {}}, num = 10) => {
const big = [], {distance = 0.03} = options
const pointInsideCircle = (point, circle, r) => {
if (r === 0) return false
const dx = circle[0] - point[0]
const dy = circle[1] - point[1]
return dx * dx + dy * dy <= r * r
}
return points.map(e => {
e.coord = e.coord.map(c => Number(c))
if (big.length == 0 ||
!big.find(b => pointInsideCircle(e.coord, b, distance)) && big.length < num && e.introduction) {
big.push(e.coord)
return {
...e,
...options.big,
}
} else {
return {
...e,
...options.normal
}
}
})
}
export default {
name: "AppBIBoard",
label: "丰都指挥舱",
components: {
FdScrollbar, Carousel, Slide,
FdItem, FdDialog, FdMap, AiWrapper, AiInfoItem, AiHighlight, AiEchart, FdCard, FengduHead, AiFitView
},
props: {
instance: Function,
dict: Object
},
data() {
return {
areaId: '',
fraternity: '',
fullscreen: false,
sta: {},
chart: {
legend: {show: false},
series: {
type: 'gauge',
startAngle: 90,
endAngle: -270,
center: ['50%', 74],
radius: 50,
progress: {
show: true,
overlap: false,
roundCap: true,
clip: false,
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{offset: 0, color: '#139AFF'},
{offset: 1, color: '#0ED5A6'},
]
}
},
},
pointer: {show: false},
splitLine: {show: false},
axisTick: {show: false},
axisLabel: {show: false},
axisLine: {
lineStyle: {width: 6, color: [[1, 'rgba(102, 121, 138, 0.4)']]},
},
detail: {
valueAnimation: true,
offsetCenter: [0, 0],
fontSize: 24,
formatter: '{value}%',
color: "#02FEFF",
fontFamily: "DIN",
width: 50,
lineHeight: 50,
padding: 12,
borderWidth: 1,
borderColor: 'rgba(102, 121, 138, 0.4)',
borderRadius: 50
},
},
},
chartData: [
// {name: "活跃居民群", value: 3502},
// {name: "全部居民群", value: 5118},
],
chartData2: [],
volunteers: {},
volunteerConfig: {
...tableConfigs,
header: ['所属团队', '姓名', '性别', '年龄'],
rowNum: 4,
columnWidth: [250, 80],
align: ['left', 'left', 'left', 'center'],
carousel: 'page',
data: [
// ['三合街道丁庄社区互助会', '张珊珊', '女', 32],
// ['三合街道丁庄社区互助会', '王富贵', '女', 32],
// ['三合街道丁庄社区互助会', '阿萨德', '男', 23],
// ['三合街道丁庄社区互助会', '阿连德', '男', 54],
// ['三合街道丁庄社区互助会', '王柏柏', '女', 66],
// ['三合街道丁庄社区互助会', '赵二狗', '男', 41],
// ['三合街道丁庄社区互助会', '唯一键', '女', 13],
// ['三合街道丁庄社区互助会', '卡萨丁', '男', 32],
// ['三合街道丁庄社区互助会', '卡萨丁', '男', 32],
// ['三合街道丁庄社区互助会', '卡萨丁', '男', 32],
// ['三合街道丁庄社区互助会', '张珊珊', '女', 32],
// ['三合街道丁庄社区互助会', '张珊珊', '女', 32],
]
},
appSta: {
...tableConfigs,
header: ['应用名称', '本日点击', '累计点击'],
rowNum: 7,
columnWidth: [250],
align: ['left', 'right', 'right'],
data: [
// ['渝快办', 178, 266],
// ['公交乘车码', 178, 266],
// ['警快办', 178, 266],
// ['医保电子凭证', 178, 266],
// ['居民上报', 178, 266],
// ['医保电子凭证', 178, 266],
// ['居民上报', 178, 266],
]
},
realtimeEvents: {
...tableConfigs,
columnWidth: [226],
align: ['center', 'left'],
data: [
// ['<div class="timeRow">2023-10-19 14:55:32</div>', '汇南社区-张三 创建了新的居民群'],
// ['<div class="timeRow">2023-10-19 14:55:32</div>', '汇南社区-张三 邀请居民"蓝天白云"加入居民群“书院社区2群”'],
// ['<div class="timeRow">2023-10-19 14:55:32</div>', '汇南社区-张三 将居民"蓝天白云"踢出居民群 “书院社区2群”'],
// ['<div class="timeRow">2023-10-19 14:55:32</div>', '滨江东路社区居民-陈思宇在丰收号小程序中进行了打卡签到'],
// ['<div class="timeRow">2023-10-19 14:55:32</div>', '滨江东路社区居民-陈思宇在丰收号小程序中进行了积分申请'],
// ['<div class="timeRow">2023-10-19 14:55:32</div>', '滨滨江东路社区居民-陈思宇在丰收号小程序中进行了物品兑换'],
// ['<div class="timeRow">2023-10-18 14:55:32</div>', '三角路社区居民-陈思宇在丰收号小程序中进行了物品兑换'],
// ['<div class="timeRow">2023-10-18 14:55:32</div>', '三角路社区居民-陈思宇在丰收号小程序中进行了物品兑换'],
// ['<div class="timeRow">2023-10-18 14:55:32</div>', '三角路社区居民-陈思宇在丰收号小程序中进行了物品兑换'],
]
},
shortcut: 3,
GongdeBank: {},
map: null,
dialog: false,
detail: {},
areaStaType: 'grid',
fraternities: [],
fraternityTypes: [],
fraternityFilter: '',
fraternityLoading: false,
fraternityExtra: false,
leftBottom: '志愿者'
}
},
computed: {
shortcuts: () => [
{k: '3', v: '昨日'},
{k: '0', v: '近七天'},
{k: '1', v: '近30天'},
{k: '2', v: '近一年'},
],
areaStaTypes: () => [
{k: 'grid', v: '网格'},
{k: 'resident', v: '居民群'},
{k: 'volunteer', v: '互助会'},
],
goodsConfig: v => ({
...tableConfigs,
header: ['兑换人', '兑换商品', '数量', '积分', '状态'],
data: v.detail.orders?.map(e => [e.integralUserName, e.goodsTitle, e.quantity, e.goodsIntegralPrice,
`<div class="statusTag ${e.status > 0 ? 'success' : ''}">${v.dict.getLabel('integralSGOStatus', e.status)}</div>`]),
align: ['left', 'left', 'right', 'right', 'center'],
}),
areaTableConfig: v => ({
...tableConfigs,
...{
grid: {
header: ['村/社区', '网格名称', '网格员人数'],
align: ['left', 'left', 'right'],
data: v.detail.girdList?.map(e => [e.parentGirdName, e.girdName, e.girdMemberCount])
},
resident: {
header: ['群名称', '群主', '群人数'],
align: ['left', 'left', 'right'],
columnWidth: [250],
data: v.detail.groupList?.map(e => [e.name, e.ownerName, e.memberCount])
},
volunteer: {
header: ['互助会类型', '互助会数量', '会员数'],
align: ['left', 'right', 'right'],
columnWidth: [250],
data: v.detail.fraternities?.map(e => [e.type, e.number, e.member_number])
}
}[v.areaStaType]
}),
fraternityConfig: v => ({
...tableConfigs,
header: ['互助会', '家长数量', '学生数量', '活动数量'],
columnWidth: [280],
align: ['left', 'right', 'right', 'right'],
rowNum: 9,
data: v.detail.list.filter(e => !v.fraternityFilter || e.fraternity_name.indexOf(v.fraternityFilter) > -1)
.map(e => [e.fraternity_name, e.par_num, e.stu_num, e.act_num])
}),
fraternityExtraConfig: v => ({
...tableConfigs, rowNum: 10,
header: ['姓名', '性别', '年龄'],
align: ['left', 'center', 'right'],
data: v.detail.extra,
...v.detail.others
}),
fraternityTypeConfig: v => ({
oddRowBGC: 'rgba(112, 112, 112, 0)',
evenRowBGC: 'rgba(112, 112, 112, 0)',
rowNum: 3,
data: v.fraternityTypes.map(e => [`
<div class="title mar-t10">${e.type}</div>
<div class="staPanel simple right flex mar-t10" >
<div class="fill"><div>互助会</div><b>${e.number || 0}</b></div>
<div class="fill"><div>会员数量</div><b>${e.member_number || 0}</b></div>
<div class="fill"><div>活动数量</div><b>${e.activity_number || 0}</b></div>
</div>
`])
})
},
watch: {
shortcut() {
this.getGdyh(this.areaId)
},
dialog(v) {
!v && (this.detail = {})
},
areaId(v) {
!!v && this.getData()
}
},
methods: {
handleFullScreen() {
this.fullscreen = this.$refs.fddv.handleFullScreen()
},
handleSetting(v) {
this.$refs.fddv.dialog = v
},
calcProgress(data = []) {
const value = data.length > 0 ? (data[0].value / data.at(-1).value * 100 || 0).toFixed(0) : 0
return [{value}]
},
getData(c = 0) {
const {areaId} = this.$data
if (areaId) {
const loading = this.$loading({
text: "正在加载数据...",
background: 'rgba(0,0,0,.91)',
})
Promise.all([
this.getMiniAppInfo(),
this.getRealTimeDynamic(areaId),
this.getWxGroupOverview(areaId),
this.getGdyh(areaId),
this.getFraternitySta(this.transferAreaCode(areaId)),
this.getFraternityTypes(areaId).then(data => this.fraternityTypes = data),
this.getFraternities(this.transferAreaCode(areaId)).then(() => this.getMapData(areaId))
]).finally(() => loading.close())
} else if (c < 10) setTimeout(() => this.getData(++c), 500)
else console.error(`尝试${c}次加载数据,无法过去数据`)
},
getMiniAppInfo() {
this.instance.post("/app/fdDiy/miniAppInfo").then(res => {
if (res?.data) {
const data = res.data.map(e => [e.name || "应用", e.lastDayClick, e.totalClick])
this.appSta = {...this.appSta, data}
}
})
},
getRealTimeDynamic(areaId) {
this.instance.post("/app/fdDiy/realTimeDynamic", null, {params: {areaId}}).then(res => {
if (res?.data) {
const meta = res.data,
data = meta.map(e => [`<div class="timeRow">${e.eventTime}</div>`, `<div class="flex">${e.bizId ? e.description.replace(e.type, `<div class="blue">${e.type}</div>`) : e.description}</div>`])
this.realtimeEvents = {...this.realtimeEvents, data, meta}
}
})
},
getWxGroupOverview(areaId) {
this.instance.post("/app/fdDiy/wxGroupOverview", null, {params: {areaId}}).then(res => {
if (res?.data) {
const {群数量 = 0, 群主人数 = 0, 群成员数量 = 0} = res.data
this.sta = {
群数量, 群主人数,
'群人员活跃数(30天)': Number(res.data['群人员活跃数(30天)']).toLocaleString(),
'群消息数(30天)': Number(res.data['群消息数(30天)']).toLocaleString(),
}
this.chartData = [
{name: "活跃居民群", value: Math.ceil(res.data["活跃居民群数量(7天)"] / 7)},
{name: "全部居民群", value: 群数量},
]
this.chartData2 = [
{name: "活跃居民数", value: res.data["活跃群成员数量(7天)"]},
{name: "群成员数量", value: 群成员数量},
]
}
})
},
getGdyh(areaId) {
this.instance.post("/app/fdDiy/gdyh", null, {params: {areaId, type: this.shortcut}}).then(res => {
if (res?.data) {
const {
宣发发布任务数 = 0,
宣发未审核数 = 0,
宣发审核通过数 = 0,
店铺总数 = 0,
店品库存量 = 0,
兑换物品数量 = 0,
获取积分总数: total = 0,
居民签到人次: 签到人数 = 0,
积分申请次数: 申请人数 = 0,
兑换总积分: useTotal = 0
} = res.data
this.GongdeBank = {
total, useTotal,
users: {
参与人数: 签到人数 + 申请人数,
申请人数,
签到人数
},
stores: {店铺总数, 店品库存量, 兑换物品数量},
tasks: {
宣发发布任务数,
审核通过率: (宣发审核通过数 / 宣发发布任务数 * 100 || 0).toFixed(2) + "%",
宣发未审核数,
}
}
}
})
},
getMapData(visibleId) {
const initMap = new Promise(resolve => {
const load = (c = 0) => {
if (this.map && this.$refs.map) {
resolve()
} else if (c < 10) setTimeout(() => load(++c), 500)
}
load()
})
this.instance.post("/app/appintegralsupermarketshop/list", null, {params: {visibleId, size: 9999}}).then(res => {
if (res?.data) {
initMap.then(() => {
this.map.clear()
this.$refs.map.init()
const {records} = res.data,
fraternities = this.fraternities.filter(e => !!e.longtitude)
this.map.on('click', e => {
if (e.data?.marker == 'store') {//点击店铺
this.getMapStore(e.data)
} else if (e.data?.marker == 'fraternity') {//点击互助会
this.dialog = true
const {fraternity_name, marker: mapType} = e.data
this.detail = {eventType: fraternity_name, mapType, ...e.data}
} else if (e.data?.unique_id) {//点击地区
this.getMapArea(e.data)
}
})
this.map.setOption({
series: {
markPoint: {
symbolSize: 24,
label: {
show: true,
position: 'right',
formatter: '{b}',
distance: 2,
},
data: [
...getCluster(records.filter(e => !!e.lng).map(e => ({
...e,
marker: 'store',
coord: [e.lng, e.lat],
name: e.title,
label: {color: "#FECA86"},
})), {
big: {symbol: "image://https://cdn.cunwuyun.cn/fengdu/fdStoreIcon.png"},
normal: {
itemStyle: {color: "#FECA86"},
label: {show: false, emphasis: {show: true, color: "#FECA86"}},
symbol: 'circle',
symbolSize: 6
}
}),
...getCluster(fraternities.map(e => ({
...e,
marker: 'fraternity',
coord: [e.longtitude, e.latitude],
name: e.fraternity_name,
label: {color: "#70FF8A"},
})), {
big: {symbol: "image://https://cdn.cunwuyun.cn/fengdu/fdFraternitIcon.png"},
normal: {
itemStyle: {color: "#70FF8A"},
label: {show: false, emphasis: {show: true, color: "#70FF8A"}},
symbol: 'circle',
symbolSize: 6
}
})
],
}
}
})
})
}
})
},
getMapStore(store = {}) {
this.instance.post("/app/fdDiy/mapShopInfo", null, {params: {id: store.id}}).then(res => {
if (res?.data) {
this.dialog = true
this.detail = {eventType: store.name, mapType: store.marker, ...res.data}
}
})
},
getMapArea(area) {
let info = {}, fraternity = {}
Promise.all([
this.instance.post("/app/fdDiy/mapAreaInfo", null, {params: {areaId: area.unique_id.padEnd(12, '0')}}).then(res => {
if (res?.data) {
return info = res.data
}
}),
this.getFraternityTypes(area.unique_id).then(list => {
let 互助会 = 0, 会员数量 = 0
list.forEach(e => {
互助会 += e.number
会员数量 += e.member_number
})
return fraternity = {互助会, 会员数量, list}
})
]).then(() => {
this.dialog = true
const {村社区数量, 居民群数量, 居民数, 网格数, 群成员数量, 户数} = info,
{互助会 = 0, 会员数量 = 0} = fraternity
this.detail = {
eventType: area.name,
mapType: 'area', ...info, fraternities: fraternity.list,
sta: {村社区数量, 居民群数量, 居民数, 网格数, 群成员数量, 户数, 互助会, 会员数量}
}
})
},
handleRealtimeEventDialog({rowIndex}) {
const row = this.realtimeEvents.meta[rowIndex]
if (row.bizId) {
const action = {
积分申请: "/app/appintegraluserapply/queryDetailById",
物品兑换: "/app/appintegralsupermarketorder/queryDetailById",
精选动态: "/app/appcontentinfo/queryDetailById",
}[row.type]
this.instance.post(action, null, {params: {id: row.bizId}}).then(res => {
if (res?.data) {
this.dialog = true
if (row.type == '积分申请') {
const {
applyItemName: 事件类型,
integralUserName: 申请人,
areaName: 所属地区,
createTime: 申请时间,
girdName: 所属网格,
content: 事件描述,
applyIntegral: 积分值,
phone: 手机号,
status,
files
} = res.data
this.detail.imgs = files?.map(e => e.accessUrl)
this.detail.form = {
事件类型,
申请人,
所属地区,
申请时间,
事件描述,
积分值,
手机号,
所属网格,
状态: `<div class="statusTag ${status > 0 ? 'success' : ''}">${this.dict.getLabel('appIntegralApplyEventStatus', status)}</div>`
}
} else if (row.type == '物品兑换') {
const {
status,
examineUserName: 核销人,
examineTime: 核销时间,
goodsPicUrl,
integralUserName: 兑换人,
goodsTitle: 兑换商品,
quantity: 数量,
usedIntegral: 消耗积分,
createTime: 兑换时间,
agentOrder
} = res.data
this.detail.imgs = [goodsPicUrl].flat().filter(Boolean) || []
this.detail.form = {
兑换人,
兑换商品,
数量,
消耗积分,
是否代兑换: this.dict.getLabel("yesOrNo", agentOrder),
兑换时间,
状态: `<div class="statusTag ${status > 0 ? 'success' : ''}">${this.dict.getLabel('appIntegralApplyEventStatus', status)}</div>`,
核销人,
核销时间
}
} else if (row.type == '精选动态') {
const {content, files, title, createUserName, girdName} = res.data
this.detail.imgs = files?.map(e => e.accessUrl)
this.detail.content = content
this.detail.header = ` <b>${title}</b>
<div class="flex normal mar-t8">
<div>${girdName}</div>
<div class="mar-l8">${createUserName}</div>
</div>`
}
this.detail = {eventType: row.type, ...this.detail}
}
})
}
},
handleJump() {
window.open("http://datas.fdxjtjyhzzyfw.cn/")
},
getVolunteerData(area_code) {
area_code = this.transferAreaCode(area_code)
return this.instance.get("/hzh/find-volunteer-list-detail", {
params: {
page_size: 40,
area_code,
team_name: this.fraternity
}
}).then(res => {
if (res?.data) {
this.volunteerConfig = {
...this.volunteerConfig,
data: res.data.map(e => [e.team_name, e.vol_name, genderDict[e.vol_gender], e.vol_age])
}
}
})
},
getFraternities(area_code) {
return this.instance.get("/hzh/find-fraternity-detail", {params: {page_size: 999, area_code}}).then(res => {
if (res?.data) {
this.fraternity = res.data[0]?.fraternity_name
return this.fraternities = res.data || []
}
})
},
getFraternitySta(area_code) {
return this.instance.get("/hzh/count-vol-team", {params: {page_size: 999, area_code}}).then(res => {
if (res?.data) {
let 团队数量 = 0, 志愿者数量 = 0, 服务学员数量 = 0
res.data.forEach(e => {
团队数量++
志愿者数量 += e.vol_num
服务学员数量 += e.stu_num
})
this.volunteers = {团队数量, 志愿者数量, 服务学员数量}
}
})
},
transferAreaCode(code) {//与互助会地区编码互通
const format = str => Number(str).toString().padStart(3, '0'),
last = /0{6}$/.test(code) ? '' : format(code.substring(6, 9))
return code.substring(0, 6) + last
},
getFraternityData(args) {
const row = this.fraternityTypes[args.rowIndex]
this.dialog = true
this.detail = {eventType: row.type, mapType: 'fraternitySta', list: []}
const area_code = this.transferAreaCode(this.areaId)
this.fraternityLoading = true
this.instance.get("/hzh/find-fraternity-member-detail", {params: {area_code}}).then(res => {
if (res?.data) {
this.detail = {...this.detail, list: res.data}
}
}).finally(() => this.fraternityLoading = false)
},
handleFraternityTableClick(args) {
const {columnIndex: type, rowIndex} = args
if (type > 0) {
const current = this.detail.list[rowIndex]
console.log(current)
this.fraternityExtra = true
this.$set(this.detail, 'extraType', type)
this.$set(this.detail, 'extraTitle', {
1: '家长名单', 2: '学生名单', 3: '活动情况'
}[type])
if (type == 3) {
this.$set(this.detail, 'others', {header: [], rowNum: 3})
this.instance.get("/hzh/find-fraternity-activity-detail", {params: {area_code: this.transferAreaCode(this.areaId)}}).then(res => {
if (res?.data) {
this.$set(this.detail, 'extra', res.data.map(e => [`
<div class="staPanel activity flex mar-t10" >
<img class="actImg" src="${e.icon_url}"/>
<div class="fill mar-l14">
<b>${e.activity_name}</b>
<div class="flex mar-b8 mar-t16 el-icon-time">${e.start_time + "-" + e.end_time}</div>
<div class="flex el-icon-location">${e.activity_address}</div></div></div>`]))
}
})
} else {
this.$set(this.detail, 'extra', current[{
1: 'parent', 2: 'student'
}[type]]?.map(e => [e.name, genderDict[e.gender], e.age]) || [])
}
}
},
getFraternityTypes(area_code) {
area_code = this.transferAreaCode(area_code)
return this.instance.get("/hzh/count-fraternity", {params: {area_code}}).then(res => {
if (res?.data) {
return res.data
}
})
}
},
created() {
Vue.use(scrollBoard)
this.dict.load('appIntegralApplyEventStatus', 'yesOrNo', 'integralSGOStatus')
},
}
</script>
<style scoped lang="scss">
.AppBIBoard {
color: #CDDBEA;
font-size: 14px;
:deep(.areaPicker) {
max-width: 300px;
input {
background: rgba(0, 54, 82, 0.9);
border: 1px solid rgba(42, 122, 146, 0.7);
border-radius: 2.2px;
color: #B3DDE5;
cursor: pointer;
&::placeholder {
color: inherit;
}
}
.el-input__icon {
color: #B3DDE5;
}
.el-input__suffix {
color: #B3DDE5;
}
}
&.fullscreen {
position: fixed;
z-index: 202310111819;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
:deep(.viewPanel) {
background-image: url("./assets/img_bg.png");
& > .fill {
display: flex;
gap: 20px;
padding: 12px 24px 0;
}
}
.left, .right {
width: 480px;
flex-shrink: 0;
}
.grid {
display: grid;
gap: 14px;
&.c-2 {
grid-template-columns:1fr 1fr;
}
&.c-3 {
grid-template-columns:1fr 1fr 1fr;
}
&.c-4 {
grid-template-columns:1fr 1fr 1fr 1fr;
}
}
:deep(.staPanel) {
text-align: center;
font-size: 15px;
line-height: 20px;
background: url("./assets/staPanel-bg.png") no-repeat;
background-size: 100% 100%;
height: 80px;
padding-top: 14px;
width: 100%;
b {
font-family: DIN;
font-size: 22px;
color: #02FEFF;
letter-spacing: 0;
line-height: 36px;
}
&.simple {
background: #ffffff0a;
padding-top: 0;
&.right {
color: #9BB7D4;
b {
color: #FFFFFF;
}
}
}
&.area {
background: #ffffff0a;
padding: 16px 8px;
gap: 14px;
b {
font-size: 16px;
color: #FFFFFF;
line-height: 16px;
}
:deep(.fdItem) {
margin-bottom: 0;
& > label {
color: #9BB7D4;
}
}
}
&.activity {
background: #ffffff0a;
padding: 14px;
height: 108px;
text-align: left;
b {
color: #FFFFFF;
}
.actImg {
width: 80px;
height: 80px;
flex-shrink: 0;
}
}
}
.chart {
.legend {
position: absolute;
left: 0;
right: 0;
bottom: 0;
}
.AiEchart {
height: 204px;
}
}
:deep(.title ) {
padding-left: 10px;
line-height: 30px;
background-image: linear-gradient(270deg, #1f436600 0%, #245a7570 99%);
}
.jumpBtn {
background-image: linear-gradient(180deg, rgba(90, 200, 246, 0.4) 0%, rgba(1, 51, 101, 0.4) 84%);
box-shadow: inset 0 2px 8px 0 rgba(51, 187, 255, 0.5);
border-radius: 15px;
font-weight: 500;
font-size: 12px;
color: #02FEFF;
padding: 8px 16px;
height: 30px;
user-select: none;
cursor: pointer;
margin-top: -7px;
}
:deep(.dv-scroll-board) {
height: 200px;
&.origin {
.row-item {
border: none;
height: unset;
line-height: normal;
}
.ceil {
padding: 0;
}
}
.header-item {
color: #02FEFF;
}
.row-item {
height: 38px;
line-height: 38px;
border-bottom: 1px solid #154270;
margin-top: -1px;
}
.timeRow {
background-image: url("./assets/realtimeIcon.png");
background-repeat: no-repeat;
background-position: 20px center;
text-indent: 24px;
}
.blue {
color: #02FEFF;
cursor: pointer;
}
.ceil > .statusTag {
margin-top: 19px;
transform: translateY(-50%);
}
}
.centerBottom {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
.shortcut {
background: #1f9ecc29;
padding: 4px 13px;
color: #1FBECC;
font-size: 13px;
margin-left: 4px;
margin-top: -7px;
border: 1px solid transparent;
cursor: pointer;
height: fit-content;
&:first-of-type {
margin-left: 0;
}
&.active {
border-color: #20B4C5;
color: #4ED8E4;
}
}
.boxSta {
margin-top: 16px;
padding-right: 40px;
justify-content: flex-end;
background: url("./assets/box.png") no-repeat 80px center;
height: 82px;
font-size: 16px;
.text {
color: #FFFFFF;
width: 200px;
justify-content: space-between;
& > p {
font-family: DIN;
font-size: 26px;
color: #02FEFF;
}
}
&.box2 {
background-image: url("./assets/box2.png");
}
}
:deep(.statusTag ) {
height: 20px;
line-height: 20px;
padding: 0 8px;
color: #FFB300;
background: #ffcb5224;
width: fit-content;
&.success {
color: #07B794;
background: #13f6c924;
}
}
:deep(.contentHead) {
width: 100%;
height: 89px;
background: url("./assets/contentHead.png") no-repeat;
margin-top: 16px;
margin-bottom: 14px;
padding: 16px;
background-size: 100% 89px;
& > b {
font-size: 16px;
color: #02FEFF;
letter-spacing: 0;
}
}
:deep(.VueCarousel) {
.goods {
font-size: 16px;
color: #02FEFF;
text-align: center;
& > img {
background: url("./assets/goodBg.png");
padding: 10px;
width: 130px;
height: 138px;
}
}
.VueCarousel-navigation-button {
width: 32px;
height: 32px;
background: url("./assets/carousel-nav-btn.png") no-repeat;
outline: none;
border-color: transparent;
&.VueCarousel-navigation-next {
transform: translate(100%, -50%) rotate(180deg);
}
&:active {
opacity: .8;
}
}
}
.fraternityImg {
width: 244px;
height: 268px;
flex-shrink: 0;
}
:deep(.el-carousel) {
.el-carousel__container {
height: 372px;
}
.el-carousel__indicators {
.el-carousel__indicator {
.el-carousel__button {
width: 6px;
height: 6px;
border-radius: 50%;
background: #679a9a80;
flex-shrink: 0;
}
&.is-active > .el-carousel__button {
background: #02FEFF;
}
}
}
}
}
</style>