迁移居民群管理
This commit is contained in:
98
src/apps/AppResident/AppResident.vue
Normal file
98
src/apps/AppResident/AppResident.vue
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<section class="comp">
|
||||||
|
<AiResult v-if="error" status="error" :tips="error"/>
|
||||||
|
<template v-else-if="isNormal">
|
||||||
|
<component ref="currentTab" :is="currentTab.comp" :isNormal="isNormal"/>
|
||||||
|
<AiTabbar :active.sync="active" :list="bottomBar"/>
|
||||||
|
</template>
|
||||||
|
<AiLoading v-else tips="居民管理加载中..."/>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import {mapActions} from "vuex";
|
||||||
|
import ResidentSta from "./components/residentSta";
|
||||||
|
import ResidentList from "./components/residentList";
|
||||||
|
import GroupList from "./components/groupList";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "comp",
|
||||||
|
appName: "居民管理",
|
||||||
|
components: {GroupList, ResidentList, ResidentSta},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
error: "",
|
||||||
|
active: 0,
|
||||||
|
isNormal: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
bottomBar() {
|
||||||
|
const link = icon => `${this.$cdn}resident/${icon}.png`
|
||||||
|
return [
|
||||||
|
{text: "统计分析", iconPath: "qwhome2", selectedIconPath: "qwhome1", comp: ResidentSta},
|
||||||
|
{text: "居民列表", iconPath: "qwjmda1", selectedIconPath: "qwjmda2", comp: ResidentList},
|
||||||
|
{text: "居民群列表", iconPath: "qwjmq1", selectedIconPath: "qwjmq2", comp: GroupList},
|
||||||
|
].map(e => ({
|
||||||
|
...e,
|
||||||
|
iconPath: link(e.iconPath),
|
||||||
|
selectedIconPath: link(e.selectedIconPath)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
currentTab() {
|
||||||
|
return this.bottomBar?.[this.active] || {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['injectJWeixin', "wxInvoke"]),
|
||||||
|
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.$route.hash == "#dev") {
|
||||||
|
this.isNormal = true
|
||||||
|
} else this.injectJWeixin(["getContext", "getCurExternalChat"]).then(() => {
|
||||||
|
this.wxInvoke(['getContext', {}, res => {
|
||||||
|
if (res.err_msg == "getContext:ok") {
|
||||||
|
if (res.entry == 'normal') this.isNormal = true
|
||||||
|
else this.wxInvoke(['getCurExternalChat', {}, res => {
|
||||||
|
if (res?.err_msg == 'getCurExternalChat:ok') {
|
||||||
|
wx.redirectTo({
|
||||||
|
url: "./groupResident"
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
wx.redirectTo({
|
||||||
|
url: "./resident"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
} else {
|
||||||
|
this.error = "wxwork:获取进入场景失败"
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
}).catch(() => {
|
||||||
|
this.error = "应用加载失败"
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onReachBottom() {
|
||||||
|
if (typeof this.$refs?.currentTab?.reachBottom == 'function') this.$refs?.currentTab.reachBottom()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.comp {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
::v-deep .u-tabbar__content__item {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.u-icon__img {
|
||||||
|
height: 44px !important;
|
||||||
|
width: 44px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-tabbar__content__item__text {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
129
src/apps/AppResident/components/document.vue
Normal file
129
src/apps/AppResident/components/document.vue
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<template>
|
||||||
|
<section class="document">
|
||||||
|
<div class="card">
|
||||||
|
<div class="info">
|
||||||
|
<u-image border-radius="4" :src="top.detail.avatar" width="118" height="118"/>
|
||||||
|
<div class="fill">
|
||||||
|
<b>{{ top.detail.realName || top.detail.name }}</b>
|
||||||
|
<u-row>
|
||||||
|
<span class="idNumber" v-html="IDObj.id"/>
|
||||||
|
<a @tap="showID=!showID">{{ IDObj.btn }}</a>
|
||||||
|
</u-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AiCell label="性别">{{ $dict.getLabel("sex", resident.sex) || "-" }}</AiCell>
|
||||||
|
<AiCell label="出生日期">{{ resident.birthDate }}</AiCell>
|
||||||
|
<AiCell label="年龄">{{ resident.age }}</AiCell>
|
||||||
|
<AiCell label="籍贯">{{ resident.birthplaceAreaName }}</AiCell>
|
||||||
|
<AiCell label="民族">{{ $dict.getLabel("nation", resident.nation) || "-" }}</AiCell>
|
||||||
|
<AiCell label="文化程度">{{ $dict.getLabel("education", resident.education) || "-" }}</AiCell>
|
||||||
|
<AiCell label="兵役状况">{{ $dict.getLabel("militaryStatus", resident.militaryStatus) || "-" }}</AiCell>
|
||||||
|
<AiCell label="政治面貌">{{ $dict.getLabel("politicsStatus", resident.politicsStatus) || "-" }}</AiCell>
|
||||||
|
<AiCell label="职业">{{ $dict.getLabel("job", resident.job) || "-" }}</AiCell>
|
||||||
|
<AiCell label="宗教信仰">{{ $dict.getLabel("faithType", resident.faithType) || "-" }}</AiCell>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<AiCell title label="联络信息"/>
|
||||||
|
<AiCell label="联系方式">{{ resident.phone }}</AiCell>
|
||||||
|
<AiCell label="现住址">{{ resident.currentAreaName + resident.currentAddress }}</AiCell>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<AiCell title label="家庭信息"/>
|
||||||
|
<AiCell label="是否户主">{{ $dict.getLabel("householdName", resident.householdName) || "-" }}</AiCell>
|
||||||
|
<AiCell label="与户主关系">{{ $dict.getLabel("householdRelation", resident.householdRelation) || "-" }}</AiCell>
|
||||||
|
<AiCell label="现住址">{{ resident.householdAreaName + resident.householdAddress }}</AiCell>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<AiCell title label="家庭成员"/>
|
||||||
|
<AiTable :data="family" :colConfigs="colConfigs"/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "document",
|
||||||
|
inject: ['top'],
|
||||||
|
computed: {
|
||||||
|
IDObj() {
|
||||||
|
return this.showID ? {
|
||||||
|
id: this.resident?.idNumber,
|
||||||
|
btn: '隐藏'
|
||||||
|
} : {
|
||||||
|
id: this.resident?.idNumber?.replace(/(\d{10}).+/g, '$1******'),
|
||||||
|
btn: '显示'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colConfigs() {
|
||||||
|
return [
|
||||||
|
{label: "与户主关系", prop: "householdRelation", width: '160rpx', dict: "householdRelation"},
|
||||||
|
{label: "姓名", prop: "name", width: '120rpx'},
|
||||||
|
{label: "性别", prop: "sex", dict: "sex"},
|
||||||
|
{label: "年龄", prop: "age"},
|
||||||
|
{label: "身份证号", prop: "idNumber", width: '320rpx'},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resident() {
|
||||||
|
let obj = {}
|
||||||
|
Object.keys(this.top.detail?.residentInfo?.resident || {}).map(e => {
|
||||||
|
obj[e] = this.top.detail?.residentInfo?.resident[e] || ""
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
family() {
|
||||||
|
return this.top.detail?.residentInfo?.family?.map(e => ({...e, householdRelation: e.householdRelation || "户主"}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showID: false,
|
||||||
|
familyList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$dict.load("sex", "nation", "education", "job",
|
||||||
|
"faithType", "politicsStatus", "militaryStatus", "householdRelation",
|
||||||
|
"householdName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.document {
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #F5F5F5;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
height: 186px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.fill {
|
||||||
|
color: #3C7FC8;
|
||||||
|
margin-left: 24px;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-size: 36px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.idNumber {
|
||||||
|
margin-right: 16px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
178
src/apps/AppResident/components/groupList.vue
Normal file
178
src/apps/AppResident/components/groupList.vue
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
<template>
|
||||||
|
<section class="groupList">
|
||||||
|
<AiTopFixed>
|
||||||
|
<u-search placeholder="请输入群名、群主名" :show-action="false" search-icon-color="#ccc"
|
||||||
|
v-model="search.name" @search="page.current=1,getList()"/>
|
||||||
|
<AiCell>
|
||||||
|
<b slot="label" class="title">共<i v-html="page.total||0"/>个居民群</b>
|
||||||
|
</AiCell>
|
||||||
|
</AiTopFixed>
|
||||||
|
<div class="mainPane">
|
||||||
|
<AiCell v-for="item in list" :key="item.id" @click.native="showResident(item)">
|
||||||
|
<template #label>
|
||||||
|
<AiImage :src="item.avatar" preview/>
|
||||||
|
</template>
|
||||||
|
<div class="card column start" flex>
|
||||||
|
<div flex class="groupName">
|
||||||
|
<b>{{ item.name || "群聊" }}</b>
|
||||||
|
<div class="personCount" v-if="item.personCount">({{ item.personCount }})</div>
|
||||||
|
</div>
|
||||||
|
<div class="owner" v-html="`群主:${item.ownerName}`"/>
|
||||||
|
<div flex class="trends">
|
||||||
|
<div flex v-html="`今日入群:<em>${item.increase||0}</em>`"/>
|
||||||
|
<div flex v-html="`今日退群:<p>${item.decrease||0}</p>`"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AiCell>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "groupList",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
page: {current: 1, size: 10, total: 0},
|
||||||
|
search: {name: ""},
|
||||||
|
list: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.$http.post("/app/wxcp/wxgroup/list", null, {
|
||||||
|
params: {...this.page, ...this.search}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.data) {
|
||||||
|
let meta = res.data.records?.map(e => ({
|
||||||
|
...e,
|
||||||
|
avatar: e?.avatar || this.$cdn + "groupAvatar.png"
|
||||||
|
}))
|
||||||
|
if (this.page.current > 1) {
|
||||||
|
this.list = [...this.list, ...meta]
|
||||||
|
} else this.list = meta
|
||||||
|
this.page.total = res.data.total
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
reachBottom() {
|
||||||
|
if (this.page.total > this.list.length) {
|
||||||
|
this.page.current++
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showResident({id}) {
|
||||||
|
id && uni.navigateTo({
|
||||||
|
url: './groupResident?id=' + id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.groupList {
|
||||||
|
::v-deep .AiTopFixed {
|
||||||
|
b.title {
|
||||||
|
color: #333;
|
||||||
|
font-size: 32px;
|
||||||
|
|
||||||
|
& > i {
|
||||||
|
color: #267FCE;
|
||||||
|
font-style: normal;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .mainPane {
|
||||||
|
background: #fff;
|
||||||
|
padding: 0 32px;
|
||||||
|
|
||||||
|
.AiCell {
|
||||||
|
align-items: center;
|
||||||
|
height: 230px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
max-width: unset;
|
||||||
|
border-bottom: 1px solid rgba(221, 221, 221, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-size: 36px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
justify-content: center;
|
||||||
|
background: #F3F4F7;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #333;
|
||||||
|
margin-left: 16px;
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.groupName {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner, .trends {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personCount {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trends {
|
||||||
|
* + * {
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: normal;
|
||||||
|
color: #5FBA95;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #F09535;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.AiImage {
|
||||||
|
margin-right: 24px;
|
||||||
|
|
||||||
|
image {
|
||||||
|
width: 112px;
|
||||||
|
height: 112px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
353
src/apps/AppResident/components/info.vue
Normal file
353
src/apps/AppResident/components/info.vue
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
<template>
|
||||||
|
<section class="info">
|
||||||
|
<div class="card">
|
||||||
|
<div class="baseInfo">
|
||||||
|
<u-image border-radius="4" :src="top.detail.avatar" width="118" height="118"/>
|
||||||
|
<div class="fill">
|
||||||
|
<b>{{ top.detail.name }}</b>
|
||||||
|
<div v-if="top.detail.type==1" class="wx">@微信</div>
|
||||||
|
<div v-if="top.detail.type==2">@企业微信</div>
|
||||||
|
</div>
|
||||||
|
<div class="certBtn" @tap="handleCert">{{ !isCert ? "实名认证" : "解绑" }}</div>
|
||||||
|
</div>
|
||||||
|
<u-row>
|
||||||
|
<AiCell class="half" top-label label="来源">{{ $dict.getLabel("wxCustomerAddWay", top.detail.addWay) }}</AiCell>
|
||||||
|
<AiCell class="half" top-label label="添加时间">{{ top.detail.createTime }}</AiCell>
|
||||||
|
<AiCell class="half" top-label label="真实姓名">{{ top.detail.realName }}</AiCell>
|
||||||
|
<AiCell class="half" top-label label="手机号码">{{ resident.phone || "-" }}</AiCell>
|
||||||
|
<AiCell class="half" top-label label="家庭积分">{{ resident.familyIntegral }}</AiCell>
|
||||||
|
<AiCell class="half" top-label label="个人积分">{{ resident.personalIntegral }}</AiCell>
|
||||||
|
</u-row>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<AiCell title label="公共标签">
|
||||||
|
<u-icon label="添加" size="38" name="iconAdd" custom-prefix="iconfont" color="#1365DD"
|
||||||
|
label-color="#1365DD" @tap="top.showTagManage=true"/>
|
||||||
|
</AiCell>
|
||||||
|
<AiCell top-label v-for="(op,name) in tagsList" :label="name" :key="name">
|
||||||
|
<u-row>
|
||||||
|
<div class="tag" v-for="(tag,j) in op" :key="j">{{ tag }}</div>
|
||||||
|
</u-row>
|
||||||
|
</AiCell>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<AiCell title label="动态"/>
|
||||||
|
<AiCell top-label>
|
||||||
|
<div class="logItem" v-for="item in customLogs" :key="item.id">
|
||||||
|
<div flex class="column" shrink>
|
||||||
|
<div class="dot"/>
|
||||||
|
<div class="line fill"/>
|
||||||
|
</div>
|
||||||
|
<div flex class="start column">
|
||||||
|
<b>{{ $dict.getLabel('wxCustomerLogType', item.type) }}</b>
|
||||||
|
<span>{{ item.createTime }}</span>
|
||||||
|
<div v-html="item.content"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AiCell>
|
||||||
|
</div>
|
||||||
|
<u-mask :show="dialog" @tap="dialog=false">
|
||||||
|
<div class="bindCert" @tap.stop>
|
||||||
|
<b class="title">实名认证</b>
|
||||||
|
<u-input class="searchInput" v-model="search" clearable placeholder="请输入姓名或身份证号" @input="handleSearch"/>
|
||||||
|
<div class="residents">
|
||||||
|
<div flex class="spb" v-for="(op,i) in result" :key="i" @tap="bindCert(op.id)">
|
||||||
|
<div v-html="op.name"/>
|
||||||
|
<div v-html="op.idNumber"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</u-mask>
|
||||||
|
<div bottom>
|
||||||
|
<u-button type="primary" @tap="handleWechat">微信联系</u-button>
|
||||||
|
<u-button v-if="isMobile" :disabled="!resident.phone" @tap="handleTel">电话联系</u-button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapActions} from "vuex";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "info",
|
||||||
|
inject: ['top'],
|
||||||
|
computed: {
|
||||||
|
resident() {
|
||||||
|
return this.top.detail?.residentInfo?.resident || {}
|
||||||
|
},
|
||||||
|
tagsList() {
|
||||||
|
let obj = {}
|
||||||
|
this.top.detail?.tags?.map(e => {
|
||||||
|
if (e.type == 1 && e?.groupName) {
|
||||||
|
if (obj?.[e.groupName]) {
|
||||||
|
obj[e.groupName].push(e.tagName)
|
||||||
|
} else {
|
||||||
|
obj[e.groupName] = [e.tagName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
isCert() {
|
||||||
|
return !!this.top?.detail?.residentInfo
|
||||||
|
},
|
||||||
|
isMobile() {
|
||||||
|
return ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]
|
||||||
|
.some(e => navigator.userAgent.indexOf(e) > -1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: false,
|
||||||
|
search: "",
|
||||||
|
result: [],
|
||||||
|
customLogs: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['injectJWeixin', 'wxInvoke']),
|
||||||
|
searchResident() {
|
||||||
|
this.$http.post("/app/appresident/check-name", null, {
|
||||||
|
params: {name: this.search}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.data) {
|
||||||
|
let reg = new RegExp(this.search, 'g')
|
||||||
|
this.result = res.data?.map(e => ({
|
||||||
|
...e,
|
||||||
|
name: e.name.replace(reg, `<b>${this.search}</b>`),
|
||||||
|
idNumber: e.idNumber.replace(reg, `<b>${this.search}</b>`),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
if (this.search?.length >= 2) {
|
||||||
|
this.searchResident()
|
||||||
|
} else {
|
||||||
|
this.result = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleCert() {
|
||||||
|
if (this.isCert) {
|
||||||
|
this.$confirm("是否要解绑当前实名认证?").then(() => {
|
||||||
|
this.$http.post("/app/wxcp/wxcustomer/unBindCustomer2Resident", null, {
|
||||||
|
params: {residentId: this.resident.id, customerId: this.top.custom}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.code == 0) {
|
||||||
|
this.$u.toast("解除绑定成功!")
|
||||||
|
this.top.getContact()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else this.dialog = true
|
||||||
|
},
|
||||||
|
bindCert(residentId) {
|
||||||
|
this.dialog = false
|
||||||
|
this.$confirm("是否要绑定该居民?").then(() => {
|
||||||
|
this.$http.post("/app/wxcp/wxcustomer/bindCustomer2Resident", null, {
|
||||||
|
params: {residentId, customerId: this.top.custom}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.code == 0) {
|
||||||
|
this.$u.toast("绑定成功!")
|
||||||
|
this.top.getContact()
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
this.$u.toast(err)
|
||||||
|
setTimeout(() => this.dialog = true, 1000)
|
||||||
|
})
|
||||||
|
}).catch(() => this.dialog = true)
|
||||||
|
},
|
||||||
|
getCustomLog(customerId) {
|
||||||
|
customerId && this.$http.post("/app/wxcp/wxcustomerlog/listAll", null, {
|
||||||
|
params: {customerId}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.data) {
|
||||||
|
this.customLogs = res.data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleTel() {
|
||||||
|
location.href = "tel:" + this.resident.phone
|
||||||
|
},
|
||||||
|
handleWechat() {
|
||||||
|
this.wxInvoke(['openUserProfile', {
|
||||||
|
type: 2,
|
||||||
|
userid: this.top.custom
|
||||||
|
}, () => 0])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$dict.load("wxCustomerAddWay", 'wxCustomerLogType')
|
||||||
|
this.getCustomLog(this.top.custom)
|
||||||
|
this.injectJWeixin('openUserProfile')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.info {
|
||||||
|
padding-bottom: 130px;
|
||||||
|
|
||||||
|
.certBtn {
|
||||||
|
cursor: pointer;
|
||||||
|
background: $uni-color-primary;
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.baseInfo {
|
||||||
|
height: 186px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.fill {
|
||||||
|
color: #3C7FC8;
|
||||||
|
margin-left: 24px;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 40px;
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-size: 36px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wx {
|
||||||
|
color: #2EA222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .AiCell {
|
||||||
|
&.half {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .u-form-item {
|
||||||
|
width: 50%;
|
||||||
|
min-height: 124px;
|
||||||
|
|
||||||
|
.u-form-item--left {
|
||||||
|
color: #999;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-form-item--right {
|
||||||
|
min-height: 0;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .u-mask {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.bindCert {
|
||||||
|
width: 600px;
|
||||||
|
padding: 32px;
|
||||||
|
min-height: 400px;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.residents {
|
||||||
|
max-height: 780px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInput {
|
||||||
|
margin: 16px 0;
|
||||||
|
border: 1px solid #D0D4DC;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 16px !important;
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spb {
|
||||||
|
height: 60px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-of-type(2n) {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .logItem {
|
||||||
|
display: flex;
|
||||||
|
min-height: 220px;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
.line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .column + .column {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: $uni-color-primary;
|
||||||
|
border: 8px solid #FFFFFF;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
width: 4px;
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start {
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
b {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: $uni-color-primary;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > b {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
142
src/apps/AppResident/components/residentList.vue
Normal file
142
src/apps/AppResident/components/residentList.vue
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<section class="residentList">
|
||||||
|
<AiTopFixed>
|
||||||
|
<u-search placeholder="请输入昵称、姓名" :show-action="false" search-icon-color="#ccc"
|
||||||
|
v-model="search.name" @search="page.current=1,getList()"/>
|
||||||
|
<AiCell>
|
||||||
|
<b slot="label" class="title">共<i v-html="page.total||0"/>个居民</b>
|
||||||
|
</AiCell>
|
||||||
|
</AiTopFixed>
|
||||||
|
<div class="mainPane">
|
||||||
|
<AiCell v-for="item in list" :key="item.id" @click.native="showResident(item)">
|
||||||
|
<template #label>
|
||||||
|
<AiImage :src="item.avatar" preview/>
|
||||||
|
</template>
|
||||||
|
<div class="card wrap start" flex>
|
||||||
|
<b>{{ item.name }}</b>
|
||||||
|
<div flex class="tag" v-for="(tag,j) in item.tags" :key="j">{{ tag.tagName }}</div>
|
||||||
|
<div class="realName" shrink v-html="`真实姓名:${item.realName||'-'}`"/>
|
||||||
|
</div>
|
||||||
|
</AiCell>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "residentList",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
page: {current: 1, size: 10, total: 0},
|
||||||
|
search: {name: ""},
|
||||||
|
list: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.$http.post("/app/wxcp/wxcustomer/list", null, {
|
||||||
|
params: {...this.page, ...this.search, type: 1}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.data) {
|
||||||
|
if (this.page.current > 1) {
|
||||||
|
this.list = [...this.list, ...res.data.records]
|
||||||
|
} else this.list = res.data.records
|
||||||
|
this.page.total = res.data.total
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
reachBottom() {
|
||||||
|
if (this.page.total > this.list.length) {
|
||||||
|
this.page.current++
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showResident({id}) {
|
||||||
|
id && uni.navigateTo({
|
||||||
|
url: './resident?id=' + id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.residentList {
|
||||||
|
::v-deep .AiTopFixed {
|
||||||
|
b.title {
|
||||||
|
color: #333;
|
||||||
|
font-size: 32px;
|
||||||
|
|
||||||
|
& > i {
|
||||||
|
color: #267FCE;
|
||||||
|
font-style: normal;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .mainPane {
|
||||||
|
background: #fff;
|
||||||
|
padding: 0 32px;
|
||||||
|
|
||||||
|
.AiCell {
|
||||||
|
flex-shrink: 0;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: unset;
|
||||||
|
border-bottom: 1px solid rgba(221, 221, 221, 1);
|
||||||
|
min-height: 160px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
b {
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
justify-content: center;
|
||||||
|
background: #F3F4F7;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #333;
|
||||||
|
margin-left: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.realName {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.AiImage {
|
||||||
|
margin-right: 24px;
|
||||||
|
|
||||||
|
image {
|
||||||
|
width: 112px;
|
||||||
|
height: 112px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
258
src/apps/AppResident/components/residentSta.vue
Normal file
258
src/apps/AppResident/components/residentSta.vue
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
<template>
|
||||||
|
<div class="resident-statistical">
|
||||||
|
<u-tabs :list="tabs" :is-scroll="false" :current="currentType" font-size="32"
|
||||||
|
bar-width="192" height="96" bg-color="#3975C6"
|
||||||
|
active-color="#fff" inactive-color="#fff" @change="handleChange"/>
|
||||||
|
<div class="bg" :style="{backgroundImage:'url(' + $cdn + 'dvcp_bg.png' + ')'}"></div>
|
||||||
|
<div class="card">
|
||||||
|
<u-row justify="between">
|
||||||
|
<div class="item" v-for="(item,index) in cardList" :key="index">
|
||||||
|
<span :style="{color:item.color} ">{{ item.value }}</span>
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</div>
|
||||||
|
</u-row>
|
||||||
|
</div>
|
||||||
|
<div class="chart">
|
||||||
|
<div id="chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import echarts from "echarts"
|
||||||
|
import {mapActions} from "vuex";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "residentSta",
|
||||||
|
props: {
|
||||||
|
isNormal: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentType: 0,
|
||||||
|
groupId: null,
|
||||||
|
chart: null,
|
||||||
|
chartData: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['injectJWeixin', "wxInvoke"]),
|
||||||
|
handleChange(i) {
|
||||||
|
this.currentType = i
|
||||||
|
this.getChart()
|
||||||
|
},
|
||||||
|
getChart() {
|
||||||
|
this.$http.post(this.currentType == 0 ? "/app/wxcp/wxgroup/groupStatistic" : "/app/wxcp/wxcustomerlog/customerStatistic", null, {
|
||||||
|
params: {
|
||||||
|
id: this.groupId
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code == 0) {
|
||||||
|
this.chartData = res.data
|
||||||
|
this.initChart()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initChart() {
|
||||||
|
this.chart = echarts.init(document.getElementById('chart'))
|
||||||
|
this.setOptions()
|
||||||
|
},
|
||||||
|
setOptions() {
|
||||||
|
const x = Object.keys(this.chartData.list)
|
||||||
|
const y = Object.values(this.chartData.list)
|
||||||
|
this.chart.setOption({
|
||||||
|
backgroundColor: "#F9F9F9",
|
||||||
|
legend: {
|
||||||
|
data: this.currentType == 0 ? ["群成员总数", "入群人数", "退群人数"] : ["居民总数", "新增居民数", "流失居民数"],
|
||||||
|
icon: "rect",
|
||||||
|
itemWidth: 8,
|
||||||
|
itemHeight: 8,
|
||||||
|
itemGap: 10,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#666666",
|
||||||
|
lineHeight: 20
|
||||||
|
},
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 60,
|
||||||
|
top: "10%",
|
||||||
|
bottom: "30%",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
marginTop: 10,
|
||||||
|
rotate: 40,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#666666'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: x.map(e => e.slice(5))
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#666666'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.currentType == 0 ? "群成员总数" : "居民总数",
|
||||||
|
type: "line",
|
||||||
|
itemStyle: {
|
||||||
|
color: "#4B87FE"
|
||||||
|
},
|
||||||
|
data: y.map(e => e.total)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: this.currentType == 0 ? "入群人数" : "新增居民数",
|
||||||
|
type: "line",
|
||||||
|
itemStyle: {
|
||||||
|
color: "#32C5FF"
|
||||||
|
},
|
||||||
|
data: y.map(e => e.increase)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: this.currentType == 0 ? "退群人数" : "流失居民数",
|
||||||
|
type: "line",
|
||||||
|
itemStyle: {
|
||||||
|
color: "#FFAA44"
|
||||||
|
},
|
||||||
|
data: y.map(e => e.decrease)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tabs() {
|
||||||
|
return [
|
||||||
|
{name: "居民群统计", value: 0},
|
||||||
|
{name: "居民统计", value: 1},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
currentTab() {
|
||||||
|
return this.tabs.find(e => e.value == this.currentType)
|
||||||
|
},
|
||||||
|
cardList() {
|
||||||
|
return this.currentType == 0 ? [
|
||||||
|
{label: "群聊总数", value: this.chartData?.groupSum || "0", color: "#354FC7"},
|
||||||
|
{label: "群成员总数", value: this.chartData?.today?.total || "0", color: "#868686"},
|
||||||
|
{label: "今日入群", value: this.chartData?.today?.increase || "0", color: "#5FBA95"},
|
||||||
|
{label: "今日退群", value: this.chartData?.today?.decrease || "0", color: "#F09535"},
|
||||||
|
] : [
|
||||||
|
{label: "居民总数", value: this.chartData?.today?.total || "0", color: "#354FC7"},
|
||||||
|
{label: "今日新增", value: this.chartData?.today?.increase || "0", color: "#5FBA95"},
|
||||||
|
{label: "今日流失", value: this.chartData?.today?.decrease || "0", color: "#F09535"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isNormal(v) {
|
||||||
|
v && this.getChart()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.$route.hash == "#dev") {
|
||||||
|
this.getChart()
|
||||||
|
} else this.injectJWeixin("getCurExternalChat").then(() => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}).then(() => {
|
||||||
|
this.wxInvoke(['getCurExternalChat', {}, res => {
|
||||||
|
if (res?.err_msg == 'getCurExternalChat:ok') {
|
||||||
|
this.groupId = res.chatId
|
||||||
|
}
|
||||||
|
this.getChart()
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.resident-statistical {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #F5F5F5;
|
||||||
|
padding-top: 96px;
|
||||||
|
|
||||||
|
::v-deep .u-tabs {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
height: 340px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 686px;
|
||||||
|
height: 232px;
|
||||||
|
margin: -140px auto 0;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 32px;
|
||||||
|
|
||||||
|
.u-row {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #354FC7;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span:last-child {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #999999;
|
||||||
|
line-height: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
margin: 24px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 32px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
#chart {
|
||||||
|
width: 686px;
|
||||||
|
height: 470px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
89
src/apps/AppResident/components/tagManage.vue
Normal file
89
src/apps/AppResident/components/tagManage.vue
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<section class="tagManage">
|
||||||
|
<div class="card" v-for="(group,i) in tags" :key="i">
|
||||||
|
<AiCell title :label="group.name"/>
|
||||||
|
<u-row>
|
||||||
|
<div class="tag" v-for="(op,j) in group.tagList" :key="j" :class="{selected:selected.includes(op.id)}"
|
||||||
|
@tap="$u.debounce(handleToggle(op.id))">
|
||||||
|
{{ op.name }}
|
||||||
|
</div>
|
||||||
|
</u-row>
|
||||||
|
</div>
|
||||||
|
<AiBack custom @back="top.showTagManage=false"/>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "tagManage",
|
||||||
|
inject: ['top'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tags: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selected() {
|
||||||
|
return this.top.groupId ? this.top.detail?.tagList.map(e => e.tagId) : this.top.detail?.tags.map(e => e.tagId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTags() {
|
||||||
|
this.$http.post(this.top.groupId ? "/app/wxcp/wxgroupchattag/listAll" : "/app/wxcp/wxcorptag/listAll", null, {
|
||||||
|
params: {size: 9999}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.data) {
|
||||||
|
this.tags = res.data.records
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleToggle(tagId) {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '提交中'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$http.post(this.top.groupId ? "/app/wxcp/wxgroupchattag/markTagForCP" : "/app/wxcp/wxcorptag/markTagForCP", null, {
|
||||||
|
params: this.top.groupId ? {tagId, groupId: this.top.groupId,} : {tagId, customerId: this.top.custom}
|
||||||
|
}).then(res => {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (res?.code == 0) {
|
||||||
|
this.$u.toast("操作成功!")
|
||||||
|
this.getTags()
|
||||||
|
this.top.getContact()
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
uni.hideLoading()
|
||||||
|
this.$u.toast(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tagManage {
|
||||||
|
padding-top: 16px;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
cursor: pointer !important;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: $uni-color-primary !important;
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: #999;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + .tag {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
302
src/apps/AppResident/groupResident.vue
Normal file
302
src/apps/AppResident/groupResident.vue
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
<template>
|
||||||
|
<div class="group-resident">
|
||||||
|
<template v-if="!showTagManage">
|
||||||
|
<AiTopFixed>
|
||||||
|
<div class="card">
|
||||||
|
<header>
|
||||||
|
<u-avatar mode="square" :src="$cdn + 'groupAvatar.png'" :size="112"></u-avatar>
|
||||||
|
{{ detail.name }}
|
||||||
|
</header>
|
||||||
|
<u-grid :col="2" :border="false">
|
||||||
|
<u-grid-item>
|
||||||
|
<label>建群日期</label>
|
||||||
|
<label v-if="detail.createTime">{{ detail.createTime.split(" ")[0] }}</label>
|
||||||
|
</u-grid-item>
|
||||||
|
<u-grid-item>
|
||||||
|
<label>成员总数</label>
|
||||||
|
<label v-if="isToday">{{ detail.statistic.today.total }}</label>
|
||||||
|
</u-grid-item>
|
||||||
|
<u-grid-item>
|
||||||
|
<label>今日入群</label>
|
||||||
|
<label v-if="isToday">{{ detail.statistic.today.increase }}</label>
|
||||||
|
</u-grid-item>
|
||||||
|
<u-grid-item>
|
||||||
|
<label>今日退群</label>
|
||||||
|
<label v-if="isToday">{{ detail.statistic.today.decrease }}</label>
|
||||||
|
</u-grid-item>
|
||||||
|
<!-- <u-grid-item>-->
|
||||||
|
<!-- <label>今日活跃人数</label>-->
|
||||||
|
<!-- <label>{{item.value}}</label>-->
|
||||||
|
<!-- </u-grid-item>-->
|
||||||
|
</u-grid>
|
||||||
|
<p class="statistics">
|
||||||
|
<span>成员性别:</span>
|
||||||
|
<label>男性</label>
|
||||||
|
<b>{{ detail.man }}</b>
|
||||||
|
<label>女性</label>
|
||||||
|
<b>{{ detail.woman }}</b>
|
||||||
|
<label>未知</label>
|
||||||
|
<b>{{ detail.unknown }}</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</AiTopFixed>
|
||||||
|
<div class="card">
|
||||||
|
<AiCell title label="群标签">
|
||||||
|
<u-icon label="添加" size="38" name="iconAdd" custom-prefix="iconfont" color="#1365DD"
|
||||||
|
label-color="#1365DD" @tap="showTagManage=true"/>
|
||||||
|
</AiCell>
|
||||||
|
<AiCell top-label v-for="(op,name) in tagsList" :label="name" :key="name">
|
||||||
|
<u-row>
|
||||||
|
<div class="tag" v-for="(tag,j) in op" :key="j">{{ tag }}</div>
|
||||||
|
</u-row>
|
||||||
|
</AiCell>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<AiCell title label="群成员"></AiCell>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="item" v-for="(item,index) in detail.groupUserList" :key="index" @click="handleWechat(item)">
|
||||||
|
<u-avatar mode="square" :src="item.avatar"></u-avatar>
|
||||||
|
<div class="info">
|
||||||
|
<div class="left">
|
||||||
|
<p>{{ item.memberName }}
|
||||||
|
<b v-if="item.customerType==2" style="color: #3C7FC8;">@{{ item.corpName }}</b>
|
||||||
|
<b v-if="item.customerType==1" style="color: #2EA222;">@微信</b>
|
||||||
|
</p>
|
||||||
|
<p>真实姓名:{{ item.realName }}</p>
|
||||||
|
</div>
|
||||||
|
<span v-if="item.userId==detail.owner">群主</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<tag-manage v-if="showTagManage"/>
|
||||||
|
<AiBack v-if="isNormal&&!showTagManage"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapActions} from "vuex";
|
||||||
|
import TagManage from "./components/tagManage";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "groupResident",
|
||||||
|
components: {TagManage},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
top: this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
detail: {},
|
||||||
|
groupId: null,
|
||||||
|
showTagManage: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tagsList() {
|
||||||
|
let obj = {}
|
||||||
|
this.detail?.tagList?.map(e => {
|
||||||
|
if (e?.groupName) {
|
||||||
|
if (obj?.[e.groupName]) {
|
||||||
|
obj[e.groupName].push(e.tagName)
|
||||||
|
} else {
|
||||||
|
obj[e.groupName] = [e.tagName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
isToday() {
|
||||||
|
return !!this.detail?.statistic?.today
|
||||||
|
},
|
||||||
|
isNormal() {
|
||||||
|
return !!this.$route.query.id;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['injectJWeixin', "wxInvoke"]),
|
||||||
|
getContact() {
|
||||||
|
if (this.isNormal) {
|
||||||
|
this.groupId = this.$route.query.id
|
||||||
|
this.getGroup(this.$route.query.id)
|
||||||
|
} else this.injectJWeixin("getCurExternalChat").then(() => {
|
||||||
|
this.wxInvoke(['getCurExternalChat', {}, res => {
|
||||||
|
if (res?.err_msg == 'getCurExternalChat:ok') {
|
||||||
|
this.groupId = res.chatId
|
||||||
|
this.getGroup(res.chatId)
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getGroup(id) {
|
||||||
|
this.$http.post("/app/wxcp/wxgroup/getDetail", null, {
|
||||||
|
params: {id}
|
||||||
|
}).then(res => {
|
||||||
|
if (res?.data) {
|
||||||
|
this.detail = res.data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleWechat({userId, type}) {
|
||||||
|
this.injectJWeixin('openUserProfile').then(() => {
|
||||||
|
this.wxInvoke(['openUserProfile', {
|
||||||
|
type,
|
||||||
|
userid: userId
|
||||||
|
}, () => 0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getContact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.group-resident {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
::v-deep .AiTopFixed {
|
||||||
|
& > .card {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .card {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
padding: 16px 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
header {
|
||||||
|
height: 192px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
|
||||||
|
.u-avatar {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .u-grid-item-box {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 16px !important;
|
||||||
|
|
||||||
|
.uni-label-pointer {
|
||||||
|
width: 100%;
|
||||||
|
line-height: 40px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni-label:last-child {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics {
|
||||||
|
height: 96px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #999999
|
||||||
|
}
|
||||||
|
|
||||||
|
label + b {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
color: #1365DD !important;
|
||||||
|
margin: 0 32px 0 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > section:first-child {
|
||||||
|
height: 90px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
height: 176px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-left: 24px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #3C7FC8;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-child {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999999;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 88px;
|
||||||
|
height: 56px;
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(19, 101, 221, 0.1);
|
||||||
|
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.02);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #1365DD;
|
||||||
|
line-height: 56px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .tag {
|
||||||
|
height: 56px;
|
||||||
|
line-height: 56px;
|
||||||
|
background: #F3F4F7;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
145
src/apps/AppResident/resident.vue
Normal file
145
src/apps/AppResident/resident.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<section class="resident">
|
||||||
|
<AiLoading v-if="!custom&&!error" tips="获取居民信息中..."/>
|
||||||
|
<AiResult v-else-if="error" status="error" :tips="error"/>
|
||||||
|
<template v-else>
|
||||||
|
<tag-manage v-if="showTagManage"/>
|
||||||
|
<template v-else>
|
||||||
|
<AiTopFixed>
|
||||||
|
<u-tabs :list="tabs" :is-scroll="false" :current="currentType" font-size="32"
|
||||||
|
bar-width="192" height="96" @change="handleTabClick"/>
|
||||||
|
</AiTopFixed>
|
||||||
|
<component :is="currentTab.comp"/>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<AiBack v-if="isNormal&&!showTagManage"/>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapActions} from "vuex";
|
||||||
|
import Info from "./components/info";
|
||||||
|
import Document from "./components/document";
|
||||||
|
import TagManage from "./components/tagManage";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "resident",
|
||||||
|
components: {TagManage, Document, Info},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
top: this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tabs() {
|
||||||
|
return [
|
||||||
|
{name: "居民信息", value: 0, comp: Info},
|
||||||
|
{name: "居民档案", value: 1, comp: Document, hide: !this.detail.residentInfo},
|
||||||
|
].filter(e => !e.hide)
|
||||||
|
},
|
||||||
|
currentTab() {
|
||||||
|
return this.tabs.find(e => e.value == this.currentType)
|
||||||
|
},
|
||||||
|
isNormal() {
|
||||||
|
return !!this.$route.query.id;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentType: 0,
|
||||||
|
detail: {},
|
||||||
|
showTagManage: false,
|
||||||
|
custom: "",
|
||||||
|
error: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['injectJWeixin', 'wxInvoke']),
|
||||||
|
handleTabClick(i) {
|
||||||
|
this.currentType = i
|
||||||
|
},
|
||||||
|
getContact() {
|
||||||
|
if (this.isNormal) {
|
||||||
|
this.getCustom(this.$route.query.id)
|
||||||
|
} else {
|
||||||
|
this.injectJWeixin("getCurExternalContact").then(() => {
|
||||||
|
this.wxInvoke(['getCurExternalContact', {}, res => {
|
||||||
|
if (res?.err_msg == 'getCurExternalContact:ok') {
|
||||||
|
this.getCustom(res.userId)
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
}).catch(({errMsg}) => {
|
||||||
|
this.error = errMsg
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCustom(id) {
|
||||||
|
this.$http.post("/app/wxcp/wxcustomer/queryCustomerById", null, {
|
||||||
|
params: {id}
|
||||||
|
}).then(ret => {
|
||||||
|
if (ret?.data) {
|
||||||
|
this.custom = id
|
||||||
|
this.detail = ret.data
|
||||||
|
if (this.detail.type == 2) {
|
||||||
|
this.error = "只能查看个人微信绑定的居民信息"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getContact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.resident {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
background: #F5F5F5;
|
||||||
|
|
||||||
|
.error {
|
||||||
|
font-size: 32px;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .u-scroll-box {
|
||||||
|
border-bottom: 1px solid #D4D4D4;
|
||||||
|
|
||||||
|
.u-tab-bar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .card {
|
||||||
|
background: #fff;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 16px 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.half {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .tag {
|
||||||
|
height: 56px;
|
||||||
|
line-height: 56px;
|
||||||
|
background: #F3F4F7;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user