This commit is contained in:
liuye
2024-07-04 17:31:16 +08:00
parent f8a7ffaca9
commit c1e1ac4b8b
29 changed files with 6984 additions and 0 deletions

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" :destroyOnclose="false" 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,652 @@
<template>
<section class="AppIntegralStatistics">
<ai-detail list>
<ai-title slot="title" title="总体统计">
<template #rightBtn>
<el-row type="flex" align="middle">
<span class="shortcut" v-for="(item,i) in timeCheck" :key="i" :class="{active:type==i}"
@click="timeChange(i)" v-text="item"/>
<el-cascader ref="cascader1" v-model="girdArr" :options="girdOptions" placeholder="所属网格" size="small"
:props="defaultProps" :show-all-levels="false" @change="gridChange" clearable/>
</el-row>
</template>
</ai-title>
<template #content>
<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>
<el-row type="flex" class="mar-t20 gap-20">
<div class="chartBox fill">
<b>个人积分排行</b>
<div>
<div id="chart1" style="height: 300px; width: 100%;"
v-show="userSortListX.length && userSortListY.length"></div>
<ai-empty v-if="!userSortListX.length && !userSortListY.length" style="height: 200px; width: 100%;"
id="empty"/>
</div>
</div>
<div class="chartBox fill">
<b>网格积分排行</b>
<div>
<div id="chart2" style="height: 300px; width: 100%;" v-show="girdSortListX.length && girdSortListY.length"/>
<ai-empty v-show="!girdSortListX.length && !girdSortListY.length" style="height: 200px; width: 100%;"
id="empty"/>
</div>
</div>
</el-row>
<el-row type="flex" class="mar-t20 gap-20">
<div class="chartBox fill" v-for="item in sta" :key="item.id">
<div flex>
<b class="fill" v-text="item.label"/>
<div class="color-primary" v-text="`总次数:${item.total}`"/>
</div>
<div class="chart" :id="item.id" v-show="item.sta.length>0"/>
<ai-empty v-if="item.sta.length==0" style="height: 200px; width: 100%;"/>
</div>
</el-row>
<ai-card class="mar-t20" 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>
</template>
</ai-detail>
<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" :unlink-panels="true"
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 dayjs from 'dayjs'
import AiDetail from "dui/packages/layout/AiDetail.vue";
import AiTitle from "dui/packages/basic/AiTitle.vue";
export default {
name: "AppIntegralStatistics",
components: {AiTitle, AiDetail},
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: [],
sta: []
}
},
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.getGridList()
this.search.startTime = this.time?.[0]
this.search.endTime = this.time?.[1]
this.getTableData()
})
},
methods: {
// 统计接口
getStatistics() {
return Promise.all([
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) {
return this.data = res.data
}
}),
this.instance.post("/app/appwechatsigninfo/userSignAndIntegralApplyAndIntegralExchangeSort", null, {
params: {
type: this.type,
girdId: this.girdId,
startTime: this.startTime,
endTime: this.endTime,
}
}).then(res => {
if (res?.data) {
const {
signCountList = [], integralApplyCountList = [], integralExchangeCountList = [],
signCount = 0, integralExchangeCount = 0, integralApplyCount = 0
} = res.data
return this.sta = [
{label: "签到次数统计", id: 'signCount', sta: signCountList, total: signCount},
{label: "积分申请次数统计", id: 'integralApplyCount', sta: integralApplyCountList, total: integralApplyCount},
{label: "积分兑换次数统计", id: 'integralExchangeCount', sta: integralExchangeCountList, total: integralExchangeCount}
]
}
}).then(() => this.sta.map(this.renderChart))
])
},
// 人员、网格排行
getRanking() {
return 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()
return 1;
}
}).then(() => {
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);
},
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);
},
onResize() {
this.myChart1?.resize()
this.myChart2?.resize()
this.sta.map(e => e.chart?.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) {
this.type = index
if (index == 3) {
this.dialogDate = true
} else {
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()
},
renderChart(item = {}) {
if (!item.chart) {
const el = document.getElementById(item.id)
el.style.width = (window.innerWidth - 435) / 3 + "px";
item.chart = echarts.init(el)
}
item.chart.setOption({
xAxis: {minInterval: 1},
tooltip: {},
legend: {},
yAxis: {type: 'category', axisLabel: {fontSize: 8}},
dataset: {dimensions: ['username', 'count', 'integral'], source: item.sta},
series: [
{name: "次数", barWidth: 10, barGap: '20%', type: 'bar'},
{name: "积分", barWidth: 10, barGap: '20%', type: 'bar'}
]
}, true)
}
},
filters: {
formatTime(num) {
if (num > 0) {
return '+' + num
} else {
return num
}
}
},
mounted() {
window.addEventListener('resize', this.onResize)
this.getRanking()
this.getStatistics()
},
destroyed() {
window.removeEventListener('resize', this.onResize)
},
}
</script>
<style lang="scss" scoped>
.AppIntegralStatistics {
height: 100%;
box-sizing: border-box;
.shortcut {
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;
}
}
.chartBox {
background: #F9F9F9;
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.1500);
border-radius: 4px;
padding: 16px;
box-sizing: border-box;
.chart {
width: 100%;
height: 300px;
}
}
.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;
}
}
: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,.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', 'JPG', 'JPEG', 'PNG'].includes(e.postfix.split('.')[1])))
this.info.videos = res.data.files.filter(e => (['mp4', '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,252 @@
<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">
<div class="img-content">
<img :src="row.goodsPicUrl" alt="" v-viewer>
<!-- <span class="type type0" v-if="row.goodsType === '0'">积分兑换</span>
<span class="type type1" v-else>京东低价商品</span> -->
<span class="type" :class="`type${row.typeText}`">{{dict.getLabel('integralSGTypeText', row.typeText)}}</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>
export default {
name: 'GirdList',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
search: {
goodsType: '',
goodsTitle: '',
current: 1,
status: '',
size: 10,
startTime: '',
endTime: '',
type: 0
},
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', 'integralSGTypeText').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketorder/list`, null, {
params: {
...this.search
}
}).then((res) => {
if (res.code == 0) {
res.data.records.map((item) => {
item.typeText = item.goodsType == 0 ? 0 : 1
})
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;
}
.img-content {
position: relative;
margin-right: 8px;
img {
width: 120px;
height: 120px;
cursor: pointer;
}
}
.type {
position: absolute;
top: 0;
left: 0;
font-size: 12px;
width: 120px;
text-align: center;
color: #fff;
z-index: 999;
}
.type1 {
background-color: #E64E39;
}
.type0 {
background-color: #FF6900;
}
}
</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,262 @@
<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-select
v-model="search.agentOrder"
@change="(search.current = 1), getList()"
placeholder="是否代兑换"
:selectList="dict.getDict('yesOrNo')">
</ai-select>
<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">
<div class="img-content">
<img :src="row.goodsPicUrl" alt="" v-viewer>
<!-- <span class="type type0" v-if="row.goodsType === '0'">积分兑换</span>
<span class="type type1" v-else>京东低价商品</span> -->
<span class="type" :class="`type${row.typeText}`">{{dict.getLabel('integralSGTypeText', row.typeText)}}</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>
export default {
name: 'ResidentList',
props: {
instance: Function,
dict: Object,
permissions: Function,
areaId: String,
},
data() {
return {
search: {
goodsType: '',
goodsTitle: '',
current: 1,
status: '',
size: 10,
startTime: '',
type: 1,
endTime: '',
agentOrder: ''
},
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: 'agentOrder', label: '是否代兑换', align: 'center', format: v => this.dict.getLabel('yesOrNo', v) },
{ 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', 'yesOrNo', 'integralSGTypeText').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketorder/list`, null, {
params: {
...this.search,
girdCode: this.areaId
}
}).then((res) => {
if (res.code == 0) {
res.data.records.map((item) => {
item.typeText = item.goodsType == 0 ? 0 : 1
})
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;
font-size: 12px;
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,629 @@
<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-item label="店铺地点" style="width: 100%;" prop="address" :rules="[{required: true, message: '请选择店铺地点', trigger: 'change'}]">
<el-input v-model="form.address" disabled size="small">
<template slot="append">
<el-button @click="showMap = true">选择位置</el-button>
</template>
</el-input>
</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>
<ai-dialog title="地图" :visible.sync="showMap" @opened="initMap" :destroyOnclose="false" 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>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">提交</el-button>
</template>
</ai-detail>
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader'
export default {
name: 'AddStore',
props: {
instance: Function,
dict: Object,
params: Object
},
data () {
return {
isShow: false,
form: {
title: '',
type: '',
serviceType: '0',
visibleNames: '',
girdList: [],
goodsList: [],
address: ''
},
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) },
{ prop: 'typeExplain', label: '商品类型说明'},
{ 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: 'typeExplain', label: '商品类型说明'},
{ prop: 'onlineTime', label: '上架时间', align: 'center' },
{ prop: 'status', width: 90, label: '状态', align: 'center', format: v => this.dict.getLabel('integralSGStatus', v) }
],
chooseList: [],
mapDetail: null,
map: null,
placeSearch: null,
placeDetail: {
lng: '',
lat: '',
address: ''
},
lng: '',
lat: '',
showMap: false,
searchPlace: '',
}
},
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: {
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] : undefined,
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()
}
},
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);
})
},
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) {
res.data.goodsList.map((item) => {
item.typeExplain = item.goods.typeExplain
})
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
}
})
this.placeDetail.lng = res.data.lng
this.placeDetail.lat = res.data.lat
this.getMap(this.info.lng,this.info.lat,this.info.address)
}
})
},
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,226 @@
<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 type0" v-if="row.type === '0'">积分兑换</span>
<span class="type type1" v-else>京东低价商品</span> -->
<span class="type" :class="`type${row.typeText}`">{{dict.getLabel('integralSGTypeText', row.typeText)}}</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>
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) }
],
userList: []
}
},
computed: {
},
created () {
this.dict.load('integralSGType', 'integralSGStatus', 'integralSGTypeText').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appintegralsupermarketgoods/list`, null, {
params: {
...this.search
}
}).then((res) => {
if (res.code == 0) {
res.data.records.map((item) => {
item.typeText = item.type == 0 ? 0 : 1
})
this.tableData = res.data.records
this.total = res.data.total
}
})
},
onAreaChange () {
this.search.current = 1
this.$nextTick(() => {
this.getTableData()
})
},
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;
font-size: 12px;
}
.type1 {
background-color: #E64E39;
}
.type0 {
background-color: #FF6900;
}
}
</style>

View File

@@ -0,0 +1,227 @@
<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>
<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.visibleId">{{ search.girdName }}</span>
<span v-else>请选择服务地区</span>
<i class="el-icon-arrow-up" v-if="!search.visibleId"></i>
<i class="el-icon-circle-close" v-if="search.visibleId" @click.stop="search.visibleId = '', search.girdName = '', search.current = 1, getList()"></i>
</div>
</ai-picker>
</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,
visibleId: '',
girdName: ''
},
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: {
onGridChange (e) {
if (e.length) {
this.search.visibleId = e[0].girdCode
this.search.girdName = e[0].girdName
this.search.current = 1
this.getList()
}
},
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 {
.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,237 @@
<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')"
@change="onChange">
</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 === '2'" label="小程序appid" prop="jdAppid" :rules="[{required: true, message: '请输入京东小程序appid', trigger: 'blur'}]">
<el-input type="input" size="small" v-model="form.jdAppid" clearable placeholder="请输入京东小程序appid"></el-input>
</el-form-item>
<el-form-item v-if="form.type === '2'" style="width: 100%" 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 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: '',
type: '',
jdUrl: '',
jdAppid: 'wx91d27dbf599dff74',
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
}]
}
})
},
onChange () {
if (this.form.type === '2') {
this.form.jdUrl = 'pages/gold/item/pages/detail/index?sku=60626768856&sourceType=wx-zhongwei&ea_ptag=17078.27.755'
} else {
this.form.jdUrl = ''
}
},
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" :destroyOnclose="false" 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>