数字平昌

This commit is contained in:
yanran200730
2023-05-17 15:12:17 +08:00
parent 57120ca85f
commit c90cdf7678
44 changed files with 9092 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
<template>
<div class="AppActivitiesManagement">
<keep-alive :include="['List']">
<component ref="component" :is="component" :permissions="permissions " @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import List from './components/List'
import Detail from './components/Detail'
export default {
name: 'AppDynamic',
label: '精选动态',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
List,
Detail
},
methods: {
onChange (data) {
if (data.type === 'Detail') {
this.component = 'Detail'
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">
.AppActivitiesManagement {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<ai-detail class="AppDynamicDetail">
<template slot="title">
<ai-title title="详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="类型" isLine :value="info.title"></ai-info-item>
<ai-info-item label="所属网格" isLine :value="info.girdName"></ai-info-item>
<ai-info-item label="文章类型" isLine :value="info.contentType === '0' ? '文章' : '视频'"></ai-info-item>
<ai-info-item label="分类" v-if="info.categoryName" isLine :value="info.categoryName"></ai-info-item>
<ai-info-item label="正文" v-if="info.contentType === '0'" isLine>
<AiArticle :value="info.content"></AiArticle>
</ai-info-item>
<ai-info-item label="附件" isLine>
<div class="files">
<div class="file-item" v-for="(item, index) in info.files" :key="index">
<video controls :src="item.url" v-if="['.mp4', '.mov'].includes(item.postfix)"></video>
<img :src="item.url" v-else v-viewer="{movable: true}">
</div>
</div>
</ai-info-item>
</ai-wrapper>
</template>
</ai-card>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Detail',
props: {
instance: Function,
dict: Object,
params: Object,
moduleId: String
},
data () {
return {
info: {},
id: ''
}
},
created () {
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appcontentinfo/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.info = res.data
this
this.info = {
...res.data,
pictureUrl: res.data.pictureUrl ? [{
url: res.data.pictureUrl
}] : [],
files: res.data.files.map(v => {
return {
...v,
postfix: v.postfix.toLowerCase()
}
})
}
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
}
}
}
</script>
<style scoped lang="scss">
.AppDynamicDetail {
.files {
display: flex;
align-items: center;
flex-wrap: wrap;
.file-item {
width: 240px;
height: 240px;
margin: 0 20px 20px 0;
img, video {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
}
</style>

View File

@@ -0,0 +1,144 @@
<template>
<ai-list class="notice">
<ai-title slot="title" title="精选动态" v-model="search.areaId" isShowBottomBorder isShowArea :hideLevel="hideLevel - 1" @change="search.current = 1, getList()"></ai-title>
<template slot="content">
<ai-search-bar class="search-bar">
<template #left>
</template>
<template #right>
<el-input
v-model="search.title"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="姓名、推送人"
clearable
@clear="search.current = 1, search.title = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<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="remove(row.id)">下架</el-button>
<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,
dict: Object
},
data() {
return {
search: {
current: 1,
size: 10,
title: '',
areaId: ''
},
total: 0,
colConfigs: [
{ prop: 'title', label: '类型', align: 'left', width: '200px' },
{ prop: 'createUserName', label: '姓名', align: 'center' },
{ prop: 'girdName', label: '所属网格', align: 'center' },
{ prop: 'examineUserName', label: '推送人', align: 'center' },
{ prop: 'createTime', label: '推送时间', align: 'center' }
],
tableData: [],
moduleId: ''
}
},
computed: {
...mapState(['user']),
hideLevel () {
return this.user.info.areaList.length || 0
},
params () {
return {
...this.search
}
}
},
created() {
this.search.areaId = this.user.info.areaId
this.getInfo()
},
methods: {
getInfo () {
this.instance.post(`/app/appintegraluserapply/queryModuleByName`).then(res => {
if (res.code == 0) {
this.moduleId = res.data
this.$nextTick(() => {
this.getList()
})
}
})
},
getList () {
this.instance.post(`/app/appcontentinfo/list-web`, null, {
params: {
moduleId: this.moduleId,
...this.search,
areaId: this.search.areaId
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
remove(id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appcontentinfo/deleteIntegralApply?ids=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('下架成功!')
this.getList()
}
})
})
},
toDetail(id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || ''
}
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,76 @@
<template>
<div class="AppHelp">
<keep-alive :include="['List']">
<component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import Detail from './components/Detail'
import List from './components/List'
import Add from './components/Add'
import GagList from './components/GagList'
export default {
name: 'AppHelp',
label: '邻里互助',
props: {
instance: Function,
dict: Object
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
Add,
List,
Detail,
GagList
},
methods: {
onChange (data) {
if (data.type === 'GagList') {
this.component = 'GagList'
}
if (data.type === 'Add') {
this.component = 'Add'
this.params = data.params
}
if (data.type === 'Detail') {
this.component = 'Detail'
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">
.AppHelp {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,177 @@
<template>
<ai-detail class="content-add">
<template slot="title">
<ai-title :title="params.id ? '编辑帖子' : '我要发帖'" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="发帖信息">
<template #content>
<el-form class="ai-form" :model="form" label-width="120px" ref="form">
<el-form-item label="发布单位" prop="publishDepartName" style="width: 100%;" :rules="[{ required: true, message: '请选择单位', trigger: 'change' }]">
<ai-picker :instance="instance" multiple @pick="e => onUserChange(e)" dialogTitle="选择部门" action="/app/wxcp/wxdepartment/departList">
<div class="time-select">
<span class="dept-name" style="color:#999;" v-if="!form.publishDepartName">选择部门</span>
<span class="dept-name" v-else>{{form.publishDepartName}}</span>
<i class="el-icon-arrow-down"></i>
</div>
</ai-picker>
</el-form-item>
<el-form-item prop="visibleAreaId" style="width: 100%;" label="发布地区" :rules="[{required: true, message: '请选择地区', trigger: 'change'}]">
<ai-area-select clearable @fullname="v => form.visibleAreaName = v" always-show :instance="instance" v-model="form.visibleAreaId" :disabled-level="disabledLevel"></ai-area-select>
</el-form-item>
<el-form-item label="发布内容" prop="content" style="width: 100%;" :rules="[{required: true, message: '请输入发布内容', trigger: 'change'}]">
<!-- <ai-editor v-model="form.content" :instance="instance"/> -->
<el-input type="textarea" placeholder="请输入内容" v-model="form.content" rows="8" maxlength="500" :show-word-limit="true"></el-input>
</el-form-item>
<el-form-item label="图片" prop="files" style="width: 100%;">
<ai-uploader
:instance="instance"
v-model="form.files"
isShowTip
:limit="9">
</ai-uploader>
</el-form-item>
<el-form-item prop="themeId" style="width: 100%;" label="关联话题">
<ai-select v-model="form.themeId" :selectList="talkList" placeholder="请选择关联话题" />
</el-form-item>
</el-form>
</template>
</ai-card>
</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,
params: Object
},
data () {
return {
info: {},
form: {
themeId: '',
content: '',
areaId: '',
visibleAreaName: '',
files: [],
areaName: '',
publishDepartName: '',
publishDepartIdList: [],
},
id: '',
department: [],
talkList: [],
isFlag: false
}
},
computed: {
...mapState(['user'])
},
created () {
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
this.form.visibleAreaId =this.user.info.areaId
this.form.visibleAreaName =this.user.info.areaName
this.disabledLevel = this.user.info.areaList.length
this.getTalkList()
},
methods: {
getInfo (id) {
this.instance.post(`/app/appneighborhoodassistance/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.form = res.data
this.form.publishDepartIdList = this.form.publishDepartId.split(',')
}
})
},
confirm () {
if(this.isFlag) return
this.$refs.form.validate((valid) => {
if (valid) {
this.isFlag = true
this.instance.post(`/app/appneighborhoodassistance/addOrUpdate`, {
...this.form,
publishDepartId: this.form.publishDepartIdList.join(',')
}).then(res => {
if (res.code == 0) {
this.$message.success('提交成功')
setTimeout(() => {
this.cancel(true)
}, 600)
}
})
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
},
onChange (e) {
if (e.length) {
this.form.publishDepartIdList = e.map(v => v.id)
this.form.publishDepartName = e.map(v => v.name).join(',')
} else {
this.form.publishDepartIdList = ''
this.form.publishDepartName = ''
}
},
onUserChange (e) {
this.deptList = e
this.onChange(e)
},
getTalkList() {
this.instance.post(`/app/appneighborhoodassistancetheme/list?size=100`).then(res => {
if (res.code == 0) {
this.talkList = []
res.data.records.map((item) => {
this.talkList.push({
dictName: item.title,
dictValue: item.id
})
})
}
})
},
}
}
</script>
<style scoped lang="scss">
.time-select {
padding: 0 16px;
height: 36px;
line-height: 36px;
border: 1px solid #d0d4dc;
border-radius: 4px;
display: flex;
justify-content: space-between;
cursor: pointer;
.el-icon-arrow-down {
line-height: 36px;
}
}
</style>

View File

@@ -0,0 +1,294 @@
<template>
<ai-detail class="detail">
<template slot="title">
<ai-title title="帖子详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="帖子信息">
<template #content>
<div class="talk-info">
<div class="user">
<img :src="info.createUserAvatar" alt="">
<div class="info">
<h2>{{info.createUserName}}</h2>
<div class="time-flex">
<span class="area-name">{{info.publishDepartName}}</span>
<span>{{info.createTime}}</span>
</div>
</div>
</div>
<div class="content">
<span v-if="info.themeId">#{{info.themeInfo.title}}</span>{{info.content}}
</div>
<ai-uploader :instance="instance" disabled v-model="info.files">
</ai-uploader>
</div>
</template>
</ai-card>
<ai-card title="评论信息">
<template #content>
<div class="comment-list" v-if="commontList.length">
<div class="title">评论</div>
<div class="item" v-for="(item, index) in commontList" :key="index">
<div class="user">
<img :src="item.createUserAvatar" alt="">
<div class="info-flex">
<h2>{{item.createUserName}}</h2>
<span>{{item.createTime}}</span>
</div>
</div>
<div class="content-flex">
<p>{{item.content}}</p>
<div @click="delCommontOrReply('评论', item.id)">删除</div>
</div>
<div class="reply-list" v-if="item.replyList && item.replyList.length && item.isShowReply">
<div class="reply-item" v-for="(reply, indexs) in item.replyList" :key="indexs">
<div class="reply-user">
<img :src="item.createUserAvatar" alt="">
<div class="reply-name">
<span>{{reply.createUserName}}</span>回复<span>{{item.createUserName}}</span>
</div>
<span class="reply-time">{{reply.createTime}}</span>
</div>
<div class="content-flex">
<p>{{reply.content}}</p>
<div @click="delCommontOrReply('回复', reply.id)">删除</div>
</div>
</div>
</div>
<div class="reply-more" @click="item.isShowReply = !item.isShowReply" :class="[item.isShowReply ? 'active' : '']">
<span class="line"></span>{{item.isShowReply ? '收起' : `展开${item.replyList.length}条回复`}}
<i class="el-icon-arrow-down"></i>
</div>
</div>
</div>
<ai-empty v-else>暂无评论</ai-empty>
</template>
</ai-card>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Detail',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
commontList: [],
info: {},
}
},
computed: {
getAvatar(row){
return row.avatar||row.photo||'https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png'
}
},
created () {
this.getInfo()
this.getList()
},
methods: {
getList() {
this.instance.post(`/app/appneighborhoodassistance/commontList?id=${this.params.id}&size=100`).then(res => {
if (res.code == 0) {
res.data.records.map((item) => {
item.isShowReply = false
})
this.commontList = res.data.records
}
})
},
getInfo () {
this.instance.post(`/app/appneighborhoodassistance/queryDetailById?id=${this.params.id}`).then(res => {
if (res.code == 0) {
if (res.data) {
this.info = res.data
}
}
})
},
cancel () {
this.$emit('change', {
type: 'List',
isRefresh: true
})
},
delCommontOrReply(text, id) {
this.$confirm(`确定删除该${text}`).then(() => {
this.instance.post(`/app/appneighborhoodassistance/delComment?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
}
}
</script>
<style scoped lang="scss">
.detail {
.talk-info {
.user {
display: flex;
margin-bottom: 8px;
img {
width: 60px;
height: 60px;
margin-right: 16px;
border-radius: 50%;
}
.info {
width: calc(100% - 76px);
h2 {
font-size: 20px;
line-height: 30px;
font-weight: 400;
}
.time-flex {
display: flex;
justify-content: space-between;
color: #999;
font-size: 14px;
line-height: 30px;
.area-name {
color: #666;
}
}
}
}
.content {
font-size: 16px;
line-height: 32px;
word-break: break-all;
span {
color: #26f;
}
}
}
.comment-list {
.title {
color: #333;
font-size: 16px;
font-weight: 700;
line-height: 40px;
margin-bottom: 8px;
}
.item {
border-bottom: 1px solid #ddd;
padding-bottom: 16px;
.user {
display: flex;
margin-bottom: 8px;
img {
width: 60px;
height: 60px;
margin-right: 16px;
border-radius: 50%;
}
.info-flex {
width: calc(100% - 76px);
display: flex;
justify-content: space-between;
line-height: 60px;
h2 {
font-size: 20px;
font-weight: 400;
}
span {
color: #999;
font-size: 14px;
}
}
}
.content-flex {
display: flex;
font-size: 16px;
line-height: 30px;
margin-bottom: 8px;
padding-left: 76px;
p {
width: calc(100% - 50px);
word-break: break-all;
}
div {
color: #26f;
width: 50px;
text-align: right;
cursor: pointer;
}
}
.reply-list {
padding-left: 100px;
.reply-item {
margin-bottom: 8px;
.reply-user {
font-size: 14px;
img {
width: 50px;
height: 50px;
border-radius: 50%;
vertical-align: middle;
}
.reply-name {
display: inline-block;
color: #333;
width: 300px;
span {
display: inline-block;
color: #666;
margin: 0 8px;
}
}
.reply-time {
color: #999;
}
}
.content-flex {
font-size: 14px;
line-height: 24px;
padding-left: 58px;
}
}
}
.reply-more {
font-size: 14px;
line-height: 28px;
color: #333;
.line {
display: inline-block;
width: 120px;
border-top: 1px solid #eee;
vertical-align: middle;
margin-right: 8px;
}
.el-icon-arrow-down {
transition: all ease 0.5s;
transform: rotate(0);
margin-left: 8px;
}
&.active .el-icon-arrow-down {
transform: rotate(180deg);
}
}
}
}
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<ai-list class="notice">
<template slot="title">
<ai-title title="禁言列表" isShowBack isShowBottomBorder @onBackClick="cancel"></ai-title>
</template>
<template slot="content">
<ai-search-bar>
<template #left>
</template>
<template #right>
<el-input
v-model="search.userName"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入用户昵称"
clearable
@clear="search.current = 1, search.userName = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column slot="options" width="100px" fixed="right" label="操作" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="remove(row.userId)">解除禁言</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
export default {
name: 'GagList',
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
userName: ''
},
total: 0,
colConfigs: [
{ prop: 'userName', label: '用户昵称', align: 'left' },
{ prop: 'areaName', label: '所属地区', align: 'center' },
{ prop: 'createTime', label: '被禁言时间', align: 'center' }
],
tableData: []
}
},
created() {
this.getList()
},
methods: {
getList() {
this.instance.post(`/app/appneighborhoodassistance/blacklist`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
remove (id) {
this.$confirm('确定对该用户解除禁言?').then(() => {
this.instance.post(`/app/appneighborhoodassistance/addBlacklist?userId=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('解除成功!')
this.getList()
}
})
})
},
cancel () {
this.$emit('change', {
type: 'List',
isRefresh: true
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,181 @@
<template>
<ai-list class="notice">
<template slot="title">
<ai-title title="邻里互助" isShowBottomBorder isShowArea :hideLevel="hideLevel-1" v-model="search.areaId" @change="changeArea">
<template #rightBtn>
<el-button size="small" type="primary" @click="toGagList">禁言名单</el-button>
</template>
</ai-title>
</template>
<template slot="content">
<ai-search-bar class="search-bar">
<template #left>
<el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd('')">我要发帖</el-button>
<el-date-picker
v-model="dateList"
@change="search.current = 1,getList()"
type="daterange"
size="small"
value-format="yyyy-MM-dd"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</template>
<template #right>
<el-input
v-model="search.createUserName"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入发帖人"
clearable
@clear="search.current = 1, search.createUserName = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<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>
<el-button type="text" @click="remove(row.id)">删除</el-button>
<el-button type="text" @click="gag(row.createUserId, row.blacklist)">{{ row.blacklist ? '解除禁言' : '禁言' }}</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,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
createUserName: '',
areaId: ''
},
total: 0,
colConfigs: [
{ prop: 'content', label: '内容', align: 'left' },
{ prop: 'createUserName', label: '发帖人', align: 'center', width: '120' },
{ prop: 'createUserAreaName', label: '所属地区', align: 'center' },
{ prop: 'createTime', label: '创建时间', align: 'center' },
{ prop: 'commentCount', label: '评论数', align: 'center', width: '120' },
{ prop: 'appreciateCount', label: '点赞数', align: 'center', width: '120' },
{ prop: 'sharedCount', label: '分享数', align: 'center', width: '120' },
{ prop: 'blacklist', label: '状态', align: 'center', format: v => v ? '禁言' : '正常' },
{ slot: 'options'},
],
tableData: [],
dateList: [],
}
},
computed: {
...mapState(['user']),
hideLevel() {
return this.user.info.areaList?.length || 0
},
},
created() {
this.search.areaId = this.user.info.areaId
this.getList()
},
methods: {
getList() {
this.instance.post(`/app/appneighborhoodassistance/list`, null, {
params: {
...this.search,
createUserAreaId: this.search.areaId,
beginDate: this.dateList[0] || '',
endDate: this.dateList[1] || '',
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
changeArea () {
this.search.current = 1
this.$nextTick(() => {
this.getList()
})
},
toGagList () {
this.$emit('change', {
type: 'GagList'
})
},
gag (id, status) {
this.$confirm(`确定${status ? '解除禁言' : '禁言'}该用户?`).then(() => {
this.instance.post(`/app/appneighborhoodassistance/addBlacklist?userId=${id}`).then(res => {
if (res.code == 0) {
this.$message.success(`${status ? '解除禁言' : '禁言'}成功!`)
this.getList()
}
})
})
},
remove (id) {
this.$confirm('确定删除该帖子?').then(() => {
this.instance.post(`/app/appneighborhoodassistance/delete?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
toDetail (id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || ''
}
})
},
toAdd(id) {
this.$emit('change', {
type: 'Add',
params: {
id: id || ''
}
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,647 @@
<template>
<section class="AppIntegralStatistics">
<el-row class="overallStatistics">
<div class="title">
<p>总体统计</p>
<div class="title_right">
<div>
<span v-for="(item,index) in timeCheck" :key="index" :class="type == index? 'active':''"
@click="timeChange(index)">{{ item }}</span>
</div>
<el-cascader ref="cascader1" v-model="girdArr" :options="girdOptions" placeholder="所属网格" size="small"
:props="defaultProps" :show-all-levels="false" @change="gridChange" clearable></el-cascader>
</div>
</div>
<div class="card_list">
<div class="card">
<h2>积分余额汇总
<el-tooltip
placement="right"
style="width: 16px;"
content="截止目前所有居民剩余可用积分余额的总和">
<i class="el-icon-warning-outline"></i>
</el-tooltip>
</h2>
<p class="color1">{{ data.nowIntegral || 0 }}</p>
</div>
<div class="card">
<h2>发放积分</h2>
<p class="color1">{{ data.addIntegral || 0 }}</p>
</div>
<div class="card">
<h2>消耗积分</h2>
<p class="color1">{{ Math.abs(data.reduceIntegral) || 0 }}</p>
</div>
</div>
<div class="echertsBox">
<div class="left_Box">
<p>个人积分排行</p>
<div>
<div id="chart1" style="height: 300px; width: 100%;" v-show="userSortListX.length && userSortListY.length"></div>
<ai-empty v-show="!userSortListX.length && !userSortListY.length" style="height: 200px; width: 100%;" id="empty"></ai-empty>
</div>
</div>
<div class="right_Box">
<p>网格积分排行</p>
<div>
<div id="chart2" style="height: 300px; width: 100%;" v-show="girdSortListX.length && girdSortListY.length"></div>
<ai-empty v-show="!girdSortListX.length && !girdSortListY.length" style="height: 200px; width: 100%;" id="empty"></ai-empty>
</div>
</div>
</div>
</el-row>
<ai-card>
<ai-title slot="title" title="积分明细"/>
<template #content>
<ai-search-bar>
<template #left>
<el-cascader ref="cascader2" v-model="girdIdArr" :options="girdOptions" placeholder="所属网格" size="small"
:props="defaultProps" :show-all-levels="false" clearable @change="gridChangeOpt"></el-cascader>
<ai-select v-model="search.integralType" placeholder="请选择类型" @change="current=1, getTableData()"
:selectList="dict.getDict('integralType')"/>
<el-date-picker v-model="time" size="small" type="daterange" value-format="yyyy-MM-dd"
range-separator="" start-placeholder="开始日期" end-placeholder="结束日期" @change="onChange">
</el-date-picker>
</template>
<template #right>
<el-input size="small" placeholder="请输入姓名" v-model="search.userName" clearable
@clear="current = 1, search.userName = '', getTableData()" suffix-icon="iconfont iconSearch"
v-throttle="() => {(current = 1), getTableData();}" />
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :total="total" :current.sync="current" :size.sync="size"
@getList="getTableData" :col-configs="colConfigs" :dict="dict">
<el-table-column slot="eventDesc" label='事件' align="center" width="400px" show-overflow-tooltip>
<template slot-scope="{ row }">
<span>{{ row.integralRuleName || row.eventDesc}}</span>
</template>
</el-table-column>
<el-table-column slot="changeIntegral" label="变动积分" align="center">
<template slot-scope="{ row }">
<span>{{ row.integralCalcType == 0 ? '-' : '+' }}{{ row.changeIntegral }}</span>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" align="center">
<template slot-scope="{ row }">
<el-button type="text" @click="open(row.id)">详情</el-button>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
<el-dialog title="详情" :visible.sync="dialog" customFooter width="700">
<ai-detail>
<template #content>
<ai-wrapper>
<ai-info-item label="姓名" :value="details.integralUserName" />
<ai-info-item label="所属网格" :value="details.girdName"/>
<ai-info-item label="事件" isLine :value="details.eventDesc">
<span v-if="details.integralRuleId">{{ details.integralRuleName }}</span>
<span v-else>{{ details.eventDesc }}</span>
</ai-info-item>
<ai-info-item label="时间" isLine :value="details.createTime"/>
<ai-info-item label="积分变动">
{{ details.integralCalcType == 0 ? '-' : '+' }}{{ details.changeIntegral }}
</ai-info-item>
<ai-info-item label="积分余额" :value="details.nowIntegral"/>
<ai-info-item label="凭证" isLine v-if="fileDownLoad.length">
<ai-file-list :fileList="fileDownLoad" style="width: 200px;" :fileOps="{name: 'name'}"></ai-file-list>
</ai-info-item>
</ai-wrapper>
</template>
</ai-detail>
<span slot="footer" class="dialog-footer" center>
<el-button @click="dialog = false" style="width: 92px">关闭</el-button>
</span>
</el-dialog>
<ai-dialog :visible.sync="dialogDate" title="选择时间" width="500px" customFooter>
<el-date-picker v-model="timeList" size="small" type="daterange" value-format="yyyy-MM-dd"
range-separator="" start-placeholder="开始日期" end-placeholder="结束日期">
</el-date-picker>
<el-button slot="footer" @click="selectDete" type="primary">确认</el-button>
</ai-dialog>
</section>
</template>
<script>
import { mapState } from "vuex"
import * as echarts from 'echarts';
import dayjs from 'dayjs'
export default {
name: "AppIntegralStatistics",
label: "积分统计",
props: {
instance: Function,
dict: Object,
permissions: Function,
},
data() {
return {
myChart1: null,
myChart2: null,
tableData: [],
search: {
current: 1,
userName: '',
girdId: '',
integralType: '',
startTime: '',
endTime: '',
},
girdIdArr:[],
total: 0,
size: 10,
current: 1,
girdList: [],
time: [],
timeCheck: ['昨日','近7天','近30天','自定义'],
dialog: false,
dialogDate: false,
timeList: [],
type: '1',
startTime: '',
endTime: '',
data: {},
girdId: '',
girdArr: [],
girdOptions: [],
defaultProps: {
label: 'girdName',
value: 'id',
children: 'children',
checkStrictly: true,
},
details: {},
fileDownLoad: [],
userSortListX: [],
userSortListY: [],
girdSortListX: [],
girdSortListY: [],
}
},
computed: {
...mapState(['user']),
colConfigs() {
return [
{ prop: "integralUserName", label: '姓名', align: "left", width: "200px" },
{ prop: "girdName", label: '所属网格', align: "center", width: "180px" },
{ slot: "eventDesc"},
{ prop: "integralType", label: '类型', align: "center", dict: "integralType"},
{ slot: "changeIntegral", label: '积分变动', align: "center", },
{ prop: "nowIntegral", label: '剩余积分', align: "center", },
{ prop: "createTime", label: '时间', align: "center", },
{ slot: "options" }
]
}
},
created() {
this.time = [dayjs().subtract(1,'week').format('YYYY-MM-DD'),dayjs().format('YYYY-MM-DD')]
this.$dict.load('epidemicDangerousAreaLevel','integralType','integralRuleEvent','integralRuleEventType').then(() => {
this.getStatistics()
this.getGridList()
this.getRanking()
this.search.startTime = this.time?.[0]
this.search.endTime = this.time?.[1]
this.getTableData()
})
},
methods: {
// 统计接口
getStatistics() {
this.instance.post('/app/appintegraluser/allAppletUserIntegral',null, {
params: {
type: this.type,
girdId: this.girdId,
startTime: this.startTime,
endTime: this.endTime,
}
}).then(res => {
if(res?.data) {
this.data = res.data
}
})
},
// 人员、网格排行
getRanking() {
this.instance.post('/app/appintegraluser/userAndGirdIntegralSortByApplet',null,{
params: {
type: this.type,
girdId: this.girdId,
startTime: this.startTime,
endTime: this.endTime
}
}).then((res) => {
if(res?.data) {
this.userSortListX = res.data.userSortList.map(e=> e.userName).reverse()
this.userSortListY = res.data.userSortList.map(e=> e.changeIntegral).reverse()
this.girdSortListX = res.data.girdSortList.map(e=> e.girdName).reverse()
this.girdSortListY = res.data.girdSortList.map(e=> e.changeIntegral).reverse()
this.getColEcherts1(this.userSortListX,this.userSortListY)
this.getColEcherts2(this.girdSortListX,this.girdSortListY)
}
})
},
// 积分明细
getTableData() {
this.instance.post('/app/appintegraluser/girdIntegralDetailByApplet',null,{
params: {
...this.search,
current: this.current,
size: this.size,
total: this.total,
}
}).then(res => {
if(res?.data) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
gridChangeOpt(val) {
this.girdIdArr = val
this.search.girdId = val?.[val.length - 1]
this.$refs.cascader2.dropDownVisible = false;
this.getTableData()
},
getColEcherts1(xData,yData) {
let chartDom1 = document.getElementById('chart1');
chartDom1.style.width = (window.innerWidth - 435) / 2 + "px";
this.myChart1 = echarts.init(chartDom1);
this.myChart1.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '16px',
right: '28px',
bottom: '14px',
top: '16px',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01],
},
yAxis: {
type: 'category',
data: xData,
axisTick: {
show: false,
},
axisLine: {
show: false,
},
},
series: [
{
data: yData,
type: 'bar',
itemStyle: {
normal: {
color: "#5087ec",
label: {
show: true, //开启显示
position: 'right', //在上方显示
textStyle: {
fontSize: 13,
color: '#666'
}
},
},
},
barWidth: 10,
barGap: '20%',
}
]
}, true);
window.addEventListener("resize", this.onResize)
},
getColEcherts2(xData,yData) {
let chartDom2 = document.getElementById('chart2');
chartDom2.style.width = (window.innerWidth - 435) / 2 + "px";
this.myChart2 = echarts.init(chartDom2);
this.myChart2.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '16px',
right: '28px',
bottom: '14px',
top: '16px',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01],
},
yAxis: {
type: 'category',
data: xData,
axisTick: {
show: false,
},
axisLine: {
show: false,
},
triggerEvent: true,
//设置文本过长超出隐藏...表示
axisLabel:{
margin: 8,
formatter: function(params){
var val=""
if(params.length > 8) {
val = params.substr(0,8)+'...'
return val
} else {
return params;
}
}
},
},
series: [
{
data: yData,
type: 'bar',
itemStyle: {
normal: {
color: "#5087ec",
label: {
show: true, //开启显示
position: 'right', //在右方显示
textStyle: {
fontSize: 13,
color: '#666'
}
},
},
},
barWidth: 10,
barGap: '20%',
}
]
}, true);
window.addEventListener("resize", this.onResize2)
// this.extension(this.myChart2)
},
onResize1() {
this.myChart1.resize()
},
onResize2() {
this.myChart2.resize()
},
gridChange(val) {
this.girdArr = val
this.girdId = val?.[val.length - 1]
this.$refs.cascader1.dropDownVisible = false;
this.getStatistics()
this.getRanking()
},
// 所有网格
getGridList() {
this.instance.post(`/app/appgirdinfo/listAll3`).then((res) => {
if (res?.code == 0) {
this.girdOptions = this.toTree(res.data)
}
})
},
// 转树形结构
toTree(data) {
let result = [];
if (!Array.isArray(data)) {
return result
}
let map = {};
data.forEach(item => {
map[item.id] = item;
});
data.forEach(item => {
let parent = map[item.parentGirdId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
return result;
},
timeChange(index) {
if(index == 3) {
this.dialogDate = true
}
this.type = index
this.getStatistics()
this.getRanking()
},
open(id) {
this.dialog = true
this.getDetail(id)
},
onChange(val) {
this.search.startTime = val?.[0]
this.search.endTime = val?.[1]
this.getTableData()
},
getDetail(id) {
this.instance.post(`/app/appintegraldetail/queryDetailById?id=${id}`).then(res=> {
if(res?.data) {
this.details = res.data
if(res.data.enclosure) {
let str = res.data.enclosure.split('/')
this.fileDownLoad = [{
url:res.data.enclosure,
name: str?.[str.length - 1]
}]
}
}
})
},
selectDete() {
if(!this.timeList || !this.timeList.length) {
return this.$message.error('请选择自定义时间');
}
this.startTime = this.timeList?.[0]
this.endTime = this.timeList?.[1]
this.dialogDate = false
this.getStatistics()
this.getRanking()
},
},
filters: {
formatTime(num) {
if(num > 0) {
return '+' + num
} else {
return num
}
}
},
mounted() {
this.getColEcherts1()
this.getColEcherts2()
},
destroyed () {
window.removeEventListener('resize', this.onResize1)
window.removeEventListener('resize', this.onResize2)
},
}
</script>
<style lang="scss" scoped>
.AppIntegralStatistics {
height: 100%;
box-sizing: border-box;
padding-top: 20px;
overflow-y: scroll;
.overallStatistics {
width: 100%;
margin-bottom: 16px;
padding: 20px;
box-sizing: border-box;
background: #FFF;
.title {
width: 100%;
display: flex;
justify-content: space-between;
margin-bottom: 16px;
p {
font-size: 16px;
font-family: MicrosoftYaHeiSemibold;
color: #222222;
font-weight: 600;
}
.title_right {
display: flex;
align-items: center;
span {
display: inline-block;
width: 70px;
height: 32px;
line-height: 32px;
border-radius: 2px;
border: 1px solid #D0D4DC;
margin-right: 8px;
text-align: center;
cursor: pointer;
}
.active {
color: #2266FF;
border: 1px solid #2266FF;
}
}
}
.card_list {
display: flex;
.card {
flex: 1;
height: 96px;
background: #F9F9F9;
border-radius: 2px;
margin-right: 20px;
padding: 16px 24px;
box-sizing: border-box;
h2 {
color: #888888;
font-weight: 600;
font-size: 16px;
}
p {
margin-top: 8px;
font-size: 24px;
font-weight: 600;
}
.color1 {
color: #2891FF;
}
.color2 {
color: #22AA99;
}
.color3 {
color: #F8B425;
}
}
.card:last-child {
margin-right: 0;
}
}
.echertsBox {
width: 100%;
margin-top: 20px;
background: #FFF;
display: flex;
.left_Box {
margin-right: 16px;
flex: 1;
}
.right_Box {
width: 100%;
flex: 1;
}
.left_Box,
.right_Box {
background: #F9F9F9;
box-shadow: 0px 4px 6px -2px rgba(15,15,21,0.1500);
border-radius: 4px;
padding: 16px;
box-sizing: border-box;
#chart1,
#chart2 {
width: 100%;
height: 300px;
}
p {
font-weight: 600;
}
}
}
}
// .chartCss {
// position: absolute;
// color: black;
// background:white;
// font-family: Aril;
// font-size: 12px;
// padding: 5px;
// display: inline;
// }
:deep( .el-dialog__footer ){
text-align: center;
}
:deep( .el-dialog__header ){
border-bottom: 1px solid #DDD;
}
:deep( .ai-detail ){
background: #FFF;
}
}
</style>

View File

@@ -0,0 +1,68 @@
<template>
<div class="AppHelp">
<keep-alive :include="['List']">
<component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import Detail from './components/Detail'
import List from './components/List'
export default {
name: 'AppIntegratingAudit',
label: '积分审核',
props: {
instance: Function,
dict: Object
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
List,
Detail
},
methods: {
onChange (data) {
if (data.type === 'Add') {
this.component = 'Add'
this.params = data.params
}
if (data.type === 'Detail') {
this.component = 'Detail'
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">
.AppHelp {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,270 @@
<template>
<ai-detail class="detail">
<template slot="title">
<ai-title title="积分审核" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
<template #rightBtn>
<el-button size="small" type="primary" @click="isShow = true" v-if="info.status === '0'">审核</el-button>
</template>
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<ai-wrapper>
<ai-info-item label="事件类型" isLine :value="info.applyItemName"></ai-info-item>
<ai-info-item label="申请人" :value="info.integralUserName"></ai-info-item>
<ai-info-item label="手机号" :value="info.phone"></ai-info-item>
<ai-info-item label="所属地区" :value="info.areaName"></ai-info-item>
<ai-info-item label="所属网格" :value="info.girdName"></ai-info-item>
<ai-info-item label="审核时间" v-if="info.status === '1'" :value="info.auditTime"></ai-info-item>
<ai-info-item label="申请时间" v-if="info.status === '0'" :value="info.createTime"></ai-info-item>
<ai-info-item label="状态">
<span>{{ dict.getLabel('appIntegralApplyEventStatus', info.status) }}</span>
<span v-if="info.status === '1'" style="margin-left: 10px; color: green">积分+{{ info.applyIntegral }}</span>
</ai-info-item>
<ai-info-item label="审批意见" v-if="info.status === '2'" isLine>
<span style="color: red;">{{ info.auditDesc }}</span>
</ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="事件信息">
<template #right>
<el-button size="small" type="primary" @click="showEvent" v-if="info.status === '0'">编辑</el-button>
</template>
<template #content>
<ai-wrapper>
<ai-info-item label="积分值" isLine :value="info.applyIntegral"></ai-info-item>
<ai-info-item label="事件描述" isLine :value="info.content"></ai-info-item>
<ai-info-item label="图片" isLine v-if="info.images && info.images.length">
<div class="files">
<!-- <div class="file-item" v-for="(item, index) in info.images" :key="index" v-viewer="{movable: true}">
<img :src="item.url">
</div> -->
<ai-uploader
:instance="instance"
fileType="img"
acceptType=".jpg,.png,.jpeg"
v-model="info.images"
:limit="9" :disabled="true">
</ai-uploader>
</div>
</ai-info-item>
<ai-info-item label="视频" isLine v-if="info.videos && info.videos.length">
<div class="files">
<div class="file-item" v-for="(item, index) in info.videos" :key="index">
<video controls :src="item.url"></video>
</div>
</div>
</ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-dialog
:visible.sync="isShow"
@onConfirm="onConfirm"
@close="onClose"
width="890px"
title="审核">
<el-form class="ai-form" :model="form" label-width="120px" ref="form">
<el-form-item label="是否通过" prop="auditStatus" style="width: 100%;" :rules="[{required: true, message: '请选择是否通过', trigger: 'change'}]">
<el-radio-group v-model="form.auditStatus">
<el-radio label="0"></el-radio>
<el-radio label="1"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="auditDesc" v-if="form.auditStatus === '0'" label="审批意见" style="width: 100%" :rules="[{required: true, message: '请输入审批意见', trigger: 'blur'}]">
<el-input size="small" type="textarea" :rows="5" v-model="form.auditDesc" clearable placeholder="请输入审批意见"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
<ai-dialog
:visible.sync="isShowEvent"
@onConfirm="onEventConfirm"
@close="onClose"
width="890px"
title="编辑">
<el-form class="ai-form" :model="eventForm" label-width="120px" ref="eventForm">
<el-form-item label="积分值" prop="applyIntegral" :rules="[{required: true, message: '请输入积分值', trigger: 'change'}]">
<el-input-number style="width: 200px;" size="small" :precision="2" type="input" v-model="eventForm.applyIntegral" clearable placeholder="请输入积分值" :min="0"></el-input-number>
</el-form-item>
<el-form-item prop="content" label="事件描述" style="width: 100%" :rules="[{required: true, message: '请输入事件描述', trigger: 'blur'}]">
<el-input size="small" type="textarea" :rows="5" :maxlength="300" show-word-limit v-model="eventForm.content" clearable placeholder="请输入事件描述"></el-input>
</el-form-item>
<el-form-item style="width: 100%" label="图片">
<ai-uploader
:instance="instance"
fileType="img"
acceptType=".jpg,.png,.jpeg"
v-model="eventForm.images"
:limit="9">
</ai-uploader>
</el-form-item>
<el-form-item style="width: 100%" label="视频">
<ai-uploader
:instance="instance"
fileType="file"
acceptType=".mp4,.MOV"
v-model="eventForm.videos"
:limit="9">
</ai-uploader>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Detail',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
info: {},
form: {
auditDesc: '',
auditStatus: ''
},
eventForm: {
files: null,
images: [],
videos: [],
content: '',
applyIntegral: ''
},
isShowEvent: false,
isShow: false
}
},
created () {
this.getInfo()
},
methods: {
getInfo () {
this.instance.post(`/app/appintegraluserapply/queryDetailById?id=${this.params.id}`).then(res => {
if (res.code == 0) {
if (res.data) {
this.info = {
...res.data,
files: res.data.files.map(v => {
return {
...v,
postfix: v.postfix.toLowerCase()
}
})
}
if (res.data.status === '0') {
this.eventForm.files = res.data.files
this.eventForm.content = res.data.content
this.eventForm.applyIntegral = res.data.applyIntegral
}
this.info.images = res.data.files.filter(e => (['jpeg', 'jpg', 'png'].includes(e.postfix.split('.')[1])))
this.info.videos = res.data.files.filter(e => (['mp4', 'MOV'].includes(e.postfix.split('.')[1])))
}
}
})
},
showEvent () {
this.eventForm.files = null
this.eventForm.content = this.info.content
this.eventForm.applyIntegral = this.info.applyIntegral
this.eventForm.images = this.info.images
this.eventForm.videos = this.info.videos
this.isShowEvent = true
},
onClose () {
this.form.auditDesc = ''
this.form.auditStatus = ''
},
onEventConfirm () {
if ((this.eventForm.images.length + this.eventForm.videos.length) > 9) {
return this.$message.error('图片和视频不得超过9个')
} else {
this.eventForm.files = [...this.eventForm.images,...this.eventForm.videos]
}
this.$refs.eventForm.validate((valid) => {
if (valid) {
this.instance.post(`/app/appintegraluserapply/updateByGirdMember`, {
...this.eventForm,
id: this.params.id,
}).then(res => {
if (res.code == 0) {
this.$message.success('编辑成功!')
this.isShowEvent = false
this.getInfo()
}
})
}
})
},
onConfirm () {
this.$refs.form.validate((valid) => {
if (valid) {
this.instance.post(`/app/appintegraluserapply/auditById`, {
...this.form,
id: this.params.id
}).then(res => {
if (res.code == 0) {
this.$message.success('审核成功!')
this.isShow = false
this.getInfo()
}
})
}
})
},
cancel () {
this.$emit('change', {
type: 'List',
isRefresh: true
})
}
}
}
</script>
<style scoped lang="scss">
.detail {
.files {
display: flex;
align-items: center;
flex-wrap: wrap;
.file-item {
width: 118px;
height: 118px;
margin: 0 20px 20px 0;
img, video {
width: 100%;
height: 100%;
object-fit: cover;
}
img {
cursor: pointer;
transition: all ease 0.3s;
&:hover {
opacity: 0.7;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,293 @@
<template>
<ai-list class="AppIntegratingAudit">
<template slot="title">
<ai-title title="积分审核" isShowBottomBorder v-model="search.areaId" isShowArea :hideLevel="hideLevel - 1" @change="changeArea">
</ai-title>
</template>
<template slot="content">
<ai-search-bar class="search-bar">
<template #left>
<ai-select
v-model="search.applyItemId"
@change="(search.current = 1), getList()"
placeholder="请选择事件/类型"
:selectList="dictList">
</ai-select>
<el-date-picker
v-model="search.createTimeStart"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择开始日期"
@change="search.current = 1, getList()">
</el-date-picker>
<el-date-picker
v-model="search.createTimeEnd"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择结束日期"
@change="search.current = 1, getList()">
</el-date-picker>
<ai-picker
:instance="instance"
:multiple="false"
dialogTitle="选择网格"
:ops="{ label: 'girdName' }"
pageTitle="网格"
action="/app/appgirdinfo/girdList"
v-model="userList"
@pick="onGridChange">
<div class="userSelcet">
<span style="color: #606266;" v-if="search.girdId">{{ search.girdName }}</span>
<span v-else>请选择网格</span>
<i class="el-icon-arrow-up" v-if="!search.girdId"></i>
<i class="el-icon-circle-close" v-if="search.girdId" @click.stop="userList = [], search.girdId = '', search.girdName = '', search.current = 1, getList()"></i>
</div>
</ai-picker>
<ai-select
v-model="search.status"
@change="(search.current = 1), getList()"
placeholder="请选择状态"
:selectList="$dict.getDict('appIntegralApplyEventStatus')">
</ai-select>
</template>
<template #right>
<el-input
v-model="search.createUserName"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入申请人、审批人"
clearable
@clear="search.current = 1, search.createUserName = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<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>
<!-- <el-button type="text" @click="remove(row.id)">删除</el-button> -->
<el-button type="text" @click="push(row.id)" v-if="row.status === '1' && row.pushStatus === '0'">推送精选</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
import { MessageBox } from 'element-ui'
import { mapState } from 'vuex'
export default {
name: 'List',
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
areaId: '',
applyItemId: '',
girdId: '',
createTimeStart: '',
createTimeEnd: '',
girdName: ''
},
userList: [],
dictList: [],
total: 10,
colConfigs: [
{ prop: 'applyItemName', label: '事件类型', align: 'left' },
{ prop: 'integralUserName', label: '申请人', align: 'center' },
{ prop: 'areaName', label: '所属地区', align: 'center' },
{ prop: 'girdName', label: '所属网格', align: 'center' },
{ prop: 'createTime', label: '申请时间', align: 'center' },
{ prop: 'status', label: '状态', align: 'center', format: v => this.dict.getLabel('appIntegralApplyEventStatus', v) },
{ prop: 'auditUserName', label: '审批人', align: 'center' },
{ prop: 'pushStatus', label: '推送精选', align: 'center', format: v => this.dict.getLabel('appIntegralApplyEventPushStatus', v) }
],
tableData: [],
dateList: []
}
},
computed: {
...mapState(['user']),
hideLevel () {
return this.user.info.areaList?.length || 0
}
},
created () {
this.search.areaId = this.user.info.areaId
this.$dict.load('appIntegralApplyEventStatus', 'appIntegralApplyEventPushStatus').then(() => {
this.getList()
this.getRulesList()
})
},
methods: {
getRulesList () {
this.instance.post(`/app/appintegralrule/listByAppletFD?current=1&size=3000`).then((res) => {
if (res.code === 0) {
this.dictList = res.data.records.map(v => {
return {
dictName: v.ruleName,
dictValue: v.id
}
})
}
})
},
onGridChange (e) {
if (e.length) {
this.search.girdId = e[0].id
this.search.girdName = e[0].girdName
this.search.current = 1
this.getList()
}
},
getList () {
this.instance.post(`/app/appintegraluserapply/list`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
changeArea () {
this.search.current = 1
this.$nextTick(() => {
this.getList()
})
},
push (id) {
MessageBox.confirm('是否将精选内容对全体居民公开,选则否将只对本村/社区居民公开。', '推送精选', {
distinguishCancelAndClose: true,
confirmButtonText: '是',
type: 'warning',
closeOnClickModal: false,
center: true,
customClass: 'AiConfirm',
cancelButtonText: '否'
}).then(() => {
this.instance.post(`/app/appintegraluserapply/pushById?id=${id}&status=1`).then(res => {
if (res.code == 0) {
this.getList()
this.$message.success('推送成功')
}
})
}).catch((e) => {
e === 'cancel' && this.instance.post(`/app/appintegraluserapply/pushById?id=${id}&status=0`).then(res => {
if (res.code == 0) {
this.getList()
this.$message.success('推送成功')
}
})
})
},
remove (id) {
this.$confirm('确定删除该帖子?').then((e) => {
this.instance.post(`/app/appintegraluserapply/delete?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
toDetail (id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || ''
}
})
},
toAdd(id) {
this.$emit('change', {
type: 'Add',
params: {
id: id || ''
}
})
}
}
}
</script>
<style lang="scss" scoped>
.AppIntegratingAudit {
.userSelcet {
display: flex;
align-items: center;
justify-content: space-between;
width: 215px;
height: 32px;
line-height: 32px;
border-radius: 4px;
border: 1px solid #d0d4dc;
overflow: hidden;
cursor: pointer;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: $placeholderColor;
}
i {
display: flex;
position: relative;
align-items: center;
justify-content: center;
width: 30px;
height: 100%;
line-height: 32px;
font-size: 14px;
text-align: center;
color: #d0d4dc;
transform: rotateZ(180deg);
}
.el-icon-circle-close:hover {
opacity: 0.6;
}
span {
flex: 1;
padding: 0 15px;
font-size: 12px;
color: $placeholderColor;
}
}
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<div class="AppActivitiesManagement">
<keep-alive :include="['List']">
<component ref="component" :is="component" :permissions="permissions " @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import List from './components/List'
export default {
name: 'AppIntegratingDjust',
label: '积分调整',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
List
},
methods: {
onChange (data) {
if (data.type === 'Detail') {
this.component = 'Detail'
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">
.AppActivitiesManagement {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,357 @@
<template>
<ai-list class="gridScoreManage">
<ai-title slot="title" title="积分调整" isShowBottomBorder></ai-title>
<template #content>
<ai-search-bar>
<template #left>
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="changeIntegral('',0)">&nbsp;批量调整积分</el-button>
<el-cascader ref="cascader1" clearable v-model="girdIdList" :options="girdOptions" placeholder="所属网格" size="small"
:props="defaultProps" :show-all-levels="false" @change="gridChange"></el-cascader>
</template>
<template #right>
<ai-import :instance="instance" :dict="dict" type="appintegraluser" name="积分管理"
@success="getTableData()">
<el-button icon="iconfont iconImport">导入</el-button>
</ai-import>
<ai-download :instance="instance" url="/app/appintegraluser/girdIntegralExport" :params="search" fileName="积分调整"
:disabled="tableData.length == 0">
</ai-download>
<el-input size="small" placeholder="姓名" v-model="search.userName" clearable
@clear="current = 1, search.userName = '', getTableData()" suffix-icon="iconfont iconSearch"
v-throttle="() => {(current = 1), getTableData();}"/>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:total="total"
:current.sync="current"
:size.sync="size"
@getList="getTableData()"
:col-configs="colConfigs"
:dict="dict"
@sort-change="changeTableSort">
<el-table-column slot="options" label="操作" width="120" align="center">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="changeIntegral(row,1)">调整积分</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog
title="调整积分"
:visible.sync="dialog"
:destroyOnClose="true"
width="720px"
@onConfirm="onConfirm"
@closed="form={},chooseUserList=[]">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="选择人员" prop="ids">
<ai-person-select :instance="instance" :customClicker="true" :chooseUserList="chooseUserList"
url="/app/appgirdmemberinfo/list" headerTitle="网格员列表"
:isMultiple="true" dialogTitle="选择" @selectPerson="selectPerson" class="aipersonselect">
<template name="option" v-slot:option="{ item }">
<span class="iconfont iconProlife">{{ item.name }}</span>
<ai-id mode="show" :show-eyes="false" :value="item.idNumber"/>
</template>
</ai-person-select>
</el-form-item>
<el-form-item label="调整说明" prop="eventDesc">
<el-input v-model.trim="form.eventDesc" placeholder="请输入..." type="textarea" :rows="4" show-word-limit
maxlength="100"></el-input>
</el-form-item>
<el-form-item label="上传凭证">
<ai-uploader :instance="instance" fileType="file" v-model="form.file" :limit="1"></ai-uploader>
</el-form-item>
<el-form-item label="类型" prop="integralCalcType">
<ai-select v-model="form.integralCalcType" :selectList="dict.getDict('integralCalcType')"/>
</el-form-item>
<el-form-item label="积分" prop="integral">
<el-input v-model.trim="form.integral" placeholder="请输入正数" size="small"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "List",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
search: {
userName: '',
girdId: '',
current: 1,
size: 10,
sortFiled: '',
sortRule: '',
},
girdIdList: [],
tableData: [],
size: 10,
total: 0,
current: 1,
girdList: [],
form: {
ids: [],
eventDesc: "",
enclosure: "", // 附件
integralCalcType: "",
integral: '',
file: [],
},
personList: [],
dialog: false,
girdOptions: [],
defaultProps: {
label: 'girdName',
value: 'id',
checkStrictly: true,
},
chooseUserList: [],
flag: false,
}
},
created() {
this.$dict.load('integralCalcType')
this.getTableData()
this.getGridList()
},
computed: {
...mapState(['user']),
colConfigs() {
return [
{ prop: "integralUserName", label: '姓名', align: "left", },
{ prop: "areaName", label: '所属地区', align: "center" },
{ prop: "auditDesc", label: '调整说明', align: "center" },
{ prop: "applyItemName", label: '类型', align: "center" },
{ prop: "applyIntegral", label: '积分', align: "center" },
{ prop: "auditTime", label: '操作时间', align: "center", sortable: "custom" },
{ prop: "auditUserName", label: '操作人', align: "center", sortable: "custom" },
{ slot: "options" },
]
},
rules() {
return {
ids: [{required: true, message: '请选择人员', trigger: 'blur'}],
eventDesc: [{required: true, message: '请输入调整说明', trigger: 'blur'}],
integralCalcType: [{required: true, message: '请选择类型', trigger: 'change'}],
integral: [{required: true, message: '请输入积分', trigger: 'blur' },
{pattern: /^([1-9]\d*|0)(\.\d{1,2})?$/, message: '请输入正数且最多只能保留两位小数'}],
}
},
},
methods: {
getTableData() {
this.instance.post(`/app/appintegraluserapply/list`,null,{
params: {
...this.search,
current: this.current,
size: this.size,
total: this.total
}
}).then(res => {
if(res?.data) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
selectPerson(val) {
if (val) {
this.personList = val
this.form.ids = [...this.personList.map(e => e.id)]
} else {
this.form.ids = this.chooseUserList.map(e => e.id)
}
},
changeIntegral(row,type) {
if(type==0) {
this.dialog = true
} else if(type ==1) {
this.chooseUserList = [{
id: row.userId,
name: row.userName
}]
this.form.ids = this.chooseUserList.map(e => e.id)
this.dialog = true
}
},
getGridList() {
this.instance.post(`/app/appgirdinfo/listAll3`).then((res) => {
if (res.code == 0) {
this.girdOptions = this.toTree(res.data)
}
})
},
// 转树形结构
toTree(data) {
let result = [];
if (!Array.isArray(data)) {
return result
}
let map = {};
data.forEach(item => {
map[item.id] = item;
});
data.forEach(item => {
let parent = map[item.parentGirdId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
return result;
},
gridChange(val) {
this.girdIdList = val
this.search.girdId = val?.[val.length - 1]
this.$refs.cascader1.dropDownVisible = false;
this.getTableData()
},
changeTableSort(col) {
if(col.prop === 'integral') { // 剩余积分
this.search.sortFiled = 0
if(col.order === 'ascending') {
this.search.sortRule = true
} else if(col.order === 'descending') {
this.search.sortRule = false
} else if(col.order === null) {
this.search.sortRule = ''
}
} else if(col.prop === 'totalIntegral') { // 累计积分
this.search.sortFiled = 1
if(col.order === 'ascending') {
this.search.sortRule = true
} else if(col.order === 'descending') {
this.search.sortRule = false
} else if(col.order === null) {
this.search.sortRule = ''
}
} else if(col.prop === 'usedIntegral') { // 已用积分
this.search.sortFiled = 2
if(col.order === 'ascending') {
this.search.sortRule = true
} else if(col.order === 'descending') {
this.search.sortRule = false
} else if(col.order === null) {
this.search.sortRule = ''
}
}
this.getTableData()
},
onConfirm() {
if(this.flag) return
if(this.form.file?.length) {
this.form.enclosure = this.form.file[0].url
}
this.$refs.form.validate((valid)=> {
if(valid) {
this.flag = true
this.instance.post(`/app/appintegraluser/changeIntegral`,{
ids: this.form.ids,
eventDesc: this.form.eventDesc,
enclosure: this.form.enclosure, // 附件
integralCalcType: this.form.integralCalcType,
integral: this.form.integral,
}).then(res => {
if(res?.code == 0) {
this.$message.success('调整积分成功')
setTimeout(() =>{
this.dialog = false
this.getTableData()
this.flag = false
}, 600)
} else {
this.flag = false
}
})
}
})
},
toDetail(id) {
this.$emit('change', {
type: 'gridScoreDetail',
params: {
id: id
}
})
}
},
}
</script>
<style lang="scss" scoped>
.gridScoreManage {
.userlist {
display: inline-block;
}
.userlist, .user {
display: inline-block;
}
.user {
position: relative;
width: 70px;
text-align: center;
.remove-icon {
position: absolute;
right: 7px;
top: -4px;
line-height: 1;
padding: 6px 0;
font-size: 16px;
cursor: pointer;
&:hover {
color: crimson;
}
}
img, h2 {
display: block;
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
margin: 0 auto 4px;
font-size: 14px;
color: #fff;
border-radius: 50%;
}
h2 {
background-color: $primaryColor;
}
span {
color: #666;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
}
}
}
</style>

View File

@@ -0,0 +1,116 @@
<template>
<ai-list v-if="!isShowDetail">
<template slot="title">
<ai-title title="订单管理" :isShowBottomBorder="false" v-model="areaId" :isShowArea="currIndex != '1'" :hideLevel="hideLevel - 1" @change="onAreaChange">
</ai-title>
</template>
<template slot="tabs">
<el-tabs v-model="currIndex">
<el-tab-pane v-for="(tab,i) in tabs" :key="i" :label="tab.label">
<component :areaId="areaId" :ref="String(i)" v-if="currIndex == i" :is="tab.comp" @change="onChange" lazy :instance="instance" :dict="dict" :permissions="permissions"/>
</el-tab-pane>
</el-tabs>
</template>
</ai-list>
<ResidentDetail v-else-if="componentName === 'ResidentDetail'" :areaId="areaId" :params="params" :instance="instance" :dict="dict" @change="onChange"></ResidentDetail>
<GirdDetail v-else-if="componentName === 'GirdDetail'" :areaId="areaId" :params="params" :instance="instance" :dict="dict" @change="onChange"></GirdDetail>
</template>
<script>
import ResidentDetail from './components/ResidentDetail'
import GirdDetail from './components/GirdDetail'
import GirdList from './components/GirdList'
import ResidentList from './components/ResidentList'
import { mapState } from 'vuex'
export default {
name: 'AppIntegratingOrder',
label: '订单管理',
components: {
ResidentList,
GirdList,
ResidentDetail,
GirdDetail
},
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
...mapState(['user']),
hideLevel () {
return this.user.info.areaList?.length || 0
},
tabs () {
const tabList = [
{label: '居民积分订单', name: 'ResidentList', comp: ResidentList, permission: ''},
{label: '网格积分订单', name: 'GirdList', comp: GirdList, permission: ''}
].filter(item => {
return true
})
return tabList
}
},
data () {
return {
activeName: 'GoodsList',
currIndex: '0',
componentName: '',
params: {},
areaName: '',
areaId: '',
isShowDetail: false
}
},
created () {
this.areaId = this.user.info.areaId
},
methods: {
onAreaChange () {
if (this.currIndex != '1') {
this.$nextTick(() => {
this.$refs[this.currIndex][0].getList()
})
}
},
onChange (data) {
if (data.type === 'GirdList') {
this.componentName = 'GirdList'
this.isShowDetail = false
this.params = data.params
}
if (data.type === 'ResidentList') {
this.componentName = 'ResidentList'
this.isShowDetail = false
this.params = data.params
}
if (data.type === 'GirdDetail') {
this.componentName = 'GirdDetail'
this.isShowDetail = true
this.params = data.params
}
if (data.type === 'ResidentDetail') {
this.componentName = 'ResidentDetail'
this.isShowDetail = true
this.params = data.params
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,172 @@
<template>
<ai-detail class="AppDynamicDetail">
<template slot="title">
<ai-title title="详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #right>
<el-button type="primary" v-if="info.status === '0'" @click="isShow = true">订单核销</el-button>
<el-button type="danger" v-if="info.status === '0' || info.status === '1'" @click="cancelOrder">取消订单</el-button>
</template>
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="订单编号" :value="info.serialNumber"></ai-info-item>
<ai-info-item label="订单状态" :value="dict.getLabel('integralSGOStatus', info.status)"></ai-info-item>
<ai-info-item label="兑换人" :value="info.integralUserName"></ai-info-item>
<ai-info-item label="创建时间" :value="info.createTime"></ai-info-item>
<ai-info-item label="备注" :value="info.remarks" isLine></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="操作信息" v-if="info.status === '1'">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="核销人" :value="info.examineUserName"></ai-info-item>
<ai-info-item label="时间" :value="info.examineTime"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="商品信息">
<template #content>
<ai-table
:isShowPagination="false"
:tableData="[info]"
:col-configs="colConfigs"
@getList="() => {}">
<el-table-column
label="商品"
slot="goods"
align="left"
width="250">
<template v-slot="{ row }">
<div class="goods">
<ai-uploader
:disabled="true"
:instance="instance"
:value="[{url: row.goodsPicUrl}]"
:limit="1">
</ai-uploader>
<p>{{ row.goodsTitle }}</p>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
<ai-dialog
title="订单核销"
:visible.sync="isShow"
:destroyOnClose="true"
width="650px"
@onConfirm="onConfirm"
@close="form.verificationCode = '', id = ''">
<el-form style="height: 200px" class="ai-form" ref="form" :model="form" label-width="120px">
<el-form-item label="核销码" style="width: 100%" prop="verificationCode" :rules="[{required: true, message: '请输入核销码', trigger: 'blur'}]">
<el-input v-model="form.verificationCode" placeholder="请输入核销码" size="small"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'GirdDetail',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
info: {},
isShow: false,
id: '',
form: {
verificationCode: ''
},
colConfigs: [
{ prop: 'goodsSerialNumber', label: '商品ID', align: 'left' },
{ slot: 'goods' },
{ prop: 'goodsType', label: '商品类型', align: 'center', format: v => this.dict.getLabel('integralSGType', v) },
{ prop: 'goodsIntegralPrice', label: '兑换所需积分', align: 'center' },
{ prop: 'payMoney', label: '兑换后补差价金额', align: 'center' },
{ prop: 'quantity', label: '数量', align: 'center' },
{ prop: 'usedIntegral', label: '消耗积分', align: 'center' }
]
}
},
created () {
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appintegralsupermarketorder/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.info = {
...res.data
}
}
})
},
cancelOrder () {
this.$confirm('确定取消该订单吗?').then(() => {
this.instance.post(`/app/appintegralsupermarketorder/cancel?id=${this.params.id}`).then(res => {
if (res.code == 0) {
this.$message.success('取消成功!')
this.getInfo(this.params.id)
}
})
})
},
onConfirm () {
this.$refs.form.validate((valid)=> {
if(valid) {
this.instance.post(`/app/appintegralsupermarketorder/examine`, null, {
params: {
...this.form,
id: this.params.id
}
}).then(res => {
if(res.code == 0) {
this.isShow = false
this.getInfo(this.params.id)
this.$message.success('核销成功')
}
})
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'GirdList',
isRefresh: !!isRefresh
})
}
}
}
</script>
<style scoped lang="scss">
.AppDynamicDetail {
.goods {
display: flex;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,223 @@
<template>
<ai-list isTabs class="GoodsList">
<template slot="content">
<ai-search-bar>
<template slot="left">
<ai-select
v-model="search.goodsType"
@change="(search.current = 1), getList()"
placeholder="请选择商品类型"
:selectList="dict.getDict('integralSGType')">
</ai-select>
<ai-select
v-model="search.status"
@change="(search.current = 1), getList()"
placeholder="请选择订单状态"
:selectList="dict.getDict('integralSGOStatus')">
</ai-select>
<el-date-picker
v-model="search.startTime"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择订单开始日期"
@change="search.current = 1, getList()">
</el-date-picker>
<el-date-picker
v-model="search.endTime"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择订单结束日期"
@change="search.current = 1, getList()">
</el-date-picker>
<ai-download
v-if="permissions('app_appintegralsupermarketorder_export')"
:instance="instance"
url="/app/appintegralsupermarketorder/export"
:params="search"
fileName="订单列表"
:disabled="tableData.length == 0">
</ai-download>
</template>
<template slot="right">
<el-input
v-model="search.goodsTitle"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入商品名称、兑换人"
clearable
@clear="search.current = 1, search.goodsTitle = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
style="margin-top: 8px;"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column
label="商品"
slot="goods"
align="left"
width="350">
<template v-slot="{ row }">
<div class="goods">
<ai-uploader
:disabled="true"
:instance="instance"
:value="[{url: row.goodsPicUrl}]"
:limit="1">
</ai-uploader>
<p>{{ row.goodsTitle }}</p>
</div>
</template>
</el-table-column>
<el-table-column label="操作" slot="options" align="center" width="210" fixed="right">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" title="取消订单" v-if="row.status === '0' || row.status === '1'" @click="cancel(row.id)">取消订单</el-button>
<el-button type="text" title="订单核销" v-if="row.status === '0'" @click="id = row.id, isShow = true">订单核销</el-button>
<el-button type="text" title="详情" @click="toDetail(row.id)">详情</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog
title="订单核销"
:visible.sync="isShow"
:destroyOnClose="true"
width="650px"
@onConfirm="onConfirm"
@close="form.verificationCode = '', id = ''">
<el-form style="height: 200px" class="ai-form" ref="form" :model="form" label-width="120px">
<el-form-item label="核销码" style="width: 100%" prop="verificationCode" :rules="[{required: true, message: '请输入核销码', trigger: 'blur'}]">
<el-input v-model="form.verificationCode" placeholder="请输入核销码" size="small"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
export default {
name: 'GirdList',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
search: {
goodsType: '',
goodsTitle: '',
current: 1,
status: '',
size: 10,
startTime: '',
endTime: ''
},
isShow: false,
form: {
verificationCode: ''
},
id: '',
total: 0,
tableData: [],
colConfigs: [
{ prop: 'goodsSerialNumber', label: '商品ID', align: 'left' },
{ slot: 'goods', align: 'center' },
{ prop: 'goodsType', label: '商品类型', align: 'center', format: v => this.dict.getLabel('integralSGType', v) },
{ prop: 'quantity', label: '数量', align: 'center', format: v => `${v}` },
{ prop: 'usedIntegral', label: '消耗积分', align: 'center' },
{ prop: 'payMoney', label: '兑换后再付', align: 'center' },
{ prop: 'integralUserName', label: '兑换人', align: 'center' },
{ prop: 'createTime', width: 150, label: '创建时间', align: 'center' },
{ prop: 'status', label: '状态', align: 'center', format: v => this.dict.getLabel('integralSGOStatus', v) }
]
}
},
computed: {
},
created () {
this.dict.load('integralSGType', 'integralSGOStatus').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketorder/list`, null, {
params: {
...this.search,
type: 0
}
}).then((res) => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
onConfirm () {
this.$refs.form.validate((valid)=> {
if(valid) {
this.instance.post(`/app/appintegralsupermarketorder/examine`, null, {
params: {
...this.form,
id: this.id
}
}).then(res => {
if(res.code == 0) {
this.isShow = false
this.getList()
this.$message.success('核销成功')
}
})
}
})
},
toDetail (id) {
this.$emit('change', {
type: 'GirdDetail',
params: {
id
}
})
},
cancel (id) {
this.$confirm('确定取消该订单吗?').then(() => {
this.instance.post(`/app/appintegralsupermarketorder/cancel?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('取消成功!')
this.getList()
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.GoodsList {
.goods {
display: flex;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,172 @@
<template>
<ai-detail class="AppDynamicDetail">
<template slot="title">
<ai-title title="详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #right>
<el-button type="primary" v-if="info.status === '0'" @click="isShow = true">订单核销</el-button>
<el-button type="danger" v-if="info.status === '0' || info.status === '1'" @click="cancelOrder">取消订单</el-button>
</template>
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="订单编号" :value="info.serialNumber"></ai-info-item>
<ai-info-item label="订单状态" :value="dict.getLabel('integralSGOStatus', info.status)"></ai-info-item>
<ai-info-item label="兑换人" :value="info.integralUserName"></ai-info-item>
<ai-info-item label="创建时间" :value="info.createTime"></ai-info-item>
<ai-info-item label="备注" :value="info.remarks" isLine></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="操作信息" v-if="info.status === '1'">
<template #content>
<ai-wrapper
label-width="120px">
<ai-info-item label="核销人" :value="info.examineUserName"></ai-info-item>
<ai-info-item label="时间" :value="info.examineTime"></ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="商品信息">
<template #content>
<ai-table
:isShowPagination="false"
:tableData="[info]"
:col-configs="colConfigs"
@getList="() => {}">
<el-table-column
label="商品"
slot="goods"
align="left"
width="250">
<template v-slot="{ row }">
<div class="goods">
<ai-uploader
:disabled="true"
:instance="instance"
:value="[{url: row.goodsPicUrl}]"
:limit="1">
</ai-uploader>
<p>{{ row.goodsTitle }}</p>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
<ai-dialog
title="订单核销"
:visible.sync="isShow"
:destroyOnClose="true"
width="650px"
@onConfirm="onConfirm"
@close="form.verificationCode = '', id = ''">
<el-form style="height: 200px" class="ai-form" ref="form" :model="form" label-width="120px">
<el-form-item label="核销码" style="width: 100%" prop="verificationCode" :rules="[{required: true, message: '请输入核销码', trigger: 'blur'}]">
<el-input v-model="form.verificationCode" placeholder="请输入核销码" size="small"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'ResidentDetail',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
info: {},
isShow: false,
id: '',
form: {
verificationCode: ''
},
colConfigs: [
{ prop: 'goodsSerialNumber', label: '商品ID', align: 'left' },
{ slot: 'goods' },
{ prop: 'goodsType', label: '商品类型', align: 'center', format: v => this.dict.getLabel('integralSGType', v) },
{ prop: 'goodsIntegralPrice', label: '兑换所需积分', align: 'center' },
{ prop: 'payMoney', label: '兑换后补差价金额', align: 'center' },
{ prop: 'quantity', label: '数量', align: 'center' },
{ prop: 'usedIntegral', label: '消耗积分', align: 'center' }
]
}
},
created () {
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appintegralsupermarketorder/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.info = {
...res.data
}
}
})
},
cancelOrder () {
this.$confirm('确定取消该订单吗?').then(() => {
this.instance.post(`/app/appintegralsupermarketorder/cancel?id=${this.params.id}`).then(res => {
if (res.code == 0) {
this.$message.success('取消成功!')
this.getInfo(this.params.id)
}
})
})
},
onConfirm () {
this.$refs.form.validate((valid)=> {
if(valid) {
this.instance.post(`/app/appintegralsupermarketorder/examine`, null, {
params: {
...this.form,
id: this.params.id
}
}).then(res => {
if(res.code == 0) {
this.isShow = false
this.getInfo(this.params.id)
this.$message.success('核销成功')
}
})
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'ResidentList',
isRefresh: !!isRefresh
})
}
}
}
</script>
<style scoped lang="scss">
.AppDynamicDetail {
.goods {
display: flex;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,256 @@
<template>
<ai-list isTabs class="GoodsList">
<template slot="content">
<ai-search-bar>
<template slot="left">
<ai-select
v-model="search.goodsType"
@change="(search.current = 1), getList()"
placeholder="请选择商品类型"
:selectList="dict.getDict('integralSGType')">
</ai-select>
<ai-select
v-model="search.status"
@change="(search.current = 1), getList()"
placeholder="请选择订单状态"
:selectList="dict.getDict('integralSGOStatus')">
</ai-select>
<el-date-picker
v-model="search.startTime"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择订单开始日期"
@change="search.current = 1, getList()">
</el-date-picker>
<el-date-picker
v-model="search.endTime"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择订单结束日期"
@change="search.current = 1, getList()">
</el-date-picker>
<ai-download
v-if="permissions('app_appintegralsupermarketorder_export')"
:instance="instance"
url="/app/appintegralsupermarketorder/export"
:params="search"
fileName="订单列表"
:disabled="tableData.length == 0">
</ai-download>
</template>
<template slot="right">
<el-input
v-model="search.goodsTitle"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入商品名称、兑换人"
clearable
@clear="search.current = 1, search.goodsTitle = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
style="margin-top: 8px;"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column
label="商品"
slot="goods"
align="left"
width="350">
<template v-slot="{ row }">
<div class="goods">
<!-- <ai-uploader
:disabled="true"
:instance="instance"
:value="[{url: row.goodsPicUrl}]"
:limit="1">
</ai-uploader> -->
<div class="img-content">
<img :src="row.goodsPicUrl" alt="" v-viewer>
<span class="type" :class="`type`+row.goodsType">{{$dict.getLabel('integralSGType', row.goodsType)}}</span>
</div>
<p>{{ row.goodsTitle }}</p>
</div>
</template>
</el-table-column>
<el-table-column label="操作" slot="options" align="center" width="210" fixed="right">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" title="取消订单" v-if="row.status === '0' || row.status === '1'" @click="cancel(row.id)">取消订单</el-button>
<el-button type="text" title="订单核销" v-if="row.status === '0'" @click="id = row.id, isShow = true">订单核销</el-button>
<el-button type="text" title="详情" @click="toDetail(row.id)">详情</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog
title="订单核销"
:visible.sync="isShow"
:destroyOnClose="true"
width="650px"
@onConfirm="onConfirm"
@close="form.verificationCode = '', id = ''">
<el-form style="height: 200px" class="ai-form" ref="form" :model="form" label-width="120px">
<el-form-item label="核销码" style="width: 100%" prop="verificationCode" :rules="[{required: true, message: '请输入核销码', trigger: 'blur'}]">
<el-input v-model="form.verificationCode" placeholder="请输入核销码" size="small"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import Viewer from 'v-viewer'
import Vue from 'vue'
Vue.use(Viewer)
export default {
name: 'ResidentList',
props: {
instance: Function,
dict: Object,
permissions: Function,
areaId: String,
},
data() {
return {
search: {
goodsType: '',
goodsTitle: '',
current: 1,
status: '',
size: 10,
startTime: '',
endTime: ''
},
isShow: false,
form: {
verificationCode: ''
},
id: '',
total: 0,
tableData: [],
colConfigs: [
{ prop: 'goodsSerialNumber', label: '商品ID', align: 'left' },
{ slot: 'goods', align: 'center' },
{ prop: 'goodsType', label: '商品类型', align: 'center', format: v => this.dict.getLabel('integralSGType', v) },
{ prop: 'quantity', label: '数量', align: 'center', format: v => `${v}` },
{ prop: 'usedIntegral', label: '消耗积分', align: 'center' },
{ prop: 'payMoney', label: '兑换后再付', align: 'center' },
{ prop: 'integralUserName', label: '兑换人', align: 'center' },
{ prop: 'createTime', width: 150, label: '创建时间', align: 'center' },
{ prop: 'status', label: '状态', align: 'center', format: v => this.dict.getLabel('integralSGOStatus', v) }
]
}
},
computed: {
},
created () {
this.dict.load('integralSGType', 'integralSGOStatus').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketorder/list`, null, {
params: {
...this.search,
type: 1,
girdCode: this.areaId
}
}).then((res) => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
onConfirm () {
this.$refs.form.validate((valid)=> {
if(valid) {
this.instance.post(`/app/appintegralsupermarketorder/examine`, null, {
params: {
...this.form,
id: this.id
}
}).then(res => {
if(res.code == 0) {
this.isShow = false
this.getList()
this.$message.success('核销成功')
}
})
}
})
},
toDetail (id) {
this.$emit('change', {
type: 'ResidentDetail',
params: {
id
}
})
},
cancel (id) {
this.$confirm('确定取消该订单吗?').then(() => {
this.instance.post(`/app/appintegralsupermarketorder/cancel?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('取消成功!')
this.getList()
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.GoodsList {
.goods {
display: flex;
align-items: center;
}
.img-content {
position: relative;
margin-right: 8px;
img {
width: 120px;
height: 120px;
cursor: pointer;
}
}
.type {
position: absolute;
top: 0;
left: 0;
width: 120px;
text-align: center;
color: #fff;
z-index: 999;
}
.type1 {
background-color: #E64E39;
}
.type0 {
background-color: #FF6900;
}
}
</style>

View File

@@ -0,0 +1,63 @@
<template>
<div class="AppHelp">
<keep-alive :include="['List']">
<component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import List from './components/List'
import Add from './components/Add'
export default {
name: 'AppIntegratingPublic',
label: '积分公示',
props: {
instance: Function,
dict: Object
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
Add,
List
},
methods: {
onChange (data) {
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">
.AppHelp {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,138 @@
<template>
<ai-detail class="content-add">
<template slot="title">
<ai-title :title="params.id ? '编辑公示' : '添加公示'" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<el-form class="ai-form" :model="form" label-width="120px" ref="form">
<el-form-item label="所属网格" prop="girdId" style="width: 100%;" :rules="[{ required: true, message: '请选择所属网格', trigger: 'change' }]">
<ai-picker :ops="{label: 'girdName'}" :instance="instance" v-model="form.girdId" @pick="e => onUserChange(e)" dialogTitle="选择所属网格" action="/app/appgirdinfo/girdList">
<div class="time-select">
<span class="dept-name" style="color:#999;" v-if="!form.girdName">选择所属网格</span>
<span class="dept-name" v-else>{{ form.girdName }}</span>
<i class="el-icon-arrow-down"></i>
</div>
</ai-picker>
</el-form-item>
<el-form-item label="一类" prop="classOne" style="width: 100%;" :rules="[{required: true, message: '请输入一类', trigger: 'change'}]">
<el-input placeholder="请输入一类" size="small" v-model="form.classOne"></el-input>
</el-form-item>
<el-form-item label="二类" prop="classTwo" style="width: 100%;" :rules="[{required: true, message: '请输入二类', trigger: 'change'}]">
<el-input placeholder="请输入二类" size="small" v-model="form.classTwo"></el-input>
</el-form-item>
<el-form-item label="三类" prop="classThree" style="width: 100%;" :rules="[{required: true, message: '请输入三类', trigger: 'change'}]">
<el-input placeholder="请输入三类" size="small" v-model="form.classThree"></el-input>
</el-form-item>
<el-form-item label="分值" prop="integral" style="width: 100%;" :rules="[{required: true, message: '请输入分值', trigger: 'change'}]">
<el-input-number v-model="form.integral" :min="0"></el-input-number>
</el-form-item>
</el-form>
</template>
</ai-card>
</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,
params: Object
},
data () {
return {
form: {
classOne: '',
classThree: '',
classTwo: '',
girdId: '',
girdName: '',
integral: ''
},
id: ''
}
},
created () {
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appintegralpublicityinfo/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.form = {
...res.data,
girdId: [res.data.girdId]
}
}
})
},
confirm () {
this.$refs.form.validate((valid) => {
if (valid) {
this.isFlag = true
this.instance.post(`/app/appintegralpublicityinfo/addOrUpdate`, {
...this.form,
girdId: this.form.girdId[0]
}).then(res => {
if (res.code == 0) {
this.$message.success('提交成功')
setTimeout(() => {
this.cancel(true)
}, 600)
}
})
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
},
onUserChange (e) {
if (e.length) {
this.form.girdName = e[0].girdName
} else {
this.form.girdId = []
this.form.girdName = ''
}
}
}
}
</script>
<style scoped lang="scss">
.time-select {
padding: 0 16px;
height: 36px;
line-height: 36px;
border: 1px solid #d0d4dc;
border-radius: 4px;
display: flex;
justify-content: space-between;
cursor: pointer;
.el-icon-arrow-down {
line-height: 36px;
}
}
</style>

View File

@@ -0,0 +1,192 @@
<template>
<ai-list class="notice">
<template slot="title">
<ai-title title="积分公示" isShowBottomBorder>
</ai-title>
</template>
<template slot="content">
<ai-search-bar class="search-bar">
<template #left>
<ai-picker
:ops="{label: 'girdName'}"
dialogTitle="选择所属网格"
action="/app/appgirdinfo/girdList"
:instance="instance"
@change="search.current = 1, getList()"
@pick="onGirdChange"
v-model="search.girdId">
<div class="userSelcet">
<span style="color: #606266;" v-if="search.girdId.length">{{ search.girdName }}</span>
<span v-else>请选择网格</span>
<i class="el-icon-arrow-up" v-if="!search.girdId.length"></i>
<i class="el-icon-circle-close" v-if="search.girdId.length" @click.stop="search.girdId = [], search.girdName, search.current = 1, getList()"></i>
</div>
</ai-picker>
<el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd('')">添加</el-button>
<ai-import
:instance="instance"
:dict="dict"
type="appintegralpublicityinfo" name="积分公示"
@success="search.current = 1, getList()">
</ai-import>
</template>
<template #right>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<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="toAdd(row.id)">详情</el-button>
<el-button type="text" @click="remove(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,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
girdId: [],
girdName: ''
},
total: 0,
colConfigs: [
{ prop: 'girdName', label: '所属网格', align: 'left' },
{ prop: 'classOne', label: '一类', align: 'center' },
{ prop: 'classTwo', label: '二类', align: 'center' },
{ prop: 'classThree', label: '三类', align: 'center' },
{ prop: 'integral', label: '分值', align: 'center' },
{ slot: 'options'},
],
tableData: []
}
},
created () {
this.getList()
},
methods: {
onGirdChange (e) {
if (e.length) {
this.search.girdName = e[0].girdName
}
this.search.current = 1
this.getList()
},
getList() {
this.instance.post(`/app/appintegralpublicityinfo/list`, null, {
params: {
...this.search,
girdName: '',
girdId: this.search.girdId.length ? this.search.girdId[0] : ''
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
remove (id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appintegralpublicityinfo/delete?ids=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
toDetail (id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || ''
}
})
},
toAdd(id) {
this.$emit('change', {
type: 'Add',
params: {
id: id || ''
}
})
}
}
}
</script>
<style lang="scss" scoped>
.notice .userSelcet {
display: flex;
align-items: center;
justify-content: space-between;
width: 215px;
height: 32px;
line-height: 32px;
border-radius: 4px;
border: 1px solid #d0d4dc;
overflow: hidden;
cursor: pointer;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: $placeholderColor;
}
i {
display: flex;
position: relative;
align-items: center;
justify-content: center;
width: 30px;
height: 100%;
line-height: 32px;
font-size: 14px;
text-align: center;
color: #d0d4dc;
transform: rotateZ(180deg);
}
.el-icon-circle-close:hover {
opacity: 0.6;
}
span {
flex: 1;
padding: 0 15px;
font-size: 12px;
color: $placeholderColor;
}
}
</style>

View File

@@ -0,0 +1,43 @@
<template>
<div class="AppHelp">
<keep-alive :include="['List']">
<component ref="component" :is="component" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import List from './components/List'
export default {
name: 'AppIntegratingRules',
label: '积分规则',
props: {
instance: Function,
dict: Object
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
List
},
methods: {
}
}
</script>
<style lang="scss">
.AppHelp {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,611 @@
<template>
<ai-list class="gridScoreRules">
<ai-title
slot="title"
title="积分规则"
isShowBottomBorder>
</ai-title>
<template slot="content">
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="dialog = true">&nbsp;添加</el-button>
<el-cascader size="small" v-model="systemRuleIdList" :options="rulesOps" placeholder="请选择事件/类型" clearable :props="rulesProps"
@change="handleTypeSearch" ref="eventTypeSearch"/>
<ai-select v-model="search.status" @change="(page.current = 1), getList()" placeholder="请选择状态" :selectList="$dict.getDict('integralRuleStatus')">
</ai-select>
<ai-picker
:instance="instance"
dialogTitle="选择网格"
:ops="{label: 'girdName', id: 'girdCode'}"
pageTitle="网格"
:action="'/app/appgirdinfo/girdList?idType=1'"
v-model="searchGirdList"
@pick="onGirdChange">
<div class="userSelcet">
<span style="color: #606266;" v-if="search.girdCode">{{ girdName }}</span>
<span v-else>有效地区</span>
<i class="el-icon-arrow-up" v-if="!search.girdCode"></i>
<i class="el-icon-circle-close" v-if="search.girdCode" @click.stop="searchGirdList = [], search.girdCode = '', name = '', search.current = 1, getList()"></i>
</div>
</ai-picker>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="page.total"
:dict="dict"
:current.sync="page.current"
:size.sync="page.size"
@getList="getList()">
<el-table-column slot="integral" label="分值" align="center">
<template slot-scope="{ row }">
<span>{{ row.integral > 0 ? "+" : "" }}{{ row.integral }}</span>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" align="center" fixed="right" width="160">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="changeStatus(row.id, 0)" v-if="row.status == 1">停用</el-button>
<el-button type="text" @click="changeStatus(row.id, 1)" v-else>启用</el-button>
<el-button type="text" @click="toEdit(row)">编辑</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog :title="dialogTitle" :visible.sync="dialog" @onConfirm="onConfirm" @close="closed" width="900px">
<div class="form_div" v-if="dialog">
<el-form ref="DialogForm" :model="form" :rules="formRules" size="small" label-suffix="" label-width="150px">
<el-form-item label="事件类型" prop="systemRuleId">
<el-cascader v-model="form.systemRuleId" ref="cascaderArr" :props="etOps" clearable placeholder="请选择" @change="handleTypeForm" :options="rulesOps"/>
</el-form-item>
<el-form-item label="自定义事件" prop="ruleName" v-if="form.systemRuleId !== '17'">
<ai-select v-model="form.ruleName" @change="change" placeholder="请选择自定义事件" :selectList="dict.getDict('appIntegralApplyEventType')">
</ai-select>
</el-form-item>
<el-form-item label="规则">
<div>常规</div>
</el-form-item>
<el-form-item label="周期范围" prop="scoringCycle">
<ai-select v-model="form.scoringCycle" :selectList="dict.getDict('integralRuleScoringCycle')" />
</el-form-item>
<el-form-item label="奖励次数">
<el-input type="number" placeholder="请输入,周期范围内,不填写表示不限制" v-model.number="form.numberLimit" clearable />
</el-form-item>
<el-form-item label="积分分值" prop="integral">
<el-input placeholder="请输入" v-model="form.integral" clearable/>
</el-form-item>
<el-form-item label="有效地区" prop="girdName" required>
<ai-picker
:instance="instance"
dialogTitle="选择网格"
:ops="{label: 'girdName', id: 'girdCode'}"
pageTitle="网格"
:action="'/app/appgirdinfo/girdList?idType=1'"
v-model="form.girdCode"
@pick="onPick">
<div class="AppAnnounceDetail-select">
<el-input size="small" class="AppAnnounceDetail-select__input" placeholder="请选择..." disabled v-model="form.girdName"></el-input>
<div class="select-left" v-if="girdList.length">
<span v-for="(item, index) in girdList" :key="index">{{ item.girdName }}</span>
</div>
<i v-if="!girdList.length">请选择</i>
<div class="select-right">{{ girdList.length ? '重新选择' : '选择' }}</div>
</div>
</ai-picker>
</el-form-item>
</el-form>
</div>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
export default {
name: "gridScoreRules",
label: "积分规则",
props: {
instance: Function,
dict: Object,
permissions: Function,
},
data() {
var validcode = (rule, value, callback) => {
if (value) {
if (value != 0) {
if (!/^(([1-9]{1}\d*)|(0{1}))(\.\d{1,2})?$/.test(value)) {
callback(new Error('请输入积分分值,只可输入正数、最多保留两位小数'))
} else {
callback();
}
} else {
callback(new Error('请输入有效的积分分值'));
}
} else {
callback(new Error('请输入积分分值'));
}
}
return {
girdList: [],
girdName: '',
search: {
status: "",
systemRuleId: "",
ruleName: "",
girdCode: ''
},
searchGirdList: [],
systemRuleIdList: [],
page: {current: 1, size: 10, total: 0},
colConfigs: [
{
prop: "parentRuleName",
label: "类型",
dict: "integralRuleEventType",
},
{prop: "ruleName", label: "事件", dict: "integralRuleEvent", align: "center"},
{prop: "ruleType", label: "规则", dict: "integralRuleRuleType", align: "center"},
{
prop: "scoringCycle",
label: "周期范围",
align: "center",
dict: "integralRuleScoringCycle",
render: (h, {row}) => {
return h(
"span",
{},
row.numberLimit.length
? $dict.getLabel("integralRuleScoringCycle", row.scoringCycle)
: $dict.getLabel("integralRuleScoringCycle", row.scoringCycle) +
row.numberLimit +
"次"
);
},
},
{slot: "integral", label: "积分分值", align: "center"},
{
prop: "validRangeData",
label: "有效地区",
align: "center",
format: v => JSON.parse(v).girdName
},
{
prop: "status",
label: "状态",
align: "center",
dict: "integralRuleStatus",
},
{slot: "options", label: "操作", align: "center"},
],
tableData: [],
dialog: false,
form: {
ruleType: "0",
systemRuleId: "",
ruleName: "",
scoringCycle: "",
numberLimit: "",
integral: "",
validRangeType: "1",
validRangeData: "",
girdName: '',
girdCode: []
},
formRules: {
systemRuleId: [
{required: true, message: "请选择事件/类型", trigger: "change"},
],
ruleName: [
{required: true, message: "请选择自定义事件", trigger: "change"},
],
scoringCycle: [
{required: true, message: "请选择周期范围", trigger: "change"},
],
integral: [{required: true, validator: validcode, trigger: "blur"},],
girdName: [
{required: true, message: "请选择网格", trigger: "change"},
],
},
rulesOps: [],
rulesProps: {
label: "ruleName",
value: "id",
checkStrictly: true,
},
radio: 0,
treeObj: {
treeList: [],
defaultProps: {
label: "girdName",
value: "id",
children: 'children',
isLeaf: 'leaf'
},
},
treeSelected: {},
girdInfoList: [],
rulueType: "0",
girdNameList: [],
list: [],
isOneAndTen: false,
};
},
created() {
this.form.areaId = this.$store.state.user.info.areaId
this.$dict.load("integralRuleStatus", "integralRuleRuleType", "integralRuleScoringCycle",
"integralRuleEvent", "integralRuleEventType", 'appIntegralApplyEventType').then(() => {
this.getList();
this.getRulesList();
});
},
methods: {
onGirdChange (e) {
if (e.length) {
this.girdName = e[0].girdName
this.search.girdCode = e[0].girdCode
} else {
this.girdName = ''
}
this.searchGirdList = e
this.page.current = 1
this.$nextTick(() => {
this.getList()
})
},
onPick (e) {
if (e.length) {
this.form.girdName = e[0].girdName
} else {
this.form.girdName = ''
this.form.girdCode = []
}
this.girdList = e
},
getList() {
this.instance
.post(`/app/appintegralrule/listByAppletFD`, null, {
params: {
...this.search,
...this.page,
},
})
.then((res) => {
if (res?.data) {
this.tableData = res.data.records;
this.page.total = res.data.total;
}
});
},
change (e) {
},
closed () {
this.form.ruleType = '0'
this.form.systemRuleId = ''
this.form.ruleName = ''
this.form.scoringCycle = ''
this.form.numberLimit = ''
this.form.integral = ''
this.form.validRangeType = '1'
this.form.validRangeData = ''
this.form.girdName = ''
this.form.girdCode = []
this.girdList = []
},
toEdit(row) {
this.form = {...row}
if (this.form?.validRangeData) {
this.form.girdName = JSON.parse(this.form.validRangeData).girdName
this.form.girdCode = [JSON.parse(this.form.validRangeData).girdCode]
this.girdList = [JSON.parse(this.form.validRangeData)]
}
this.$nextTick(() => {
this.dialog = true;
});
},
remove(id) {
this.$confirm("删除后不可恢复,是否要删除该规则?", {
type: "error",
}).then(() => {
this.instance
.post(`/app/appintegralrule/delete?ids=${id}`)
.then((res) => {
if (res.code == 0) {
this.$message.success("删除成功!");
this.getList();
}
});
});
},
changeStatus(id, status) {
let text = status == 1 ? "启用" : "停用";
this.$confirm(`确定${text}该条规则?`).then(() => {
this.instance
.post(`/app/appintegralrule/enableStatus?id=${id}`)
.then((res) => {
if (res.code == 0) {
this.$message.success(`${text}成功!`);
this.getList();
}
});
});
},
onConfirm() {
this.$refs.DialogForm.validate((valid) => {
if (valid) {
let formData = this.$copy(this.form);
// formData.ladderRule = JSON.stringify(formData.ladderRule)
formData.integral = formData.integral || 0;
this.instance
.post(`/app/appintegralrule/addOrUpdate`, {
...formData,
appCodeType: '2',
girdCode: '',
validRangeData: JSON.stringify({
id: this.girdList[0].id,
girdName: this.girdList[0].girdName,
girdCode: this.girdList[0].girdCode
})
})
.then((res) => {
if (res.code == 0) {
this.$message.success(
`${this.isEdit ? "编辑成功" : "添加成功"}`
);
this.dialog = false;
this.getList();
this.closed();
this.girdInfoList = []
this.girdNameList = []
}
});
} else {
return false;
}
});
},
handleTypeSearch(v) {
this.systemRuleIdList = v
this.search.systemRuleId = v?.[v.length - 1];
this.search.ruleName = this.$refs.eventTypeSearch.getCheckedNodes()[0]?.label
this.page.current = 1;
this.$refs.eventTypeSearch.dropDownVisible = false;
this.getList();
},
handleTypeForm(v) {
if (this.dialog) {
if(v[1] == '17') {
this.form.scoringCycle = '0'
this.form.numberLimit = '1'
this.isOneAndTen = true
} else {
this.form.scoringCycle = ''
this.form.numberLimit = ''
this.isOneAndTen = false
}
this.form.systemRuleId = v?.[v.length - 1];
}
},
handleDelete(i) {
this.$confirm("是否要删除该规则?")
.then(() => {
this.form.ladderRule.splice(i, 1);
})
.catch(() => 0);
},
checkIntegral(v) {
return /\.\d{2,}$/.test(v) ? Math.abs(v).toFixed(1) : Math.abs(v);
},
getRulesList() {
this.instance
.post(`/app/appintegralsystemrule/listByAppletFD?current=1&size=3000`)
.then((res) => {
if (res?.data) {
this.rulesOps = this.toTree(res.data.records)
this.rulesOps.push({
ruleName: "积分申请",
id: "积分申请",
});
}
});
},
// 转树形结构
toTree(data) {
let result = [];
if (!Array.isArray(data)) {
return result;
}
let map = {};
data.forEach((item) => {
map[item.id] = item;
});
data.forEach((item) => {
let parent = map[item.parentRuleId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
return result;
}
},
computed: {
isEdit() {
return !!this.form.id;
},
dialogTitle() {
return this.isEdit ? "编辑积分规则" : "添加积分规则";
},
etOps() {
return {
value: "id",
label: "ruleName",
};
},
},
}
</script>
<style lang="scss" scoped>
.gridScoreRules {
height: 100%;
background: #f3f6f9;
.userSelcet {
display: flex;
align-items: center;
justify-content: space-between;
width: 215px;
height: 32px;
line-height: 32px;
border-radius: 4px;
border: 1px solid #d0d4dc;
overflow: hidden;
cursor: pointer;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: $placeholderColor;
}
i {
display: flex;
position: relative;
align-items: center;
justify-content: center;
width: 30px;
height: 100%;
line-height: 32px;
font-size: 14px;
text-align: center;
color: #d0d4dc;
transform: rotateZ(180deg);
}
.el-icon-circle-close:hover {
opacity: 0.6;
}
span {
flex: 1;
padding: 0 15px;
font-size: 12px;
color: $placeholderColor;
}
}
:deep( .ai-list__content--right ){
width: 100%;
}
// :deep( .searchRightZone ){
// display: flex;
// }
.AppAnnounceDetail-select {
display: flex;
align-items: center;
min-height: 32px;
line-height: 1;
background: #F5F5F5;
border-radius: 4px;
border: 1px solid #D0D4DC;
cursor: pointer;
overflow: hidden;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: #26f;
}
& > i {
flex: 1;
height: 100%;
line-height: 32px;
padding: 0 12px;
color: #888888;
font-size: 14px;
font-style: normal;
border-right: 1px solid #D0D4DC;
background: #fff;
}
.AppAnnounceDetail-select__input {
position: absolute;
left: 0;
top: 0;
z-index: -1;
opacity: 0;
height: 100%;
}
.select-right {
height: 100%;
padding: 0 12px;
color: #222222;
font-size: 12px;
cursor: pointer;
transition: all ease 0.3s;
&:hover {
opacity: 0.5;
}
}
.select-left {
display: flex;
flex-wrap: wrap;
flex: 1;
padding: 5px 0 0px 12px;
border-right: 1px solid #D0D4DC;
border-radius: 4px 0 0 4px;
background: #fff;
em {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
color: #222222;
font-size: 12px;
font-style: normal;
}
span {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
padding: 0 8px;
font-size: 12px;
color: #222222;
background: #F3F4F7;
border-radius: 2px;
border: 1px solid #D0D4DC;
}
}
}
:deep( .ai-dialog ){
.el-cascader {
width: 100%;
}
.tableInput {
& > input {
text-align: center;
border: none;
background: transparent;
}
}
}
}
</style>

View File

@@ -0,0 +1,106 @@
<template>
<ai-list v-if="!isShowDetail">
<template slot="title">
<ai-title title="积分超市" :isShowBottomBorder="false">
</ai-title>
</template>
<template slot="tabs">
<el-tabs v-model="currIndex">
<el-tab-pane v-for="(tab,i) in tabs" :key="i" :label="tab.label">
<component :ref="String(i)" v-if="currIndex == i" :is="tab.comp" @change="onChange" lazy :instance="instance" :dict="dict" :permissions="permissions"/>
</el-tab-pane>
</el-tabs>
</template>
</ai-list>
<AddGoods v-else-if="componentName === 'AddGoods'" :params="params" :instance="instance" :dict="dict" @change="onChange"></AddGoods>
<AddStore v-else-if="componentName === 'AddStore'" :params="params" :instance="instance" :dict="dict" @change="onChange"></AddStore>
</template>
<script>
import AddGoods from './components/addGoods'
import AddStore from './components/AddStore'
import StoreList from './components/StoreList'
import GoodsList from './components/GoodsList'
import { mapState } from 'vuex'
export default {
name: 'AppIntegratingSupermarket',
label: '积分超市',
components: {
GoodsList,
StoreList,
AddGoods,
AddStore
},
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
...mapState(['user']),
tabs () {
const tabList = [
{label: '商品信息', name: 'GoodsList', comp: GoodsList, permission: ''},
{label: '店铺管理', name: 'StoreList', comp: StoreList, permission: ''}
].filter(item => {
return true
})
return tabList
}
},
data () {
return {
activeName: 'GoodsList',
currIndex: '0',
componentName: '',
params: {},
isShowDetail: false
}
},
methods: {
onAreaChange () {
if (this.currIndex === '0') {
this.$nextTick(() => {
this.$refs[this.currIndex][0].getList()
})
}
},
onChange (data) {
if (data.type === 'GoodsList') {
this.componentName = 'GoodsList'
this.isShowDetail = false
this.params = data.params
}
if (data.type === 'StoreList') {
this.componentName = 'StoreList'
this.isShowDetail = false
this.params = data.params
}
if (data.type === 'AddGoods') {
this.componentName = 'AddGoods'
this.isShowDetail = true
this.params = data.params
}
if (data.type === 'AddStore') {
this.componentName = 'AddStore'
this.isShowDetail = true
this.params = data.params
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,507 @@
<template>
<ai-detail class="appgoods">
<template slot="title">
<ai-title :title="id ? '编辑店铺' : '添加店铺'" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<el-form class="ai-form" :model="form" label-width="120px" ref="form">
<el-form-item style="width: 100%" label="店铺名称" prop="title" :rules="[{required: true, message: '请输入店铺名称', trigger: 'blur'}]">
<el-input type="input" size="small" v-model="form.title" clearable placeholder="请输入店铺名称" :maxlength="50" show-word-limit></el-input>
</el-form-item>
<el-form-item style="width: 100%" label="店铺类型" prop="type" :rules="[{required: true, message: '请选择店铺类型', trigger: 'change'}]">
<ai-select
v-model="form.type"
placeholder="请选择店铺类型"
:selectList="dict.getDict('integralSSType')">
</ai-select>
</el-form-item>
<el-form-item label="可见范围" label-width="120px" prop="serviceType" :rules="[{required: true, message: '请选择可见范围', trigger: 'change'}]">
<el-radio-group v-model="form.serviceType" @change="form.girdList = [], girdList = [], form.visibleNames = ''">
<el-radio label="0">不限</el-radio>
<el-radio label="1">仅指定网格可见</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选择网格" v-if="form.serviceType === '1'" style="width: 100%;" label-width="120px" prop="visibleNames" :rules="[{ required: true, message: '请选择网格', trigger: 'change' }]">
<ai-picker
:instance="instance"
multiple
dialogTitle="选择网格"
:ops="{label: 'girdName', id: 'girdCode'}"
pageTitle="网格"
:action="'/app/appgirdinfo/girdList?idType=1'"
v-model="form.girdList"
@pick="onPick"
@change="onSelcetChange">
<div class="AppAnnounceDetail-select">
<el-input size="small" class="AppAnnounceDetail-select__input" placeholder="请选择..." disabled v-model="form.visibleNames"></el-input>
<div class="select-left" v-if="form.girdList.length">
<span v-for="(item, index) in girdList" :key="index" v-if="index < 9">{{ item.girdName }}</span>
<em v-if="girdList.length > 9">{{ girdList.length }}</em>
</div>
<i v-if="!form.girdList.length">请选择</i>
<div class="select-right">{{ form.girdList.length ? '重新选择' : '选择' }}</div>
</div>
</ai-picker>
</el-form-item>
</el-form>
</template>
</ai-card>
<ai-card title="商品列表">
<template #right>
<el-button type="primary" icon="iconfont iconAdd" @click="showList">选择商品</el-button>
</template>
<template #content>
<ai-table
:tableData="form.goodsList"
:isShowPagination="false"
:col-configs="chooseColConfigs"
@getList="() => {}">
<el-table-column
label="商品"
slot="goods"
align="left"
width="300">
<template v-slot="{ row }">
<div class="goods">
<ai-uploader
:disabled="true"
:instance="instance"
:value="[{url: row.goods.picUrl}]"
:limit="1">
</ai-uploader>
<p>{{ row.goods.title }}</p>
</div>
</template>
</el-table-column>
<el-table-column
label="兑换所需积分"
slot="integralPrice"
align="center"
width="140">
<template v-slot="{ row }">
<el-input-number style="width: 120px;" :precision="2" size="small" type="input" v-model="row.integralPrice" :min="0" placeholder="请输入"></el-input-number>
</template>
</el-table-column>
<el-table-column
label="兑换后补差价金额"
slot="payMoney"
align="center"
width="150">
<template v-slot="{ row }">
<el-input-number style="width: 120px;" :precision="2" size="small" type="input" v-if="row.goods.type !== '0'" :min="0" v-model="row.payMoney" clearable placeholder="请输入"></el-input-number>
</template>
</el-table-column>
<el-table-column
label="库存"
slot="stock"
align="center"
width="140">
<template v-slot="{ row }">
<el-input-number style="width: 120px;" :precision="0" size="small" type="input" v-model="row.stock" :min="0" clearable placeholder="请输入"></el-input-number>
</template>
</el-table-column>
<el-table-column label="操作" slot="options" align="center" width="180" fixed="right">
<template v-slot="{ row, $index }">
<div class="table-options">
<el-button v-if="row.id" type="text" :title="row.status === '0' ? '上架' : '下架'" @click="changeStatus(row.status, $index)">{{ row.status === '0' ? '上架' : '下架' }}</el-button>
<el-button type="text" title="删除" @click="remove($index)">删除</el-button>
<el-button type="text" title="复制链接" v-if="row.goods.type === '1'" @click="copy(row.goods.jdUrl)">复制链接</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
<ai-dialog
title="选择商品"
:visible.sync="isShow"
:destroyOnClose="true"
@confirm="onConfirm"
width="1080px">
<ai-search-bar>
<template slot="left">
<ai-select
v-model="search.type"
@change="(search.current = 1), getList()"
placeholder="请选择商品类型"
:selectList="dict.getDict('integralSGType')">
</ai-select>
<ai-select
v-model="search.status"
@change="(search.current = 1), getList()"
placeholder="请选择状态"
:selectList="dict.getDict('integralSGStatus')">
</ai-select>
</template>
<template slot="right">
<el-input
v-model="search.title"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入商品名称"
clearable
@clear="search.current = 1, search.title = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
ref="aiTable"
style="margin-top: 8px;"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:current.sync="search.current"
:size.sync="search.size"
@selection-change="handleSelectionChange"
@getList="getList">
<el-table-column
label="商品"
slot="goods"
align="left"
width="300">
<template v-slot="{ row }">
<div class="goods">
<ai-uploader
:disabled="true"
:instance="instance"
:value="[{url: row.picUrl}]"
:limit="1">
</ai-uploader>
<p>{{ row.title }}</p>
</div>
</template>
</el-table-column>
</ai-table>
</ai-dialog>
</template>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">提交</el-button>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'AddStore',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
isShow: false,
form: {
title: '',
type: '',
serviceType: '0',
visibleNames: '',
girdList: [],
goodsList: []
},
girdList: [],
id: '',
search: {
type: '',
title: '',
current: 1,
status: '',
size: 10
},
total: 0,
tableData: [],
chooseColConfigs: [
{ slot: 'goods' },
{ prop: 'goods', label: '商品类型', align: 'center', format: v => this.dict.getLabel('integralSGType', v.type) },
{ slot: 'integralPrice' },
{ slot: 'payMoney' },
{ prop: 'goods', label: '商品链接', align: 'center', format: v => v.jdUrl },
{ slot: 'stock' },
{ prop: 'status', width: 90, label: '状态', align: 'center', format: v => this.dict.getLabel('integralSGStatus', v) || '已上架' }
],
colConfigs: [
{ type: 'selection', label: '', align: 'left' },
{ prop: 'serialNumber', label: '商品ID', align: 'center' },
{ slot: 'goods', align: 'center' },
{ prop: 'type', label: '商品类型', align: 'center', format: v => this.dict.getLabel('integralSGType', v) },
{ prop: 'onlineTime', label: '上架时间', align: 'center' },
{ prop: 'status', width: 90, label: '状态', align: 'center', format: v => this.dict.getLabel('integralSGStatus', v) }
],
chooseList: []
}
},
created () {
this.dict.load('integralSGType', 'integralSGStatus').then(() => {
this.getList()
})
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketgoods/list`, null, {
params: {
...this.search
}
}).then((res) => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
copy (url) {
let oInput = document.createElement('input')
oInput.value = url
document.body.appendChild(oInput)
oInput.select()
document.execCommand('Copy')
this.$message({
message: '已复制',
type: 'success'
})
oInput.remove()
},
changeStatus (status, index) {
this.$confirm(`确定${status === '0' ? '上架' : '下架'}该商品?`).then(() => {
this.$set(this.form.goodsList[index], 'status', status === '0' ? '1' : '0')
this.$message.success(`${status === '0' ? '上架' : '下架'}成功`)
})
},
handleSelectionChange (e) {
this.chooseList = e
},
remove (index) {
this.$confirm('确定删除该商品吗?').then(() => {
this.form.goodsList.splice(index, 1)
this.$message.success('删除成功!')
})
},
showList () {
this.isShow = true
// this.$nextTick(() => {
// if (this.form.goodsList.length) {
// this.form.goodsList.map(v => v.goods).forEach(v => {
// if (this.tableData.filter(e => e.id === v.id).length) {
// this.$refs.aiTable.toggleRowSelection(this.tableData.filter(e => e.id === v.id)[0], true)
// }
// })
// }
// })
},
onConfirm () {
this.isShow = false
let list = this.chooseList.filter(v => {
return this.form.goodsList.map(e => e.goodsId).indexOf(v.id) === -1
}).map(v => {
return {
goods: {
...v
},
goodsId: v.id,
integralPrice: undefined,
payMoney: undefined,
stock: undefined,
status: v.status
}
})
this.form.goodsList = [
...this.form.goodsList ,
...list
]
},
getInfo (id) {
this.instance.post(`/app/appintegralsupermarketshop/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.form = {
...res.data,
visibleNames: '1',
girdList: res.data.serviceType === '1' ? res.data.visibleConfig.map(v => v.visibleId) : []
}
this.girdList = res.data.visibleConfig.map(v => {
return {
girdCode: v.visibleId,
girdName: v.visibleName
}
})
}
})
},
onPick (e) {
this.girdList = e
},
onSelcetChange (e) {
if (e.length) {
this.form.visibleNames = '1'
} else {
this.form.visibleNames = ''
this.form.girdList = []
}
},
confirm () {
this.$refs.form.validate((valid) => {
if (valid) {
if (!this.form.goodsList.length) {
return this.$message.error('请选择商品')
}
for (let i = 0; i < this.form.goodsList.length; i++) {
if (!this.form.goodsList[i].integralPrice) {
return this.$message.error('请输入兑换所需积分')
}
if (!this.form.goodsList[i].payMoney && this.form.goodsList[i].goods.type === '1') {
return this.$message.error('请输入兑换后补差价金额')
}
if (!this.form.goodsList[i].stock) {
return this.$message.error('请输入库存')
}
}
this.instance.post(`/app/appintegralsupermarketshop/addOrUpdate`, {
...this.form,
id: this.params.id || '',
visibleConfig: this.form.serviceType === '1' ? this.girdList.map(v => {
return {
visibleId: v.girdCode,
visibleName: v.girdName
}
}) : []
}).then(res => {
if (res.code == 0) {
this.$message.success('提交成功')
setTimeout(() => {
this.cancel(true)
}, 600)
}
})
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'GoodsList',
isRefresh: !!isRefresh
})
}
}
}
</script>
<style scoped lang="scss">
.appgoods {
.goods {
display: flex;
align-items: center;
width: 250px;
height: 120px;
}
.AppAnnounceDetail-select {
display: flex;
align-items: center;
min-height: 32px;
line-height: 1;
background: #F5F5F5;
border-radius: 4px;
border: 1px solid #D0D4DC;
cursor: pointer;
overflow: hidden;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: #26f;
}
& > i {
flex: 1;
height: 100%;
line-height: 32px;
padding: 0 12px;
color: #888888;
font-size: 14px;
font-style: normal;
border-right: 1px solid #D0D4DC;
background: #fff;
}
.AppAnnounceDetail-select__input {
position: absolute;
left: 0;
top: 0;
z-index: -1;
opacity: 0;
height: 100%;
}
.select-right {
height: 100%;
padding: 0 12px;
color: #222222;
font-size: 12px;
cursor: pointer;
transition: all ease 0.3s;
&:hover {
opacity: 0.5;
}
}
.select-left {
display: flex;
flex-wrap: wrap;
flex: 1;
padding: 5px 0 0px 12px;
border-right: 1px solid #D0D4DC;
border-radius: 4px 0 0 4px;
background: #fff;
em {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
color: #222222;
font-size: 12px;
font-style: normal;
}
span {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
padding: 0 8px;
font-size: 12px;
color: #222222;
background: #F3F4F7;
border-radius: 2px;
border: 1px solid #D0D4DC;
}
}
}
}
</style>

View File

@@ -0,0 +1,214 @@
<template>
<ai-list isTabs class="GoodsList">
<template slot="content">
<ai-search-bar>
<template slot="left">
<el-button type="primary" icon="iconfont iconAdd" @click="toAdd('')">添加</el-button>
<ai-select
v-model="search.type"
@change="(search.current = 1), getList()"
placeholder="请选择商品类型"
:selectList="dict.getDict('integralSGType')">
</ai-select>
<ai-select
v-model="search.status"
@change="(search.current = 1), getList()"
placeholder="请选择类型"
:selectList="dict.getDict('integralSGStatus')">
</ai-select>
</template>
<template slot="right">
<el-input
v-model="search.title"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入商品名称"
clearable
@clear="search.current = 1, search.title = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
style="margin-top: 8px;"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column
label="商品"
slot="goods"
align="left"
width="450">
<template v-slot="{ row }">
<div class="goods">
<!-- <ai-uploader class="upload"
:disabled="true"
:instance="instance"
:value="[{url: row.picUrl}]"
:limit="1">
<span class="type" :class="`type`+row.type">{{$dict.getLabel('integralSGType', row.type)}}</span>
</ai-uploader> -->
<div class="img-content">
<img :src="row.picUrl" alt="" v-viewer>
<span class="type" :class="`type`+row.type">{{$dict.getLabel('integralSGType', row.type)}}</span>
</div>
<p>{{ row.title }}</p>
</div>
</template>
</el-table-column>
<el-table-column label="操作" slot="options" align="center" width="210" fixed="right">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" title="编辑" @click="toAdd(row.id)">编辑</el-button>
<el-button type="text" :title="row.status === '0' ? '上架' : '下架'" @click="changeStatus(row)">{{ row.status === '0' ? '上架' : '下架' }}</el-button>
<el-button type="text" title="删除" @click="remove(row.id)">删除</el-button>
<el-button type="text" title="复制链接" v-if="row.type === '1'" @click="copy(row.jdUrl)">复制链接</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
import Viewer from 'v-viewer'
import Vue from 'vue'
Vue.use(Viewer)
export default {
name: 'GoodsList',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
search: {
type: '',
title: '',
current: 1,
status: '',
size: 10
},
total: 0,
tableData: [],
colConfigs: [
{ prop: 'serialNumber', label: '商品ID', align: 'left' },
{ slot: 'goods', align: 'center' },
{ prop: 'type', label: '商品类型', align: 'center', format: v => this.dict.getLabel('integralSGType', v) },
{ prop: 'onlineTime', label: '上架时间', align: 'center' },
{ prop: 'status', label: '状态', align: 'center', format: v => this.dict.getLabel('integralSGStatus', v) }
]
}
},
computed: {
},
created () {
this.dict.load('integralSGType', 'integralSGStatus').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketgoods/list`, null, {
params: {
...this.search
}
}).then((res) => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
toAdd (id) {
this.$emit('change', {
type: 'AddGoods',
params: {
id: id || ''
}
})
},
copy (url) {
let oInput = document.createElement('input')
oInput.value = url
document.body.appendChild(oInput)
oInput.select()
document.execCommand('Copy')
this.$message({
message: '已复制',
type: 'success'
})
oInput.remove()
},
remove (id) {
this.$confirm('确定删除该商品吗?').then(() => {
this.instance.post(`/app/appintegralsupermarketgoods/delete?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
changeStatus (row) {
this.$confirm(`确定${row.status === '0' ? '上架' : '下架'}该商品?`).then(() => {
this.instance.post(`/app/appintegralsupermarketgoods/online?id=${row.id}`).then(res => {
if (res.code == 0) {
this.$message.success(`${row.status === '0' ? '上架' : '下架'}成功`)
this.getList()
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.GoodsList {
.goods {
display: flex;
align-items: center;
}
.img-content {
position: relative;
margin-right: 8px;
img {
width: 120px;
height: 120px;
cursor: pointer;
}
}
.type {
position: absolute;
top: 0;
left: 0;
width: 120px;
text-align: center;
color: #fff;
z-index: 999;
}
.type1 {
background-color: #E64E39;
}
.type0 {
background-color: #FF6900;
}
}
</style>

View File

@@ -0,0 +1,159 @@
<template>
<ai-list isTabs class="order_management">
<template slot="content">
<ai-search-bar>
<template slot="left">
<el-button type="primary" icon="iconfont iconAdd" @click="toAdd('')">添加</el-button>
<ai-select
v-model="search.type"
@change="(search.current = 1), getList()"
placeholder="请选择店铺类型"
:selectList="dict.getDict('integralSSType')">
</ai-select>
<ai-select
v-model="search.status"
@change="(search.current = 1), getList()"
placeholder="请选择状态"
:selectList="dict.getDict('integralSSStatus')">
</ai-select>
</template>
<template slot="right">
<el-input
v-model="search.title"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入店铺名称"
clearable
@clear="search.current = 1, search.title = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
style="margin-top: 8px;"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column label="网格店铺服务网格" slot="grid" align="center">
<template v-slot="{ row }">
<span>{{ row.type === '0' ? (row.visibleNames ? row.visibleNames : row.serviceType === '0' ? '不限' : '-') : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="居民店铺服务地区" slot="area" align="center">
<template v-slot="{ row }">
<span>{{ row.type === '1' ? (row.visibleNames ? row.visibleNames : row.serviceType === '0' ? '不限' : '-') : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" slot="options" align="center" width="160" fixed="right">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" title="编辑" @click="toAdd(row.id)">编辑</el-button>
<el-button type="text" :title="row.status === '0' ? '启用' : '停用'" @click="changeStatus(row)">{{ row.status === '0' ? '启用' : '停用' }}</el-button>
<el-button type="text" title="删除" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
export default {
name: 'GoodsList',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
search: {
type: '',
title: '',
current: 1,
status: '',
size: 10
},
total: 0,
tableData: [],
shopList: [],
colConfigs: [
{ prop: 'serialNumber', label: '店铺ID', align: 'left' },
{ prop: 'title', label: '店铺名称', align: 'center' },
{ prop: 'type', label: '店铺类型', align: 'center', format: v => this.dict.getLabel('integralSSType', v) },
{ slot: 'grid', label: '网格店铺服务网格', align: 'center' },
{ slot: 'area', label: '居民店铺服务地区', align: 'center' },
{ prop: 'goodsCount', label: '店铺商品数', align: 'center' },
{ prop: 'status', label: '状态', align: 'center', format: v => this.dict.getLabel('integralSSStatus', v) }
]
}
},
computed: {
},
created () {
this.dict.load('integralSSType', 'integralSSStatus', 'integralSSSType').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketshop/list`, null, {
params: {
...this.search
}
}).then((res) => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
toAdd (id) {
this.$emit('change', {
type: 'AddStore',
params: {
id: id || ''
}
})
},
remove (id) {
this.$confirm('确定删除该商品吗?').then(() => {
this.instance.post(`/app/appintegralsupermarketshop/delete?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
changeStatus (row) {
this.$confirm(`确定${row.status === '0' ? '启用' : '停用'}该商铺?`).then(() => {
this.instance.post(`/app/appintegralsupermarketshop/enable?id=${row.id}`).then(res => {
if (res.code == 0) {
this.$message.success(`${row.status === '0' ? '启用' : '停用'}成功`)
this.getList()
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.order_management {
}
</style>

View File

@@ -0,0 +1,220 @@
<template>
<ai-detail class="appgoods">
<template slot="title">
<ai-title :title="id ? '编辑商品' : '添加商品'" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<el-form class="ai-form" :model="form" label-width="120px" ref="form">
<el-form-item style="width: 100%" label="商品名称" prop="title" :rules="[{required: true, message: '请输入商品名称', trigger: 'blur'}]">
<el-input type="input" size="small" v-model="form.title" clearable placeholder="请输入商品名称" :maxlength="50" show-word-limit></el-input>
</el-form-item>
<el-form-item style="width: 100%" label="商品类型" prop="type" :rules="[{required: true, message: '请选择商品类型', trigger: 'change'}]">
<ai-select
v-model="form.type"
placeholder="请选择商品类型"
:selectList="dict.getDict('integralSGType')">
</ai-select>
</el-form-item>
<el-form-item style="width: 100%" label="商品类型说明" prop="typeExplain">
<el-input type="input" size="small" v-model="form.typeExplain" clearable placeholder="请输入商品类型说明" maxlength="50" show-word-limit></el-input>
</el-form-item>
<el-form-item style="width: 100%" label="商品图片" prop="picUrl" :rules="[{required: true, message: '请选择商品图片', trigger: 'change'}]">
<ai-uploader
:instance="instance"
isShowTip
isCrop
:cropOps="{
fixedNumber: [1, 1]
}"
v-model="form.picUrl"
:limit="1">
<template slot="tips">
<p>建议尺寸400*400支持上传jpg/png格式图片最多上传一张单个图片大小不超过10M</p>
</template>
</ai-uploader>
</el-form-item>
<el-form-item label="零售价格" prop="retailPrice" :rules="[{required: true, message: '请输入零售价格', trigger: 'change'}]">
<el-input-number style="width: 200px;" size="small" :precision="2" type="input" v-model="form.retailPrice" clearable placeholder="请输入零售价格" :min="0"></el-input-number>
</el-form-item>
<el-form-item v-if="form.type === '1'" label="商品链接" prop="jdUrl" :rules="[{required: true, message: '请输入商品链接', trigger: 'blur'}]">
<el-input type="input" size="small" v-model="form.jdUrl" clearable placeholder="请输入商品链接"></el-input>
</el-form-item>
<el-form-item style="width: 100%" label="商品描述" prop="description">
<el-input type="textarea" :rows="4" size="small" v-model="form.description" clearable placeholder="请输入商品描述"></el-input>
</el-form-item>
</el-form>
</template>
</ai-card>
</template>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">提交</el-button>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Add',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
form: {
typeExplain: '',
title: '',
description: '',
jdUrl: '',
retailPrice: undefined,
picUrl: []
},
girdList: [],
cropOps: {
width: '800px',
height: '800px'
},
id: ''
}
},
created () {
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appintegralsupermarketgoods/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.form = res.data
this.form.picUrl = [{
url: res.data.picUrl
}]
}
})
},
confirm () {
this.$refs.form.validate((valid) => {
if (valid) {
this.instance.post(`/app/appintegralsupermarketgoods/addOrUpdate`, {
...this.form,
picUrl: this.form.picUrl[0].url,
id: this.params.id || ''
}).then(res => {
if (res.code == 0) {
this.$message.success('提交成功')
setTimeout(() => {
this.cancel(true)
}, 600)
}
})
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'GoodsList',
isRefresh: !!isRefresh
})
}
}
}
</script>
<style scoped lang="scss">
.appgoods {
.AppAnnounceDetail-select {
display: flex;
align-items: center;
min-height: 32px;
line-height: 1;
background: #F5F5F5;
border-radius: 4px;
border: 1px solid #D0D4DC;
cursor: pointer;
overflow: hidden;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: #26f;
}
& > i {
flex: 1;
height: 100%;
line-height: 32px;
padding: 0 12px;
color: #888888;
font-size: 14px;
font-style: normal;
border-right: 1px solid #D0D4DC;
background: #fff;
}
.AppAnnounceDetail-select__input {
position: absolute;
left: 0;
top: 0;
z-index: -1;
opacity: 0;
height: 100%;
}
.select-right {
height: 100%;
padding: 0 12px;
color: #222222;
font-size: 12px;
cursor: pointer;
transition: all ease 0.3s;
&:hover {
opacity: 0.5;
}
}
.select-left {
display: flex;
flex-wrap: wrap;
flex: 1;
padding: 5px 0 0px 12px;
border-right: 1px solid #D0D4DC;
border-radius: 4px 0 0 4px;
background: #fff;
em {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
color: #222222;
font-size: 12px;
font-style: normal;
}
span {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
padding: 0 8px;
font-size: 12px;
color: #222222;
background: #F3F4F7;
border-radius: 2px;
border: 1px solid #D0D4DC;
}
}
}
}
</style>

View File

@@ -0,0 +1,70 @@
<template>
<div class="AppHelp">
<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: 'AppIntegratingTask',
label: '积分任务',
props: {
instance: Function,
dict: Object
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
Add,
List,
Detail
},
methods: {
onChange (data) {
if (data.type === 'Add') {
this.component = 'Add'
this.params = data.params
}
if (data.type === 'Detail') {
this.component = 'Detail'
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">
.AppHelp {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,433 @@
<template>
<ai-detail class="activitiesAdd">
<ai-title slot="title" :title="id? '编辑活动':'创建活动'" isShowBottomBorder isShowBack @onBackClick="cancel(true)"/>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<el-form class="ai-form" ref="form" :model="form" :rules="formRules" size="small" label-width="120px">
<el-form-item label="活动名称" prop="title" style="width: 100%">
<el-input v-model="form.title" placeholder="请输入" show-word-limit maxlength="64"></el-input>
</el-form-item>
<el-form-item label="活动说明" style="width: 100%">
<el-input type="textarea" :rows="5" v-model="form.detail" placeholder="请输入" show-word-limit maxlength="500"></el-input>
</el-form-item>
<el-form-item label="活动图片" prop="files">
<ai-uploader
:instance="instance"
v-model="form.files"
:limit="9">
</ai-uploader>
</el-form-item>
<el-form-item prop="areaId" style="width: 100%;" label="活动地区" :rules="[{required: true, message: '请选择地区', trigger: 'change'}]">
<ai-area-select
clearable
@fullname="v => form.areaName = v"
:disabled-level="$store.state.user.info.areaList.length"
always-show
:instance="instance"
v-model="form.areaId">
</ai-area-select>
</el-form-item>
<el-form-item label="活动地点" style="width: 100%;" prop="address" :rules="[{required: true, message: '请选择活动地点', trigger: 'change'}]">
<el-input v-model="form.address" disabled>
<template slot="append">
<el-button @click="showMap = true">选择位置</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="积分类型" prop="type" style="width: 100%" :rules="[{required: true, message: '请选择积分类型', trigger: 'change'}]">
<el-radio-group v-model="form.type" @change="$refs.form.clearValidate()">
<el-radio label="0">打卡得积分</el-radio>
<el-radio label="1">报名得积分</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="打卡范围" prop="clockRange" style="width: 100%" v-if="form.type === '0'">
<el-input type="number" v-model="form.clockRange" placeholder="请输入" >
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item :label="form.type === '0' ? '进场打卡时间' : '报名时间'" prop="intoTime">
<el-date-picker style="width: 100%" v-model="form.intoTime" type="datetimerange" start-placeholder="开始日期" :picker-options="timeOption"
end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss" ></el-date-picker>
</el-form-item>
<el-form-item v-if="form.type === '0'" label="进场得积分" prop="intoIntegral">
<el-input v-model="form.intoIntegral" type="number" placeholder="请输入" style="width: 100%">
<template slot="append">积分</template>
</el-input>
</el-form-item>
<el-form-item v-if="form.type === '0'" :label="form.type === '0' ? '离场打卡时间' : '离场时间'" prop="exitTime">
<el-date-picker style="width: 100%" v-model="form.exitTime" type="datetimerange" start-placeholder="开始日期" :picker-options="timeOption"
end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item v-if="form.type === '0'" label="离场得积分" prop="exitIntegral">
<el-input v-model="form.exitIntegral" type="number" placeholder="请输入" style="width: 100%">
<template slot="append">积分</template>
</el-input>
</el-form-item>
<el-form-item v-if="form.type === '1'" label="报名得积分" prop="enrollIntegral" :rules="[{required: true, message: '请选择报名得积分', trigger: 'blur'}]">
<el-input v-model="form.enrollIntegral" type="number" placeholder="请输入" style="width: 100%">
<template slot="append">积分</template>
</el-input>
</el-form-item>
</el-form>
<ai-dialog title="地图" :visible.sync="showMap" @opened="initMap" width="800px" class="mapDialog" @onConfirm="selectMap">
<div id="map"></div>
<el-form label-width="80px" style="padding: 10px 20px 0 20px;">
<el-row type="flex" justify="space-between">
<el-form-item label="经度">
<el-input disabled size="small" v-model="placeDetail.lng"></el-input>
</el-form-item>
<el-form-item label="纬度">
<el-input disabled size="small" v-model="placeDetail.lat"></el-input>
</el-form-item>
</el-row>
</el-form>
<el-input id="searchPlaceInput" size="medium" class="searchPlaceInput" clearable v-model="searchPlace" autocomplete="on" @change="placeSearch.search(searchPlace)" placeholder="请输入关键字">
<el-button type="primary" slot="append" @click="placeSearch.search(searchPlace)">搜索</el-button>
</el-input>
<div id="searchPlaceOutput" />
</ai-dialog>
</template>
</ai-card>
</template>
<template #footer>
<el-button class="delete-btn footer-btn" @click="cancel(false)">取消</el-button>
<el-button class="footer-btn" type="primary" @click="confirm()">保存</el-button>
</template>
</ai-detail>
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader'
export default {
props: {
instance: Function,
dict: Object,
params: Object,
},
data() {
var validLocation = (rule, value, callback) => {
if(!this.form.lat || !this.form.lng) {
return callback(new Error('请标绘地理位置'))
} else {
callback()
}
}
return {
form: {
title: '',
detail: '',
lng: '',
lat: '',
type: '0',
areaId: '',
enrollIntegral: '',
files: [],
address: '',
clockRange: '',
intoTime: [],
intoBegintime: '',
intoEndtime: '',
intoIntegral: '',
exitTime: [],
exitBegintime: '',
exitEndtime: '',
exitIntegral: '',
},
formRules: {
title: [{required: true, message: "请输入活动名称", trigger: "blur"}],
location: [{required: true, validator: validLocation, trigger: "blur"}],
clockRange: [{required: true, message: "请输入打卡范围", trigger: "blur"}],
intoTime: [{required: true, message: "请选择进场打卡时间", trigger: "blur"}],
intoIntegral: [{required: true, message: "请输入进场得积分", trigger: "blur"}],
exitTime: [{required: true, message: "请选择离场打卡时间", trigger: "blur"}]
},
id: '',
isEdit: false,
info: {},
mapDetail: null,
map: null,
placeSearch: null,
placeDetail: {
lng: '',
lat: '',
address: ''
},
lng: '',
lat: '',
showMap: false,
searchPlace: '',
timeOption: {
disabledDate(date) {
return date.getTime() < Date.now() - 24 * 60 * 60 * 1000
}
},
}
},
created() {
this.form.areaId = this.$store.state.user.info.areaId
this.$dict.load('tfx_activityStatus')
if(this.params && this.params.id) {
this.id = this.params.id
this.getDetail()
setTimeout(() => {
},500)
}
},
watch: {
'form.intoTime': {
handler(val) {
if(val) {
this.form.intoBegintime = val[0]
this.form.intoEndtime = val[1]
}
}
},
'form.exitTime': {
handler(val) {
if(val) {
this.form.exitBegintime = val[0]
this.form.exitEndtime = val[1]
}
}
}
},
mounted () {
this.$nextTick(() => {
this.instance.post("/app/appdvcpconfig/getCorpLocation").then(res => {
if (res.code == 0) {
this.lng = res.data.lng
this.lat = res.data.lat
}
})
})
},
methods: {
selectMap() {
this.form.lng = this.placeDetail.lng
this.form.lat = this.placeDetail.lat
this.form.address = this.placeDetail.address
this.showMap = false
},
initMap() {
if (this.map) return
AMapLoader.load({
key: '54a02a43d9828a8f9cd4f26fe281e74e',
version: '2.0',
plugins: ['AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.Geocoder'],
}).then((AMap) => {
this.map = new AMap.Map('map', {
resizeEnable: true,
zooms: [6, 20],
center: this.form.lng ? [this.form.lng, this.form.lat] : [this.lng, this.lat],
zoom: 11,
})
this.placeSearch = new AMap.PlaceSearch({ map: this.map })
new AMap.AutoComplete({
input: 'searchPlaceInput',
output: 'searchPlaceOutput',
}).on('select', (e) => {
if (e?.poi) {
this.placeSearch.setCity(e.poi.adcode)
this.movePosition(e.poi.location)
}
})
this.map.on('click', (e) => {
new AMap.Geocoder().getAddress(e.lnglat, (sta, res) => {
if (res?.regeocode) {
this.placeDetail = {
lng: e.lnglat?.lng,
lat: e.lnglat?.lat,
address: res.regeocode.formattedAddress,
}
}
})
this.movePosition(e.lnglat)
})
})
},
movePosition(center) {
if (this.map) {
this.map.clearMap()
this.map.panTo(center)
this.map.add([
new AMap.Marker({
position: center,
clickable: true,
}),
])
this.map.setFitView()
}
},
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
},
getMap(lng,lat,address) {
AMapLoader.load({
key: '54a02a43d9828a8f9cd4f26fe281e74e',
version: '2.0',
plugins: ['AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.Geocoder'],
}).then((AMap) => {
this.mapDetail = new AMap.Map(document.getElementById("mapDetail"), {
resizeEnable: true,
zooms: [6, 20],
zoom: 11,
center:[lng,lat],
})
let marker = new AMap.Marker({
position: new AMap.LngLat(lng,lat),
title: address
})
this.mapDetail.add(marker);
})
},
update() {
this.isEdit = true
this.getDetail()
},
getDetail() {
this.instance.post(`/app/appintegraltask/queryDetailById`,null, {
params: {id:this.id}
}).then((res) => {
if(res?.data) {
this.form = res.data
this.form.intoTime = [res.data.intoBegintime,res.data.intoEndtime]
this.form.exitTime = [res.data.exitBegintime,res.data.exitEndtime]
this.info = res.data
this.placeDetail.lng = res.data.lng
this.placeDetail.lat = res.data.lat
this.getMap(this.info.lng,this.info.lat,this.info.address)
}
})
},
selectCandidate(v) {
this.form.candidateUsers = v
},
selectVote(e) {
this.form.voteUsers = e
},
handleAreaSelect(v) {
this.form.areaName = v?.[0]?.label
},
confirm() {
this.$refs.form.validate((valid) => {
if (valid) {
if(new Date(this.form.intoBegintime).getTime() <= Date.now()){
return this.$message.error('进场打卡开始时间不能小于当前时间')
}
let intoEnd = new Date(this.form.intoEndtime).getTime()
let exitBegin = new Date(this.form.exitBegintime).getTime()
if(exitBegin <= intoEnd) {
return this.$message.error('离场的开始时间不能小于且等于进场的结束时间')
}
this.instance.post(`/app/appintegraltask/addOrUpdate`,{
...this.form
}).then(res => {
if(res.code == 0) {
this.$message.success(this.id ? '编辑成功' : '新增成功')
this.cancel(true)
}
}).catch(err => {
console.log(err);
})
}
})
},
},
}
</script>
<style lang="scss" scope>
.activitiesAdd {
height: 100%;
:deep( .amap-logo ){
display: none!important;
}
:deep( .amap-copyright ){
display: none!important;
}
:deep( .el-date-editor .el-input ){
width: 100%;
}
.tips {
width: 100%;
border: 1px solid #f82;
background-color: #fff3e9;
color: #f82;
padding: 8px 16px;
box-sizing: border-box;
border-radius: 4px;
margin-bottom: 32px;
font-size: 13px;
}
.amap-container {
height: 380px;
}
}
.mapDialog {
.el-dialog__body {
padding: 0;
.ai-dialog__content {
padding: 0;
}
.ai-dialog__content--wrapper {
padding: 0 !important;
position: relative;
}
}
#map {
width: 100%;
height: 420px;
}
.searchPlaceInput {
position: absolute;
width: 250px;
top: 30px;
left: 25px;
}
#searchPlaceOutput {
position: absolute;
width: 250px;
left: 25px;
height: initial;
top: 80px;
background: white;
z-index: 250;
max-height: 300px;
overflow-y: auto;
.auto-item {
text-align: left;
font-size: 14px;
padding: 8px;
box-sizing: border-box;
}
}
}
</style>

View File

@@ -0,0 +1,192 @@
<template>
<ai-detail class="activitiesAdd">
<template slot="title">
<ai-title title="活动详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<ai-wrapper>
<ai-info-item label="活动名称" :value="info.title"></ai-info-item>
<ai-info-item label="创建人" :value="info.createUserName"></ai-info-item>
<ai-info-item label="活动说明" isLine :value="info.detail"></ai-info-item>
<ai-info-item label="活动图片" isLine>
<ai-uploader
disabled
:instance="instance"
:value="info.files">
</ai-uploader>
</ai-info-item>
<ai-info-item label="活动地区" isLine :value="info.areaName"></ai-info-item>
<ai-info-item label="活动地点" :value="info.address"></ai-info-item>
<ai-info-item label="地图位置" isLine><div id="mapDetail"></div></ai-info-item>
<ai-info-item label="积分类型" isLine :value="info.type === '0' ? '打卡得积分' : '报名得积分'"></ai-info-item>
<ai-info-item label="活动状态">
{{ dict.getLabel('fdIntegralTaskStatus',info.status) }}
</ai-info-item>
<ai-info-item v-if="info.type === '0'" label="打卡范围">{{ info.clockRange }}</ai-info-item>
<ai-info-item :label="info.type === '0' ? '进场打卡时间' : '报名时间'">{{ info.intoBegintime }}{{ info.intoEndtime}}</ai-info-item>
<ai-info-item v-if="info.type === '0'" label="进场得积分">{{ info.intoIntegral || 0 }}</ai-info-item>
<ai-info-item v-if="info.type === '0'" :label="info.type === '0' ? '离场打卡时间' : '离场时间'">{{ info.exitBegintime }}{{ info.exitEndtime}}</ai-info-item>
<ai-info-item v-if="info.type === '0'" label="离场得积分">{{ info.exitIntegral || 0 }}</ai-info-item>
<ai-info-item v-if="info.type === '1'" label="报名得积分">{{ info.enrollIntegral || 0 }}</ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="打卡记录" v-if="info.type === '0'">
<template #content>
<ai-wrapper>
<ai-info-item label="参与人数" :value="search1.total"></ai-info-item>
</ai-wrapper>
<ai-table
:tableData="tableData1"
:col-configs="colConfigs1"
:total="search1.total"
:current.sync="search1.current"
:size.sync="search1.size"
@getList="getList1">
</ai-table>
</template>
</ai-card>
<ai-card title="报名记录" v-if="info.type === '1'">
<template #content>
<ai-wrapper>
<ai-info-item label="参与人数" :value="search1.total"></ai-info-item>
</ai-wrapper>
<ai-table
:tableData="tableData1"
:col-configs="colConfigs2"
:total="search1.total"
:current.sync="search1.current"
:size.sync="search1.size"
@getList="getList1">
</ai-table>
</template>
</ai-card>
</template>
</ai-detail>
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader'
export default {
props: {
instance: Function,
dict: Object,
params: Object,
},
data () {
return {
id: '',
info: {},
mapDetail: null,
map: null,
search1: {
total: 0,
current: 1,
size: 10
},
tableData1: [],
colConfigs1: [
{prop: 'realName', label: '姓名', align: 'center' },
{prop: 'phone', label: '联系方式', align: 'center' },
{prop: 'areaName', label: '所属地区', align: 'center' },
{prop: 'intoClockTime', label: '进场打卡时间', align: 'center' },
{prop: 'exitClockTime', label: '离场打卡时间', align: 'center' }
],
colConfigs2: [
{prop: 'realName', label: '姓名', align: 'center' },
{prop: 'phone', label: '联系方式', align: 'center' },
{prop: 'areaName', label: '所属地区', align: 'center' },
{prop: 'enrollClockTime', label: '报名时间', align: 'center' }
]
}
},
created () {
this.id = this.params.id
this.$dict.load('tfx_activityStatus').then(() => {
this.getDetail()
})
this.getList1()
},
methods: {
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
},
getList1 () {
this.instance.post(`/app/appintegraltask/clockList?taskId=${this.params.id}`,null, {
params: {
...this.search1,
}
}).then(res=> {
if(res?.data) {
this.tableData1 = res.data.records
this.search1.total = res.data.total
}
})
},
getMap(lng,lat,address) {
AMapLoader.load({
key: '54a02a43d9828a8f9cd4f26fe281e74e',
version: '2.0',
plugins: ['AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.Geocoder'],
}).then((AMap) => {
this.mapDetail = new AMap.Map(document.getElementById("mapDetail"), {
resizeEnable: true,
zooms: [6, 20],
zoom: 11,
center:[lng,lat],
})
let marker = new AMap.Marker({
position: new AMap.LngLat(lng,lat),
title: address
})
this.mapDetail.add(marker);
})
},
update() {
this.isEdit = true
this.getDetail()
},
getDetail() {
this.instance.post(`/app/appintegraltask/queryDetailById?id=${this.params.id}`).then((res) => {
if(res.data) {
this.info = res.data
this.getMap(this.info.lng, this.info.lat, this.info.address)
}
})
}
}
}
</script>
<style lang="scss" scope>
.activitiesAdd {
height: 100%;
:deep( .amap-logo ){
display: none!important;
}
:deep( .amap-copyright ){
display: none!important;
}
:deep( .el-date-editor .el-input ){
width: 100%;
}
.amap-container {
height: 380px;
}
}
</style>

View File

@@ -0,0 +1,223 @@
<template>
<ai-list class="activitiesList">
<ai-title
slot="title"
title="活动管理"
isShowBottomBorder
isShowArea
:hideLevel="$store.state.user.info.areaList.length - 1"
v-model="search.areaId"
:instance="instance"
@change="search.current = 1, getList()">
</ai-title>
<template #content>
<ai-search-bar bottomBorder>
<template #left>
<el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd('')" >创建活动</el-button>
<el-date-picker
v-model="search.beginDate"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择开始日期"
@change="search.current = 1, getList()">
</el-date-picker>
<el-date-picker
v-model="search.endDate"
type="date"
size="small"
value-format="yyyy-MM-dd"
placeholder="选择结束日期"
@change="search.current = 1, getList()">
</el-date-picker>
<ai-select
v-model="search.status"
clearable
placeholder="活动状态"
:selectList="dict.getDict('fdIntegralTaskStatus')"
@change="search.current = 1, getList()">
</ai-select>
</template>
<template #right>
<el-input
v-model="search.title"
size="small"
placeholder="活动名称/创建人"
clearable
v-throttle="() => {getList()}"
@clear="search.title = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:total="search.total"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList"
:col-configs="colConfigs"
:dict="dict">
<el-table-column slot="qrcode" width="200px" label="二维码" align="center">
<template slot-scope="{ row }">
<div class="qrcode">
<el-button type="text" @click="qrcode(row.qrCode, row.id)">{{ row.qrCode ? '预览' : '生成' }}</el-button>
</div>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" fixed="right" align="center" width="120px">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click.native="toDetail(row.id)">详情</el-button>
<el-button type="text" :disabled="row.status ==2" @click.native="stopBtn(row.id)">结束</el-button>
<!-- <el-button type="text" @click.native="handleDelete(row.id)">删除</el-button> -->
</div>
</template>
</el-table-column>
</ai-table>
<div class="qrCode" v-viewer="{movable: true}" v-show="false">
<img :src="img">
</div>
</template>
</ai-list>
</template>
<script>
export default {
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
total: 0,
title: '',
areaId: '',
status: '',
beginDate: '',
endDate: ''
},
tableData: [],
img: '',
isLoading: false,
}
},
created () {
this.search.areaId = this.$store.state.user.info.areaId
this.$dict.load('fdIntegralTaskStatus').then(()=> {
this.getList()
})
},
computed: {
colConfigs() {
return [
{ prop: "title", label: "活动名称", align: "left" },
{ prop: "areaName", label: "活动地区", align: "center" },
{ prop: "createUserName", label: "创建人", align: "center" },
{
prop: "intoBegintime",
label: "开始结束时间",
align: "center",
width: "400px",
render: (h, {row}) => h('p',
{
textAlign: 'center'
},
row.exitEndtime ? `${row.intoBegintime}${row.exitEndtime}` : row.intoBegintime)
},
{ prop: "status", label: "活动状态", align: "center", dict:"fdIntegralTaskStatus" },
{ slot: "qrcode" },
{ slot: "options", },
]
}
},
methods: {
getList() {
this.instance.post(`/app/appintegraltask/list`,null, {
params: {
...this.search,
}
}).then(res=> {
if(res?.data) {
this.tableData = res.data.records
this.search.total = res.data.total
}
})
},
toDetail (id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || '',
}
})
},
toAdd(id) {
this.$emit('change', {
type: 'Add',
params: {
id: id || '',
}
})
},
qrcode (qrcode, id) {
if (!qrcode) {
this.isLoading = true
this.instance.post(`/app/appintegraltask/generateQrCode?id=${id}&width=400&height=400`).then(res => {
if (res.code == 0) {
this.$message.success('二维码生成成功!')
this.getList()
}
this.isLoading = false
})
} else {
this.img = qrcode
this.$nextTick(() => {
setTimeout(() => {
const viewer = this.$el.querySelector('.qrCode').$viewer
viewer.view()
}, 600)
})
}
},
handleDelete (id) {
this.$confirm('确定删除该活动?').then(() => {
this.instance.post(`/app/appintegraltask/delete?ids=${id}`).then(res=>{
if(res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
stopBtn (id) {
this.$confirm('确定要结束该活动吗?').then(() => {
this.instance.post(`/app/appintegraltask/stop?id=${id}`).then(res=>{
if(res.code == 0) {
this.$message.success('结束成功!')
this.getList()
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.activitiesList {
height: 100%;
}
</style>

View File

@@ -0,0 +1,202 @@
<template>
<div class="AppPageSet">
<ai-list class="list">
<ai-title slot="title" title="引导页设置" isShowBottomBorder :instance="instance"></ai-title>
<template slot="content">
<ai-search-bar bottomBorder>
<template #left>
<el-button type="primary" @click="isShow = true" icon="iconfont iconAdd">添加</el-button>
</template>
<template #right>
<span class="text">是否开启引导页</span> <el-switch v-model="isStart" @change="changeStatus" active-value="1" inactive-value="0"></el-switch>
</template>
</ai-search-bar>
<div class="content">
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 16px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column
slot="index"
type="index"
width="100px"
label="序号"
align="center">
</el-table-column>
<el-table-column slot="urlList" label="缩略图" align="center">
<template slot-scope="{ row }">
<ai-uploader
:instance="instance"
disabled
v-model="row.urlList"
:limit="1">
</ai-uploader>
</template>
</el-table-column>
<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="edit(row)">编辑</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</div>
<ai-dialog
:visible.sync="isShow"
width="890px"
:title="form.id ? '编辑引导页' : '添加引导页'"
@close="onClose"
@onConfirm="onConfirm">
<el-form ref="form" class="ai-form" :model="form" label-width="110px" label-position="right">
<el-form-item label="缩略图" style="width: 100%;" prop="urlList" :rules="[{required: true, message: '请上传缩略图', trigger: 'blur'}]">
<ai-uploader v-if="isShow"
:instance="instance"
isShowTip
v-model="form.urlList"
:limit="1">
</ai-uploader>
</el-form-item>
<el-form-item label="引导页名称" style="width: 100%" prop="title" :rules="[{required: true, message: '请输入引导页名称', trigger: 'blur'}]">
<el-input v-model="form.title" size="small" placeholder="请输入引导页名称" maxlength="20"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-list>
</div>
</template>
<script>
export default {
name: 'AppPageSet',
label: '引导页设置',
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
},
colConfigs: [
{ slot: 'index', label: '序号', align: 'center'},
{ slot: 'urlList' },
{ prop: 'title', label: '引导页名称', align: 'center' },
{ prop: 'createTime', label: '创建时间', width: '200', align: 'center' },
{ prop: 'createUserName', label: '创建人', width: '200', align: 'center' },
{ slot: 'option'}
],
tableData: [],
total: 0,
isStart: '',
isFlag: false,
isShow: false,
form: {
urlList: [],
picUrl: '',
title: ''
}
}
},
created () {
this.getList()
this.getStatus()
},
methods: {
getList () {
this.instance.post(`/app/appwechatguidepage/list`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
res.data.records.map((item) => {
item.urlList = [{url: item.picUrl}]
})
this.tableData = res.data.records
this.total = res.data.total
}
})
},
remove (id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appwechatguidepage/delete?ids=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
edit (e) {
this.form = {...e}
this.form.urlList = [{
url: e.picUrl
}]
this.isShow = true
},
onClose () {
this.form.id = ''
this.form.urlList = null
this.form.title = ''
this.isShow = false
},
onConfirm () {
this.$refs.form.validate((valid) => {
if (valid) {
this.instance.post(`/app/appwechatguidepage/addOrUpdate`, {
...this.form,
picUrl: this.form.urlList[0].url
}).then(res => {
if (res.code == 0) {
this.$message.success('提交成功!')
this.onClose()
this.getList()
}
})
}
})
},
getStatus() {
this.instance.post(`/app/appwechatguidepage/enableStatus`).then(res => {
if (res.code == 0) {
this.isStart = res.data
this.isFlag = false
}
})
},
changeStatus() {
if(this.isFlag == true) return
this.isFlag = true
this.instance.post(`/app/appwechatguidepage/enable`).then(res => {
if (res.code == 0) {
this.$message.success(this.isStart == 1 ? '引导页已开启' : '引导页已关闭')
this.getStatus()
}
})
}
}
}
</script>
<style lang="scss">
.AppPageSet {
.text {
color: #666;
font-size: 14px;
margin-right: 8px;
}
}
</style>

View File

@@ -0,0 +1,69 @@
<template>
<div class="AppHelp">
<keep-alive :include="['List']">
<component ref="component" :permissions="permissions" :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: 'AppResidentInfo',
label: '居民信息',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
Add,
List,
Detail
},
methods: {
onChange (data) {
if (data.type === 'Add') {
this.component = 'Add'
this.params = data.params
}
if (data.type === 'Detail') {
this.component = 'Detail'
this.params = data.params
}
if (data.type === 'List') {
this.component = 'List'
this.params = data.params
this.$nextTick(() => {
this.$refs.component.getList()
})
}
}
}
}
</script>
<style lang="scss">
.AppHelp {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,717 @@
<template>
<ai-detail class="residentDetail">
<ai-title slot="title" :title="pageTitle" isShowBack @onBackClick="back" isShowBottomBorder>
<template v-if="params.type === 'Detail'" #rightBtn>
<el-button icon="iconfont iconDelete" @click="handleDelete()">
删除人员
</el-button>
</template>
</ai-title>
<template #content>
<el-form class="content-right" :model="baseInfo" ref="ruleForm" :rules="rules" label-width="130px"
label-position="right" size="small">
<template v-if="params.type === 'Detail'">
<ai-edit-card slot="" :title="baseInfo.name||'基本信息'" :show-btn="permissions('app_appresident_edit')"
@save="saveFrom" @cancel="getDetail(baseInfo.id)">
<template #edit>
<el-row type="flex">
<div class="fill">
<el-form-item label="姓名:" prop="name">
<el-input
v-model="baseInfo.name"
autocomplete="off"
placeholder="请输入姓名"
maxlength="20"
:disabled="isEdit"
show-word-limit/>
</el-form-item>
<el-form-item label="身份证号:" prop="idNumber">
<el-input v-model="baseInfo.idNumber" placeholder="请输入身份证号" :maxlength="18" show-word-limit
@change="idChange" :disabled="isEdit"/>
</el-form-item>
<el-form-item label="性别:" prop="sex">
<ai-select v-model="baseInfo.sex" :selectList="dict.getDict('sex')" :disabled="isEdit"/>
</el-form-item>
</div>
<el-form-item label="个人照片:" prop="photo" class="fill">
<ai-avatar :instance="instance" v-model="baseInfo.photo"/>
</el-form-item>
</el-row>
<el-row type="flex">
<div class="fill">
<el-form-item label="出生日期:">
<el-date-picker
disabled
:editable="false"
value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd"
v-model="baseInfo.birthday"
type="date"
placeholder="选择日期"
/>
</el-form-item>
<el-form-item label="文化程度:" prop="education">
<ai-select v-model="baseInfo.education" :selectList="dict.getDict('education')"/>
</el-form-item>
<el-form-item label="政治面貌:" prop="politicsStatus">
<ai-select v-model="baseInfo.politicsStatus" :selectList="dict.getDict('politicsStatus')"/>
</el-form-item>
<el-form-item label="职业:" prop="job">
<ai-select v-model="baseInfo.job" :selectList="dict.getDict('job')"/>
</el-form-item>
<el-form-item label="宗教信仰:" prop="faithType">
<ai-select v-model="baseInfo.faithType" :selectList="dict.getDict('faithType')"/>
</el-form-item>
</div>
<div class="fill">
<el-form-item label="年龄:" prop="age">
<el-input
disabled
v-model="baseInfo.age"
autocomplete="off"
size="small"
placeholder="请输入年龄"
type="number"
@mousewheel.native.prevent
/>
<!-- <p v-else>{{baseInfo.age}}</p> -->
</el-form-item>
<el-form-item label="民族:" prop="nation">
<ai-select v-model="baseInfo.nation" :selectList="dict.getDict('nation')"/>
</el-form-item>
<el-form-item label="婚姻状况:" prop="maritalStatus">
<ai-select v-model="baseInfo.maritalStatus" :selectList="dict.getDict('maritalStatus')"/>
</el-form-item>
<el-form-item label="兵役状况:" prop="militaryStatus">
<ai-select v-model="baseInfo.militaryStatus" :selectList="dict.getDict('militaryStatus')"/>
</el-form-item>
</div>
</el-row>
<el-form-item label="籍贯:" prop=" birthplaceAreaId">
<ai-area-select
clearable
always-show
:instance="instance"
v-model="baseInfo.birthplaceAreaId"
:areaLevel="3"
/>
</el-form-item>
</template>
<template>
<ai-card title="基本信息">
<ai-wrapper class="fill" slot="content">
<ai-info-item label="性别" :value="dict.getLabel('sex', baseInfo.sex)"/>
<ai-info-item label="身份证号">
<ai-id mode="show" v-model="baseInfo.idNumber" right-btn class="line-center"/>
</ai-info-item>
<ai-info-item label="出生日期" :value="baseInfo.birthday"/>
<ai-info-item label="年龄" :value="baseInfo.age"/>
<ai-info-item label="籍贯" :value="baseInfo.birthplaceAreaName"/>
<ai-info-item label="民族" :value="dict.getLabel('nation', baseInfo.nation)"/>
<ai-info-item label="文化程度" :value="dict.getLabel('education', baseInfo.education)"/>
<ai-info-item label="婚姻状况" :value="dict.getLabel('maritalStatus', baseInfo.maritalStatus)"/>
<ai-info-item label="政治面貌" :value="dict.getLabel('politicsStatus', baseInfo.politicsStatus)"/>
<ai-info-item label="兵役状况" :value="dict.getLabel('militaryStatus', baseInfo.militaryStatus)"/>
<ai-info-item label="宗教信仰" :value="dict.getLabel('faithType', baseInfo.faithType)"/>
<ai-info-item label="职业" :value="dict.getLabel('job', baseInfo.job)"/>
</ai-wrapper>
<ai-avatar v-model="baseInfo.photo" :editable="false"/>
</ai-card>
</template>
</ai-edit-card>
<ai-edit-card title="联络信息"
@save="saveFrom" @cancel="getDetail(baseInfo.id)">
<template #edit>
<el-form-item label="联系方式:" prop="phone">
<el-input v-model="baseInfo.phone" size="small" placeholder="请输入联系方式" :maxlength="11"
show-word-limit/>
</el-form-item>
<el-form-item label="现住址:" prop="currentAreaId">
<ai-area-select clearable always-show :instance="instance" :disabled-level="disabledLevel"
v-model="baseInfo.currentAreaId" :valueLevel="4"/>
<el-form-item>
<el-input v-model="baseInfo.currentAddress" placeholder="详细地址" maxlength="30" show-word-limit
clearable/>
</el-form-item>
</el-form-item>
</template>
<template>
<ai-card title="联络信息" type="flex">
<ai-wrapper slot="content">
<ai-info-item label="联系方式" :value="baseInfo.phone"/>
<ai-info-item label="现住址" isLine
:value="[baseInfo.currentAreaName, baseInfo.currentAddress].join('')"/>
</ai-wrapper>
</ai-card>
</template>
</ai-edit-card>
<ai-edit-card title="户籍信息"
@save="saveFrom" @cancel="getDetail(baseInfo.id)">
<template #edit>
<el-row type="flex">
<div class="fill">
<el-form-item label="是否户主:" prop="householdName">
<ai-select v-model="baseInfo.householdName" :selectList="dict.getDict('householdName')"
@change="householdRelationChange"/>
</el-form-item>
<el-form-item label="与户主关系:" prop="householdRelation" v-if="baseInfo.householdName==0">
<ai-select v-model="baseInfo.householdRelation"
:selectList="dict.getDict('householdRelation')"/>
</el-form-item>
</div>
<div class="fill">
<el-form-item label="户主身份证号:" prop="householdIdNumber" v-if="baseInfo.householdName==0">
<el-input v-model="baseInfo.householdIdNumber" placeholder="请输入户主身份证号" :maxlength="18"
clearable/>
</el-form-item>
</div>
</el-row>
<el-form-item label="户籍地:" prop="householdAreaId">
<ai-area-select clearable always-show :instance="instance" v-model="baseInfo.householdAreaId"/>
<el-form-item v-if="baseInfo.householdAreaId">
<el-input v-model="baseInfo.householdAddress" placeholder="详细地址" maxlength="30" show-word-limit
clearable/>
</el-form-item>
</el-form-item>
</template>
<template>
<ai-card title="户籍信息">
<ai-wrapper slot="content">
<ai-info-item label="是否户主" :value="dict.getLabel('householdName', baseInfo.householdName)"/>
<template v-if="baseInfo.householdName==0">
<ai-info-item label="与户主关系"
:value="dict.getLabel('householdRelation', baseInfo.householdRelation)"/>
<ai-info-item label="户主身份证号" :value="baseInfo.householdIdNumber"/>
</template>
<ai-info-item label="户籍地" isLine
:value="[baseInfo.householdAreaName, baseInfo.householdAddress].join('')"/>
</ai-wrapper>
</ai-card>
</template>
</ai-edit-card>
</template>
<template v-else>
<ai-card title="基本信息">
<template #content>
<el-row type="flex">
<div class="fill">
<el-form-item label="姓名:" prop="name">
<el-input
v-model="baseInfo.name"
autocomplete="off"
size="small"
placeholder="请输入姓名"
maxlength="20"
show-word-limit/>
</el-form-item>
<el-form-item label="身份证号:" prop="idNumber">
<el-input v-model="baseInfo.idNumber" placeholder="请输入身份证号" :maxlength="18" show-word-limit />
</el-form-item>
<el-form-item label="性别:" prop="sex">
<ai-select v-model="baseInfo.sex" :selectList="dict.getDict('sex')"/>
</el-form-item>
</div>
<el-form-item label="个人照片:" prop="photo" class="fill">
<ai-avatar :instance="instance" v-model="baseInfo.photo"/>
</el-form-item>
</el-row>
<el-row type="flex">
<div class="fill">
<el-form-item label="出生日期:">
<el-date-picker
value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd"
v-model="baseInfo.birthday"
type="date"
placeholder="选择日期"
/>
</el-form-item>
<el-form-item label="文化程度:" prop="education">
<ai-select v-model="baseInfo.education" :selectList="dict.getDict('education')"/>
</el-form-item>
<el-form-item label="政治面貌:" prop="politicsStatus">
<ai-select v-model="baseInfo.politicsStatus" :selectList="dict.getDict('politicsStatus')"/>
</el-form-item>
<el-form-item label="职业:" prop="job">
<ai-select v-model="baseInfo.job" :selectList="dict.getDict('job')"/>
</el-form-item>
<el-form-item label="宗教信仰:" prop="faithType">
<ai-select v-model="baseInfo.faithType" :selectList="dict.getDict('faithType')"/>
</el-form-item>
</div>
<div class="fill">
<el-form-item label="年龄:" prop="age">
<el-input
v-model="baseInfo.age"
size="small"
placeholder="请输入年龄"
type="number"
/>
<!-- <p v-else>{{baseInfo.age}}</p> -->
</el-form-item>
<el-form-item label="民族:" prop="nation">
<ai-select v-model="baseInfo.nation" :selectList="dict.getDict('nation')"/>
</el-form-item>
<el-form-item label="婚姻状况:" prop="maritalStatus">
<ai-select v-model="baseInfo.maritalStatus" :selectList="dict.getDict('maritalStatus')"/>
</el-form-item>
<el-form-item label="兵役状况:" prop="militaryStatus">
<ai-select v-model="baseInfo.militaryStatus" :selectList="dict.getDict('militaryStatus')"/>
</el-form-item>
</div>
</el-row>
<el-form-item label="籍贯:" prop=" birthplaceAreaId">
<ai-area-select
clearable
always-show
:instance="instance"
v-model="baseInfo.birthplaceAreaId"
:areaLevel="3"
/>
</el-form-item>
</template>
</ai-card>
<ai-card title="联络信息">
<template #content>
<el-form-item label="联系方式:" prop="phone">
<el-input v-model="baseInfo.phone" size="small" placeholder="请输入联系方式" :maxlength="11"
show-word-limit/>
</el-form-item>
<el-form-item label="现住址:" prop="currentAreaId">
<ai-area-select clearable always-show :instance="instance" :disabled-level="disabledLevel"
v-model="baseInfo.currentAreaId" :valueLevel="4"/>
<el-form-item>
<el-input v-model="baseInfo.currentAddress" placeholder="详细地址" maxlength="30" show-word-limit
clearable/>
</el-form-item>
</el-form-item>
<el-form-item label="所属网格" prop="girdName" required>
<ai-picker
:instance="instance"
dialogTitle="选择所属网格"
:ops="{label: 'girdName', id: 'id'}"
pageTitle="所属网格"
:action="'/app/appgirdinfo/girdList'"
v-model="baseInfo.girdId"
@pick="onPick">
<div class="AppAnnounceDetail-select">
<el-input size="small" class="AppAnnounceDetail-select__input" placeholder="请选择..." disabled v-model="baseInfo.girdName"></el-input>
<div class="select-left" v-if="girdList.length">
<span v-for="(item, index) in girdList" :key="index">{{ item.girdName }}</span>
</div>
<i v-if="!girdList.length">请选择网格</i>
<div class="select-right">{{ girdList.length ? '重新选择' : '选择网格' }}</div>
</div>
</ai-picker>
</el-form-item>
</template>
</ai-card>
<ai-card title="户籍信息">
<template #content>
<el-row type="flex">
<div class="fill">
<el-form-item label="是否户主:" prop="isHousehold">
<ai-select v-model="baseInfo.isHousehold" :selectList="dict.getDict('householdName')"
@change="householdRelationChange"/>
</el-form-item>
<el-form-item label="与户主关系:" prop="householdRelation" v-if="baseInfo.isHousehold==0">
<ai-select v-model="baseInfo.householdRelation" :selectList="dict.getDict('householdRelation')"/>
</el-form-item>
</div>
<div class="fill">
<el-form-item label="户主身份证号:" prop="householdIdNumber" v-if="baseInfo.isHousehold==0">
<el-input v-model="baseInfo.householdIdNumber" placeholder="请输入户主身份证号" :maxlength="18"
clearable/>
</el-form-item>
</div>
</el-row>
<el-form-item label="户籍地:" prop="householdAreaId">
<ai-area-select clearable always-show :instance="instance" v-model="baseInfo.householdAreaId"/>
<el-form-item v-if="baseInfo.householdAreaId">
<el-input v-model="baseInfo.householdAddress" placeholder="详细地址" maxlength="30" show-word-limit
clearable/>
</el-form-item>
</el-form-item>
</template>
</ai-card>
</template>
</el-form>
</template>
<template v-if="params.type !== 'Detail'" #footer>
<el-button @click="back">取消</el-button>
<el-button type="primary" @click="saveFrom()">保存</el-button>
</template>
</ai-detail>
</template>
<script>
import {mapState} from "vuex";
import {ID} from "dui/lib/js/utils";
export default {
name: "Add",
props: {
instance: Function,
dict: Object,
permissions: Function,
params: Object,
name: {default: ""}
},
data() {
let IdNumberPass = (rule, value, callback) => {
if (value) {
if (ID.check(value)) {
callback();
} else {
callback(new Error("身份证号格式错误"));
}
} else {
callback(new Error("请输入身份证号"));
}
};
return {
buildingCascader: true,
houseCascader: true,
navId: 0,
girdList: [],
baseInfo: {
registerStatus: "",
tips: [],
age: "",
girdName: '',
girdId: [],
birthplaceAreaId: "",
currentAddress: "",
currentAreaId: "",
education: "",
faithType: "",
fileStatus: "",
householdAddress: "",
householdAreaId: "",
householdIdNumber: "",
isHousehold: "",
householdRelation: "",
id: "",
idNumber: "",
job: "",
logoutDescription: "",
logoutReason: "",
logoutTime: "",
logoutUserId: "",
maritalStatus: "",
militaryStatus: "",
name: "",
nation: "",
phone: "",
photo: "",
politicsStatus: "",
sex: "",
tsrqInfos: []
},
family: [],
familyInfo: {},
writeInfo: {
id: "",
logoutReason: "",
logoutDescription: "",
fileStatus: "1"
},
rules: {
name: [{required: true, message: "请输入姓名", trigger: "blur"}],
idNumber: [
{required: true, message: "请输入身份证号", trigger: "blur"}
],
sex: [{required: true, message: "请选择性别", trigger: "change"}],
currentAreaId: [{message: "请选择现住址", required: true, trigger: "change"},
{
validator: (r, v, cb) => {
if (/.+0{3}$/.test(v)) {
cb("现住址必须选到村级")
} else cb()
}, trigger: "blur"
}],
currentAddress: [{message: "请选择现住址详细地址", required: true}],
householdAreaId: [
{
validator: (r, v, cb) => {
if (/.+0{3}$/.test(v) && v) {
cb("户籍地必须选到村级")
} else cb()
}, trigger: "blur"
}
],
phone: [
{required: true, message: "请输入联系方式", trigger: "blur"}
],
girdName: [
{required: true, message: "请选择网格", trigger: "change"}
],
householdName: [
{required: true, message: "请选择是否户主", trigger: "change"}
],
householdRelation: [
{required: true, message: "请选择与户主关系", trigger: "change"}
],
householdIdNumber: [
{required: true, validator: IdNumberPass, trigger: "blur"}
],
householdAddress: [
{required: true, message: "请选择户籍地详细地址", trigger: "blur"}
]
},
imgUrl: "",
fileList: [],
disabledLevel: 0,
currentTab: "0"
};
},
computed: {
...mapState(["user"]),
isEdit() {
return this.params.type === 'Edit'
},
pageTitle() {
return {
Edit: '编辑居民信息',
Add: '添加居民信息',
Detail: '居民信息详情'
}[this.params.type]
},
hasSpecial() {
//是否有特殊人员信息
return this.baseInfo.tsrqInfos?.length > 0
},
colConfigs() {
return [
{
label: "与户主关系", align: 'center',
render: (h, {row}) => h('p', this.dict.getLabel('householdRelation', row.householdRelation))
},
{label: "姓名", prop: "name"},
{label: "性别", prop: "sex", dict: "sex", align: 'center'},
{label: "年龄", prop: "age", align: 'center'},
{label: "身份证号", render: (h, {row}) => h('p', ID.hideId(row.idNumber))},
{slot: "options"}
]
}
},
methods: {
back() {
this.$emit('change', {
type: 'List'
})
},
onPick (e) {
if (e.length) {
this.baseInfo.girdName = e[0].girdName
} else {
this.baseInfo.girdName = ''
this.baseInfo.girdId = []
}
this.girdList = e
},
householdRelationChange() {
this.baseInfo.householdIdNumber = "";
this.baseInfo.householdRelation = "";
},
saveFrom(cb) {
this.$refs.ruleForm.validate(v => {
if (v) {
this.saveFromFn().then(() => {
if (cb) {
cb()
this.getDetail()
} else {
this.back()
}
});
}
});
},
familyInit() {
Object.keys(this.writeInfo).forEach(e => {
this.writeInfo[e] = "";
});
this.$refs.writeInfo.resetFields();
},
saveFromFn() {
let {currentHouseList, householdHouseList, tips} = this.baseInfo
return this.instance.post(`/app/appresidentapplet/addOrUpdate`, {
residentType: this.$route.query.type,
...this.baseInfo,
currentHouseList: currentHouseList?.join("|"),
householdHouseList: householdHouseList?.join("|"),
tips: tips?.join("|"),
girdId: this.baseInfo.girdId[0]
}).then(res => {
if (res?.code == 0) {
this.$message.success("保存成功");
}
})
},
getDetail() {
this.instance.post(`/app/appresidentapplet/queryDetailById?id=${this.params.id}`).then(res => {
if (res?.data) {
this.baseInfo = {
...res.data,
girdId: [res.data.girdId]
};
this.girdList = [{
id: res.data.girdId,
girdName: res.data.girdName
}]
}
});
},
handleDelete() {
this.$confirm("删掉档案后,\n" +
`<span class="username">${this.baseInfo.name}</span>` +
" 的历史相关信息可能无法追溯查看,是否确定删除该人员档案?", {
title: "档案删除"
}).then(() => {
this.instance.post(`/app/appresidentapplet/delete?ids=${this.baseInfo.id}`).then(res => {
if (res && res.code == 0) {
this.$message.success("删除成功");
this.$router.push({query: {}});
}
});
}).catch(() => 0)
},
idChange(val) {
if (val.length == 18) {
this.IdCard(val);
}
},
IdCard(UUserCard) {
if (UUserCard) {
const idCard = new ID(UUserCard)
this.baseInfo.age = idCard.age
this.baseInfo.sex = idCard.sex
this.baseInfo.birthday = idCard.birthday
}
}
},
created() {
this.$dict.load(['sex', 'education', 'politicsStatus', 'job', 'faithType', 'nation', 'maritalStatus', 'militaryStatus', 'householdName', 'householdRelation', ]).then(()=> {
this.disabledLevel = this.user.info.areaMap[this.user.info.areaId].length;
if (!this.params.id) {
// this.baseInfo.householdAreaId = JSON.parse(JSON.stringify(this.user.info.areaId))
this.baseInfo.currentAreaId = this.user.info.areaId
} else {
this.getDetail(this.params.id);
}
})
}
};
</script>
<style lang="scss" scoped>
.residentDetail {
font-size: 14px;
.AppAnnounceDetail-select {
display: flex;
align-items: center;
min-height: 32px;
line-height: 1;
background: #F5F5F5;
border-radius: 4px;
border: 1px solid #D0D4DC;
cursor: pointer;
overflow: hidden;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: #26f;
}
& > i {
flex: 1;
height: 100%;
line-height: 32px;
padding: 0 12px;
color: #888888;
font-size: 14px;
font-style: normal;
border-right: 1px solid #D0D4DC;
background: #fff;
}
.AppAnnounceDetail-select__input {
position: absolute;
left: 0;
top: 0;
z-index: -1;
opacity: 0;
height: 100%;
}
.select-right {
height: 100%;
padding: 0 12px;
color: #222222;
font-size: 12px;
cursor: pointer;
transition: all ease 0.3s;
&:hover {
opacity: 0.5;
}
}
.select-left {
display: flex;
flex-wrap: wrap;
flex: 1;
padding: 5px 0 0px 12px;
border-right: 1px solid #D0D4DC;
border-radius: 4px 0 0 4px;
background: #fff;
em {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
color: #222222;
font-size: 12px;
font-style: normal;
}
span {
height: 22px;
line-height: 22px;
margin: 0 4px 5px 0;
padding: 0 8px;
font-size: 12px;
color: #222222;
background: #F3F4F7;
border-radius: 2px;
border: 1px solid #D0D4DC;
}
}
}
:deep( .AiID ){
line-height: unset;
.el-button--text {
padding: 0 8px;
height: fit-content;
}
}
:deep( .el-form-item ){
margin-bottom: 10px;
.el-form-item {
margin-top: 10px;
}
.el-cascader, .el-select, .el-date-editor {
width: 100%;
}
}
:deep( .el-form-item__error ){
position: static;
}
}
</style>

View File

@@ -0,0 +1,122 @@
<template>
<ai-detail class="activitiesAdd">
<template slot="title">
<ai-title title="居民信息详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<ai-wrapper class="fill" slot="content">
<ai-info-item label="姓名" isLine :value="baseInfo.name"/>
<ai-info-item label="照片">
<ai-avatar v-model="baseInfo.photo" :editable="false"/>
</ai-info-item>
<ai-info-item label="性别" :value="dict.getLabel('sex', baseInfo.sex)"/>
<ai-info-item label="身份证号" :value="baseInfo.idNumber">
<!-- <ai-id mode="show" v-model="baseInfo.idNumber" right-btn class="line-center"/> -->
</ai-info-item>
<ai-info-item label="出生日期" :value="baseInfo.birthday"/>
<ai-info-item label="年龄" :value="baseInfo.age"/>
<ai-info-item label="籍贯" :value="baseInfo.birthplaceAreaName"/>
<ai-info-item label="民族" :value="dict.getLabel('nation', baseInfo.nation)"/>
<ai-info-item label="文化程度" :value="dict.getLabel('education', baseInfo.education)"/>
<ai-info-item label="婚姻状况" :value="dict.getLabel('maritalStatus', baseInfo.maritalStatus)"/>
<ai-info-item label="政治面貌" :value="dict.getLabel('politicsStatus', baseInfo.politicsStatus)"/>
<ai-info-item label="兵役状况" :value="dict.getLabel('militaryStatus', baseInfo.militaryStatus)"/>
<ai-info-item label="宗教信仰" :value="dict.getLabel('faithType', baseInfo.faithType)"/>
<ai-info-item label="职业" :value="dict.getLabel('job', baseInfo.job)"/>
</ai-wrapper>
</ai-card>
<ai-card title="联络信息" type="flex">
<ai-wrapper slot="content">
<ai-info-item label="联系方式" :value="baseInfo.phone"/>
<ai-info-item label="现住址" isLine :value="[baseInfo.currentAreaName, baseInfo.currentAddress].join('')"/>
<ai-info-item label="所属网格" :value="baseInfo.girdName"/>
</ai-wrapper>
</ai-card>
<ai-card title="户籍信息">
<ai-wrapper slot="content">
<ai-info-item label="是否户主" :value="dict.getLabel('householdName', baseInfo.isHousehold)"/>
<template v-if="baseInfo.isHousehold == 0">
<ai-info-item label="与户主关系" :value="dict.getLabel('householdRelation', baseInfo.householdRelation)"/>
<ai-info-item label="户主身份证号" :value="baseInfo.householdIdNumber"/>
</template>
<ai-info-item label="户籍地" isLine :value="[baseInfo.householdAreaName, baseInfo.householdAddress].join('')"></ai-info-item>
</ai-wrapper>
</ai-card>
</template>
</ai-detail>
</template>
<script>
import { ID } from 'dui/lib/js/utils'
export default {
props: {
instance: Function,
dict: Object,
params: Object,
},
data () {
return {
baseInfo: {},
id: ''
}
},
created () {
this.id = this.params.id
this.getDetail()
},
methods: {
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
},
IdCard (UUserCard) {
if (UUserCard) {
const idCard = new ID(UUserCard)
this.baseInfo.age = idCard.age
this.baseInfo.sex = idCard.sex
this.baseInfo.birthday = idCard.birthday
}
},
getDetail() {
this.instance.post(`/app/appresidentapplet/queryDetailById?id=${this.params.id}`).then(res => {
if (res.data) {
this.baseInfo = {
...res.data
}
this.IdCard(res.data.idNumber)
}
})
}
}
}
</script>
<style lang="scss" scope>
.activitiesAdd {
height: 100%;
:deep( .amap-logo ){
display: none!important;
}
:deep( .amap-copyright ){
display: none!important;
}
:deep( .el-date-editor .el-input ){
width: 100%;
}
.amap-container {
height: 380px;
}
}
</style>

View File

@@ -0,0 +1,156 @@
<template>
<ai-list class="activitiesList">
<ai-title
slot="title"
title="居民信息"
isShowBottomBorder
isShowArea
:hideLevel="$store.state.user.info.areaList.length - 1"
v-model="search.areaId"
:instance="instance"
@change="search.current = 1, getList()">
</ai-title>
<template #content>
<ai-search-bar bottomBorder>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="toAdd('')">添加</el-button>
<ai-import
:instance="instance"
:dict="dict"
type="appresidentapplet"
name="居民信息"
@success="search.current = 1, getList()">
</ai-import>
</template>
<template #right>
<el-input
v-model="search.name"
size="small"
placeholder="姓名/身份证/联系方式"
clearable
v-throttle="() => {search.current = 1, getList()}"
@clear="search.name = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:total="search.total"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList"
:col-configs="colConfigs"
:dict="dict">
<el-table-column slot="options" label="操作" fixed="right" align="center" width="160px">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
<el-button type="text" @click="toAdd(row.id)">编辑</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
export default {
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
total: 0,
name: '',
areaId: ''
},
tableData: []
}
},
computed: {
colConfigs() {
return [
{ label: "姓名", prop: "name", align: "left" },
// { label: "性别", prop: "sex", dict: 'sex', align: "center" },
{ prop: 'idNumber', label: '身份证号', align: 'center'},
// format: v => v.substring(0, 10) + '****' + v.substring(14, 18)
// { label: "年龄", prop: "age", align: "center"},
{ label: "现住址", prop: "currentAreaName", align: "center" },
{ label: "网格", prop: "girdName", align: "center" },
{ label: "民族", prop: "nation", align: "center", dict: "nation" },
{ label: "文化程度", prop: "education", align: "center", dict: "education" },
{ label: "政治面貌", prop: "politicsStatus", align: "center", dict: "politicsStatus" },
{ slot: "options", },
]
}
},
created () {
this.search.areaId = this.$store.state.user.info.areaId
this.$dict.load(['nation', 'education', 'politicsStatus']).then(()=> {
this.getList()
})
},
methods: {
getList() {
this.instance.post(`/app/appresidentapplet/list`,null, {
params: {
...this.search,
}
}).then(res=> {
if(res?.data) {
this.tableData = res.data.records
this.search.total = res.data.total
}
})
},
toDetail (id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || '',
type: 'Detail'
}
})
},
toAdd(id) {
this.$emit('change', {
type: 'Add',
params: {
type: id ? 'Edit' : 'Add',
id: id || ''
}
})
},
remove (id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appresidentapplet/delete?ids=${id}`).then(res=>{
if(res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.activitiesList {
height: 100%;
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<div class="AppActivitiesManagement">
<keep-alive :include="['List']">
<component ref="component" :is="component" :permissions="permissions " @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive>
</div>
</template>
<script>
import List from './components/List'
import Detail from './components/Detail'
export default {
name: 'AppResidentIntegrating',
label: '居民积分',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data () {
return {
component: 'List',
params: {},
include: []
}
},
components: {
List,
Detail
},
methods: {
onChange (data) {
if (data.type === 'Detail') {
this.component = 'Detail'
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">
.AppActivitiesManagement {
height: 100%;
background: #F3F6F9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,181 @@
<template>
<ai-detail>
<template slot="title">
<ai-title title="详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<el-row style="margin-bottom: 16px;">
<div class="card_list">
<div class="card">
<h2>姓名</h2>
<p class="color1">{{ info.userName }}</p>
</div>
<div class="card">
<h2>积分余额</h2>
<p class="color2">{{ info.integral || 0 }}</p>
</div>
<div class="card">
<h2>已用积分</h2>
<p class="color3">{{ info.usedIntegral || 0 }}</p>
</div>
</div>
</el-row>
<ai-card>
<ai-title slot="title" title="余额变动明细"/>
<template #content>
<ai-search-bar>
<template #left>
<ai-select v-model="search.type" placeholder="请选择类型" @change="search.current = 1, getList()" :selectList="dict.getDict('integralType')"/>
</template>
<template #right>
<ai-download
:instance="instance"
:url="`/app/appintegraluser/changeIntegralExport?id=${params.id}`"
:params="search"
fileName="余额变动明细"
:disabled="tableData.length == 0">
<el-button size="small">导出</el-button>
</ai-download>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :total="total" :current.sync="search.current" :size.sync="search.size"
@getList="getList" :col-configs="colConfigs" :dict="dict">
<el-table-column slot="changeIntegral" label="变动积分" align="center">
<template slot-scope="{ row }">
<span>{{ row.integralCalcType == 0 ? '-' : '+' }}{{ row.changeIntegral }}</span>
</template>
</el-table-column>
<el-table-column slot="eventDesc" label='事件' align="center" show-overflow-tooltip>
<template slot-scope="{ row }">
<span>{{ row.eventDesc || row.eventName }}</span>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
</template>
</ai-detail>
</template>
<script>
export default {
name: 'Detail',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
info: {},
id: '',
search: {
current: 1,
size: 10,
type: ''
},
total: 0,
tableData: []
}
},
computed: {
colConfigs() {
return [
{prop: "doTime", label: '时间', align: "left", width: "200px"},
{prop: "integralType", label: '类型', align: "center", dict: "integralType"},
{slot: "changeIntegral"},
{prop: "nowIntegral", label: '剩余积分', align: "center" },
{slot: "eventDesc"},
]
}
},
created () {
console.log(this.params)
this.dict.load('integralType').then(() => {
this.getInfo()
this.getList()
})
},
methods: {
getInfo (id) {
this.instance.post(`/app/appintegraluser/girdDetail?id=${this.params.id}`).then(res => {
if (res.code === 0) {
this.info = res.data
}
})
},
getList () {
this.instance.post(`/app/appintegraluser/getChangeDetail`, null, {
params: {
...this.search,
id: this.params.id
}
}).then(res => {
if (res.code === 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
}
}
}
</script>
<style scoped lang="scss">
.card_list {
display: flex;
.card {
flex: 1;
height: 96px;
background: #FFFFFF;
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.1500);
border-radius: 4px;
margin-right: 20px;
padding: 16px 24px;
box-sizing: border-box;
h2 {
color: #888888;
font-weight: 600;
font-size: 16px;
}
p {
margin-top: 8px;
font-size: 24px;
font-weight: 600;
}
.color1 {
color: #2891FF;
}
.color2 {
color: #22AA99;
}
.color3 {
color: #F8B425;
}
}
.card:last-child {
margin-right: 0;
}
}
</style>

View File

@@ -0,0 +1,277 @@
<template>
<ai-list class="notice">
<ai-title slot="title" title="居民积分" v-model="search.areaId" isShowBottomBorder isShowArea :hideLevel="hideLevel - 1" @change="search.current = 1, getList()"></ai-title>
<template slot="content">
<ai-search-bar class="search-bar">
<template #left>
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="changeIntegral('',0)">&nbsp;批量调整积分</el-button>
<ai-download
:instance="instance"
url="/app/appwechatuserqujing/export"
:params="params"
v-if="permissions('app_appwechatuserqujing_export')"
fileName="居民积分"
:disabled="tableData.length == 0">
<el-button icon="iconfont iconExported" :disabled="tableData.length == 0">导出</el-button>
</ai-download>
</template>
<template #right>
<el-input
v-model="search.idNumber"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="姓名/身份证/手机号"
clearable
@clear="search.current = 1, search.idNumber = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<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.integralUserId)">详情</el-button>
<el-button type="text" @click="changeIntegral(row,1)">调整积分</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog
title="调整积分"
:visible.sync="dialog"
:destroyOnClose="true"
width="720px"
@onConfirm="onConfirm"
@closed="form={},chooseUserList=[]">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="选择人员" prop="ids" v-if="!isEdit">
<ai-person-select :instance="instance" keys="openId" customRightText :customClicker="true" :chooseUserList="chooseUserList"
url="/app/appwechatuserqujing/listByFdAppletUser" headerTitle="用户列表"
:isMultiple="true" dialogTitle="选择" @selectPerson="selectPerson" class="aipersonselect">
<template name="option" v-slot:option="{ item }">
<span class="iconfont iconProlife">{{ item.realName }}</span>
<ai-id mode="show" :show-eyes="false" :value="item.idNumber"/>
</template>
</ai-person-select>
</el-form-item>
<el-form-item label="调整说明" prop="eventDesc">
<el-input v-model="form.eventDesc" placeholder="请输入..." type="textarea" :rows="4" show-word-limit
maxlength="100"></el-input>
</el-form-item>
<el-form-item label="类型" prop="integralCalcType">
<ai-select v-model="form.integralCalcType" :selectList="dict.getDict('integralCalcType')"/>
</el-form-item>
<el-form-item label="积分" prop="integral">
<el-input v-model.trim="form.integral" placeholder="请输入正数" size="small"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'List',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
search: {
current: 1,
size: 10,
idNumber: '',
areaId: ''
},
form: {
ids: [],
eventDesc: "",
enclosure: "",
integralCalcType: "",
integral: ''
},
dialog: false,
chooseUserList: [],
total: 0,
isEdit: false,
colConfigs: [
{ prop: 'realName', label: '姓名', align: 'left', width: '200px' },
{ prop: 'phone', label: '手机号', align: 'center' },
{ prop: 'idNumber', label: '身份证号', align: 'center' },
{ prop: 'areaName', label: '所属地区', align: 'center' },
{ prop: 'girdName', label: '所属网格', align: 'center' },
{ prop: 'integral', label: '剩余积分', align: 'center' },
{ prop: 'usedIntegral', label: '已用积分', align: 'center' }
],
tableData: []
}
},
computed: {
...mapState(['user']),
hideLevel () {
return this.user.info.areaList.length || 0
},
params () {
return {
...this.search,
size: 1000
}
},
rules() {
return {
ids: [{required: true, message: '请选择人员', trigger: 'blur'}],
eventDesc: [{required: true, message: '请输入调整说明', trigger: 'blur'}],
integralCalcType: [{required: true, message: '请选择类型', trigger: 'blur'}],
integral: [{required: true, message: '请输入积分', trigger: 'blur' },
{pattern: /^([1-9]\d*|0)(\.\d{1,2})?$/, message: '请输入正数且最多只能保留两位小数'}],
}
}
},
created() {
this.search.areaId = this.user.info.areaId
this.dict.load('integralCalcType').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appwechatuserqujing/listByFdAppletUser`, null, {
params: {
...this.search,
areaId: this.search.areaId
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
selectPerson(val) {
if (val) {
this.personList = val
this.form.ids = [...this.personList.map(e => e.openId)]
} else {
this.form.ids = this.chooseUserList.map(e => e.openId)
}
},
changeIntegral(row,type) {
this.isEdit = false
if(type==0) {
this.dialog = true
} else if(type ==1) {
this.isEdit = true
this.chooseUserList = [{
openId: row.openId,
name: row.realName
}]
this.form = {
ids: this.chooseUserList.map(e => e.openId)
}
this.dialog = true
}
},
changeTableSort(col) {
if(col.prop === 'integral') { // 剩余积分
this.search.sortFiled = 0
if(col.order === 'ascending') {
this.search.sortRule = true
} else if(col.order === 'descending') {
this.search.sortRule = false
} else if(col.order === null) {
this.search.sortRule = ''
}
} else if(col.prop === 'totalIntegral') { // 累计积分
this.search.sortFiled = 1
if(col.order === 'ascending') {
this.search.sortRule = true
} else if(col.order === 'descending') {
this.search.sortRule = false
} else if(col.order === null) {
this.search.sortRule = ''
}
} else if(col.prop === 'usedIntegral') { // 已用积分
this.search.sortFiled = 2
if(col.order === 'ascending') {
this.search.sortRule = true
} else if(col.order === 'descending') {
this.search.sortRule = false
} else if(col.order === null) {
this.search.sortRule = ''
}
}
this.getList()
},
onConfirm() {
if(this.flag) return
if(this.form.file?.length) {
this.form.enclosure = this.form.file[0].url
}
this.$refs.form.validate((valid)=> {
if(valid) {
this.flag = true
this.instance.post(`/app/appintegraluser/changeIntegral`,{
ids: this.form.ids,
eventDesc: this.form.eventDesc,
enclosure: this.form.enclosure, // 附件
integralCalcType: this.form.integralCalcType,
integral: this.form.integral,
integralUserType: 3
}).then(res => {
if(res?.code == 0) {
this.$message.success('调整积分成功')
setTimeout(() =>{
this.dialog = false
this.getList()
this.flag = false
}, 600)
} else {
this.flag = false
}
})
}
})
},
toDetail(id) {
this.$emit('change', {
type: 'Detail',
params: {
id: id || ''
}
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,70 @@
<template>
<div class="AppSubjectSet">
<keep-alive :include="['List']">
<component
ref="component"
:is="component"
@change="onChange"
:params="params"
:instance="instance"
:dict="dict"
></component>
</keep-alive>
</div>
</template>
<script>
import List from "./components/List";
import Add from "./components/Add";
export default {
name: "AppSubjectSet",
label: "话题设置",
props: {
instance: Function,
dict: Object,
},
data() {
return {
component: "List",
params: {},
include: [],
};
},
components: {
Add,
List,
},
methods: {
onChange(data) {
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">
.AppSubjectSet {
height: 100%;
background: #f3f6f9;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,128 @@
<template>
<ai-detail class="content-add">
<template slot="title">
<ai-title :title="params.id ? '编辑话题' : '添加话题'" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
</ai-title>
</template>
<template slot="content">
<ai-card title="基本信息">
<template #content>
<el-form class="ai-form" :model="form" label-width="120px" ref="form">
<el-form-item prop="title" style="width: 100%;" label="话题名称" :rules="[{required: true, message: '请输入话题名称', trigger: 'change'}]">
<el-input size="small" placeholder='如“社会保障”仅限4个字' v-model="form.title" maxlength="4" :show-word-limit="true"></el-input>
</el-form-item>
<el-form-item prop="showIndex" style="width: 100%;" label="排序" :rules="[{required: true, message: '请输入排序', trigger: 'change'}]">
<el-input-number v-model="form.showIndex" :min="1" :max="100" size="small"></el-input-number>
<span class="tips">请输入数字数字越小排序越前</span>
</el-form-item>
<el-form-item label="话题描述" prop="description" style="width: 100%;" :rules="[{required: true, message: '请输入发布内容', trigger: 'change'}]">
<el-input type="textarea" placeholder="限500字" v-model="form.description" rows="8" maxlength="500" :show-word-limit="true"></el-input>
</el-form-item>
<el-form-item label="话题图标" prop="files" style="width: 100%;" :rules="[{required: true, message: '请上传话题图标', trigger: 'change'}]">
<ai-uploader
:instance="instance"
v-model="form.files"
isShowTip
:limit="1">
</ai-uploader>
</el-form-item>
<el-form-item prop="status" style="width: 100%;" label="是否启用" :rules="[{required: true}]">
<el-radio-group v-model="form.status">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</template>
</ai-card>
</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,
params: Object
},
data () {
return {
form: {
title: '',
showIndex: '',
description: '',
files: [],
picUrl: '',
status: '1',
},
isFlag: false
}
},
computed: {
...mapState(['user'])
},
created () {
if (this.params && this.params.id) {
this.id = this.params.id
this.getInfo(this.params.id)
}
},
methods: {
getInfo (id) {
this.instance.post(`/app/appneighborhoodassistancetheme/queryDetailById?id=${id}`).then(res => {
if (res.code === 0) {
this.form = res.data
this.form.files = [{url: res.data.picUrl}]
}
})
},
confirm () {
if(this.isFlag) return
this.$refs.form.validate((valid) => {
if (valid) {
this.isFlag = true
this.instance.post(`/app/appneighborhoodassistancetheme/addOrUpdate`, {
...this.form,
picUrl: this.form.files[0].url
}).then(res => {
if (res.code == 0) {
this.$message.success('提交成功')
setTimeout(() => {
this.cancel(true)
}, 600)
}
})
}
})
},
cancel (isRefresh) {
this.$emit('change', {
type: 'List',
isRefresh: !!isRefresh
})
},
}
}
</script>
<style scoped lang="scss">
.tips {
display: inline-block;
margin-left: 8px;
color: #999;
font-size: 12px;
}
</style>

View File

@@ -0,0 +1,144 @@
<template>
<ai-list class="notice">
<template slot="title">
<ai-title title="话题设置" isShowBottomBorder>
</ai-title>
</template>
<template slot="content">
<ai-search-bar class="search-bar">
<template #left>
<el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd('')">添加话题</el-button>
</template>
<template #right>
<el-input
v-model="search.title"
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
placeholder="请输入话题名称/创建人"
clearable
@clear="search.current = 1, search.title = '', getList()"
suffix-icon="iconfont iconSearch">
</el-input>
</template>
</ai-search-bar>
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 6px;"
:current.sync="search.current"
:size.sync="search.size"
@getList="getList">
<el-table-column slot="status" width="120px" label="是否启用" align="center">
<template slot-scope="{ row }">
<el-switch v-model="row.status" @change="changeStatus(row)" active-value="1" inactive-value="0"></el-switch>
</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="toAdd(row.id)">编辑</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
export default {
name: 'List',
props: {
instance: Function,
dict: Object
},
data () {
return {
search: {
current: 1,
size: 10,
title: '',
status: ''
},
total: 10,
colConfigs: [
{ prop: 'title', label: '话题名称', align: 'left', width: '200' },
{ prop: 'description', label: '话题描述', align: 'left', 'show-overflow-tooltip': true},
{ prop: 'partakeCount', label: '参与话题数', align: 'center', width: '120' },
{ prop: 'showIndex', label: '排序', align: 'center', width: '120' },
{ prop: 'createUserName', label: '创建人', align: 'center', width: '120' },
{ prop: 'createTime', label: '创建时间', align: 'center', width: '180' },
{ slot: 'status'},
{ slot: 'options'},
],
tableData: [],
}
},
created() {
this.getList()
},
methods: {
getList() {
this.instance.post(`/app/appneighborhoodassistancetheme/list`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
remove (id) {
this.$confirm('确定删除该话题?').then(() => {
this.instance.post(`/app/appneighborhoodassistancetheme/delete?ids=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
toAdd(id) {
this.$emit('change', {
type: 'Add',
params: {
id: id || ''
}
})
},
changeStatus(row) {
this.$confirm(`确定${row.status == 1 ? '启用' : '关闭'}该话题?`).then(() => {
this.instance.post(`/app/appneighborhoodassistancetheme/enable?id=${row.id}`).then(res => {
if (res.code == 0) {
this.$message.success('操作成功!')
this.getList()
}
})
}).catch(() => {
this.getList()
})
// this.instance.post(`/app/appneighborhoodassistancetheme/enable?id=${row.id}`).then(res => {
// if (res.code == 0) {
// this.$message.success('操作成功!')
// this.getList()
// }
// })
}
}
}
</script>
<style lang="scss" scoped>
</style>