目录代码整合
This commit is contained in:
66
packages/wxwork/AppBuddyMessage/AppBuddyMessage.vue
Normal file
66
packages/wxwork/AppBuddyMessage/AppBuddyMessage.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="doc-circulation ailist-wrapper">
|
||||
<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: 'AppBuddyMessage',
|
||||
label: '好友欢迎语',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
component: 'List',
|
||||
params: {},
|
||||
include: []
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
Add,
|
||||
List
|
||||
},
|
||||
|
||||
mounted () {
|
||||
},
|
||||
|
||||
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">
|
||||
.doc-circulation {
|
||||
height: 100%;
|
||||
background: #F3F6F9;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
378
packages/wxwork/AppBuddyMessage/components/Add.vue
Normal file
378
packages/wxwork/AppBuddyMessage/components/Add.vue
Normal file
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<ai-detail>
|
||||
<template slot="title">
|
||||
<ai-title title="新建欢迎语" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<ai-card>
|
||||
<template #title>
|
||||
<div class="ai-card__title">
|
||||
<h2>基本信息</h2>
|
||||
<span>*一个成员如果被设置了多个欢迎语,将会使用最新设置或修改的欢迎语</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #content>
|
||||
<el-form class="ai-form" :rules="rules" ref="userForm" :model="form" label-width="100px" label-position="right">
|
||||
<el-form-item label="使用成员" prop="users" style="width: 100%">
|
||||
<el-input size="small" placeholder="请选择..." disabled v-model="users">
|
||||
<ai-wechat-selecter slot="append" :instance="instance" v-model="form.users">
|
||||
<el-button type="info">选择</el-button>
|
||||
</ai-wechat-selecter>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</ai-card>
|
||||
|
||||
<ai-card title="发送欢迎语">
|
||||
<template #content>
|
||||
<el-form class="ai-form" ref="form" :model="form" label-width="110px" label-position="right">
|
||||
<el-form-item class="el-form-item__textarea" label="文本内容" prop="explain" style="width: 100%">
|
||||
<span @click="insertNickname" class="el-form-item__btn" type="text">[插入居民昵称]</span>
|
||||
<el-input type="textarea" placeholder="请输入…" v-model="form.content" maxlength="1000" :rows="5" show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="其他类型" prop="explain" style="width: 100%">
|
||||
<el-radio-group v-model="form.type" @change="onTypeChange">
|
||||
<el-radio label="image">图片</el-radio>
|
||||
<el-radio label="link">链接</el-radio>
|
||||
<el-radio label="video">视频</el-radio>
|
||||
<el-radio label="miniapp">小程序</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片" prop="files" style="width: 100%" v-if="form.type === 'image'">
|
||||
<ai-uploader :instance="instance" isWechat v-model="form.files" :limit="1" url="/app/wxcp/upload/uploadFile?type=image"></ai-uploader>
|
||||
</el-form-item>
|
||||
<el-form-item label="链接" prop="linkUrl" style="width: 100%" v-if="form.type === 'link'">
|
||||
<el-input placeholder="链接地址请以http或https开头" :rows="2" type="textarea" v-model="form.linkUrl"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="链接图片地址" prop="picUrl" style="width: 100%" v-if="form.type === 'link'">
|
||||
<ai-uploader :instance="instance" v-model="form.picUrl" :limit="1"></ai-uploader>
|
||||
</el-form-item>
|
||||
<el-form-item label="链接标题" prop="title" style="width: 100%" v-if="form.type === 'link'">
|
||||
<el-input placeholder="请输入链接标题" v-model="form.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="视频" prop="files" style="width: 100%" v-if="form.type === 'video'">
|
||||
<ai-uploader :instance="instance" fileType="file" isWechat acceptType=".mp4" v-model="form.files" :limit="1" url="/app/wxcp/upload/uploadFile?type=video"></ai-uploader>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="小程序" prop="applets" style="width: 100%" v-if="form.type === 'miniapp'">
|
||||
<el-radio-group v-model="form.applets">
|
||||
<div class="appletss">
|
||||
<div class="applets-item" @click="form.applets = '0'" :class="[form.applets === '0' ? 'applets-active' : '']">
|
||||
<el-radio label="0"></el-radio>
|
||||
<img src="http://www.9665.com/uploadfile/2018/0607/20180607042142312.png">
|
||||
<span>小程序</span>
|
||||
</div>
|
||||
<div class="applets-item" @click="form.applets = '1'" :class="[form.applets === '1' ? 'applets-active' : '']">
|
||||
<el-radio label="1"></el-radio>
|
||||
<img src="http://www.9665.com/uploadfile/2018/0607/20180607042142312.png">
|
||||
<span>小程序</span>
|
||||
</div>
|
||||
<div class="applets-item" @click="form.applets = '2'" :class="[form.applets === '2' ? 'applets-active' : '']">
|
||||
<el-radio label="2"></el-radio>
|
||||
<img src="http://www.9665.com/uploadfile/2018/0607/20180607042142312.png">
|
||||
<span>小程序</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="小程序标题" prop="title" style="width: 100%" v-if="form.type === 'miniapp'">
|
||||
<el-input placeholder="请输入小程序标题" v-model="form.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="小程序APPID" prop="appid" style="width: 100%" v-if="form.type === 'miniapp'">
|
||||
<el-input placeholder="请输入小程序APPID" v-model="form.appid"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="小程序跳转页面" prop="page" style="width: 100%" v-if="form.type === 'miniapp'">
|
||||
<el-input placeholder="如pages/home/Home" v-model="form.page"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="小程序图片" prop="files" style="width: 100%" v-if="form.type === 'miniapp'">
|
||||
<ai-uploader :instance="instance" v-model="form.files" isWechat :limit="1" url="/app/wxcp/upload/uploadFile?type=image"></ai-uploader>
|
||||
</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>
|
||||
const validateUser = (rule, value, callback) => {
|
||||
if (!value.length) {
|
||||
callback(new Error('请选择使用成员'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'Add',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
params: Object
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
isShow: false,
|
||||
info: {},
|
||||
form: {
|
||||
users: [],
|
||||
appId: '',
|
||||
page: '',
|
||||
title: '',
|
||||
miniappImg: [],
|
||||
picUrl: [],
|
||||
content: '',
|
||||
files: [],
|
||||
linkUrl: '',
|
||||
isRemind: true,
|
||||
type: 'text'
|
||||
},
|
||||
rules: {
|
||||
users: [
|
||||
{ required: true, message: '请选择使用成员', trigger: 'change' },
|
||||
{ validator: validateUser, trigger: 'change' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
users () {
|
||||
return this.form.users.map(v => v.name).join(',')
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
},
|
||||
|
||||
methods: {
|
||||
getInfo (id) {
|
||||
this.instance.post(`/app/appleavemessage/queryDetailById?id=${id}`).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.info = res.data
|
||||
this.info.appLeaveMessageReplyList = res.data.appLeaveMessageReplyList.map(item => {
|
||||
item.images = JSON.parse(item.images).map(item => {
|
||||
return {
|
||||
...item,
|
||||
url: item.accessUrl
|
||||
}
|
||||
})
|
||||
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
validateUser (rule, value, callback) {
|
||||
if (!value.length) {
|
||||
callback(new Error('请选择使用成员'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
|
||||
onClose () {
|
||||
this.form.explain = ''
|
||||
},
|
||||
|
||||
onTypeChange () {
|
||||
this.form.files = []
|
||||
this.form.picUrl = []
|
||||
this.form.appId = ''
|
||||
this.form.page = ''
|
||||
this.form.title = ''
|
||||
this.form.miniappImg = []
|
||||
this.form.linkUrl = ''
|
||||
},
|
||||
|
||||
insertNickname () {
|
||||
this.form.content = this.form.content + '[用户昵称]'
|
||||
},
|
||||
|
||||
onChange () {
|
||||
|
||||
},
|
||||
|
||||
confirm () {
|
||||
this.$refs.userForm.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.form.type === 'text' && !this.form.content) {
|
||||
return this.$message.error('请输入消息内容')
|
||||
}
|
||||
|
||||
if (this.form.type === 'image' && !this.form.files.length) {
|
||||
return this.$message.error('请上传图片')
|
||||
}
|
||||
// if (this.form.type === 'file' && !this.form.files.length) {
|
||||
// return this.$message.error('请上传附件')
|
||||
// }
|
||||
if (this.form.type === 'video' && !this.form.files.length) {
|
||||
return this.$message.error('请上传视频')
|
||||
}
|
||||
if (this.form.type === 'link' && !this.form.linkUrl) {
|
||||
return this.$message.error('请输入链接')
|
||||
}
|
||||
if (this.form.type === 'link' && !this.form.picUrl.length) {
|
||||
return this.$message.error('请输入链接图片')
|
||||
}
|
||||
if (this.form.type === 'link' && !this.form.title) {
|
||||
return this.$message.error('请输入链接标题')
|
||||
}
|
||||
if (this.form.type === 'miniapp' && !this.form.title) {
|
||||
return this.$message.error('请输入小程序标题')
|
||||
}
|
||||
if (this.form.type === 'miniapp' && !this.form.appid) {
|
||||
return this.$message.error('请输入小程序appid')
|
||||
}
|
||||
if (this.form.type === 'miniapp' && !this.form.page) {
|
||||
return this.$message.error('请输入小程序page')
|
||||
}
|
||||
if (this.form.type === 'miniapp' && !this.form.files.length) {
|
||||
return this.$message.error('请上传小程序图片')
|
||||
}
|
||||
|
||||
this.instance.post(`/app/wxcp/wxwelcomeword/add`, {
|
||||
type: '1',
|
||||
content: this.form.content || '',
|
||||
isNotify: '0',
|
||||
media: {
|
||||
createdAt: this.form.files.length ? this.form.files[0].media.createdAt : '',
|
||||
file: this.form.files.length ? this.form.files[0] : {},
|
||||
mediaId: this.form.files.length ? this.form.files[0].media.mediaId : '',
|
||||
sysFileId: this.form.files.length ? this.form.files[0].id : '',
|
||||
type: this.form.type,
|
||||
linkUrl: this.form.type === 'link' && this.form.linkUrl ? this.form.linkUrl : '',
|
||||
title: this.form.title,
|
||||
appId: this.form.appid,
|
||||
page: this.form.page,
|
||||
picUrl: this.form.type === 'link' ? this.form.picUrl[0].url : ''
|
||||
},
|
||||
users: this.form.users.map(item => {
|
||||
return {
|
||||
type: 0,
|
||||
objectId: item.id,
|
||||
objectName: item.name
|
||||
}
|
||||
})
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('提交成功')
|
||||
setTimeout(() => {
|
||||
this.cancel(true)
|
||||
}, 600)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
cancel (isRefresh) {
|
||||
this.$emit('change', {
|
||||
type: 'list',
|
||||
isRefresh: isRefresh ? true : false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.ai-card__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
margin-right: 20px;
|
||||
color: #222222;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #888888;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.appletss {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.applets-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 400px;
|
||||
height: 60px;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding: 0 17px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #D0D4DC;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: #2266FF;
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.el-radio__label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 0 8px 0 13px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #222222;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-radio {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.applets-active {
|
||||
border-color: #2266FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
padding-left: 8px;
|
||||
color: #222222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.el-form-item-item__textarea {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.el-form-item__btn {
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
left: 12px;
|
||||
line-height: 1;
|
||||
z-index: 1;
|
||||
color: #2266FF;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
235
packages/wxwork/AppBuddyMessage/components/List.vue
Normal file
235
packages/wxwork/AppBuddyMessage/components/List.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<ai-list class="message">
|
||||
<template slot="title">
|
||||
<ai-title title="好友欢迎语" isShowBottomBorder></ai-title>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<ai-search-bar class="search-bar">
|
||||
<template #left>
|
||||
<el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd">添加好友欢迎语</el-button>
|
||||
</template>
|
||||
<template slot="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="type" width="240px" label="消息内容" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
width="400"
|
||||
:visible-arrow="false"
|
||||
popper-class="wechat-message__container"
|
||||
trigger="hover">
|
||||
<div class="count" slot="reference">共{{ row.count }}条</div>
|
||||
<div class="message-info">
|
||||
<h2 v-if="row.content" :style="{marginBottom: row.media ? '16px' : '0'}">{{ row.content }}</h2>
|
||||
<div class="message-info__wrapper" v-if="row.media && (row.media.file || row.media.type === 'link')">
|
||||
<img v-if="row.media.type === 'image' || row.media.type === 'miniapp'" :src="row.media.file.url">
|
||||
<video v-if="row.media.type === 'video'" :src="row.media.file.url"></video>
|
||||
<img v-if="row.media.type === 'link'" :src="row.media.picUrl">
|
||||
<div class="message-info__wrapper--right">
|
||||
<h3 v-if="row.media.type === 'miniapp'">{{ row.media.title }}</h3>
|
||||
<h3 v-if="row.media.type === 'image'">{{ row.media.file.name }}</h3>
|
||||
<h3 v-if="row.media.type === 'link'">{{ row.media.linkUrl }}</h3>
|
||||
<h3 v-if="row.media.type === 'video'">{{ row.media.file.name }}</h3>
|
||||
<p v-if="row.media.type === 'image'">{{ row.media.file.fileSizeStr }}</p>
|
||||
<p v-if="row.media.type === 'link'">{{ row.media.title }}</p>
|
||||
<p v-if="row.media.type === 'video'">{{ row.media.file.fileSizeStr }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" width="120px" fixed="right" label="操作" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<!-- <el-button type="text" disabled @click="toAdd(row.id)" title="编辑">编辑</el-button> -->
|
||||
<el-button type="text" @click="remove(row.id)" title="删除">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'List',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
content: ''
|
||||
},
|
||||
currIndex: 0,
|
||||
total: 10,
|
||||
colConfigs: [
|
||||
{ prop: 'type', label: '类型', align: 'left', width: '160',
|
||||
render: (h, params) => {
|
||||
let str = ''
|
||||
|
||||
if (params.row.content) {
|
||||
str = '文字'
|
||||
}
|
||||
|
||||
if (params.row.media && params.row.media.type === 'link') {
|
||||
str += this.dict.getLabel('wxMsgType', params.row.media.type) ? (params.row.content ? '+' : '') + this.dict.getLabel('wxMsgType', params.row.media.type) : ''
|
||||
}
|
||||
|
||||
if (params.row.media && params.row.media.file) {
|
||||
str += this.dict.getLabel('wxMsgType', params.row.media.type) ? (params.row.content ? '+' : '') + this.dict.getLabel('wxMsgType', params.row.media.type) : ''
|
||||
}
|
||||
|
||||
return h('span', {
|
||||
style: {
|
||||
}
|
||||
}, str)
|
||||
}
|
||||
},
|
||||
{ slot: 'type' },
|
||||
{ prop: 'users', label: '使用成员', align: 'left', formart: v => v.join(';') },
|
||||
{ prop: 'createUser', label: '创建人' },
|
||||
{ prop: 'createTime', label: '编辑时间' },
|
||||
{ slot: 'options', label: '操作' }
|
||||
],
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.dict.load(['wxMsgType']).then(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList() {
|
||||
this.instance.post(`/app/wxcp/wxwelcomeword/list`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records.map(item => {
|
||||
let count = 0
|
||||
|
||||
if (item.content) {
|
||||
count = count + 1
|
||||
}
|
||||
|
||||
if (item.media && item.media.type !== 'text') {
|
||||
count = count + 1
|
||||
}
|
||||
|
||||
item.count = count
|
||||
return item
|
||||
})
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
remove (id) {
|
||||
this.$confirm('确定删除该数据?').then(() => {
|
||||
this.instance.post(`/app/wxcp/wxwelcomeword/delete?id=${id}`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
toAdd (id) {
|
||||
this.$emit('change', {
|
||||
type: 'Add',
|
||||
params: {
|
||||
id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.message {
|
||||
.count {
|
||||
cursor: pointer;
|
||||
color: #2266FF;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-info {
|
||||
padding: 8px;
|
||||
min-height: 116px;
|
||||
|
||||
h2 {
|
||||
color: #222222;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.message-info__wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 368px;
|
||||
height: 60px;
|
||||
padding: 10px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #D0D4DC;
|
||||
|
||||
.message-info__wrapper--right {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
h3 {
|
||||
width: 100%;
|
||||
color: #222222;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
img, video {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 10px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 6px;
|
||||
font-size: 14px;
|
||||
color: #888888;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
69
packages/wxwork/AppClientMassTextin/AppClientMassTextin.vue
Normal file
69
packages/wxwork/AppClientMassTextin/AppClientMassTextin.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<ai-list v-if="!isShowDetail">
|
||||
<template slot="title">
|
||||
<ai-title title="客户群发" :isShowBottomBorder="true"></ai-title>
|
||||
</template>
|
||||
<template slot="tabs">
|
||||
<component :ref="activeName" :is="activeName" @change="change" :instance="instance" :dict="dict"
|
||||
:permissions="permissions"/>
|
||||
</template>
|
||||
</ai-list>
|
||||
<component v-else :is="activeName" :params="params" @change="change" :instance="instance" :dict="dict" :permissions="permissions"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableList from './components/TableList.vue'
|
||||
import NewClientMass from './components/NewClientMass.vue'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AppClientMassTextin',
|
||||
label: '客户群发',
|
||||
components: {
|
||||
TableList,
|
||||
NewClientMass,
|
||||
},
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
activeName: 'TableList',
|
||||
params: {},
|
||||
isShowDetail: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
change(val) {
|
||||
console.log(val);
|
||||
if (val.type) {
|
||||
this.activeName = val.type
|
||||
switch (val.type) {
|
||||
case "NewClientMass":
|
||||
this.isShowDetail = true
|
||||
this.params = val.row
|
||||
break;
|
||||
case "TableList":
|
||||
this.isShowDetail = false
|
||||
this.$nextTick(() => {
|
||||
this.$refs[this.activeName].getList()
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
342
packages/wxwork/AppClientMassTextin/components/NewClientMass.vue
Normal file
342
packages/wxwork/AppClientMassTextin/components/NewClientMass.vue
Normal file
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<div class="new-client-mass">
|
||||
<ai-detail>
|
||||
<template #title>
|
||||
<ai-title :title="params.isAdd?'新建群发':'群发详情'" isShowBottomBorder :isShowBack="true" @onBackClick="onBack"
|
||||
:isShowBottomBorder="true"></ai-title>
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-card title="基本信息" v-if="params.isAdd && !isEditInfo">
|
||||
<template #content>
|
||||
<ai-wrapper>
|
||||
<el-form ref="form"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
size="small"
|
||||
style="width: 100%;"
|
||||
label-width="120px">
|
||||
<el-form-item label="群发账号" prop="documentName">
|
||||
<el-row type="flex">
|
||||
<div class="input"></div>
|
||||
<ai-person-select :instance="instance" url="/app/appvillagecadres/list" :isMultiple="true"
|
||||
@selectPerson="getSelect" btnText="选择" dialogTitle="选择">
|
||||
<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-row>
|
||||
</el-form-item>
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-form-item label="群发时间" prop="documentName">
|
||||
<el-radio-group>
|
||||
<el-radio :label="1">立即发送</el-radio>
|
||||
<el-radio :label="2">定时发送</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="发送时间" prop="documentName">
|
||||
<el-time-picker
|
||||
class="select-width"
|
||||
:picker-options="{ selectableRange: '18:30:00 - 20:30:00'}"
|
||||
placeholder="请选择...">
|
||||
</el-time-picker>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-form-item label="选择居民" prop="documentName">
|
||||
<el-radio-group>
|
||||
<el-radio :label="1">全部居民</el-radio>
|
||||
<el-radio :label="2">筛选居民</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-form-item label="最早添加日期" prop="documentName">
|
||||
<el-time-picker
|
||||
class="select-width"
|
||||
:picker-options="{ selectableRange: '18:30:00 - 20:30:00'}"
|
||||
placeholder="请选择...">
|
||||
</el-time-picker>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
<el-form-item label="最晚添加日期" prop="documentName">
|
||||
<el-time-picker
|
||||
class="select-width"
|
||||
:picker-options="{ selectableRange: '18:30:00 - 20:30:00'}"
|
||||
placeholder="请选择...">
|
||||
</el-time-picker>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-form-item label="居民公共标签" prop="documentName">
|
||||
<el-row type="flex">
|
||||
<div class="input"></div>
|
||||
<el-button class="person-select" @click="dialog=true">选择</el-button>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-form-item label="排除居民" prop="documentName">
|
||||
<el-row type="flex">
|
||||
<div class="input"></div>
|
||||
<ai-person-select :instance="instance" url="/app/appvillagecadres/list" :isMultiple="true"
|
||||
@selectPerson="getSelect" btnText="选择" dialogTitle="选择">
|
||||
<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-row>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</ai-card>
|
||||
|
||||
<ai-card title="基本信息" v-if="!params.isAdd && !isEditInfo">
|
||||
<template #right>
|
||||
<el-button icon="iconfont iconjdq_led_edit" size="mini" @click="editInfo()">编辑</el-button>
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-wrapper label-width="96px">
|
||||
<ai-info-item label="群发账号" isLine>刘仕伟;郭麒麟;陶瑞武</ai-info-item>
|
||||
<ai-info-item label="群发时间">定时发送</ai-info-item>
|
||||
<ai-info-item label="发送时间">2021-05-12 18:00</ai-info-item>
|
||||
<ai-info-item label="选择居民" isLine>筛选居民</ai-info-item>
|
||||
<ai-info-item label="最早添加日期">2021-05-12 18:00</ai-info-item>
|
||||
<ai-info-item label="最晚添加日期">2021-05-12 18:00</ai-info-item>
|
||||
<ai-info-item label="居民公共标签" isLine>2021-05-12 18:00</ai-info-item>
|
||||
<ai-info-item label="排除居民" isLine>陶瑞武</ai-info-item>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</ai-card>
|
||||
|
||||
<ai-card title="群发消息内容" class="msg-title" v-if="params.isAdd && !isEditMsg">
|
||||
<template #right>
|
||||
*居民每个月最多接收来自同一企业管理员的4条群发消息,4条消息可在同一天发送
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-wrapper>
|
||||
<el-form ref="form"
|
||||
style="width: 100%;"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
size="small"
|
||||
label-width="120px">
|
||||
<el-form-item label="文本内容">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="请输入回复内容">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="其他类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio label="none">无</el-radio>
|
||||
<el-radio label="img">图片</el-radio>
|
||||
<el-radio label="link">链接</el-radio>
|
||||
<el-radio label="file">附件</el-radio>
|
||||
<el-radio label="video">视频</el-radio>
|
||||
<el-radio label="miniProgrom">小程序</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="comp.label" v-for="(comp,index) in msgConnect" :key="index">
|
||||
<ai-uploader :instance="instance" :limit="1" v-if="comp.type=='img'"></ai-uploader>
|
||||
<el-input placeholder="链接地址请以http或https开头" v-if="comp.type=='link'">
|
||||
<template slot="prepend">Http://</template>
|
||||
</el-input>
|
||||
<ai-uploader :instance="instance" fileType="file" :limit="9" v-if="comp.type=='file'"></ai-uploader>
|
||||
<el-radio-group v-if="comp.type=='miniProgrom'" class="radio-group-wrap">
|
||||
<div class="radio-wrap" v-for="(item,index) in 5" :key="index">
|
||||
<el-radio label="none">
|
||||
<span class="iconfont iconwenmingxiangfeng"></span>
|
||||
小程序名称
|
||||
</el-radio>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</ai-card>
|
||||
|
||||
<ai-card title="群发消息内容" v-if="!params.isAdd && !isEditMsg">
|
||||
<template #right>
|
||||
<el-button icon="iconfont iconjdq_led_edit" size="mini">编辑</el-button>
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-wrapper label-width="96px">
|
||||
<ai-info-item label="文本内容" isLine>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</ai-info-item>
|
||||
<ai-info-item label="小程序" isLine>
|
||||
<div class="radio-group-wrap">
|
||||
<div class="radio-wrap" v-for="(item,index) in 5" :key="index">
|
||||
<span class="iconfont iconwenmingxiangfeng"></span>
|
||||
<span style="margin-left: 8px;">小程序名称</span>
|
||||
</div>
|
||||
</div>
|
||||
</ai-info-item>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</ai-card>
|
||||
|
||||
</template>
|
||||
|
||||
<template slot="footer" v-if="params.isAdd">
|
||||
<el-button style="width:120px">取消</el-button>
|
||||
<el-button type="primary" style="width:120px">提 交</el-button>
|
||||
</template>
|
||||
</ai-detail>
|
||||
<ai-dialog
|
||||
title="选择标签"
|
||||
width="800px"
|
||||
:visible.sync="dialog"
|
||||
:destroyOnClose="true"
|
||||
@onConfirm="onConfirm">
|
||||
<el-form ref="form" label-width="80px">
|
||||
<el-form-item label="标签规则">
|
||||
<el-radio-group>
|
||||
<el-radio :label="0">以下标签满足其一</el-radio>
|
||||
<el-radio :label="1">以下标签同时满足</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "NewClientMass",
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
type: "0"
|
||||
},
|
||||
isAdd: true,
|
||||
isEditInfo:false,
|
||||
isEditMsg:false,
|
||||
dialog: false,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function,
|
||||
params:Object
|
||||
},
|
||||
|
||||
computed: {
|
||||
formRules() {
|
||||
return {
|
||||
documentName: [{required: true, message: "请输入公文名称", trigger: 'change'}],
|
||||
documentCode: [{required: true, message: "请输入公文编号", trigger: 'change'}],
|
||||
documentType: [{required: true, message: "请选择公文类型", trigger: 'change'}],
|
||||
readType: [{required: true, message: "请选择阅示类型", trigger: 'change'}],
|
||||
}
|
||||
},
|
||||
msgConnect() {
|
||||
return [
|
||||
{type: 'none', label: ''},
|
||||
{type: 'img', label: '图片'},
|
||||
{type: 'link', label: '链接'},
|
||||
{type: 'file', label: "附件"},
|
||||
{type: 'video', label: "视频"},
|
||||
{type: 'miniProgrom', label: "小程序"},
|
||||
].filter(e => e.type == this.form.type)
|
||||
}
|
||||
},
|
||||
create() {
|
||||
},
|
||||
methods: {
|
||||
editInfo(){
|
||||
this.$emit("change",{
|
||||
type:"NewClientMass",
|
||||
row:{row:this.params.row,isAdd:true}
|
||||
})
|
||||
this.isEditInfo=true
|
||||
},
|
||||
onConfirm(){
|
||||
|
||||
},
|
||||
getSelect() {
|
||||
|
||||
},
|
||||
onBack() {
|
||||
this.$emit("change", {
|
||||
type: 'TableList'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.new-client-mass {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background: #f3f6f9;
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
min-height: 32px;
|
||||
line-height: 32px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d0d4dc;
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
&:after {
|
||||
content: "请选择...";
|
||||
color: #888888;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-width {
|
||||
width: 328px;
|
||||
}
|
||||
|
||||
.radio-group-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.radio-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 17px;
|
||||
width: 400px;
|
||||
height: 60px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #D0D4DC;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .AiPersonSelect {
|
||||
& > button {
|
||||
background: #F5F5F5;
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
border: 1px solid #D0D4DC;
|
||||
color: #222222;
|
||||
}
|
||||
}
|
||||
|
||||
.person-select {
|
||||
background: #F5F5F5;
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
border: 1px solid #D0D4DC;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
::v-deep .msg-title {
|
||||
& > .aibar {
|
||||
justify-content: flex-start;
|
||||
|
||||
& > .aibar-right {
|
||||
margin-left: 64px;
|
||||
font-size: 14px;
|
||||
color: #888888 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
141
packages/wxwork/AppClientMassTextin/components/TableList.vue
Normal file
141
packages/wxwork/AppClientMassTextin/components/TableList.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<ai-list isTabs>
|
||||
<template slot="content">
|
||||
<ai-search-bar class="search-bar" bottomBorder>
|
||||
<template slot="left">
|
||||
<el-button type="primary" icon="iconfont iconAdd" @click="newClient">新建群发</el-button>
|
||||
</template>
|
||||
<template slot="right">
|
||||
<el-input
|
||||
v-model="search.name"
|
||||
class="search-input"
|
||||
size="small"
|
||||
v-throttle="() => {search.current = 1, getList()}"
|
||||
placeholder="请输入标题或编号"
|
||||
clearable
|
||||
@clear="search.current = 1, search.name = '', getList()"
|
||||
suffix-icon="iconfont iconSearch" />
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:stripe="true"
|
||||
:total="total"
|
||||
ref="aitableex"
|
||||
style="margin-top: 20px;"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size"
|
||||
@getList="getList">
|
||||
<el-table-column slot="identityNumber" label="群发内容" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-popover
|
||||
placement="right"
|
||||
width="400"
|
||||
trigger="hover">
|
||||
<div>您可以查询到本村(居)的基本情况、小微权力公开情况,并进行留言评价。您自己操作一下看看,是不是很简单?</div>
|
||||
<!-- <ai-file-list :fileList="fileList" :fileOps="{name: 'name', size: 'fileSizeStr'}"></ai-file-list>-->
|
||||
<span style="cursor: pointer;" slot="reference">{{row.identityNumber}}</span>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span class="table-btn" title="提醒发送" @click="remind(row.id)">提醒发送</span>
|
||||
<span class="table-btn" title="详情" @click="toDetail(row)">详情</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TableList',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions:Function
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
fileList:[],
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
name: ''
|
||||
},
|
||||
total: 0,
|
||||
colConfigs: [
|
||||
{ prop: 'name', label: '群发类型', align: 'center', width: '200' },
|
||||
{ prop: 'phone', label: '类型', align: 'center' },
|
||||
{ slot: 'identityNumber', label: '群发内容', align: 'center'},
|
||||
{ prop: 'registTime', label: '发送时间', align: 'center' },
|
||||
{ prop: 'recordUser', label: '已发送成员', align: 'center' },
|
||||
{ prop: 'recordUser', label: '未发送成员', align: 'center' },
|
||||
{ prop: 'recordUser', label: '已送达成员', align: 'center' },
|
||||
{ prop: 'recordUser', label: '未送达成员', align: 'center' },
|
||||
{ slot: 'options', label: '操作', align: 'center' }
|
||||
],
|
||||
tableData: [],
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.getList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
toDetail(row){
|
||||
this.$emit("change",{
|
||||
type:"NewClientMass",
|
||||
row:{row,isAdd:false}
|
||||
})
|
||||
},
|
||||
newClient(){
|
||||
this.$emit("change",{
|
||||
type:"NewClientMass",
|
||||
row:{row:"",isAdd:true}
|
||||
})
|
||||
},
|
||||
|
||||
getList() {
|
||||
this.instance.post(`/app/apppetition/list`, null, {
|
||||
params: {
|
||||
...this.search,
|
||||
status: 1
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
remind (id) {
|
||||
|
||||
},
|
||||
|
||||
onAdd () {
|
||||
this.$emit('change', {
|
||||
type: 'add'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-btn{
|
||||
font-size: 14px;
|
||||
color: #2266FF;
|
||||
cursor: pointer;
|
||||
&:nth-child(1){
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
119
packages/wxwork/AppMsgTemplate/AppMsgTemplate.vue
Normal file
119
packages/wxwork/AppMsgTemplate/AppMsgTemplate.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<ai-list v-if="showList">
|
||||
<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"
|
||||
:name="String(i)"
|
||||
>
|
||||
<component
|
||||
:is="tab.comp"
|
||||
v-if="currIndex === String(i)"
|
||||
:ref="currIndex"
|
||||
:instance="instance"
|
||||
:dict="dict"
|
||||
@change="changeDetail"
|
||||
></component>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
</ai-list>
|
||||
|
||||
<component
|
||||
v-else
|
||||
@change="changeDetail"
|
||||
:is="componentName"
|
||||
:params="params"
|
||||
:instance="instance"
|
||||
:dict="dict"
|
||||
></component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import commonList from './components/commonList.vue'
|
||||
import systemList from './components/systemList.vue'
|
||||
import Detail from './components/Detail'
|
||||
import detailSystem from './components/detailSystem'
|
||||
|
||||
export default {
|
||||
label: '消息推送',
|
||||
name: 'AppMsgTemplate',
|
||||
// 组件
|
||||
components: {
|
||||
commonList,
|
||||
systemList,
|
||||
Detail,
|
||||
detailSystem
|
||||
},
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currIndex: '0',
|
||||
showList: true,
|
||||
componentName: '',
|
||||
params: {}
|
||||
}
|
||||
},
|
||||
// 计算
|
||||
computed: {
|
||||
tabs() {
|
||||
return [
|
||||
{
|
||||
label: '公共模板',
|
||||
name: 'commonList',
|
||||
comp: commonList
|
||||
},
|
||||
{
|
||||
label: '系统模板',
|
||||
name: 'systemList',
|
||||
comp: systemList
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
// 监听
|
||||
watch: {},
|
||||
// 实例创建后
|
||||
created() {},
|
||||
// 实例渲染后
|
||||
mounted() {},
|
||||
// 方法
|
||||
methods: {
|
||||
changeDetail(data) {
|
||||
if (data.type === 'detail') {
|
||||
this.showList = false
|
||||
this.componentName = 'Detail'
|
||||
this.params = data.params
|
||||
}
|
||||
if (data.type === 'detailSystem') {
|
||||
this.showList = false
|
||||
this.componentName = 'detailSystem'
|
||||
this.params = data.params
|
||||
}
|
||||
if (data.type === 'list') {
|
||||
this.showList = true
|
||||
this.$nextTick(() => {
|
||||
if (data.isRefresh) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.appmsgtemplate {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
194
packages/wxwork/AppMsgTemplate/components/Detail.vue
Normal file
194
packages/wxwork/AppMsgTemplate/components/Detail.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<!-- <div> -->
|
||||
<ai-detail class="detail">
|
||||
<template slot="title">
|
||||
<ai-title
|
||||
title="详情"
|
||||
:isShowBack="true"
|
||||
@onBackClick="onBack"
|
||||
isShowBottomBorder
|
||||
>
|
||||
</ai-title>
|
||||
|
||||
<ai-wrapper label-width="80px" :columnsNumber="1">
|
||||
<!-- 消息类型 -->
|
||||
<ai-info-item label="消息类型">
|
||||
<span v-for="(item, index) in types" :key="index">
|
||||
<span v-if="item.label == infoList.msgtype">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</span>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 名称 -->
|
||||
<ai-info-item label="名称">
|
||||
<span>{{ infoList.name }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 文本 -->
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'text'">
|
||||
<span>{{ infoList.content }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 图片 -->
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'image'">
|
||||
<img
|
||||
:src="infoList.media.file.accessUrl"
|
||||
alt=""
|
||||
style="width:100px;height:100px;"
|
||||
/>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 视频 -->
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'video'">
|
||||
<video
|
||||
:src="infoList.media.file.accessUrl"
|
||||
alt=""
|
||||
style="width:100px;height:100px;"
|
||||
/>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 音频 -->
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'voice'">
|
||||
<ai-audio
|
||||
:src="infoList.media.file.accessUrl"
|
||||
alt=""
|
||||
style="width:100px;height:100px;"
|
||||
></ai-audio>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item
|
||||
label="标题"
|
||||
v-if="
|
||||
infoList.msgtype == 'video' ||
|
||||
infoList.msgtype == 'voice' ||
|
||||
infoList.msgtype == 'news'
|
||||
"
|
||||
>
|
||||
<span>{{ infoList.title }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item
|
||||
label="文件描述"
|
||||
v-if="
|
||||
infoList.msgtype != 'text' &&
|
||||
infoList.msgtype != 'image' &&
|
||||
infoList.msgtype != 'voice'
|
||||
"
|
||||
>
|
||||
<span>{{ infoList.description }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="跳转地址" v-if="infoList.msgtype == 'news'">
|
||||
<span>{{ infoList.url }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="创建人">
|
||||
<span>{{ infoList.createUserName }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="创建时间">
|
||||
<span>{{ infoList.createTime }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="图片链接" v-if="infoList.msgtype == 'news'">
|
||||
<span>{{ infoList.picurl }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="是否启用">
|
||||
<span>{{
|
||||
this.dict.getLabel('wxAppMsgTemplateStatus', infoList.status)
|
||||
}}</span>
|
||||
</ai-info-item>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</ai-detail>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Detail',
|
||||
// 组件
|
||||
components: {},
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
params: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
infoList: []
|
||||
}
|
||||
},
|
||||
// 计算
|
||||
computed: {
|
||||
types() {
|
||||
return [
|
||||
{ name: '文本', label: 'text' },
|
||||
{ name: '图片', label: 'image' },
|
||||
{ name: '视频', label: 'video' },
|
||||
// { name: '附件', label: 'file' },
|
||||
{ name: '音频', label: 'voice' },
|
||||
{ name: '文本卡片', label: 'textcard' },
|
||||
{ name: '图文', label: 'news' }
|
||||
// { name: '图文消息', label: 'mpnews' }
|
||||
]
|
||||
}
|
||||
},
|
||||
// 监听
|
||||
watch: {},
|
||||
// 实例创建后
|
||||
onShow() {},
|
||||
created() {
|
||||
this.dict.load('wxAppMsgTemplateStatus').then(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
// 实例渲染后
|
||||
mounted() {},
|
||||
// 方法
|
||||
methods: {
|
||||
getList(id) {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxapplicationmsgtemplate/detail?id=${this.params.id}`)
|
||||
.then(res => {
|
||||
if (res.code === 0) {
|
||||
this.infoList = res.data
|
||||
// this.grooupTableData = res.data.groupInfos
|
||||
}
|
||||
})
|
||||
},
|
||||
onBack() {
|
||||
this.$emit('change', {
|
||||
type: 'list'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.detail {
|
||||
.AiTitle {
|
||||
::v-deep.ai-detail__title {
|
||||
// padding: 10px 0 0 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-wrapper {
|
||||
margin-top: 10px;
|
||||
padding-top: 20px;
|
||||
background-color: #fff;
|
||||
.ai-info-item {
|
||||
.ai-info-item__right {
|
||||
video {
|
||||
width: 85px;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
511
packages/wxwork/AppMsgTemplate/components/commonList.vue
Normal file
511
packages/wxwork/AppMsgTemplate/components/commonList.vue
Normal file
@@ -0,0 +1,511 @@
|
||||
<template class="commonList">
|
||||
<section>
|
||||
<ai-list>
|
||||
<template slot="content">
|
||||
<el-button class="btn" style="margin-bottom: 10px;" type="primary" icon="iconfont iconAdd" size="small" @click="showAdd">添加公共模板</el-button>
|
||||
|
||||
<ai-table :tableData="tableData" :col-configs="colConfigs" :total="page.total" :current.sync="page.current" :size.sync="page.size" @getList="getList" :dict="dict" style="height: 610px;overflow: auto;">
|
||||
<!-- 消息类型 -->
|
||||
<el-table-column slot="msgtype" label="消息类型" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-for="(item, index) in types" :key="index">
|
||||
<span v-if="item.label == row.msgtype">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<el-table-column slot="content" label="消息内容" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-left__wrapper">
|
||||
<img :src="row.media.file && row.media.file.accessUrl" v-if="row.media && (row.msgtype == 'image' || row.msgtype == 'news')" class="media" style="width: 40px;height: 40px;" />
|
||||
|
||||
<video :src="row.media.file && row.media.file.accessUrl" v-if="row.media && row.msgtype == 'video'" class="media" style="width: 40px;height: 40px;"></video>
|
||||
|
||||
<ai-audio :src="row.media.file && row.media.file.accessUrl" v-if="row.media && row.msgtype == 'voice'" class="media" style="width: 40px;height: 40px;"></ai-audio>
|
||||
|
||||
<div class="table-left__wrapper--right">
|
||||
<el-tooltip class="item" effect="dark" :content="row.content" placement="top">
|
||||
<div v-if="row.msgtype != 'news'">
|
||||
{{ row.content }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column slot="options" label="操作" align="center" fixed="right" width="200">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<ai-wechat-selecter style="display:inline-block;margin-right:8px ;" v-if="row.status == 1" :instance="instance" @change="(h) => release(h, row)">
|
||||
<el-button type="text">发送</el-button>
|
||||
</ai-wechat-selecter>
|
||||
|
||||
<el-button type="text" @click="remove(row.id)">删除</el-button>
|
||||
<el-button type="text" @click="toEdit(row)">编辑</el-button>
|
||||
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
|
||||
<!-- 新增的弹窗 -->
|
||||
<ai-dialog :title="dialog.title" :visible.sync="visible" @onCancel="onCancel" @onConfirm="addConfirm" width="800px">
|
||||
<div class="form_div">
|
||||
<el-form ref="ruleForm" :model="dialogInfo" :rules="formRules" size="small" label-suffix=":" label-width="100px">
|
||||
<!-- 新增名称 -->
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input clearable placeholder="请输入名称" v-model="dialogInfo.name" show-word-limit :maxlength="128"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增状态 -->
|
||||
<el-form-item label="状态" prop="status">
|
||||
<ai-select v-model="dialogInfo.status" placeholder="请选择状态" :selectList="$dict.getDict('wxAppMsgTemplateStatus')"></ai-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增消息类型 -->
|
||||
<el-form-item label="消息类型" prop="msgtype">
|
||||
<el-radio-group v-model="dialogInfo.msgtype" @change="onChange">
|
||||
<el-radio :label="item.label" v-for="(item, index) in types" :key="index">{{ item.name }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增图片 -->
|
||||
<el-form-item :label="compLabel" prop="sysFileId" v-if="dialogInfo.msgtype == 'image' || dialogInfo.msgtype == 'video' || dialogInfo.msgtype == 'voice' || dialogInfo.msgtype == 'news'">
|
||||
<ai-uploader :instance="instance" v-model="fileList" :acceptType="acceptType" :url="'/app/wxcp/upload/uploadFile?type=' + fileTypeList" isWechat :fileType="fileType" :limit="1"></ai-uploader>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增消息类型对应的消息内容 -->
|
||||
<el-form-item label="消息内容" prop="content" v-if="dialogInfo.msgtype == 'text'">
|
||||
<el-input type="textarea" placeholder="请输入消息内容" :maxlength="2048" show-word-limit v-model="dialogInfo.content"> </el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增标题 -->
|
||||
<el-form-item label="标题" prop="title" v-if="dialogInfo.msgtype != 'text' && dialogInfo.msgtype != 'image' && dialogInfo.msgtype != 'voice'">
|
||||
<el-input clearable placeholder="请输入标题" v-model="dialogInfo.title" show-word-limit :maxlength="128"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增文件描述 -->
|
||||
<el-form-item label="文件描述" prop="description" v-if="dialogInfo.msgtype != 'text' && dialogInfo.msgtype != 'image' && dialogInfo.msgtype != 'voice'">
|
||||
<el-input clearable placeholder="请输入文件描述" v-model="dialogInfo.description" show-word-limit :maxlength="512" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增跳转地址 -->
|
||||
<el-form-item label="跳转地址" prop="url" v-if="dialogInfo.msgtype == 'news' || dialogInfo.msgtype == 'textcard'">
|
||||
<el-input clearable placeholder="请输入要跳转的链接地址http/https" v-model="dialogInfo.url" show-word-limit :maxlength="128" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增图片链接 -->
|
||||
<el-form-item label="图片链接" prop="picurl" v-if="dialogInfo.msgtype != 'text' && dialogInfo.msgtype != 'video' && dialogInfo.msgtype != 'textcard' && dialogInfo.msgtype != 'voice' && dialogInfo.msgtype != 'image'">
|
||||
<el-input clearable placeholder="请输入图片链接" v-model="dialogInfo.picurl" show-word-limit :maxlength="128" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="dialog-footer" slot="footer">
|
||||
<el-button @click="onCancel" size="medium">取消</el-button>
|
||||
<el-button @click="onConfirm('ruleForm')" type="primary" size="medium">确认</el-button>
|
||||
</div>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'commonList',
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
},
|
||||
|
||||
data() {
|
||||
var checkAge = (rule, value, callback) => {
|
||||
if (this.dialogInfo.msgtype == 'image' && this.fileList.length == 0) {
|
||||
return callback(new Error('请上传图片'))
|
||||
} else if (this.dialogInfo.msgtype == 'file' && this.fileList.length == 0) {
|
||||
return callback(new Error('请上传文件'))
|
||||
} else if (this.dialogInfo.msgtype == 'video' && this.fileList.length == 0) {
|
||||
return callback(new Error('请上传视频'))
|
||||
} else if (this.dialogInfo.msgtype == 'voice' && this.fileList.length == 0) {
|
||||
return callback(new Error('请上传音频'))
|
||||
} else if (this.dialogInfo.msgtype == 'news' && this.fileList.length == 0) {
|
||||
return callback(new Error('请上传图片'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
visible: false,
|
||||
total: 10,
|
||||
colConfigs: [
|
||||
{
|
||||
prop: 'name',
|
||||
label: '名称',
|
||||
},
|
||||
{
|
||||
slot: 'msgtype',
|
||||
label: '类型',
|
||||
},
|
||||
// {
|
||||
// slot: 'content',
|
||||
// label: '消息内容'
|
||||
// },
|
||||
// {
|
||||
// prop: 'title',
|
||||
// label: '标题'
|
||||
// },
|
||||
{
|
||||
prop: 'createUserName',
|
||||
label: '创建人',
|
||||
'show-overflow-tooltip': true,
|
||||
},
|
||||
{ prop: 'createTime', label: '创建时间' },
|
||||
{
|
||||
prop: 'status',
|
||||
label: '状态',
|
||||
dict: 'wxAppMsgTemplateStatus',
|
||||
},
|
||||
{ slot: 'options', label: '操作', align: 'center' },
|
||||
],
|
||||
tableData: [],
|
||||
page: {
|
||||
size: 10,
|
||||
current: 1,
|
||||
total: 0,
|
||||
},
|
||||
dialog: {
|
||||
title: '',
|
||||
visible: false,
|
||||
},
|
||||
dialogInfo: {
|
||||
name: '',
|
||||
title: '',
|
||||
msgtype: 'text',
|
||||
content: '',
|
||||
createUserName: '',
|
||||
createTime: '',
|
||||
status: '',
|
||||
id: '',
|
||||
isSystem: 0,
|
||||
mediaId: '',
|
||||
sysFileId: '',
|
||||
media: {},
|
||||
description: '',
|
||||
url: '',
|
||||
picurl: '',
|
||||
},
|
||||
fileList: [],
|
||||
formRules: {
|
||||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||||
content: [{ required: true, message: '请输入消息内容', trigger: 'blur' }],
|
||||
sysFileId: [{ required: true, validator: checkAge, trigger: 'change' }],
|
||||
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '请输入文件描述或链接', trigger: 'blur' }],
|
||||
url: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入要跳转的链接地址http/https',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
picurl: [{ required: true, message: '请输入图片链接', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'blur' }],
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
types() {
|
||||
return [
|
||||
{ name: '文本', label: 'text' },
|
||||
{ name: '图片', label: 'image' },
|
||||
{ name: '视频', label: 'video' },
|
||||
// { name: '附件', label: 'file' },
|
||||
// { name: '音频', label: 'voice' },
|
||||
{ name: '文本卡片', label: 'textcard' },
|
||||
{ name: '图文', label: 'news' },
|
||||
// { name: '图文消息', label: 'mpnews' }
|
||||
]
|
||||
},
|
||||
|
||||
compLabel() {
|
||||
return this.types.find((e) => e.label == this.dialogInfo.msgtype)?.name
|
||||
},
|
||||
fileTypeList() {
|
||||
var type = ''
|
||||
if (this.dialogInfo.msgtype == 'text' || this.dialogInfo.msgtype == 'textcard') {
|
||||
type = 'text'
|
||||
} else if (this.dialogInfo.msgtype == 'image' || this.dialogInfo.msgtype == 'news') {
|
||||
type = 'image'
|
||||
} else if (this.dialogInfo.msgtype == 'video') {
|
||||
type = 'video'
|
||||
} else if (this.dialogInfo.msgtype == 'voice') {
|
||||
type = 'voice'
|
||||
}
|
||||
return type
|
||||
},
|
||||
fileType() {
|
||||
// return this.dialogInfo.msgtype == 'image' ? 'img' : 'file'
|
||||
var type = ''
|
||||
if (this.dialogInfo.msgtype == 'image' || this.dialogInfo.msgtype == 'news') {
|
||||
type = 'img'
|
||||
} else {
|
||||
type = 'file'
|
||||
}
|
||||
return type
|
||||
},
|
||||
acceptType() {
|
||||
return {
|
||||
image: '.jpg,.png,.jpeg',
|
||||
video: '.mp4',
|
||||
voice: '.AMR', // 无效的设置
|
||||
}[this.dialogInfo.msgtype]
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.dict.load('wxAppMsgTemplateStatus').then(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList() {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxapplicationmsgtemplate/list`, null, {
|
||||
params: {
|
||||
...this.page,
|
||||
isSystem: 0,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 增加按钮
|
||||
showAdd() {
|
||||
this.dialog.title = '添加'
|
||||
this.dialogInfo.msgtype = 'text'
|
||||
this.visible = true
|
||||
this.getList()
|
||||
},
|
||||
|
||||
// 确定增加
|
||||
addConfirm() {
|
||||
var media = null
|
||||
if (this.dialogInfo.msgtype != 'text' && this.fileList.length) {
|
||||
media = {
|
||||
mediaId: this.fileList[0].media.mediaId,
|
||||
createdAt: this.fileList[0].media.createdAt,
|
||||
sysFileId: this.fileList[0].id,
|
||||
type: this.dialogInfo.msgtype,
|
||||
url: this.fileList[0].url,
|
||||
}
|
||||
}
|
||||
|
||||
this.$refs['ruleForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxapplicationmsgtemplate/addOrUpdate`, {
|
||||
createTime: this.dialogInfo.createTime,
|
||||
createUserName: this.dialogInfo.createUserName,
|
||||
status: this.dialogInfo.status,
|
||||
id: this.dialogInfo.id,
|
||||
isSystem: 0,
|
||||
msgtype: this.dialogInfo.msgtype,
|
||||
// mediaId: this.fileList[0].media.mediaId,
|
||||
// sysFileId: this.fileList[0].id
|
||||
name: this.dialogInfo.name,
|
||||
title: this.dialogInfo.title,
|
||||
content: this.dialogInfo.content,
|
||||
description: this.dialogInfo.description,
|
||||
url: this.dialogInfo.url,
|
||||
picurl: this.dialogInfo.picurl,
|
||||
media,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('操作成功')
|
||||
this.getList()
|
||||
this.visible = false
|
||||
this.infoInit()
|
||||
}
|
||||
})
|
||||
.catch((err) => {})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 新增完成后初始化页面
|
||||
infoInit() {
|
||||
for (let key in this.dialogInfo) {
|
||||
this.dialogInfo[key] = ''
|
||||
}
|
||||
this.dialogInfo.msgtype = 'text'
|
||||
this.dialogInfo.media = {}
|
||||
this.dialogInfo.isSystem = '0'
|
||||
this.fileList = []
|
||||
},
|
||||
|
||||
// 删除
|
||||
remove(id) {
|
||||
this.$confirm('删除后不可恢复,是否要删除该事项?', {
|
||||
type: 'error',
|
||||
}).then(() => {
|
||||
this.instance.post(`/app/wxcp/wxapplicationmsgtemplate/delete?id=${id}`).then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 编辑
|
||||
toEdit(row) {
|
||||
const info = JSON.parse(JSON.stringify(row))
|
||||
this.dialog.title = '编辑'
|
||||
this.visible = true
|
||||
this.dialogInfo = { ...info }
|
||||
this.fileList = [
|
||||
{
|
||||
accessUrl: '',
|
||||
url: '',
|
||||
id: '',
|
||||
media: {
|
||||
mediaId: '',
|
||||
createdAt: '',
|
||||
sysFileId: '',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
if (this.dialogInfo.msgtype != 'text' && this.dialogInfo.msgtype != 'textcard') {
|
||||
this.dialogInfo.media == info.media
|
||||
|
||||
this.fileList[0].accessUrl = info.media.file.accessUrl
|
||||
this.fileList[0].url = info.media.file.url
|
||||
this.fileList[0].id = info.media.sysFileId
|
||||
|
||||
this.fileList[0].media.mediaId = info.media.mediaId
|
||||
this.fileList[0].media.createdAt = info.media.createdAt
|
||||
this.fileList[0].media.sysFileId = info.media.sysFileId
|
||||
}
|
||||
},
|
||||
|
||||
// 详情
|
||||
toDetail(id) {
|
||||
this.$emit('change', {
|
||||
type: 'detail',
|
||||
params: {
|
||||
type: 'commonListDetail',
|
||||
id: id,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
// 发送
|
||||
release(person, row) {
|
||||
// let typeList = ['templateId', 'toParty', 'toTag']
|
||||
// person[0].type==0?'toParty':'templateId'
|
||||
let typeList = ['toUser', 'toParty']
|
||||
var type = typeList[person[0].type]
|
||||
// console.log(type)
|
||||
|
||||
// 姓名id
|
||||
const nameList = []
|
||||
person.map((item) => {
|
||||
if (item.type == 0) {
|
||||
nameList.push(item.id)
|
||||
}
|
||||
})
|
||||
var toUser = nameList.join('|')
|
||||
// console.log(toUser)
|
||||
|
||||
// 部门id
|
||||
const typesList = []
|
||||
person.map((item) => {
|
||||
if (item.type == 1) {
|
||||
typesList.push(item.name)
|
||||
}
|
||||
})
|
||||
var typeType = typesList.join('|')
|
||||
// console.log(typeType)
|
||||
|
||||
this.instance
|
||||
.post('/app/wxcp/wxapplicationmsgtemplate/sendByTemplate', {
|
||||
type: typesList.join('|'), // 部门id,如果只发送给个人,则此处为空
|
||||
toUser: nameList.join('|'),
|
||||
templateId: row.id,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res?.code == 0) {
|
||||
this.$message.success('发送成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// radio多选框切换
|
||||
onChange() {
|
||||
this.$refs['ruleForm'].clearValidate()
|
||||
this.dialogInfo.name = ''
|
||||
this.dialogInfo.content = ''
|
||||
this.dialogInfo.title = ''
|
||||
this.dialogInfo.description = ''
|
||||
this.dialogInfo.url = ''
|
||||
this.dialogInfo.picurl = ''
|
||||
this.dialogInfo.status = ''
|
||||
this.fileList = []
|
||||
},
|
||||
|
||||
// 新增弹窗的取消按钮
|
||||
onCancel() {
|
||||
this.dialogInfo.name = ''
|
||||
this.dialogInfo.content = ''
|
||||
this.dialogInfo.title = ''
|
||||
this.dialogInfo.description = ''
|
||||
this.dialogInfo.url = ''
|
||||
this.dialogInfo.picurl = ''
|
||||
this.dialogInfo.status = ''
|
||||
this.fileList = []
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.commonList {
|
||||
// overflow: hidden;
|
||||
// white-space: nowrap;
|
||||
// text-overflow: ellipsis;
|
||||
height: 100%;
|
||||
background: #f3f6f9;
|
||||
::v-deep.btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
::v-deep.cell {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
::v-deep.table-left__wrapper {
|
||||
::v-deep .img {
|
||||
// object-fit: fill;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
// vertical-align: middle;
|
||||
// box-sizing: content-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
193
packages/wxwork/AppMsgTemplate/components/detailSystem.vue
Normal file
193
packages/wxwork/AppMsgTemplate/components/detailSystem.vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<ai-detail class="detail">
|
||||
<template slot="title">
|
||||
<ai-title
|
||||
title="详情"
|
||||
:isShowBack="true"
|
||||
@onBackClick="onBack"
|
||||
isShowBottomBorder
|
||||
>
|
||||
</ai-title>
|
||||
|
||||
<ai-wrapper label-width="80px" :columnsNumber="1">
|
||||
<ai-info-item label="消息类型">
|
||||
<span v-for="(item, index) in types" :key="index">
|
||||
<span v-if="item.label == infoList.msgtype">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="名称">
|
||||
<span>{{ infoList.name }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'text'">
|
||||
<span>{{ infoList.content }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 图片 -->
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'image'">
|
||||
<img
|
||||
:src="infoList.media.file.accessUrl"
|
||||
alt=""
|
||||
style="width:100px;height:100px;"
|
||||
/>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 视频 -->
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'video'">
|
||||
<video
|
||||
:src="infoList.media.file.accessUrl"
|
||||
alt=""
|
||||
style="width:100px;height:100px;"
|
||||
/>
|
||||
</ai-info-item>
|
||||
|
||||
<!-- 音频 -->
|
||||
<ai-info-item label="消息内容" v-if="infoList.msgtype == 'voice'">
|
||||
<ai-audio
|
||||
:src="infoList.media.file.accessUrl"
|
||||
alt=""
|
||||
style="width:100px;height:100px;"
|
||||
></ai-audio>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item
|
||||
label="标题"
|
||||
v-if="
|
||||
infoList.msgtype == 'video' ||
|
||||
infoList.msgtype == 'voice' ||
|
||||
infoList.msgtype == 'news'
|
||||
"
|
||||
>
|
||||
<span>{{ infoList.title }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item
|
||||
label="文件描述"
|
||||
v-if="
|
||||
infoList.msgtype != 'text' &&
|
||||
infoList.msgtype != 'image' &&
|
||||
infoList.msgtype != 'voice'
|
||||
"
|
||||
>
|
||||
<span>{{ infoList.description }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="跳转地址" v-if="infoList.msgtype == 'news'">
|
||||
<span>{{ infoList.url }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="创建人">
|
||||
<span>{{ infoList.createUserName }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="创建时间">
|
||||
<span>{{ infoList.createTime }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="图片链接" v-if="infoList.msgtype == 'news'">
|
||||
<span>{{ infoList.picurl }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="标识">
|
||||
<span>{{ infoList.uniqueId }}</span>
|
||||
</ai-info-item>
|
||||
|
||||
<ai-info-item label="是否启用">
|
||||
<span>{{
|
||||
this.dict.getLabel('wxAppMsgTemplateStatus', infoList.status)
|
||||
}}</span>
|
||||
</ai-info-item>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Detail',
|
||||
// 组件
|
||||
components: {},
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
params: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
infoList: []
|
||||
}
|
||||
},
|
||||
// 计算
|
||||
computed: {
|
||||
types() {
|
||||
return [
|
||||
{ name: '文本', label: 'text' },
|
||||
{ name: '图片', label: 'image' },
|
||||
{ name: '视频', label: 'video' },
|
||||
// { name: '附件', label: 'file' },
|
||||
{ name: '音频', label: 'voice' },
|
||||
{ name: '文本卡片', label: 'textcard' },
|
||||
{ name: '图文', label: 'news' }
|
||||
// { name: '图文消息', label: 'mpnews' }
|
||||
]
|
||||
}
|
||||
},
|
||||
// 监听
|
||||
watch: {},
|
||||
// 实例创建后
|
||||
onShow() {},
|
||||
created() {
|
||||
this.dict.load('wxAppMsgTemplateStatus').then(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
// 实例渲染后
|
||||
mounted() {},
|
||||
// 方法
|
||||
methods: {
|
||||
getList(id) {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxapplicationmsgtemplate/detail?id=${this.params.id}`)
|
||||
.then(res => {
|
||||
if (res.code === 0) {
|
||||
this.infoList = res.data
|
||||
// this.grooupTableData = res.data.groupInfos
|
||||
}
|
||||
})
|
||||
},
|
||||
onBack() {
|
||||
this.$emit('change', {
|
||||
type: 'list'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.detail {
|
||||
.AiTitle {
|
||||
::v-deep.ai-detail__title {
|
||||
// padding: 10px 0 0 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-wrapper {
|
||||
margin-top: 10px;
|
||||
padding-top: 20px;
|
||||
background-color: #fff;
|
||||
.ai-info-item {
|
||||
.ai-info-item__right {
|
||||
video {
|
||||
width: 85px;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
733
packages/wxwork/AppMsgTemplate/components/systemList.vue
Normal file
733
packages/wxwork/AppMsgTemplate/components/systemList.vue
Normal file
@@ -0,0 +1,733 @@
|
||||
<template>
|
||||
<div class="systemList">
|
||||
<section>
|
||||
<ai-list>
|
||||
<template slot="content">
|
||||
<el-alert
|
||||
class="el-alert"
|
||||
title="系统模板为内置模板,如非对业务熟悉,请勿修改或删除"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
</el-alert>
|
||||
|
||||
<el-button
|
||||
class="btn"
|
||||
style="margin-bottom: 10px;"
|
||||
type="primary"
|
||||
icon="iconfont iconAdd"
|
||||
size="small"
|
||||
@click="showAdd"
|
||||
>添加系统模板</el-button
|
||||
>
|
||||
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
ref="aitableex"
|
||||
:total="page.total"
|
||||
:current.sync="page.current"
|
||||
:size.sync="page.size"
|
||||
@getList="getList"
|
||||
:dict="dict"
|
||||
style="height: 610px;overflow: auto;"
|
||||
>
|
||||
<!-- 消息类型 -->
|
||||
<el-table-column slot="msgtype" label="消息类型" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-for="(item, index) in types" :key="index">
|
||||
<span v-if="item.label == row.msgtype">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<el-table-column slot="content" label="消息内容" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-left__wrapper">
|
||||
<img
|
||||
:src="row.media.file && row.media.file.accessUrl"
|
||||
v-if="
|
||||
row.media &&
|
||||
(row.msgtype == 'image' || row.msgtype == 'news')
|
||||
"
|
||||
class="media"
|
||||
style="width: 40px;height: 40px;"
|
||||
/>
|
||||
|
||||
<video
|
||||
:src="row.media.file && row.media.file.accessUrl"
|
||||
v-if="row.media && row.msgtype == 'video'"
|
||||
class="media"
|
||||
style="width: 40px;height: 40px;"
|
||||
></video>
|
||||
|
||||
<audio
|
||||
:src="row.media.file && row.media.file.accessUrl"
|
||||
v-if="row.media && row.msgtype == 'voice'"
|
||||
class="media"
|
||||
style="width: 40px;height: 40px;"
|
||||
></audio>
|
||||
|
||||
<div class="table-left__wrapper--right">
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
:content="row.content"
|
||||
placement="top"
|
||||
>
|
||||
<div v-if="row.msgtype != 'news'">
|
||||
{{ row.content }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column
|
||||
slot="options"
|
||||
label="操作"
|
||||
align="center"
|
||||
fixed="right"
|
||||
width="200"
|
||||
>
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<ai-wechat-selecter
|
||||
style="display:inline-block;margin-right:8px ;"
|
||||
v-if="row.status == 1"
|
||||
:instance="instance"
|
||||
@change="h => release(h, row)"
|
||||
>
|
||||
<el-button type="text">发送</el-button>
|
||||
</ai-wechat-selecter>
|
||||
|
||||
<el-button type="text" @click="remove(row.id)"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="text" @click="toEdit(row)">编辑</el-button>
|
||||
<el-button type="text" @click="toDetail(row.id)"
|
||||
>详情</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
|
||||
<!-- 新增的弹窗 -->
|
||||
<ai-dialog
|
||||
:title="dialog.title"
|
||||
:visible.sync="visible"
|
||||
@onCancel="onCancel"
|
||||
@onConfirm="addConfirm"
|
||||
width="800px"
|
||||
>
|
||||
<div class="form_div">
|
||||
<el-form
|
||||
ref="ruleForm"
|
||||
:model="dialogInfo"
|
||||
:rules="formRules"
|
||||
size="small"
|
||||
label-suffix=":"
|
||||
label-width="100px"
|
||||
>
|
||||
<!-- 新增名称 -->
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入名称"
|
||||
v-model="dialogInfo.name"
|
||||
show-word-limit
|
||||
:maxlength="128"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增标识uniqueId -->
|
||||
<el-form-item label="标识" prop="uniqueId">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入标识"
|
||||
v-model="dialogInfo.uniqueId"
|
||||
show-word-limit
|
||||
:maxlength="128"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增状态 -->
|
||||
<el-form-item label="状态" prop="status">
|
||||
<ai-select
|
||||
v-model="dialogInfo.status"
|
||||
placeholder="请选择状态"
|
||||
:selectList="$dict.getDict('wxAppMsgTemplateStatus')"
|
||||
></ai-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增消息类型 -->
|
||||
<el-form-item label="消息类型" prop="msgtype">
|
||||
<el-radio-group v-model="dialogInfo.msgtype" @change="onChange">
|
||||
<el-radio
|
||||
:label="item.label"
|
||||
v-for="(item, index) in types"
|
||||
:key="index"
|
||||
>{{ item.name }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="compLabel"
|
||||
prop="sysFileId"
|
||||
v-if="
|
||||
dialogInfo.msgtype != 'text' && dialogInfo.msgtype != 'textcard'
|
||||
"
|
||||
>
|
||||
<ai-uploader
|
||||
:instance="instance"
|
||||
v-model="fileList"
|
||||
:acceptType="acceptType"
|
||||
:url="'/app/wxcp/upload/uploadFile?type=' + fileTypeList"
|
||||
isWechat
|
||||
:fileType="fileType"
|
||||
:limit="1"
|
||||
></ai-uploader>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增消息类型对应的消息内容 -->
|
||||
<el-form-item
|
||||
label="消息内容"
|
||||
prop="content"
|
||||
v-if="dialogInfo.msgtype == 'text'"
|
||||
>
|
||||
<el-input
|
||||
type="textarea"
|
||||
placeholder="请输入消息内容"
|
||||
:maxlength="2048"
|
||||
show-word-limit
|
||||
v-model="dialogInfo.content"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增标题 -->
|
||||
<el-form-item
|
||||
label="标题"
|
||||
prop="title"
|
||||
v-if="
|
||||
dialogInfo.msgtype != 'text' &&
|
||||
dialogInfo.msgtype != 'image' &&
|
||||
dialogInfo.msgtype != 'voice'
|
||||
"
|
||||
>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入标题"
|
||||
v-model="dialogInfo.title"
|
||||
show-word-limit
|
||||
:maxlength="128"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增文件描述 -->
|
||||
<el-form-item
|
||||
label="文件描述"
|
||||
prop="description"
|
||||
v-if="
|
||||
dialogInfo.msgtype != 'text' &&
|
||||
dialogInfo.msgtype != 'image' &&
|
||||
dialogInfo.msgtype != 'voice'
|
||||
"
|
||||
>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入文件描述"
|
||||
v-model="dialogInfo.description"
|
||||
show-word-limit
|
||||
:maxlength="512"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增跳转地址 -->
|
||||
<el-form-item
|
||||
label="跳转地址"
|
||||
prop="url"
|
||||
v-if="
|
||||
dialogInfo.msgtype == 'news' || dialogInfo.msgtype == 'textcard'
|
||||
"
|
||||
>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入要跳转的链接地址http/https"
|
||||
v-model="dialogInfo.url"
|
||||
show-word-limit
|
||||
:maxlength="128"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 新增图片链接 -->
|
||||
<el-form-item
|
||||
label="图片链接"
|
||||
prop="picurl"
|
||||
v-if="
|
||||
dialogInfo.msgtype != 'text' &&
|
||||
dialogInfo.msgtype != 'video' &&
|
||||
dialogInfo.msgtype != 'textcard' &&
|
||||
dialogInfo.msgtype != 'voice' &&
|
||||
dialogInfo.msgtype != 'image'
|
||||
"
|
||||
>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入图片链接"
|
||||
v-model="dialogInfo.picurl"
|
||||
show-word-limit
|
||||
:maxlength="128"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="dialog-footer" slot="footer">
|
||||
<el-button @click="onCancel" size="medium">取消</el-button>
|
||||
<el-button @click="onConfirm('ruleForm')" type="primary" size="medium"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'systemList',
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data() {
|
||||
var checkAge = (rule, value, callback) => {
|
||||
if (this.dialogInfo.msgtype == 'image' && this.fileList.length == 0) {
|
||||
return callback(new Error('请上传图片'))
|
||||
} else if (
|
||||
this.dialogInfo.msgtype == 'file' &&
|
||||
this.fileList.length == 0
|
||||
) {
|
||||
return callback(new Error('请上传文件'))
|
||||
} else if (
|
||||
this.dialogInfo.msgtype == 'video' &&
|
||||
this.fileList.length == 0
|
||||
) {
|
||||
return callback(new Error('请上传视频'))
|
||||
} else if (
|
||||
this.dialogInfo.msgtype == 'voice' &&
|
||||
this.fileList.length == 0
|
||||
) {
|
||||
return callback(new Error('请上传音频'))
|
||||
} else if (
|
||||
this.dialogInfo.msgtype == 'news' &&
|
||||
this.fileList.length == 0
|
||||
) {
|
||||
return callback(new Error('请上传图片'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
visible: false,
|
||||
|
||||
total: 10,
|
||||
colConfigs: [
|
||||
{
|
||||
prop: 'name',
|
||||
label: '名称'
|
||||
},
|
||||
{
|
||||
slot: 'msgtype',
|
||||
label: '类型'
|
||||
},
|
||||
// {
|
||||
// slot: 'content',
|
||||
// label: '消息内容'
|
||||
// },
|
||||
// {
|
||||
// prop: 'title',
|
||||
// label: '标题'
|
||||
// },
|
||||
{
|
||||
prop: 'uniqueId',
|
||||
label: '标识'
|
||||
},
|
||||
{
|
||||
prop: 'createUserName',
|
||||
label: '创建人',
|
||||
'show-overflow-tooltip': true
|
||||
},
|
||||
{ prop: 'createTime', label: '创建时间' },
|
||||
{
|
||||
prop: 'status',
|
||||
label: '状态',
|
||||
dict: 'wxAppMsgTemplateStatus'
|
||||
},
|
||||
{ slot: 'options', label: '操作', align: 'center' }
|
||||
],
|
||||
tableData: [],
|
||||
page: {
|
||||
size: 10,
|
||||
current: 1,
|
||||
total: 0
|
||||
},
|
||||
dialog: {
|
||||
title: '',
|
||||
visible: false
|
||||
},
|
||||
dialogInfo: {
|
||||
title: '',
|
||||
msgtype: 'text',
|
||||
content: '',
|
||||
createUserName: '',
|
||||
createTime: '',
|
||||
status: '',
|
||||
id: '',
|
||||
isSystem: 1,
|
||||
mediaId: '',
|
||||
sysFileId: '',
|
||||
media: {},
|
||||
description: '',
|
||||
url: '',
|
||||
picurl: '',
|
||||
uniqueId: ''
|
||||
},
|
||||
fileList: [],
|
||||
formRules: {
|
||||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||||
content: [
|
||||
{ required: true, message: '请输入消息内容', trigger: 'blur' }
|
||||
],
|
||||
sysFileId: [{ required: true, validator: checkAge, trigger: 'change' }],
|
||||
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
|
||||
description: [
|
||||
{ required: true, message: '请输入文件描述或链接', trigger: 'blur' }
|
||||
],
|
||||
uniqueId: [{ required: true, message: '请输入标识', trigger: 'blur' }],
|
||||
url: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入要跳转的链接地址http/https',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
picurl: [
|
||||
{ required: true, message: '请输入图片链接', trigger: 'blur' }
|
||||
],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
types() {
|
||||
return [
|
||||
{ name: '文本', label: 'text' },
|
||||
{ name: '图片', label: 'image' },
|
||||
{ name: '视频', label: 'video' },
|
||||
// { name: '附件', label: 'file' },
|
||||
// { name: '音频', label: 'voice' },
|
||||
{ name: '文本卡片', label: 'textcard' },
|
||||
{ name: '图文', label: 'news' }
|
||||
// { name: '图文消息', label: 'mpnews' }
|
||||
]
|
||||
},
|
||||
|
||||
compLabel() {
|
||||
return this.types.find(e => e.label == this.dialogInfo.msgtype)?.name
|
||||
},
|
||||
fileTypeList() {
|
||||
var type = ''
|
||||
if (
|
||||
this.dialogInfo.msgtype == 'text' ||
|
||||
this.dialogInfo.msgtype == 'textcard'
|
||||
) {
|
||||
type = 'text'
|
||||
} else if (
|
||||
this.dialogInfo.msgtype == 'image' ||
|
||||
this.dialogInfo.msgtype == 'news'
|
||||
) {
|
||||
type = 'image'
|
||||
} else if (this.dialogInfo.msgtype == 'video') {
|
||||
type = 'video'
|
||||
} else if (this.dialogInfo.msgtype == 'voice') {
|
||||
type = 'voice'
|
||||
}
|
||||
return type
|
||||
},
|
||||
fileType() {
|
||||
// return this.dialogInfo.msgtype == 'image' ? 'img' : 'file'
|
||||
var type = ''
|
||||
if (
|
||||
this.dialogInfo.msgtype == 'image' ||
|
||||
this.dialogInfo.msgtype == 'news'
|
||||
) {
|
||||
type = 'img'
|
||||
} else {
|
||||
type = 'file'
|
||||
}
|
||||
return type
|
||||
},
|
||||
acceptType() {
|
||||
return {
|
||||
image: '.jpg,.png,.jpeg',
|
||||
video: '.mp4',
|
||||
voice: '.AMR' // 无效的设置
|
||||
}[this.dialogInfo.msgtype]
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.dict.load(['wxAppMsgTemplateStatus']).then(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList() {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxapplicationmsgtemplate/list`, null, {
|
||||
params: {
|
||||
...this.page,
|
||||
isSystem: 1
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 新增按钮
|
||||
showAdd() {
|
||||
this.dialog.title = '添加'
|
||||
this.dialogInfo.msgtype = 'text'
|
||||
this.visible = true
|
||||
this.getList()
|
||||
},
|
||||
|
||||
// 确定新增
|
||||
addConfirm() {
|
||||
var media = null
|
||||
if (this.dialogInfo.msgtype != 'text' && this.fileList.length) {
|
||||
media = {
|
||||
mediaId: this.fileList[0].media.mediaId,
|
||||
createdAt: this.fileList[0].media.createdAt,
|
||||
sysFileId: this.fileList[0].id,
|
||||
type: this.dialogInfo.msgtype,
|
||||
title: this.dialogInfo.title,
|
||||
url: this.dialogInfo.url
|
||||
}
|
||||
}
|
||||
|
||||
this.$refs['ruleForm'].validate(valid => {
|
||||
if (valid) {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxapplicationmsgtemplate/addOrUpdate`, {
|
||||
createTime: this.dialogInfo.createTime,
|
||||
createUserName: this.dialogInfo.createUserName,
|
||||
status: this.dialogInfo.status,
|
||||
id: this.dialogInfo.id,
|
||||
isSystem: 1,
|
||||
msgtype: this.dialogInfo.msgtype,
|
||||
title: this.dialogInfo.title,
|
||||
name: this.dialogInfo.name,
|
||||
content: this.dialogInfo.content,
|
||||
description: this.dialogInfo.description,
|
||||
url: this.dialogInfo.url,
|
||||
picurl: this.dialogInfo.picurl,
|
||||
uniqueId: this.dialogInfo.uniqueId,
|
||||
media
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('操作成功')
|
||||
this.getList()
|
||||
this.visible = false
|
||||
this.dialogInfo = {}
|
||||
this.infoInit()
|
||||
}
|
||||
})
|
||||
.catch(err => {})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 新增完成后初始化页面
|
||||
infoInit() {
|
||||
for (let key in this.dialogInfo) {
|
||||
this.dialogInfo[key] = ''
|
||||
}
|
||||
this.dialogInfo.msgtype = 'text'
|
||||
this.dialogInfo.media = {}
|
||||
this.dialogInfo.isSystem = '0'
|
||||
this.fileList = []
|
||||
},
|
||||
|
||||
// 删除
|
||||
remove(id) {
|
||||
this.$confirm('删除后不可恢复,是否要删除该事项?', {
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxapplicationmsgtemplate/delete?id=${id}`)
|
||||
.then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 编辑
|
||||
toEdit(row) {
|
||||
const info = JSON.parse(JSON.stringify(row))
|
||||
this.dialog.title = '编辑'
|
||||
this.visible = true
|
||||
this.dialogInfo = { ...info }
|
||||
this.fileList = [
|
||||
{
|
||||
accessUrl: '',
|
||||
url: '',
|
||||
id: '',
|
||||
media: {
|
||||
mediaId: '',
|
||||
createdAt: '',
|
||||
sysFileId: ''
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
if (
|
||||
this.dialogInfo.msgtype != 'text' &&
|
||||
this.dialogInfo.msgtype != 'textcard'
|
||||
) {
|
||||
this.dialogInfo.media == info.media
|
||||
|
||||
this.fileList[0].accessUrl = info.media.file.accessUrl
|
||||
this.fileList[0].url = info.media.file.url
|
||||
this.fileList[0].id = info.media.sysFileId
|
||||
|
||||
this.fileList[0].media.mediaId = info.media.mediaId
|
||||
this.fileList[0].media.createdAt = info.media.createdAt
|
||||
this.fileList[0].media.sysFileId = info.media.sysFileId
|
||||
}
|
||||
},
|
||||
|
||||
// 查看详情
|
||||
toDetail(id) {
|
||||
this.$emit('change', {
|
||||
type: 'detailSystem',
|
||||
params: {
|
||||
type: 'detailSystemList',
|
||||
id: id
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 发送
|
||||
release(person, row) {
|
||||
// let typeList = ['templateId', 'toParty', 'toTag']
|
||||
let typeList = ['toUser', 'toParty']
|
||||
var type = typeList[person[0].type]
|
||||
// console.log(type)
|
||||
|
||||
// 姓名id
|
||||
const nameList = []
|
||||
person.map(item => {
|
||||
if (item.type == 0) {
|
||||
nameList.push(item.id)
|
||||
}
|
||||
})
|
||||
var toUser = nameList.join('|')
|
||||
// console.log(toUser)
|
||||
|
||||
// 部门id
|
||||
const typesList = []
|
||||
person.map(item => {
|
||||
if (item.type == 1) {
|
||||
typesList.push(item.name)
|
||||
}
|
||||
})
|
||||
var typeType = typesList.join('|')
|
||||
// console.log(typeType)
|
||||
|
||||
this.instance
|
||||
.post('/app/wxcp/wxapplicationmsgtemplate/sendByTemplate', {
|
||||
type: typesList.join('|'), // 部门id,如果只发送给个人,则此处为空
|
||||
toUser: nameList.join('|'),
|
||||
templateId: row.id
|
||||
})
|
||||
.then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.$message.success('发送成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// radio多选框切换
|
||||
onChange() {
|
||||
this.$refs['ruleForm'].clearValidate()
|
||||
this.dialogInfo.name = ''
|
||||
this.dialogInfo.content = ''
|
||||
this.dialogInfo.title = ''
|
||||
this.dialogInfo.description = ''
|
||||
this.dialogInfo.url = ''
|
||||
this.dialogInfo.picurl = ''
|
||||
this.dialogInfo.status = ''
|
||||
this.dialogInfo.uniqueId = ''
|
||||
this.fileList = []
|
||||
},
|
||||
|
||||
// 新增弹窗的取消按钮
|
||||
onCancel() {
|
||||
this.dialogInfo.name = ''
|
||||
this.dialogInfo.content = ''
|
||||
this.dialogInfo.title = ''
|
||||
this.dialogInfo.description = ''
|
||||
this.dialogInfo.url = ''
|
||||
this.dialogInfo.picurl = ''
|
||||
this.dialogInfo.status = ''
|
||||
this.dialogInfo.uniqueId = ''
|
||||
this.fileList = []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.systemList {
|
||||
// overflow: hidden;
|
||||
// white-space: nowrap;
|
||||
// text-overflow: ellipsis;
|
||||
height: 100%;
|
||||
.el-alert {
|
||||
margin-bottom: 10px;
|
||||
// ::v-deep.el-alert__content {
|
||||
// color: red;
|
||||
// }
|
||||
::v-deep.el-alert__closebtn {
|
||||
color: #8d96a9;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
81
packages/wxwork/AppNotification/AppNotification.vue
Normal file
81
packages/wxwork/AppNotification/AppNotification.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<section style="height: 100%;">
|
||||
<ai-list v-if="!showDetail">
|
||||
<template slot="title">
|
||||
<ai-title title="通知公告"></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" :name="String(i)">
|
||||
<component :is="tab.comp" v-if="currIndex==i" :ref="currIndex" :instance="instance" :dict="dict"
|
||||
@goPage="goPage"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
</ai-list>
|
||||
<component v-else :is="currentComp" :instance="instance" :dict="dict" :detail="detailRow" @gotoEdit="gotoAdd" ></component>
|
||||
</section>
|
||||
</template>
|
||||
<script>
|
||||
import add from './components/add';
|
||||
import detail from './components/detail'
|
||||
import manageDetail from './components/manageDetail'
|
||||
import recentNotice from './components/recentNotice'
|
||||
import noticeManage from './components/noticeManage'
|
||||
|
||||
export default {
|
||||
name: 'AppNotification',
|
||||
label: "通知公告",
|
||||
components: {add, detail, recentNotice,noticeManage,manageDetail},
|
||||
provide() {
|
||||
return {
|
||||
top: this
|
||||
}
|
||||
},
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currIndex: "0",
|
||||
currentComp: "",
|
||||
showDetail: false,
|
||||
detailRow: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tabs() {
|
||||
return [
|
||||
{label: "最新公告", name: "recentNotice", comp: recentNotice, detail: detail},
|
||||
{label: "公告管理", name: "noticeManage", comp: noticeManage, detail: manageDetail},
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
goPage(params) {
|
||||
params.row && (this.detailRow = params.row)
|
||||
this.currentComp = params.comp
|
||||
|
||||
if (params.comp == 'detail' || params.comp == 'add' || params.comp == "manageDetail") {
|
||||
this.showDetail = true
|
||||
}
|
||||
},
|
||||
goBack() {
|
||||
this.showDetail = false;
|
||||
},
|
||||
gotoAdd(obj) {
|
||||
this.showDetail = true
|
||||
this.detailRow = obj
|
||||
this.currentComp = 'add'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .ai-list__content--right-wrapper {
|
||||
background-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
</style>
|
||||
186
packages/wxwork/AppNotification/components/add.vue
Normal file
186
packages/wxwork/AppNotification/components/add.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<ai-detail>
|
||||
<template slot="title">
|
||||
<ai-title :title="detail.id ? '编辑公告' : '创建公告'" isShowBack isShowBottomBorder
|
||||
@onBackClick="$parent.goBack"></ai-title>
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-card title="基本信息">
|
||||
<template #content>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="公告标题" prop="title">
|
||||
<el-input v-model="form.title" size="small" placeholder="请输入" show-word-limit maxlength="30"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="公告内容" prop="content">
|
||||
<ai-editor v-model="form.content" :instance="instance"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="附件">
|
||||
<ai-uploader :instance="instance" v-model="form.files" fileType="file" isShowTip></ai-uploader>
|
||||
</el-form-item>
|
||||
<el-form-item label="发送对象" prop="persons">
|
||||
<el-row type="flex" align="middle">
|
||||
<div class="text-area">
|
||||
<span v-text="item.name"/>
|
||||
<span v-if="persons.length">等{{ persons.length }}人</span>
|
||||
</div>
|
||||
<ai-wechat-selecter v-model="form.persons" :instance="instance" @change="onChange">
|
||||
<el-button type="info">选择</el-button>
|
||||
</ai-wechat-selecter>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-row type="flex">
|
||||
<el-form-item label="发送时间" prop="type">
|
||||
<el-radio-group v-model="form.type" @change="form.releaseTime = null">
|
||||
<el-radio :label="0">立即发送</el-radio>
|
||||
<el-radio :label="1">定时发送</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="releaseTime" class="picker" v-if="form.type==1">
|
||||
<el-date-picker
|
||||
v-model="form.releaseTime"
|
||||
style="margin-left: 10px;"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
size="small"
|
||||
type="datetime"
|
||||
placeholder="请选择">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="$parent.goBack">取消</el-button>
|
||||
<el-button type="primary" @click="confim(0)">保存</el-button>
|
||||
<el-button type="primary" @click="confim(1)">发布</el-button>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "add",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
detail: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
id: null,
|
||||
title: "",
|
||||
content: "",
|
||||
files: [],
|
||||
persons: [],
|
||||
type: 0,
|
||||
releaseTime: null,
|
||||
},
|
||||
persons: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rules() {
|
||||
return {
|
||||
title: [
|
||||
{required: true, message: '请输入公告标题'},
|
||||
],
|
||||
content: [
|
||||
{required: true, message: '请输入公告内容'},
|
||||
],
|
||||
persons: [
|
||||
{required: true, message: '请选择发送对象'},
|
||||
],
|
||||
type: [
|
||||
{required: true},
|
||||
],
|
||||
releaseTime: [
|
||||
{required: true, message: '请选择发送时间'},
|
||||
],
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(e) {
|
||||
this.form.persons = e;
|
||||
this.persons = e;
|
||||
this.$refs["form"].validateField("persons");
|
||||
},
|
||||
confim(e) {
|
||||
this.$refs["form"].validate(v => {
|
||||
if (v) {
|
||||
if (this.form.releaseTime && (new Date(this.form.releaseTime).getTime() <= Date.now())) {
|
||||
return this.$message.error("发送时间要大于当前时间")
|
||||
}
|
||||
this.instance.post("/app/appannouncement/addOrUpdate", {
|
||||
...this.form,
|
||||
status: e
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success(e == 0 ? "保存成功" : "发布成功");
|
||||
this.$parent.goBack();
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getDetail() {
|
||||
this.instance.post("/app/appannouncement/detail", null, {
|
||||
params: {
|
||||
id: this.detail.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
Object.keys(this.form).map(e => this.form[e] = res.data[e]);
|
||||
this.form.type = res.data.releaseTime ? 1 : 0;
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.detail?.id) {
|
||||
this.getDetail();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .picker {
|
||||
width: 300px;
|
||||
|
||||
.el-form-item__content {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-area {
|
||||
width: 995px;
|
||||
height: 32px;
|
||||
background-color: #F5F5F5;
|
||||
border: 1px solid #d0d4dc;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0 6px;
|
||||
color: #666666;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
::v-deep .AiOpenData {
|
||||
height: auto !important;
|
||||
|
||||
&:after {
|
||||
content: "、";
|
||||
}
|
||||
|
||||
&:nth-child(2):after {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
</style>
|
||||
95
packages/wxwork/AppNotification/components/detail.vue
Normal file
95
packages/wxwork/AppNotification/components/detail.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<ai-detail>
|
||||
<template slot="title">
|
||||
<ai-title title="公告详情" isShowBack isShowBottomBorder @onBackClick="$parent.goBack"></ai-title>
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-card :title="detailObj.title" class="title">
|
||||
<template #content>
|
||||
<el-row type="flex" justify="space-between" class="info">
|
||||
<span>时间:{{ detailObj.releaseTime }}</span>
|
||||
<span style="display:flex">发布单位:
|
||||
<span v-text="detailObj.unitName"/>
|
||||
</span>
|
||||
<span style="display:flex">发布人:
|
||||
<span v-text="detailObj.releaseUserName"/>
|
||||
</span>
|
||||
</el-row>
|
||||
<div v-html="detailObj.content" style="margin: 20px 0;"></div>
|
||||
</template>
|
||||
</ai-card>
|
||||
<ai-card title="附件" v-if="detailObj.files && detailObj.files.length">
|
||||
<template #content>
|
||||
<el-row type="flex" justify="space-between" class="file" v-for="(item,index) in detailObj.files" :key="index"
|
||||
@click.native="open(item)">
|
||||
<span>{{ item.fileName }}</span>
|
||||
<span>{{ (item.size / 1024).toFixed(2) }}KB</span>
|
||||
</el-row>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "detail",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
detail: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
detailObj: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(item) {
|
||||
window.open(item.url);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.instance.post("/app/appannouncement/detail", null, {
|
||||
params: {
|
||||
id: this.detail.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
this.detailObj = res.data;
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .title {
|
||||
.aibar-left {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 8px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d0d4dc;
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.info {
|
||||
& > span {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
204
packages/wxwork/AppNotification/components/manageDetail.vue
Normal file
204
packages/wxwork/AppNotification/components/manageDetail.vue
Normal file
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<ai-detail>
|
||||
<template slot="title">
|
||||
<ai-title title="公告详情" isShowBack isShowBottomBorder @onBackClick="$parent.goBack"></ai-title>
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-sidebar v-model="index" :tabTitle="tabTitle" @change="onChange"></ai-sidebar>
|
||||
<template v-if="index==0">
|
||||
<ai-card :title="detailObj.title" class="title">
|
||||
<template #content>
|
||||
<el-row type="flex" justify="space-between" class="info">
|
||||
<span>时间:{{ detailObj.releaseTime }}</span>
|
||||
<span style="display:flex">发布单位:
|
||||
<span v-text="detailObj.unitName"/>
|
||||
</span>
|
||||
<span style="display:flex">发布人:
|
||||
<span v-text="detailObj.releaseUserName"/>
|
||||
</span>
|
||||
</el-row>
|
||||
<div v-html="detailObj.content" style="margin: 20px 0;"></div>
|
||||
</template>
|
||||
</ai-card>
|
||||
<ai-card title="附件" v-if="detailObj.files && detailObj.files.length">
|
||||
<template #right>
|
||||
<span class="Edit" @click="downFileAll"><i class="iconfont iconDownload"></i>下载全部</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<ai-file-list :fileList="detailObj.files" :fileOps="{ name: 'fileName', size: 'size' }"></ai-file-list>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ai-list>
|
||||
<template #content>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<ai-select
|
||||
v-model="search.readStatus"
|
||||
@change="search.current=1,getList()"
|
||||
placeholder="查阅状态"
|
||||
:selectList="dict.getDict('announcementReadStatus')"
|
||||
></ai-select>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input v-model="search.readUserName" @keyup.enter.native="getList()" placeholder="姓名"
|
||||
size="small" suffix-icon="iconfont iconSearch" clearable
|
||||
@clear="search.current=1,getList()"></el-input>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size"
|
||||
@getList="getList">
|
||||
<el-table-column slot="readUserName" label="姓名" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-text="row.readUserName"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="unitName" label="所属部门" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-text="row.unitName"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "manageDetail",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
detail: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
index: 0,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
detailObj: {},
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tabTitle() {
|
||||
return ["公告详情", "查询情况"];
|
||||
},
|
||||
colConfigs() {
|
||||
return [
|
||||
{slot: "readUserName"},
|
||||
{prop: "readUserPhone", label: "手机号", align: "center"},
|
||||
{slot: "unitName"},
|
||||
{
|
||||
prop: "readStatus", label: "查阅状态", align: "center",
|
||||
render: (h, {row}) => [<span
|
||||
style={{color: this.dict.getColor("announcementReadStatus", row.readStatus)}}>{this.dict.getLabel("announcementReadStatus", row.readStatus)}</span>]
|
||||
},
|
||||
];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
downFileAll() {
|
||||
if (this.detailObj.files.length > 0) {
|
||||
this.instance.post('/app/appannouncement/downLoadAllFileForDetail', null, {
|
||||
responseType: 'blob',
|
||||
params: {
|
||||
id: this.detailObj.id
|
||||
}
|
||||
}).then((res) => {
|
||||
const link = document.createElement('a')
|
||||
let blob = new Blob([res], {type: 'application/octet-stream'})
|
||||
link.style.display = 'none'
|
||||
link.href = URL.createObjectURL(blob)
|
||||
var num = ''
|
||||
for (let i = 0; i < 10; i++) {
|
||||
num += Math.ceil(Math.random() * 10)
|
||||
}
|
||||
link.setAttribute('download', '公告文件' + '.zip')
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
})
|
||||
} else {
|
||||
this.$message.error('暂无附件提供下载')
|
||||
}
|
||||
},
|
||||
onChange(val) {
|
||||
if (val == 0) {
|
||||
this.getDetail();
|
||||
} else {
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
getDetail() {
|
||||
this.instance.post("/app/appannouncement/detail", null, {
|
||||
params: {
|
||||
id: this.detail.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
this.detailObj = res.data;
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
getList() {
|
||||
this.instance.post("/app/appannouncementreader/list", null, {
|
||||
params: {
|
||||
announcementId: this.detail.id,
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
this.tableData = res.data.records;
|
||||
this.total = res.data.total;
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.dict.load("announcementReadStatus").then(this.getDetail);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .title {
|
||||
.aibar-left {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 8px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d0d4dc;
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.info {
|
||||
& > span {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
267
packages/wxwork/AppNotification/components/noticeManage.vue
Normal file
267
packages/wxwork/AppNotification/components/noticeManage.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<section style="height: 100%;">
|
||||
<ai-list isTabs>
|
||||
<template slot="content">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<ai-select v-model="search.status" placeholder="发布状态" :selectList="dict.getDict('announcementStatus')"
|
||||
@change="search.current = 1, getList()"></ai-select>
|
||||
<el-date-picker
|
||||
type="daterange"
|
||||
size="small"
|
||||
v-model="date"
|
||||
@change="search.current = 1,getList()"
|
||||
range-separator="至"
|
||||
value-format="yyyy-MM-dd"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期">
|
||||
</el-date-picker>
|
||||
</template>
|
||||
<template slot="right">
|
||||
<el-input
|
||||
v-model="search.title"
|
||||
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-search-bar>
|
||||
<template #left>
|
||||
<el-button icon="iconfont iconAdd" type="primary" @click="add">创建公告</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size"
|
||||
@getList="getList">
|
||||
<el-table-column slot="releaseUserName" label="发布人" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-text="row.releaseUserName"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="unitName" label="发布部门" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-text="row.unitName"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column slot="options" width="250" label="操作" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" @click="toDetail(row)">详情</el-button>
|
||||
<el-button type="text" v-if="row.status==0" @click="publish(row,0)">发布</el-button>
|
||||
<el-button type="text" v-if="row.status==1" @click="publish(row,1)">撤回</el-button>
|
||||
<el-button type="text" @click="toEdit(row)" v-if="row.status==0 || row.status==3">编辑</el-button>
|
||||
<el-button type="text" @click="handleDel(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
<ai-dialog
|
||||
title="查阅状态"
|
||||
:visible.sync="visible"
|
||||
@closed="row={},readList=[]"
|
||||
:customFooter="true"
|
||||
width="800px">
|
||||
<template v-if="readObj.read && readObj.read.length">
|
||||
<header>已读人员</header>
|
||||
<div class="wrap">
|
||||
<div class="item" v-for="(item,index) in readObj.read" :key="index">
|
||||
<img :src="item.avatar" alt="">
|
||||
<span v-text="item.name"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="readObj.unRead && readObj.unRead.length">
|
||||
<header>未读人员</header>
|
||||
<div class="wrap">
|
||||
<div class="item" v-for="(item,index) in readObj.unRead" :key="index">
|
||||
<img :src="item.avatar" alt="">
|
||||
<span v-text="item.name"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "noticeManage",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: {
|
||||
title: "",
|
||||
status: "",
|
||||
size: 10,
|
||||
current: 1,
|
||||
},
|
||||
date: [],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
visible: false,
|
||||
row: {},
|
||||
readObj: {},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
colConfigs() {
|
||||
return [
|
||||
{prop: 'title', label: '标题'},
|
||||
{
|
||||
prop: 'readNum', label: '查询状态', align: 'center',
|
||||
render: (h, {row}) => [<span class='status'
|
||||
onClick={this.showDialog.bind(this, row)}>{row.readNum}人已读,</span>,
|
||||
<span class='status' onClick={this.showDialog.bind(this, row)}>{row.unReadNum}人未读</span>]
|
||||
},
|
||||
{slot: 'releaseUserName'},
|
||||
{slot: 'unitName'},
|
||||
{prop: 'releaseTime', label: '发布时间', align: 'center'},
|
||||
{
|
||||
prop: 'status', label: '发布状态', align: 'center',
|
||||
render: (h, {row}) => [<span
|
||||
style={{color: this.dict.getColor("announcementStatus", row.status)}}>{this.dict.getLabel("announcementStatus", row.status)}</span>]
|
||||
},
|
||||
{slot: 'options'},
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
showDialog(row) {
|
||||
this.row = row;
|
||||
this.getReadList();
|
||||
},
|
||||
toDetail(row) {
|
||||
this.$emit('goPage', {
|
||||
comp: 'manageDetail',
|
||||
row
|
||||
});
|
||||
},
|
||||
toEdit(row) {
|
||||
this.$emit('goPage', {
|
||||
comp: 'add',
|
||||
row
|
||||
});
|
||||
},
|
||||
publish(row, status) {
|
||||
this.$confirm(`是否要${status == 0 ? '发布' : '撤回'}该公告?`).then(() => {
|
||||
this.instance.post("/app/appannouncement/update-status", null, {
|
||||
params: {
|
||||
id: row.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success(status == 0 ? '发布成功' : '撤回成功');
|
||||
this.getList();
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
handleDel(row) {
|
||||
this.$confirm("是否要删除该公布?").then(() => {
|
||||
this.instance.post("/app/appannouncement/delete", null, {
|
||||
params: {
|
||||
ids: row.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success("删除成功");
|
||||
this.getList();
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
add() {
|
||||
this.$emit('goPage', {
|
||||
comp: 'add',
|
||||
row: {},
|
||||
});
|
||||
},
|
||||
getReadList() {
|
||||
this.instance.post("/app/appannouncementreader/list-unread", null, {
|
||||
params: {
|
||||
id: this.row.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
this.readObj = res.data;
|
||||
this.visible = true;
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
getList() {
|
||||
this.instance.post("/app/appannouncement/list-mgr", null, {
|
||||
params: {
|
||||
...this.search,
|
||||
startTime: this.date?.length ? this.date[0] : null,
|
||||
endTime: this.date?.length ? this.date[1] : null,
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
this.tableData = res.data.records;
|
||||
this.total = res.data.total;
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("announcementStatus").then(this.getList)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .status {
|
||||
color: rgba(41, 107, 251, 100);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
::v-deep .wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
width: 50px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-right: 22px;
|
||||
margin-bottom: 22px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
217
packages/wxwork/AppNotification/components/recentNotice.vue
Normal file
217
packages/wxwork/AppNotification/components/recentNotice.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<ai-list isTabs>
|
||||
<template #content>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<ai-select
|
||||
v-model="search.readStatus"
|
||||
@change="getList()"
|
||||
placeholder="查阅状态"
|
||||
:selectList="dict.getDict('announcementReadStatus')"
|
||||
></ai-select>
|
||||
<el-date-picker
|
||||
v-model="date"
|
||||
@change="search.current = 1,getList()"
|
||||
type="daterange"
|
||||
size="small"
|
||||
value-format="yyyy-MM-dd"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期">
|
||||
</el-date-picker>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input v-model="search.title" @keyup.enter.native="getList()" placeholder="标题"
|
||||
size="small" suffix-icon="iconfont iconSearch" clearable
|
||||
@clear="search.current=1,getList()"></el-input>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<div class="body">
|
||||
<ul v-if="tableData.length">
|
||||
<li v-for="(item,index) in tableData" :key="index" @click="goDetail(item)">
|
||||
<label>
|
||||
<!-- <em v-if="item.readStatus==0"></em>-->
|
||||
<div class="status" v-if="item.readStatus==0">未读</div>
|
||||
<div class="status read" v-else>已读</div>
|
||||
{{ item.title }}</label>
|
||||
<el-row type="flex" justify="space-between" class="row">
|
||||
<span style="display:flex">
|
||||
<b>发布人:</b>
|
||||
<span v-text="item.releaseUserName"/>
|
||||
</span>
|
||||
<span style="display:flex">
|
||||
<b>发布部门:</b>
|
||||
<span v-text="item.unitName"/>
|
||||
</span>
|
||||
<span>
|
||||
<b>发布日期:</b>
|
||||
{{ item.releaseTime }}</span>
|
||||
</el-row>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="no-data" style="height:160px;" v-else></div>
|
||||
</div>
|
||||
<div class="pagination" v-if="tableData.length>0">
|
||||
<el-pagination
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
background
|
||||
:current-page.sync="search.current"
|
||||
:page-sizes="[5, 10, 50, 100,200]"
|
||||
:page-size="search.size"
|
||||
layout="slot,->,prev, pager, next,sizes,jumper"
|
||||
:total="total">
|
||||
<div class="page" style="text-align: left">共
|
||||
<em>{{ total }}</em>
|
||||
条记录
|
||||
</div>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "recentNotice",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: {
|
||||
readStatus: "",
|
||||
title: "",
|
||||
current: 1,
|
||||
size: 10,
|
||||
},
|
||||
date: [],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pickerChange(e) {
|
||||
console.log(e);
|
||||
},
|
||||
goDetail(item) {
|
||||
this.$emit('goPage', {
|
||||
row: item,
|
||||
comp: 'detail'
|
||||
});
|
||||
},
|
||||
getList() {
|
||||
this.instance.post(`/app/appannouncement/list-latest`, null, {
|
||||
params: {
|
||||
...this.search,
|
||||
startTime: this.date?.length ? this.date[0] : null,
|
||||
endTime: this.date?.length ? this.date[1] : null,
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
this.tableData = res.data.records;
|
||||
this.total = res.data.total;
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.search.current = val;
|
||||
this.getList();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.search.size = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.dict.load("announcementReadStatus").then(_ => this.getList())
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.body {
|
||||
|
||||
ul {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
height: 86px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
padding: 16px 215px 16px 32px;
|
||||
margin-top: 10px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
color: #222222;
|
||||
|
||||
.status {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
background: #FEF4E5;
|
||||
color: #F79300;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.read {
|
||||
background: #EEEEEE;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
em {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #ff4422;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .row {
|
||||
margin-top: 10px;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: #222222;
|
||||
|
||||
b {
|
||||
font-size: 14px;
|
||||
color: #888888;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .page {
|
||||
font-size: 12px;
|
||||
color: #555555;
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
color: rgb(34, 102, 255);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,89 @@
|
||||
<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>
|
||||
<Detail v-else-if="componentName === 'Detail'" :params="params" :instance="instance" :dict="dict" :permissions="permissions" @change="onChange"></Detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import List from './components/List.vue'
|
||||
import Statistics from './components/Statistics'
|
||||
import Tags from './components/Tags'
|
||||
import Detail from './components/Detail'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AppResidentGroupManage',
|
||||
label: '居民群管理',
|
||||
|
||||
components: {
|
||||
List,
|
||||
Tags,
|
||||
Detail,
|
||||
Statistics
|
||||
},
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
|
||||
tabs () {
|
||||
const tabList = [
|
||||
{label: '居民群列表', name: 'List', comp: List, permission: ''},
|
||||
{label: '居民群统计', name: 'Statistics', comp: Statistics, permission: ''},
|
||||
{label: '居民群标签', name: 'Tags', comp: Tags, permission: ''}
|
||||
].filter(item => {
|
||||
return true
|
||||
})
|
||||
|
||||
return tabList
|
||||
}
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
activeName: 'JoinEvent',
|
||||
currIndex: '0',
|
||||
componentName: '',
|
||||
params: {},
|
||||
isShowDetail: false
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
},
|
||||
|
||||
methods: {
|
||||
onChange (data) {
|
||||
if (data.type === 'list') {
|
||||
this.componentName = 'List'
|
||||
this.isShowDetail = false
|
||||
this.params = data.params
|
||||
}
|
||||
|
||||
if (data.type === 'detail') {
|
||||
this.componentName = 'Detail'
|
||||
this.isShowDetail = true
|
||||
this.params = data.params
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
377
packages/wxwork/AppResidentGroupManage/components/Detail.vue
Normal file
377
packages/wxwork/AppResidentGroupManage/components/Detail.vue
Normal file
@@ -0,0 +1,377 @@
|
||||
<template>
|
||||
<ai-detail class="AppResidentManage">
|
||||
<template slot="title">
|
||||
<ai-title title="居民群详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<div class="detail-top">
|
||||
<div class="detail-top__header">
|
||||
<div class="header-left">
|
||||
<img src="https://cdn.cunwuyun.cn/dvcp/h5/groupAvatar.png">
|
||||
<div class="header-left__right">
|
||||
<h2>{{ info.name }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<!-- <div class="header-right__item">-->
|
||||
<!-- <span>成员总数</span>-->
|
||||
<!-- <h3>{{ chartData.groupSum}}</h3>-->
|
||||
<!-- </div>-->
|
||||
<div class="header-right__item">
|
||||
<span>成员总数</span>
|
||||
<h3>{{chartData.today && chartData.today.total }}</h3>
|
||||
</div>
|
||||
<div class="header-right__item">
|
||||
<span>今日新增</span>
|
||||
<h3>{{ chartData.today && chartData.today.increase }}</h3>
|
||||
</div>
|
||||
<div class="header-right__item">
|
||||
<span>今日流失</span>
|
||||
<h3>{{chartData.today && chartData.today.decrease}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-top__content">
|
||||
<ai-wrapper
|
||||
label-width="80px">
|
||||
<ai-info-item label="群主" :value="info.ownerName"></ai-info-item>
|
||||
<ai-info-item label="群公告" :value="info.notice" isLine></ai-info-item>
|
||||
<ai-info-item label="群聊标签" isLine>
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" v-for="(item, index) in info.tagList" size="small" :key="index">{{ item.tagName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</ai-info-item>
|
||||
</ai-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
<ai-card title="图表数据">
|
||||
<template slot="content">
|
||||
<div id="lineChart"></div>
|
||||
</template>
|
||||
</ai-card>
|
||||
<ai-card title="成员列表">
|
||||
<template slot="content">
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
border
|
||||
ref="aitableex"
|
||||
@getList="getDynamicInfo"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size">
|
||||
<el-table-column slot="options" label="操作" width="100" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" v-if="row.type==2 && row.avatar" @click="toDetail(row)">居民详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
params: Object
|
||||
},
|
||||
|
||||
computed: {
|
||||
colConfigs() {
|
||||
return [
|
||||
{
|
||||
prop: 'memberName', label: '成员',render:(h,{row})=>[<img class="avatar" src={row.avatar || "https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png"} />,
|
||||
<span>{row.memberName}</span>,
|
||||
<span style={{color:row.customerType==1 ? '#2EA222' : '#3C7FC8',marginLeft:'8px'}}>{ row.customerType?(row.customerType==1 ? '@微信' : '@' + row.corpName):'' }</span>],
|
||||
},
|
||||
{prop: 'type', label: '类型',render:(h,{row})=>[<span>{this.dict.getLabel("wxGroupMemberType",row.type)}</span>]},
|
||||
{prop: 'joinTime', label: '入群时间'},
|
||||
{prop: 'joinScene', label: '入群方式',render:(h,{row})=>[<span>{this.dict.getLabel("wxGroupMemberJoinScene",row.joinScene)}</span>]},
|
||||
{slot: "options"},
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
info: {},
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10
|
||||
},
|
||||
total: 0,
|
||||
tableData: [],
|
||||
chart: null,
|
||||
chartData: {},
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.dict.load("wxGroupMemberJoinScene", "wxGroupMemberType")
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.params && this.params.id) {
|
||||
this.getInfo()
|
||||
this.getDynamicInfo()
|
||||
this.getChart()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getChart() {
|
||||
this.instance.post(`/app/wxcp/wxgroup/groupStatistic`, null, {
|
||||
params: {
|
||||
id: this.params.id
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
this.chartData = res.data
|
||||
this.initChart()
|
||||
}
|
||||
})
|
||||
},
|
||||
initChart() {
|
||||
this.chart = echarts.init(document.getElementById('lineChart'))
|
||||
this.setOptions()
|
||||
},
|
||||
setOptions() {
|
||||
const x = Object.keys(this.chartData.list)
|
||||
const y = Object.values(this.chartData.list)
|
||||
this.chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
type: "plain"
|
||||
},
|
||||
grid: {
|
||||
left: '20px',
|
||||
right: '38px',
|
||||
bottom: '14px',
|
||||
top: '30px',
|
||||
containLabel: true
|
||||
},
|
||||
color: ['#2266FF', '#22AA99', '#F8B425'],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
axisLabel: {
|
||||
align: 'center',
|
||||
padding: [2, 0, 0, 0],
|
||||
interval: 0,
|
||||
fontSize: 14,
|
||||
color: '#666666'
|
||||
},
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#E1E5EF'
|
||||
}
|
||||
},
|
||||
data: x
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
length: 0,
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: ['#E1E5EF'],
|
||||
width: 1,
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
nameTextStyle: {
|
||||
color: '#666666',
|
||||
align: 'left'
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666666'
|
||||
},
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '成员总数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.total)
|
||||
},
|
||||
{
|
||||
name: '新增成员数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.increase)
|
||||
},
|
||||
{
|
||||
name: '流失成员数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.decrease)
|
||||
}
|
||||
]
|
||||
})
|
||||
},
|
||||
|
||||
getInfo() {
|
||||
this.instance.post(`/app/wxcp/wxgroup/getDetail?id=${this.params.id}`).then(res => {
|
||||
if (res && res.data) {
|
||||
this.info = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getDynamicInfo() {
|
||||
this.instance.post(`/app/wxcp/wxgroup/listMember?groupId=${this.params.id}`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
toDetail(row) {
|
||||
this.$router.push({
|
||||
name: '68',
|
||||
query: {
|
||||
id: row.userId,
|
||||
type: 'Detail'
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
cancel(isRefresh) {
|
||||
this.$emit('change', {
|
||||
type: 'list',
|
||||
isRefresh: isRefresh ? true : false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AppResidentManage {
|
||||
::v-deep .ai-detail__content--wrapper {
|
||||
max-width: 100% !important;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h2, h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.detail-top {
|
||||
padding: 30px 40px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 2px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.detail-top__content {
|
||||
padding-top: 32px;
|
||||
}
|
||||
|
||||
.detail-top__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 32px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
||||
.header-right {
|
||||
.header-right__item {
|
||||
width: 120px;
|
||||
margin-right: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div {
|
||||
text-align: center;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: #888888;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button {
|
||||
height: 28px;
|
||||
margin-left: 8px;
|
||||
border-radius: 14px;
|
||||
font-size: 12px;
|
||||
padding: 7px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-left, .header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 6px;
|
||||
color: #222222;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #2EA222;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#lineChart {
|
||||
width: 100%;
|
||||
height: 336px;
|
||||
}
|
||||
|
||||
.table-tags {
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
object-fit: fill;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
401
packages/wxwork/AppResidentGroupManage/components/List.vue
Normal file
401
packages/wxwork/AppResidentGroupManage/components/List.vue
Normal file
@@ -0,0 +1,401 @@
|
||||
<template>
|
||||
<ai-list class="AppPetitionManage" isTabs>
|
||||
<template slot="content">
|
||||
<ai-search-bar class="search-bar">
|
||||
<template slot="left">
|
||||
<!-- <ai-select-->
|
||||
<!-- v-model="search.owner"-->
|
||||
<!-- filterable-->
|
||||
<!-- @change="search.current = 1, getList()"-->
|
||||
<!-- placeholder="群主"-->
|
||||
<!-- :selectList="userList">-->
|
||||
<!-- </ai-select>-->
|
||||
<ai-select
|
||||
v-model="search.tagId"
|
||||
@change="search.current = 1, getList()"
|
||||
placeholder="选择标签"
|
||||
:selectList="subTags">
|
||||
</ai-select>
|
||||
</template>
|
||||
<template slot="right">
|
||||
<el-input
|
||||
v-model="search.name"
|
||||
class="search-input"
|
||||
size="small"
|
||||
v-throttle="() => {search.current = 1, getList()}"
|
||||
placeholder="请输入群主/群名称"
|
||||
clearable
|
||||
@change="getList"
|
||||
@clear="search.current = 1, search.name = '', getList()"
|
||||
suffix-icon="iconfont iconSearch">
|
||||
</el-input>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-search-bar>
|
||||
<template slot="left">
|
||||
<el-button type="primary" icon="iconfont iconAdd" @click="isShow = true" :disabled="!ids.length">批量打群标签
|
||||
</el-button>
|
||||
</template>
|
||||
<template slot="left">
|
||||
<el-button type="primary" icon="iconfont iconDelete" @click="onConfirm(1)" :disabled="!ids.length">批量移除群标签
|
||||
</el-button>
|
||||
</template>
|
||||
<template slot="right">
|
||||
<el-button type="primary" icon="iconfont iconResetting" @click="update">更新数据</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
ref="aitableex"
|
||||
:current.sync="search.current"
|
||||
@selection-change="handleSelectionChange"
|
||||
:size.sync="search.size"
|
||||
v-loading="isLoading"
|
||||
@getList="getList">
|
||||
<el-table-column slot="avatar" label="" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="avatar" style="text-align: right;justify-content: end;">
|
||||
<img :src="row.avatar">
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="userinfo" label="群名称" show-overflow-tooltip width="300px" align="left">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="userinfo">
|
||||
<div class="userinfo-right ellipsis">
|
||||
<div class="userinfo-right__top">
|
||||
<h3>{{ row.name || "群聊" }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="tags" label="群标签" align="left">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" v-for="(item, index) in row.tagList" size="medium" :key="index">{{ item.tagName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" width="100" fixed="right" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
<ai-dialog
|
||||
:visible.sync="isShow"
|
||||
width="800px"
|
||||
title="批量打标签"
|
||||
@close="chooseTags = []"
|
||||
@onConfirm="onConfirm">
|
||||
<div class="tags">
|
||||
<div class="tag-item" v-for="(item, index) in tags" :key="index">
|
||||
<h2>{{ item.name }}</h2>
|
||||
<div class="tag-item__right">
|
||||
<el-button
|
||||
:type="chooseTags.indexOf(item.id) === -1 ? '' : 'primary'"
|
||||
v-for="(item, index) in item.tagList"
|
||||
@click="choose(item.id)"
|
||||
:key="index">
|
||||
{{ item.name }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ai-dialog>
|
||||
</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,
|
||||
name: '',
|
||||
// tagId: '',
|
||||
owner: ''
|
||||
},
|
||||
isLoading: false,
|
||||
isShow: false,
|
||||
ids: [],
|
||||
total: 10,
|
||||
chooseTags: [],
|
||||
tags: [],
|
||||
colConfigs: [
|
||||
{type: 'selection'},
|
||||
// {slot: 'avatar'},
|
||||
{slot: 'userinfo'},
|
||||
{prop: 'ownerName', label: '群主', align: 'center'},
|
||||
{slot: 'tags'},
|
||||
{
|
||||
prop: 'personCount', label: '群人数', align: 'center'
|
||||
},
|
||||
{prop: 'increase', label: '当日入群人数', align: 'center'},
|
||||
{prop: 'decrease', label: '当日退群人数', align: 'center'},
|
||||
{
|
||||
prop: 'createTime', label: '创建时间', align: 'left'
|
||||
},
|
||||
{slot: 'options', label: '操作', align: 'center'}],
|
||||
tableData: [],
|
||||
subTags: [],
|
||||
userList: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user'])
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.getTags()
|
||||
this.getSubTags()
|
||||
// this.getWxUserList()
|
||||
this.getList()
|
||||
this.isLoading = true
|
||||
},
|
||||
|
||||
methods: {
|
||||
getWxUserList () {
|
||||
this.instance.post(`/app/wxcp/wxuser/listByDepartId`, {
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.userList = res.data.map(item => {
|
||||
item.dictName = item.name
|
||||
item.dictValue = item.id
|
||||
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
update() {
|
||||
this.instance.post(`/app/wxcp/wxgroup/sync`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('更新成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getSubTags() {
|
||||
this.instance.post(`/app/wxcp/wxgroupchattag/listAllTag`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.subTags = res.data?.map(item => {
|
||||
return {
|
||||
dictName: item.name,
|
||||
dictValue: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getList() {
|
||||
this.instance.post(`/app/wxcp/wxgroup/list`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
this.isLoading = false
|
||||
}).catch(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
choose(id) {
|
||||
const index = this.chooseTags.indexOf(id)
|
||||
if (index === -1) {
|
||||
this.chooseTags.push(id)
|
||||
} else {
|
||||
this.chooseTags.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
onConfirm(type = 0) {
|
||||
if (type == 0 && !this.chooseTags.length) {
|
||||
return this.$message.error('请选择标签')
|
||||
}
|
||||
this.instance.post(`/app/wxcp/wxgroupchattag/markTagForWeb`, {
|
||||
tagIds: this.chooseTags, groupIds: this.ids.map(v => v.id), type,
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
type == 0 ? (this.isShow = false) : false
|
||||
this.$message.success(type == 0 ? "添加成功" : "删除成功")
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getTags() {
|
||||
this.instance.post(`/app/wxcp/wxgroupchattag/listAll?size=999`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tags = res.data.records
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
this.$emit('change', {
|
||||
type: 'add'
|
||||
})
|
||||
},
|
||||
|
||||
handleSelectionChange(e) {
|
||||
this.ids = e
|
||||
},
|
||||
|
||||
removeAll() {
|
||||
this.remove(this.ids.map(v => v.id).join(','))
|
||||
},
|
||||
|
||||
remove(id) {
|
||||
this.$confirm('确定删除该数据?').then(() => {
|
||||
this.instance.post(`/app/apppetition/delete?ids=${id}`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
toDetail(id) {
|
||||
this.$emit('change', {
|
||||
type: 'detail', params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
this.$emit('change', {
|
||||
type: 'add'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-tags {
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #D0D4DC;
|
||||
background: #F3F4F7;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
color: #222222;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tags {
|
||||
.tag-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 30px;
|
||||
padding-top: 30px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
h2 {
|
||||
width: 88px;
|
||||
margin-right: 40px;
|
||||
text-align: right;
|
||||
color: #888888;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
text-align: right;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
|
||||
.userinfo-right__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.userinfo-right__bottom {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
font-style: normal;
|
||||
color: #888888;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-right: 8px;
|
||||
color: #222222;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #2EA222;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
207
packages/wxwork/AppResidentGroupManage/components/Statistics.vue
Normal file
207
packages/wxwork/AppResidentGroupManage/components/Statistics.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<ai-list class="statistics" isTabs style="width: 100%">
|
||||
<template slot="content" v-loading="loading">
|
||||
<div class="statistics-top">
|
||||
<div class="statistics-top__item">
|
||||
<span>群聊总数</span>
|
||||
<h2 style="color: #2266FF;">{{ groupSum }}</h2>
|
||||
</div>
|
||||
<div class="statistics-top__item">
|
||||
<span>群成员总人数</span>
|
||||
<h2>{{ info.total }}</h2>
|
||||
</div>
|
||||
<div class="statistics-top__item">
|
||||
<span>今日入群人数</span>
|
||||
<h2 style="color: #22AA99;">{{ info.increase }}</h2>
|
||||
</div>
|
||||
<div class="statistics-top__item">
|
||||
<span>今日退群人数</span>
|
||||
<h2 style="color: #F8B425">{{ info.decrease }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<ai-card title="趋势图">
|
||||
<template #content>
|
||||
<div class="chart" style="height: 340px; width: 100%;"></div>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'Statistics',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
info: {},
|
||||
chartWidth: '',
|
||||
groupSum: "",
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.chart = echarts.init(document.querySelector('.chart'))
|
||||
window.addEventListener('resize', this.onResize)
|
||||
this.getInfo()
|
||||
})
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
window.removeEventListener('resize', this.onResize)
|
||||
},
|
||||
|
||||
methods: {
|
||||
onResize() {
|
||||
this.chart.resize()
|
||||
},
|
||||
|
||||
getInfo() {
|
||||
this.instance.post(`/app/wxcp/wxgroup/groupStatistic`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.info = res.data.today
|
||||
this.groupSum = res.data.groupSum
|
||||
this.initChart(res.data.list)
|
||||
this.loading = false
|
||||
} else {
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
initChart(data) {
|
||||
const x = Object.keys(data)
|
||||
const y = Object.values(data)
|
||||
let option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
type: "plain"
|
||||
},
|
||||
grid: {
|
||||
left: '20px',
|
||||
right: '38px',
|
||||
bottom: '14px',
|
||||
top: '30px',
|
||||
containLabel: true
|
||||
},
|
||||
color: ['#2266FF', '#22AA99', '#F8B425'],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
axisLabel: {
|
||||
align: 'center',
|
||||
padding: [2, 0, 0, 0],
|
||||
interval: 0,
|
||||
fontSize: 14,
|
||||
color: '#666666'
|
||||
},
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#E1E5EF'
|
||||
}
|
||||
},
|
||||
data: x
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
length: 0,
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: ['#E1E5EF'],
|
||||
width: 1,
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
nameTextStyle: {
|
||||
color: '#666666',
|
||||
align: 'left'
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666666'
|
||||
},
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '群成员总数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.total)
|
||||
},
|
||||
{
|
||||
name: '新增入群人数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.increase)
|
||||
},
|
||||
{
|
||||
name: '退群人数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.decrease)
|
||||
}
|
||||
]
|
||||
}
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.statistics {
|
||||
::v-deep .ai-list__content--right-wrapper {
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
padding: 12px 0 12px !important;
|
||||
}
|
||||
|
||||
.statistics-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
& > div {
|
||||
flex: 1;
|
||||
height: 96px;
|
||||
line-height: 1;
|
||||
margin-right: 20px;
|
||||
padding: 16px 24px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
color: #888888;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
201
packages/wxwork/AppResidentGroupManage/components/Tags.vue
Normal file
201
packages/wxwork/AppResidentGroupManage/components/Tags.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<ai-list class="AppPetitionManage" isTabs>
|
||||
<template slot="content">
|
||||
<ai-search-bar>
|
||||
<template slot="left">
|
||||
<el-button type="primary" icon="iconfont iconAdd" @click="id = '', form.name = '', form.tagList = [], isShow = true">添加群标签组</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
ref="aitableex"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size"
|
||||
@getList="getList">
|
||||
<el-table-column slot="tags" label="群标签" align="left">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" v-for="(item, index) in row.tagList" size="small" :key="index">{{ item.name }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" width="180" fixed="right" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<!-- <el-button type="text" @click="edit(row)">添加标签</el-button> -->
|
||||
<el-button type="text" @click="edit(row)">编辑</el-button>
|
||||
<el-button type="text" @click="remove(row.id)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
<ai-dialog
|
||||
:visible.sync="isShow"
|
||||
width="800px"
|
||||
title="添加群标签组"
|
||||
@onConfirm="onConfirm"
|
||||
@onCancel="onCancel">
|
||||
<el-form class="ai-form" ref="form" label-width="120px" :model="form">
|
||||
<el-form-item style="width: 100%" label="群标签组名称" prop="name" :rules="[{ required: true, message: '请输入群标签组名称', trigger: 'blur' }]">
|
||||
<el-input size="small" v-model.trim="form.name" :maxlength="15" show-word-limit placeholder="请输入群标签组名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%" label="群标签" prop="tagList" :rules="[{ required: true, message: '请输入群标签组名称', trigger: 'blur' }]">
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" color="#fff" closable @close="onClose(index)" v-for="(item, index) in form.tagList" :key="index">{{ item.name }}</el-tag>
|
||||
<el-input
|
||||
v-if="inputVisible"
|
||||
v-model.trim="tagName"
|
||||
size="small"
|
||||
style="width: 100px;"
|
||||
maxlength="30"
|
||||
clearable
|
||||
@keyup.enter.native="handleInputConfirm"
|
||||
@blur="handleInputConfirm">
|
||||
</el-input>
|
||||
<el-button v-else size="small" icon="iconfont iconAdd" @click="inputVisible = true">添加</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Tags',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10
|
||||
},
|
||||
tagName: '',
|
||||
form: {
|
||||
name: '',
|
||||
tagList: []
|
||||
},
|
||||
inputVisible: false,
|
||||
isShow: false,
|
||||
ids: [],
|
||||
total: 10,
|
||||
colConfigs: [
|
||||
{ prop: 'name', label: '群标签组名称', align: 'left', width: 160 },
|
||||
{ slot: 'tags' },
|
||||
{ slot: 'options', label: '操作', align: 'center' }
|
||||
],
|
||||
id: '',
|
||||
tableData: [],
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.getList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList() {
|
||||
this.instance.post(`/app/wxcp/wxgroupchattag/listAll`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onCancel () {
|
||||
this.form.name = ''
|
||||
this.form.tagList = []
|
||||
this.id = ''
|
||||
this.inputVisible = ''
|
||||
this.isShow = false
|
||||
},
|
||||
|
||||
edit (e) {
|
||||
this.id = e.id
|
||||
this.form = JSON.parse(JSON.stringify(e))
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.isShow = true
|
||||
})
|
||||
},
|
||||
|
||||
handleInputConfirm () {
|
||||
if (!this.tagName) {
|
||||
return
|
||||
}
|
||||
|
||||
this.form.tagList.push({
|
||||
name: this.tagName
|
||||
})
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.tagName = ''
|
||||
this.inputVisible = false
|
||||
})
|
||||
},
|
||||
|
||||
onClose (index) {
|
||||
this.form.tagList.splice(index, 1)
|
||||
},
|
||||
|
||||
onConfirm() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance.post(this.id ? '/app/wxcp/wxgroupchattag/update' : `/app/wxcp/wxgroupchattag/add`, {
|
||||
name: this.form.name,
|
||||
id: this.id || null,
|
||||
tagList: this.form.tagList
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.getList()
|
||||
this.isShow = false
|
||||
this.$message.success(`${this.id}` ? '编辑成功' : '提交成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
remove (id) {
|
||||
this.$confirm('确定删除该数据?').then(() => {
|
||||
this.instance.post(`/app/wxcp/wxgroupchattag/delete?id=${id}&type=0`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-tags {
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
border: 1px solid #D0D4DC;
|
||||
background: #F3F4F7;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
color: #222222;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
94
packages/wxwork/AppResidentManage/AppResidentManage.vue
Normal file
94
packages/wxwork/AppResidentManage/AppResidentManage.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<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>
|
||||
<Detail v-else-if="componentName === 'Detail'" :params="params" :instance="instance" :dict="dict" :permissions="permissions" @change="onChange"></Detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import List from './components/List.vue'
|
||||
import Statistics from './components/Statistics'
|
||||
import Tags from './components/Tags'
|
||||
import Detail from './components/Detail'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AppResidentManage',
|
||||
label: '居民信息管理',
|
||||
|
||||
components: {
|
||||
List,
|
||||
Tags,
|
||||
Detail,
|
||||
Statistics
|
||||
},
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
|
||||
tabs () {
|
||||
const tabList = [
|
||||
{label: '居民列表', name: 'List', comp: List, permission: ''},
|
||||
{label: '居民统计', name: 'Statistics', comp: Statistics, permission: ''},
|
||||
{label: '居民标签', name: 'Tags', comp: Tags, permission: ''}
|
||||
].filter(item => {
|
||||
return true
|
||||
})
|
||||
|
||||
return tabList
|
||||
}
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
activeName: 'JoinEvent',
|
||||
currIndex: '0',
|
||||
componentName: '',
|
||||
params: {},
|
||||
isShowDetail: false
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (this.$route.query.id) {
|
||||
this.componentName = this.$route.query?.type
|
||||
this.params = {id: this.$route.query?.id}
|
||||
this.isShowDetail = true
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onChange (data) {
|
||||
if (data.type === 'list') {
|
||||
this.componentName = 'List'
|
||||
this.isShowDetail = false
|
||||
this.params = data.params
|
||||
}
|
||||
|
||||
if (data.type === 'detail') {
|
||||
this.componentName = 'Detail'
|
||||
this.isShowDetail = true
|
||||
this.params = data.params
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
465
packages/wxwork/AppResidentManage/components/Detail.vue
Normal file
465
packages/wxwork/AppResidentManage/components/Detail.vue
Normal file
@@ -0,0 +1,465 @@
|
||||
<template>
|
||||
<ai-detail class="AppResidentManage">
|
||||
<template slot="title">
|
||||
<ai-title title="居民信息详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<div class="detail-top">
|
||||
<div class="detail-top__header">
|
||||
<div class="header-left">
|
||||
<img :src="info.avatar">
|
||||
<div class="header-left__right">
|
||||
<h2>{{ info.name }}</h2>
|
||||
<p>{{ info.corpFullName }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="header-right__item">
|
||||
<span>家庭积分</span>
|
||||
<h3>{{ info.residentInfo ? (info.residentInfo.resident.familyIntegral || 0) : '0' }}</h3>
|
||||
</div>
|
||||
<div class="header-right__item">
|
||||
<span>个人积分</span>
|
||||
<h3>{{ info.residentInfo ? (info.residentInfo.resident.personalIntegral || 0) : '0' }}</h3>
|
||||
</div>
|
||||
<el-button type="primary" size="mini" v-if="info.realName" @click="toDetail">查看居民档案</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-top__content">
|
||||
<ai-wrapper
|
||||
label-width="80px">
|
||||
<ai-info-item label="添加渠道" :value="dict.getLabel('wxCustomerAddWay', info.addWay)"></ai-info-item>
|
||||
<ai-info-item label="添加时间" :value="info.createTime"></ai-info-item>
|
||||
<ai-info-item label="真实姓名" :value="info.realName"></ai-info-item>
|
||||
<ai-info-item label="手机号码"
|
||||
:value="info.residentInfo ? info.residentInfo.resident.phone : '-'"></ai-info-item>
|
||||
<ai-info-item label="标签" isLine>
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" v-for="(item, index) in info.tags" size="small" :key="index">{{ item.tagName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</ai-info-item>
|
||||
</ai-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-bottom">
|
||||
<div class="detail-bottom__left">
|
||||
<h2>居民动态</h2>
|
||||
<div class="step-list">
|
||||
<div class="step-item" v-for="(item, index) in logList" :key="index">
|
||||
<i class="step-item__left"></i>
|
||||
<div class="step-item__right">
|
||||
<h2>{{ dict.getLabel('wxCustomerLogType', item.type) }}</h2>
|
||||
<p>{{ item.createTime }}</p>
|
||||
<div class="step-item__right--bottom" v-html="item.content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="empty" class="no-data" v-if="!logList.length" style="height:160px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-bottom__right">
|
||||
<div class="detail-table">
|
||||
<h2>所属员工</h2>
|
||||
<ai-table
|
||||
class="detail-table__table"
|
||||
:border="true"
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
:stripe="false"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size"
|
||||
@getList="getDynamicInfo">
|
||||
<el-table-column slot="userinfo" label="所属员工" width="268px" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="userinfo">
|
||||
<img :src="row.avatar">
|
||||
<h3>{{ row.userName }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</div>
|
||||
<div class="detail-table">
|
||||
<h2>所在群聊</h2>
|
||||
<ai-table
|
||||
class="detail-table__table"
|
||||
:border="true"
|
||||
:tableData="grooupTableData"
|
||||
:col-configs="groupColConfigs"
|
||||
:total="grooupTableData.length"
|
||||
:stripe="false"
|
||||
:current.sync="groupSearch.current"
|
||||
:size.sync="groupSearch.size"
|
||||
@getList="getInfo">
|
||||
</ai-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Detail',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
params: Object
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
tags: ['青年', '郑村村', '郑村村', '青年', '郑村村', '郑村村'],
|
||||
info: {},
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10
|
||||
},
|
||||
groupSearch: {
|
||||
current: 1,
|
||||
size: 10
|
||||
},
|
||||
groupTotal: 0,
|
||||
form: {
|
||||
explain: '',
|
||||
imgs: []
|
||||
},
|
||||
total: 0,
|
||||
logList: [],
|
||||
colConfigs: [
|
||||
{slot: 'userinfo'},
|
||||
{prop: 'addWay', label: '客户来源', align: 'center', formart: v => this.dict.getLabel('wxCustomerAddWay', v)},
|
||||
{prop: 'createTime', label: '添加时间', align: 'center'}
|
||||
],
|
||||
groupColConfigs: [
|
||||
{prop: 'name', label: '群名称', align: 'left'},
|
||||
{prop: 'ownerName', label: '群主', align: 'center'},
|
||||
{prop: 'personCount', label: '群人数', align: 'left'},
|
||||
{prop: 'joinTime', label: '居民入群时间', align: 'left'}
|
||||
],
|
||||
grooupTableData: [],
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.params && this.params.id) {
|
||||
this.getInfo()
|
||||
this.dict.load(['wxCustomerLogType', 'wxCustomerAddWay']).then(() => {
|
||||
this.getWxcustomerlog()
|
||||
this.getDynamicInfo()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getInfo(id) {
|
||||
this.instance.post(`/app/wxcp/wxcustomer/queryCustomerById?id=${this.params.id}`).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.info = res.data
|
||||
this.grooupTableData = res.data.groupInfos
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getDynamicInfo(id) {
|
||||
this.instance.post(`/app/wxcp/wxcustomer/getDynamicInfoById?customerId=${this.params.id}`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getWxcustomerlog() {
|
||||
this.instance.post(`/app/wxcp/wxcustomerlog/listAll?customerId=${this.params.id}`).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.logList = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
toDetail() {
|
||||
this.$router.push({
|
||||
name: '67',
|
||||
query: {
|
||||
id: this.info.residentInfo.resident.id,
|
||||
type: '0'
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onClose() {
|
||||
this.form.explain = ''
|
||||
},
|
||||
|
||||
confirm() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance.post(`/app/appgirdmemberevent/rejectEvent`, {
|
||||
eventId: this.params.id,
|
||||
explain: this.form.explain
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('驳回成功')
|
||||
setTimeout(() => {
|
||||
this.cancel()
|
||||
}, 600)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
cancel(isRefresh) {
|
||||
this.$emit('change', {
|
||||
type: 'list',
|
||||
isRefresh: isRefresh ? true : false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AppResidentManage {
|
||||
::v-deep .ai-detail__content--wrapper {
|
||||
max-width: 100% !important;
|
||||
padding: 20px;
|
||||
}
|
||||
h2, h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 8px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: normal;
|
||||
color: #222222;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-bottom {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
|
||||
.step-list {
|
||||
padding: 36px 40px 40px;
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
position: relative;
|
||||
padding-bottom: 36px;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
top: 14px;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: #EEEEEE;
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
.step-item__right--bottom {
|
||||
color: #555;
|
||||
font-size: 14px;
|
||||
|
||||
b {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
::v-deep i {
|
||||
font-style: normal;
|
||||
color: #2266FF;
|
||||
}
|
||||
}
|
||||
|
||||
.step-item__right {
|
||||
flex: 1;
|
||||
|
||||
h2 {
|
||||
line-height: 22px;
|
||||
color: #222222;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 22px;
|
||||
margin: 2px 0 4px;
|
||||
color: #888888;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.step-item__left {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
margin-right: 20px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #2266FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-bottom__left {
|
||||
flex-shrink: 1;
|
||||
width: 360px;
|
||||
height: fit-content;
|
||||
margin-right: 20px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 2px;
|
||||
|
||||
& > h2 {
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
padding: 0 16px;
|
||||
color: #222222;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
border: 1px solid #EEEEEE;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-bottom__right {
|
||||
flex: 1;
|
||||
|
||||
& > div {
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 2px;
|
||||
|
||||
&:last-child {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
& > h2 {
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
padding: 0 16px;
|
||||
color: #222222;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
border: 1px solid #EEEEEE;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-table__table {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-top {
|
||||
padding: 30px 40px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 2px;
|
||||
|
||||
.detail-top__content {
|
||||
padding-top: 32px;
|
||||
}
|
||||
|
||||
.detail-top__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 32px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
||||
.header-right {
|
||||
.header-right__item {
|
||||
width: 120px;
|
||||
margin-right: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div {
|
||||
text-align: center;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: #888888;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button {
|
||||
height: 28px;
|
||||
margin-left: 8px;
|
||||
border-radius: 14px;
|
||||
font-size: 12px;
|
||||
padding: 7px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-left, .header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 6px;
|
||||
color: #222222;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #2EA222;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-tags {
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
457
packages/wxwork/AppResidentManage/components/List.vue
Normal file
457
packages/wxwork/AppResidentManage/components/List.vue
Normal file
@@ -0,0 +1,457 @@
|
||||
<template>
|
||||
<ai-list class="AppPetitionManage" isTabs>
|
||||
<template slot="content">
|
||||
<ai-search-bar class="search-bar">
|
||||
<template slot="left">
|
||||
<ai-select
|
||||
v-model="search.isRealName"
|
||||
@change="search.current = 1, getList()"
|
||||
placeholder="是否实名"
|
||||
:selectList="realStatus">
|
||||
</ai-select>
|
||||
<ai-select
|
||||
v-model="search.wxUserId"
|
||||
filterable
|
||||
@change="search.current = 1, getList()"
|
||||
placeholder="所属员工"
|
||||
:selectList="userList">
|
||||
</ai-select>
|
||||
<ai-select
|
||||
v-model="search.tagId"
|
||||
@change="search.current = 1, getList()"
|
||||
placeholder="选择标签"
|
||||
:selectList="subTags">
|
||||
</ai-select>
|
||||
</template>
|
||||
<template slot="right">
|
||||
<el-input
|
||||
v-model="search.name"
|
||||
class="search-input"
|
||||
size="small"
|
||||
v-throttle="() => {search.current = 1, getList()}"
|
||||
placeholder="请输入备注、昵称、姓名"
|
||||
clearable
|
||||
@change="getList"
|
||||
@clear="search.current = 1, search.name = '', getList()"
|
||||
suffix-icon="iconfont iconSearch">
|
||||
</el-input>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-search-bar>
|
||||
<template slot="left">
|
||||
<el-button type="primary" icon="iconfont iconAdd" @click="isRemove = false, isShow = true" :disabled="!ids.length">批量打标签</el-button>
|
||||
<el-button type="primary" icon="iconfont iconDelete" @click="isRemove = true, isShow = true" :disabled="!ids.length">批量移除标签</el-button>
|
||||
</template>
|
||||
<template slot="right">
|
||||
<el-button type="primary" icon="iconfont iconResetting" @click="update" :loading="btnLoading">更新数据</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
ref="aitableex"
|
||||
:current.sync="search.current"
|
||||
@selection-change="handleSelectionChange"
|
||||
:size.sync="search.size"
|
||||
v-loading="isLoading"
|
||||
@getList="getList">
|
||||
<el-table-column slot="avatar" label="" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="avatar" style="text-align: right; justify-content: end;">
|
||||
<img :src="row.avatar">
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="userinfo" label="备注名" width="280px" align="left">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="userinfo">
|
||||
<div class="userinfo-right ellipsis">
|
||||
<el-tooltip effect="dark" :content="row.corpFullName ? (row.remark || row.name) + '@' + row.corpFullName : (row.remark || row.name) + ''" placement="top">
|
||||
<div class="userinfo-right__top">
|
||||
<h3>{{ row.remark || row.name }}</h3>
|
||||
<span class="ellipsis">{{ row.corpFullName ? '@' + row.corpFullName : '' }}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div class="userinfo-right__bottom">
|
||||
<el-tooltip effect="dark" :content="row.name" placement="top">
|
||||
<i>昵称:{{ row.name }}</i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="tags" label="标签" align="left">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" v-for="(item, index) in row.tags" size="medium" :key="index">{{ item.tagName }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" width="100" fixed="right" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
<ai-dialog
|
||||
:visible.sync="isShow"
|
||||
width="800px"
|
||||
:title="isRemove ? '批量移除标签' : '批量打标签'"
|
||||
@close="onClose"
|
||||
@onConfirm="onConfirm">
|
||||
<div class="tags">
|
||||
<div class="tag-item" v-for="(item, index) in tags" :key="index">
|
||||
<h2>{{ item.name }}</h2>
|
||||
<div class="tag-item__right">
|
||||
<el-button
|
||||
:type="chooseTags.indexOf(item.id) === -1 ? '' : 'primary'"
|
||||
v-for="(item, index) in item.tagList"
|
||||
@click="choose(item.id)"
|
||||
:key="index">
|
||||
{{ item.name }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ai-dialog>
|
||||
</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,
|
||||
name: '',
|
||||
tagId: '',
|
||||
wxUserId: '',
|
||||
isRealName: ''
|
||||
},
|
||||
isRemove: false,
|
||||
btnLoading: false,
|
||||
isLoading: false,
|
||||
isShow: false,
|
||||
ids: [],
|
||||
total: 10,
|
||||
chooseTags: [],
|
||||
tags: [],
|
||||
colConfigs: [
|
||||
{ type: 'selection' },
|
||||
{ slot: 'avatar' },
|
||||
{ slot: 'userinfo' },
|
||||
{ prop: 'realName', label: '真实姓名', align: 'center' },
|
||||
{
|
||||
prop: 'identityNumber', label: '是否实名', align: 'center',
|
||||
render: (h, params) => {
|
||||
return h('span', {
|
||||
}, params.row.realName ? '是' : '否')
|
||||
}
|
||||
},
|
||||
{ prop: 'wxUserNames', label: '所属员工', align: 'center' },
|
||||
{ slot: 'tags' },
|
||||
{ prop: 'createTime', label: '添加时间', align: 'left' },
|
||||
{ prop: 'addWay', label: '添加渠道', align: 'center', formart: v => this.dict.getLabel('wxCustomerAddWay', v) },
|
||||
{ slot: 'options', label: '操作', align: 'center' }
|
||||
],
|
||||
tableData: [],
|
||||
realStatus: [{
|
||||
dictName: '是',
|
||||
dictValue: '1'
|
||||
}, {
|
||||
dictName: '否',
|
||||
dictValue : '0'
|
||||
}],
|
||||
subTags: [],
|
||||
userList: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user'])
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.getTags()
|
||||
this.getSubTags()
|
||||
this.getWxUserList()
|
||||
this.isLoading = true
|
||||
this.dict.load(['wxCustomerAddWay']).then(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
getWxUserList () {
|
||||
this.instance.post(`/app/wxcp/wxuser/listByDepartId`, {
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.userList = res.data.map(item => {
|
||||
item.dictName = item.name
|
||||
item.dictValue = item.id
|
||||
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
update () {
|
||||
this.btnLoading = true
|
||||
this.instance.post(`/app/wxcp/wxusercustomer/sync`, null, {
|
||||
timeout: 1000000
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('更新成功')
|
||||
this.getList()
|
||||
}
|
||||
|
||||
this.btnLoading = false
|
||||
}).catch(() => {
|
||||
|
||||
this.btnLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
onClose () {
|
||||
this.chooseTags = []
|
||||
},
|
||||
|
||||
getSubTags () {
|
||||
this.instance.post(`/app/wxcp/wxcorptag/listAllTags`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.subTags = res.data.map(item => {
|
||||
return {
|
||||
dictName: item.name,
|
||||
dictValue: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getList() {
|
||||
this.instance.post(`/app/wxcp/wxcustomer/list`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
|
||||
this.isLoading = false
|
||||
} else {
|
||||
this.isLoading = false
|
||||
}
|
||||
}).catch(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
choose (id) {
|
||||
const index = this.chooseTags.indexOf(id)
|
||||
if (index === -1) {
|
||||
this.chooseTags.push(id)
|
||||
} else {
|
||||
this.chooseTags.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
onConfirm () {
|
||||
if (!this.chooseTags.length) {
|
||||
return this.$message.error('请选择标签')
|
||||
}
|
||||
|
||||
if (this.isRemove) {
|
||||
this.instance.post(`/app/wxcp/wxcorptag/markTagForWeb`, {
|
||||
delTagIds: this.chooseTags,
|
||||
customerIds: this.ids.map(v => v.id)
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.isShow = false
|
||||
this.$message.success('移除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.instance.post(`/app/wxcp/wxcorptag/markTagForWeb`, {
|
||||
addTagIds: this.chooseTags,
|
||||
customerIds: this.ids.map(v => v.id)
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.isShow = false
|
||||
this.getList()
|
||||
this.$message.success('添加标签成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
getTags () {
|
||||
this.instance.post(`/app/wxcp/wxcorptag/listAll?size=100`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tags = res.data.records
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onAdd () {
|
||||
this.$emit('change', {
|
||||
type: 'add'
|
||||
})
|
||||
},
|
||||
|
||||
handleSelectionChange(e) {
|
||||
this.ids = e
|
||||
},
|
||||
|
||||
removeAll () {
|
||||
this.remove(this.ids.map(v => v.id).join(','))
|
||||
},
|
||||
|
||||
remove (id) {
|
||||
this.$confirm('确定删除该数据?').then(() => {
|
||||
this.instance.post(`/app/apppetition/delete?ids=${id}`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
toDetail (id) {
|
||||
this.$emit('change', {
|
||||
type: 'detail',
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onAdd () {
|
||||
this.$emit('change', {
|
||||
type: 'add'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-tags {
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #D0D4DC;
|
||||
background: #F3F4F7;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
color: #222222;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tags {
|
||||
.tag-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 30px;
|
||||
padding-top: 30px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
h2 {
|
||||
width: 88px;
|
||||
margin-right: 40px;
|
||||
text-align: right;
|
||||
color: #888888;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
text-align: right;
|
||||
img {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
|
||||
.userinfo-right__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.userinfo-right__bottom {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
font-style: normal;
|
||||
color: #888888;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-right: 8px;
|
||||
color: #222222;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #3C7FC8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
201
packages/wxwork/AppResidentManage/components/Statistics.vue
Normal file
201
packages/wxwork/AppResidentManage/components/Statistics.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<ai-list class="statistics" isTabs style="width: 100%">
|
||||
<template slot="content" v-loading="loading">
|
||||
<div class="statistics-top">
|
||||
<div class="statistics-top__item">
|
||||
<span>居民总数</span>
|
||||
<h2 style="color: #2266FF;">{{ info.total }}</h2>
|
||||
</div>
|
||||
<div class="statistics-top__item">
|
||||
<span>今日新增</span>
|
||||
<h2 style="color: #22AA99;">{{ info.increase }}</h2>
|
||||
</div>
|
||||
<div class="statistics-top__item">
|
||||
<span>今日流失</span>
|
||||
<h2 style="color: #F8B425">{{ info.decrease }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<ai-card title="趋势图">
|
||||
<template #content>
|
||||
<div class="chart" style="height: 340px; width: 100%;"></div>
|
||||
<ai-empty v-if="false" style="height: 148px;"></ai-empty>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
export default {
|
||||
name: 'Statistics',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
chart: null,
|
||||
info: {},
|
||||
chartWidth: '',
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.chart = echarts.init(document.querySelector('.chart'))
|
||||
window.addEventListener('resize', this.onResize)
|
||||
this.getInfo()
|
||||
})
|
||||
},
|
||||
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.onResize)
|
||||
},
|
||||
|
||||
methods: {
|
||||
onResize () {
|
||||
this.chart.resize()
|
||||
},
|
||||
|
||||
getInfo () {
|
||||
this.instance.post(`/app/wxcp/wxcustomerlog/customerStatistic`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.info = res.data.today
|
||||
this.initChart(res.data.list)
|
||||
this.loading = false
|
||||
} else {
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
initChart (data) {
|
||||
const x = Object.keys(data)
|
||||
const y = Object.values(data)
|
||||
let option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
type: "plain"
|
||||
},
|
||||
grid: {
|
||||
left: '20px',
|
||||
right: '38px',
|
||||
bottom: '14px',
|
||||
top: '30px',
|
||||
containLabel: true
|
||||
},
|
||||
color: ['#2266FF', '#22AA99', '#F8B425'],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
axisLabel: {
|
||||
align: 'center',
|
||||
padding: [2, 0, 0, 0],
|
||||
interval: 0,
|
||||
fontSize: 14,
|
||||
color: '#666666'
|
||||
},
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#E1E5EF'
|
||||
}
|
||||
},
|
||||
data: x
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
length: 0,
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle:{
|
||||
color: ['#E1E5EF'],
|
||||
width: 1,
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
nameTextStyle: {
|
||||
color: '#666666',
|
||||
align: 'left'
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666666'
|
||||
},
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '居民总数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.total)
|
||||
},
|
||||
{
|
||||
name: '新增居民数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.increase)
|
||||
},
|
||||
{
|
||||
name: '流失居民数',
|
||||
type: 'line',
|
||||
data: y.map(v => v.decrease)
|
||||
}
|
||||
]
|
||||
}
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.statistics {
|
||||
::v-deep .ai-list__content--right-wrapper {
|
||||
background: transparent!important;
|
||||
box-shadow: none!important;
|
||||
padding: 12px 0 12px!important;
|
||||
}
|
||||
|
||||
.statistics-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
& > div {
|
||||
flex: 1;
|
||||
height: 96px;
|
||||
line-height: 1;
|
||||
margin-right: 20px;
|
||||
padding: 16px 24px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
color: #888888;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
201
packages/wxwork/AppResidentManage/components/Tags.vue
Normal file
201
packages/wxwork/AppResidentManage/components/Tags.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<ai-list class="AppPetitionManage" isTabs>
|
||||
<template slot="content">
|
||||
<ai-search-bar>
|
||||
<template slot="left">
|
||||
<el-button type="primary" icon="iconfont iconAdd" @click="id = '', form.name = '', form.tagList = [], isShow = true">添加标签组</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table
|
||||
:tableData="tableData"
|
||||
:col-configs="colConfigs"
|
||||
:total="total"
|
||||
ref="aitableex"
|
||||
:current.sync="search.current"
|
||||
:size.sync="search.size"
|
||||
@getList="getList">
|
||||
<el-table-column slot="tags" label="标签" align="left">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" v-for="(item, index) in row.tagList" size="small" :key="index">{{ item.name }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" width="180" fixed="right" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<!-- <el-button type="text" @click="edit(row)">添加标签</el-button> -->
|
||||
<el-button type="text" @click="edit(row)">编辑</el-button>
|
||||
<el-button type="text" @click="remove(row.id)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
<ai-dialog
|
||||
:visible.sync="isShow"
|
||||
width="800px"
|
||||
title="添加标签组"
|
||||
@onConfirm="onConfirm"
|
||||
@onCancel="onCancel">
|
||||
<el-form class="ai-form" ref="form" label-width="100px" :model="form">
|
||||
<el-form-item style="width: 100%" label="标签组名称" prop="name" :rules="[{ required: true, message: '请输入标签组名称', trigger: 'blur' }]">
|
||||
<el-input size="small" v-model.trim="form.name" :maxlength="15" show-word-limit placeholder="请输入标签组名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%" label="标签" prop="tagList" :rules="[{ required: true, message: '请输入标签组名称', trigger: 'blur' }]">
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" color="#fff" closable @close="onClose(index)" v-for="(item, index) in form.tagList" :key="index">{{ item.name }}</el-tag>
|
||||
<el-input
|
||||
v-if="inputVisible"
|
||||
v-model.trim="tagName"
|
||||
size="small"
|
||||
style="width: 100px;"
|
||||
maxlength="30"
|
||||
clearable
|
||||
@keyup.enter.native="handleInputConfirm"
|
||||
@blur="handleInputConfirm">
|
||||
</el-input>
|
||||
<el-button v-else size="small" icon="iconfont iconAdd" @click="inputVisible = true">添加</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</template>
|
||||
</ai-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Tags',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10
|
||||
},
|
||||
tagName: '',
|
||||
form: {
|
||||
name: '',
|
||||
tagList: []
|
||||
},
|
||||
inputVisible: false,
|
||||
isShow: false,
|
||||
ids: [],
|
||||
total: 10,
|
||||
colConfigs: [
|
||||
{ prop: 'name', label: '标签组', align: 'left', width: 160 },
|
||||
{ slot: 'tags' },
|
||||
{ slot: 'options', label: '操作', align: 'center' }
|
||||
],
|
||||
id: '',
|
||||
tableData: [],
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.getList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList() {
|
||||
this.instance.post(`/app/wxcp/wxcorptag/listAll`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onCancel () {
|
||||
this.form.name = ''
|
||||
this.form.tagList = []
|
||||
this.id = ''
|
||||
this.inputVisible = ''
|
||||
this.isShow = false
|
||||
},
|
||||
|
||||
edit (e) {
|
||||
this.id = e.id
|
||||
this.form = JSON.parse(JSON.stringify(e))
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.isShow = true
|
||||
})
|
||||
},
|
||||
|
||||
handleInputConfirm () {
|
||||
if (!this.tagName) {
|
||||
return
|
||||
}
|
||||
|
||||
this.form.tagList.push({
|
||||
name: this.tagName
|
||||
})
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.tagName = ''
|
||||
this.inputVisible = false
|
||||
})
|
||||
},
|
||||
|
||||
onClose (index) {
|
||||
this.form.tagList.splice(index, 1)
|
||||
},
|
||||
|
||||
onConfirm () {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance.post(this.id ? '/app/wxcp/wxcorptag/update' : `/app/wxcp/wxcorptag/add`, {
|
||||
name: this.form.name,
|
||||
id: this.id || '',
|
||||
tagList: this.form.tagList
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.getList()
|
||||
this.isShow = false
|
||||
this.$message.success(`${this.id}` ? '编辑成功' : '提交成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
remove (id) {
|
||||
this.$confirm('确定删除该数据?').then(() => {
|
||||
this.instance.post(`/app/wxcp/wxcorptag/delete?id=${id}&type=0`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-tags {
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
border: 1px solid #D0D4DC;
|
||||
background: #F3F4F7;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
color: #222222;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
617
packages/wxwork/AppVerbalTrick/AppVerbalTrick.vue
Normal file
617
packages/wxwork/AppVerbalTrick/AppVerbalTrick.vue
Normal file
@@ -0,0 +1,617 @@
|
||||
<template>
|
||||
<section style="height: 100%;">
|
||||
<ai-list>
|
||||
<template slot="title">
|
||||
<ai-title title="话术库" :isShowBottomBorder="true"></ai-title>
|
||||
</template>
|
||||
<template #left>
|
||||
<div class="left-tree">
|
||||
<div class="tree-title">
|
||||
<label>分组</label>
|
||||
<el-button icon="iconfont iconAdd" style="height: 28px;line-height: 0;" @click="groupDialog = true">添加分组
|
||||
</el-button>
|
||||
</div>
|
||||
<el-scrollbar style="height: calc(100% - 40px)">
|
||||
<el-menu style="height: 100%;" default-active="0" active-text-color="#26f" text-color="#222"
|
||||
mode="vertical">
|
||||
<el-menu-item v-for="(item, index) in sortList" :key="index" class="menu-item" @click="currentMenu(item)"
|
||||
:index="index.toString()">
|
||||
<label class="item-title">{{ item.name }}</label>
|
||||
<el-popover style="width: 30px" placement="left" trigger="click">
|
||||
<div style="display: flex;flex-direction: column;align-items: center;">
|
||||
<div style="cursor: pointer;" @click="editGroup(item, index)">编辑</div>
|
||||
<div style="cursor: pointer;" @click="delteGroup(item, index)">删除</div>
|
||||
</div>
|
||||
<span class="iconfont iconMore" slot="reference"></span>
|
||||
</el-popover>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<ai-search-bar>
|
||||
<template slot="left">
|
||||
<el-select v-model="search.contentType" placeholder="类型" size="small" clearable @change="getList()">
|
||||
<el-option v-for="(item, index) in types" :key="index" :label="item.name" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<template slot="right">
|
||||
<el-input v-model="search.title" class="search-input" size="small"
|
||||
@keyup.enter.native=";(search.current = 1), getList()" placeholder="请输入标题或话术内容或创建人" clearable
|
||||
@clear=";(search.current = 1), (search.title = ''), getList()" suffix-icon="iconfont iconSearch"/>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-search-bar>
|
||||
<template slot="left">
|
||||
<el-button icon="iconfont iconAdd" type="primary" @click="add">添加话术</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="tableData" :col-configs="colConfigs" :stripe="true" :total="total" ref="aitableex"
|
||||
style="margin-top: 8px;" :current.sync="search.current" :size.sync="search.size" @getList="getList">\
|
||||
|
||||
<el-table-column slot="content" label="话术内容" align="left" width="280">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-left__wrapper">
|
||||
<video :src="row.file && row.file.url" v-if="row.file && row.contentType == 'video'"
|
||||
class="media"></video>
|
||||
<img :src="row.file && row.file.url" v-if="row.file && row.contentType == 'image'" class="media"/>
|
||||
<audio :src="row.file && row.file.url" v-if="row.file && row.contentType == 'voice'"
|
||||
class="media"></audio>
|
||||
<div class="table-left__wrapper--right">
|
||||
<el-tooltip class="item" effect="dark" :content="row.content" placement="top">
|
||||
<div class="table-left__wrapper--text" v-if="row.contentType == 'text'">{{ row.content }}</div>
|
||||
</el-tooltip>
|
||||
<div class="ellipsis" v-if="row.contentType !== 'text'">{{ row.file.name }}</div>
|
||||
<div v-if="row.contentType !== 'text'">{{ row.file.size | size }}KB</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column slot="options" label="操作" width="160px" align="center" fixed="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span class="table-btn" title="编辑" @click="editTrick(row)">编辑</span>
|
||||
<span class="table-btn" title="删除" @click="deleteTrick(row)">删除</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
<ai-dialog :visible.sync="groupDialog" @closed="sortForm = {},sortForm.type=1" width="800px" title="添加分组"
|
||||
@onConfirm="onConfirm">
|
||||
<el-form size="small" label-width="100px" ref="groupForm" :model="sortForm" :rules="sortRules">
|
||||
<el-form-item label="分组名称" prop="name">
|
||||
<el-input clearable v-model.trim="sortForm.name" placeholder="请输入..." :maxlength="10" show-word-limit/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="可见范围" prop="type">-->
|
||||
<!-- <el-radio-group v-model="sortForm.type">-->
|
||||
<!-- <el-radio label="0">个人话术</el-radio>-->
|
||||
<!-- <el-radio label="1">公共话术</el-radio>-->
|
||||
<!-- </el-radio-group>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="可用部门" prop="documentName">-->
|
||||
<!-- <el-row type="flex">-->
|
||||
<!-- <div class="input"></div>-->
|
||||
<!-- <ai-person-select :instance="instance" url="/app/appvillagecadres/list" :isMultiple="true"-->
|
||||
<!-- btnText="选择" dialogTitle="选择">-->
|
||||
<!-- <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-row>-->
|
||||
<!-- </el-form-item>-->
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
|
||||
<ai-dialog :visible.sync="trickDialog" @close="onClose" @onConfirm="trickConfirm" width="800px" title="添加话术">
|
||||
<el-form size="small" label-width="100px" ref="trickFom" :model="trickForm" :rules="trickRules">
|
||||
<!-- <el-form-item label="分组" prop="handleResult">-->
|
||||
<!-- <ai-select-->
|
||||
<!-- v-model="search.type"-->
|
||||
<!-- @change="search.current = 1, getList()"-->
|
||||
<!-- placeholder="类型"-->
|
||||
<!-- :selectList="sortList"-->
|
||||
<!-- ></ai-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input clearable placeholder="请输入..." v-model="trickForm.title" show-word-limit :maxlength="20"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="话术类型" prop="contentType">
|
||||
<el-radio-group v-model="trickForm.contentType" @change="onChange">
|
||||
<el-radio :label="item.value" v-for="(item, index) in types" :key="index">{{ item.name }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="文本内容" class="el-form-item__textarea" prop="content" v-if="trickForm.contentType == 'text'">
|
||||
<span @click="insertNickname" class="el-form-item__btn" type="text">[插入用户昵称]</span>
|
||||
<span @click="insertRemark" class="el-form-item__remark" type="text">[插入备注名]</span>
|
||||
<el-input type="textarea" v-model="trickForm.content" placeholder="请输入..." :rows="5" show-word-limit
|
||||
:maxlength="255"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="compLabel" prop="fileId" v-else>
|
||||
<ai-uploader :instance="instance" v-model="fileList" :acceptType="acceptType"
|
||||
:url="'/app/wxcp/upload/uploadFile?type=' + trickForm.contentType" isWechat :fileType="fileType"
|
||||
:limit="1" @change="change"></ai-uploader>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AppVerbalTrick',
|
||||
label: '话术库',
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
groupDialog: false,
|
||||
trickDialog: false,
|
||||
search: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
contentType: '',
|
||||
},
|
||||
sortForm: {
|
||||
name: '',
|
||||
type: '1',
|
||||
},
|
||||
trickForm: {
|
||||
title: '',
|
||||
contentType: 'text',
|
||||
content: '',
|
||||
fileId: '',
|
||||
mediaId: '',
|
||||
},
|
||||
row: {},
|
||||
current: {},
|
||||
sortList: [],
|
||||
total: 0,
|
||||
tableData: [],
|
||||
fileList: [],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
acceptType() {
|
||||
return {
|
||||
"image": ".jpg,.png,.jpeg",
|
||||
"video": ".mp4"
|
||||
}[this.trickForm.contentType]
|
||||
},
|
||||
compLabel() {
|
||||
return this.types.find((e) => e.value == this.trickForm.contentType)?.name
|
||||
},
|
||||
fileType() {
|
||||
return this.trickForm.contentType == 'image' ? 'img' : 'file'
|
||||
},
|
||||
types() {
|
||||
return [
|
||||
{name: '文本', value: 'text'},
|
||||
{name: '图片', value: 'image'},
|
||||
{name: '视频', value: 'video'},
|
||||
// // {name: "附件", value: "file"},
|
||||
// {name: "音频", value: "voice"},
|
||||
]
|
||||
},
|
||||
trickRules() {
|
||||
return {
|
||||
title: [{required: true, message: '请输入标题', trigger: 'blur'}],
|
||||
contentType: [{required: true, message: '请选择话术类型', trigger: 'change'}],
|
||||
content: [{required: true, validator: (rule, value, callback)=>{
|
||||
if(this.trickForm.contentType == 'text' && !value){
|
||||
return callback("请输入文本内容")
|
||||
}else {
|
||||
callback()
|
||||
}
|
||||
}}],
|
||||
fileId: [{required: true, validator: (rule, value, callback)=>{
|
||||
if(this.trickForm.contentType != 'text' && !value){
|
||||
return callback("请上传文件")
|
||||
}else {
|
||||
callback()
|
||||
}
|
||||
}}],
|
||||
}
|
||||
},
|
||||
// message() {
|
||||
// return (() => {
|
||||
// if (this.trickForm.contentType == 'text') {
|
||||
// return '请输入文本内容'
|
||||
// } else if (this.trickForm.contentType == 'image') {
|
||||
// return '请上传图片'
|
||||
// } else if (this.trickForm.contentType == 'file') {
|
||||
// return '请上传文件'
|
||||
// } else if (this.trickForm.contentType == 'video') {
|
||||
// return '请上传视频'
|
||||
// } else if (this.trickForm.contentType == 'voice') {
|
||||
// return '请上传音频'
|
||||
// }
|
||||
// })()
|
||||
// },
|
||||
sortRules() {
|
||||
return {
|
||||
name: [{required: true, message: '请输入分组名称', trigger: 'blur'}],
|
||||
type: [{required: true, message: '请选择可见范围', trigger: 'change'}],
|
||||
}
|
||||
},
|
||||
colConfigs() {
|
||||
return [
|
||||
{slot: 'type'},
|
||||
{slot: 'content'},
|
||||
{prop: 'title', label: '标题'},
|
||||
{
|
||||
prop: 'contentType',
|
||||
label: '类型',
|
||||
align: 'center',
|
||||
render: (h, {row}) => {
|
||||
return h('span', {}, this.types.find((e) => row.contentType == e.value)?.name)
|
||||
},
|
||||
},
|
||||
{prop: 'userName', label: '创建人', align: 'center'},
|
||||
{prop: 'createTime', label: '创建时间', align: 'center'},
|
||||
{slot: 'options', label: '操作', align: 'center'},
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.getSortList()
|
||||
},
|
||||
|
||||
filters: {
|
||||
size(size) {
|
||||
return (size / 1024).toFixed(1)
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
insertNickname() {
|
||||
this.trickForm.content += '[用户昵称]'
|
||||
},
|
||||
insertRemark() {
|
||||
this.trickForm.content += '[备注名]'
|
||||
},
|
||||
onClose() {
|
||||
this.trickForm.title = ''
|
||||
this.trickForm.content = ''
|
||||
this.trickForm.fileId = ''
|
||||
this.trickForm.mediaId = ''
|
||||
this.trickForm.createdAt = ''
|
||||
this.trickForm.contentType = 'text'
|
||||
this.fileList = []
|
||||
},
|
||||
onChange() {
|
||||
this.trickForm.fileId = ''
|
||||
this.trickForm.content = ''
|
||||
this.trickForm.mediaId = ''
|
||||
this.trickForm.createdAt = ''
|
||||
this.fileList = []
|
||||
},
|
||||
add() {
|
||||
if (this.sortList.length == 0) return this.$message.error('请先添加分组')
|
||||
this.$nextTick(() => {
|
||||
this.trickDialog = true
|
||||
})
|
||||
},
|
||||
change(e) {
|
||||
if(e.length){
|
||||
this.trickForm.fileId = e[0].id
|
||||
this.trickForm.mediaId = e[0].media.mediaId
|
||||
this.trickForm.createdAt = e[0].media.createdAt
|
||||
}else {
|
||||
this.trickForm.fileId =""
|
||||
this.trickForm.mediaId = ""
|
||||
this.trickForm.createdAt = ""
|
||||
}
|
||||
},
|
||||
editTrick(row) {
|
||||
this.row = row
|
||||
this.trickForm = {...row}
|
||||
row.file && (this.trickForm.fileId = row.file.id)
|
||||
this.$nextTick(() => {
|
||||
this.trickDialog = true
|
||||
})
|
||||
this.fileList.push(row.file)
|
||||
},
|
||||
deleteTrick(row) {
|
||||
this.$confirm('确认要删除吗?').then(() => {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxspeechtechnique/delete`, null, {
|
||||
params: {
|
||||
id: row.id,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
trickConfirm() {
|
||||
this.$refs['trickFom'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxspeechtechnique/addOrUpdate`, {
|
||||
...this.trickForm,
|
||||
category: this.current.id,
|
||||
userId: this.user.info.id,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.trickDialog = false
|
||||
this.$message.success(this.trickForm.id ? '编辑成功' : '添加成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
currentMenu(item) {
|
||||
this.current = item
|
||||
this.getList()
|
||||
},
|
||||
delteGroup(item, index) {
|
||||
this.$confirm('是否要删除?').then(() => {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxspeechtechniquecategory/delete`, null, {
|
||||
params: {
|
||||
id: item.id,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getSortList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
editGroup(item, index) {
|
||||
this.sortForm = {...item}
|
||||
this.groupDialog = true
|
||||
},
|
||||
onConfirm() {
|
||||
this.$refs['groupForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxspeechtechniquecategory/addOrUpdate`, {
|
||||
...this.sortForm,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.groupDialog = false
|
||||
this.$message.success(this.sortForm.id ? '编辑成功' : '添加成功')
|
||||
this.getSortList()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getSortList() {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxspeechtechniquecategory/listAll`, null, {
|
||||
params: {
|
||||
// type: 1,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.data.length) {
|
||||
this.sortList = res.data
|
||||
this.current = res.data[0]
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
getList(item) {
|
||||
this.instance
|
||||
.post(`/app/wxcp/wxspeechtechnique/list`, null, {
|
||||
params: {
|
||||
category: this.current.id,
|
||||
...this.search,
|
||||
status: 1,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.left-tree {
|
||||
width: 264px;
|
||||
background: #fafafb;
|
||||
box-shadow: -1px 0px 0px 0px #e5e5e5;
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
|
||||
.tree-title {
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 8px 0 16px;
|
||||
background: #eeeff1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
& > label {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #222222;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-item {
|
||||
box-sizing: border-box;
|
||||
padding: 8px 16px 8px 24px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-right: 2px solid transparent;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.item-title {
|
||||
display: inline-block;
|
||||
width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.iconMore {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .ai-list__content--right {
|
||||
|
||||
.ai-list__content--right-wrapper {
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .has-gutter tr {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
::v-deep .el-table__row td {
|
||||
height: 68px;
|
||||
}
|
||||
|
||||
// ::v-deep .el-table__row td .cell {
|
||||
// width: 240px;
|
||||
// height: 44px;
|
||||
// }
|
||||
|
||||
.table-btn {
|
||||
font-size: 14px;
|
||||
color: #2266ff;
|
||||
cursor: pointer;
|
||||
|
||||
&:nth-child(1) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
min-height: 32px;
|
||||
line-height: 32px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d0d4dc;
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
&:after {
|
||||
content: '请选择...';
|
||||
color: #888888;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .media {
|
||||
object-fit: fill;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
::v-deep .AiPersonSelect {
|
||||
& > button {
|
||||
background: #f5f5f5;
|
||||
border-radius: 0px 2px 2px 0px;
|
||||
border: 1px solid #d0d4dc;
|
||||
color: #222222;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .is-active {
|
||||
border-right: 2px solid #2266ff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-popper {
|
||||
min-width: 76px !important;
|
||||
}
|
||||
|
||||
.table-left__wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.table-left__wrapper--text {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
width: 170px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item__textarea {
|
||||
position: relative;
|
||||
|
||||
.el-form-item__btn, .el-form-item__remark {
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
left: 12px;
|
||||
line-height: 1;
|
||||
z-index: 1;
|
||||
color: #2266FF;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.el-form-item__remark {
|
||||
left: 108px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
66
packages/wxwork/AppVillageCode/AppVillageCode.vue
Normal file
66
packages/wxwork/AppVillageCode/AppVillageCode.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="doc-circulation ailist-wrapper">
|
||||
<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: 'AppVillageCode',
|
||||
label: '一村一码',
|
||||
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
component: 'List',
|
||||
params: {},
|
||||
include: []
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
Add,
|
||||
List
|
||||
},
|
||||
|
||||
mounted () {
|
||||
},
|
||||
|
||||
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">
|
||||
.doc-circulation {
|
||||
height: 100%;
|
||||
background: #F3F6F9;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
119
packages/wxwork/AppVillageCode/components/Add.vue
Normal file
119
packages/wxwork/AppVillageCode/components/Add.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<ai-detail>
|
||||
<template slot="title">
|
||||
<ai-title title="添加二维码" isShowBack isShowBottomBorder @onBackClick="cancel(false)">
|
||||
</ai-title>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<ai-card title="基本信息">
|
||||
<template #content>
|
||||
<el-form class="ai-form" ref="form" :model="form" label-width="110px" label-position="right">
|
||||
<el-form-item label="地区" style="width: 100%;" prop="codeName">
|
||||
<span style="color: #666;">{{ form.areaName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="二维码名称" prop="codeName" :rules="[{ required: true, message: '请输入二维码名称', trigger: 'blur' }]">
|
||||
<el-input size="small" maxlength="30" show-word-limit placeholder="请输入二维码名称" style="width: 328px;" v-model="form.codeName"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%;" label="二维码类型" prop="type" :rules="[{ required: true, message: '请选择二维码类型', trigger: 'change' }]">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio label="0">群二维码</el-radio>
|
||||
<el-radio label="1">个人二维码</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传二维码" prop="codeUrl" style="width: 100%;" :rules="[{ required: true, message: '请上传二维码', trigger: 'change' }]">
|
||||
<ai-uploader :instance="instance" v-model="form.codeUrl" :limit="1"></ai-uploader>
|
||||
</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 {
|
||||
info: {},
|
||||
form: {
|
||||
areaId: '',
|
||||
codeName: '',
|
||||
areaName: '',
|
||||
code: '',
|
||||
codeUrl: [],
|
||||
type: '',
|
||||
},
|
||||
id: ''
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (this.params && this.params.areaId && !this.params.id) {
|
||||
this.form.areaId = this.params.areaId
|
||||
this.form.areaName = this.params.areaName
|
||||
}
|
||||
|
||||
if (this.params && this.params.id) {
|
||||
this.id = this.params.id
|
||||
this.getInfo(this.params.id)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getInfo (id) {
|
||||
this.instance.post(`/app/appeveryvillagecode/queryDetailById?id=${id}`).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.form = res.data
|
||||
this.form.codeUrl = [{
|
||||
url: res.data.codeUrl
|
||||
}]
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onClose () {
|
||||
this.form.explain = ''
|
||||
},
|
||||
|
||||
confirm () {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance.post(`/app/appeveryvillagecode/addOrUpdate`, {
|
||||
...this.form,
|
||||
codeUrl: this.form.codeUrl[0].url
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('提交成功')
|
||||
setTimeout(() => {
|
||||
this.cancel(true)
|
||||
}, 600)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
cancel (isRefresh) {
|
||||
this.$emit('change', {
|
||||
type: 'list',
|
||||
isRefresh: !!isRefresh
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
413
packages/wxwork/AppVillageCode/components/List.vue
Normal file
413
packages/wxwork/AppVillageCode/components/List.vue
Normal file
@@ -0,0 +1,413 @@
|
||||
<template>
|
||||
<ai-list class="villagecode">
|
||||
<template slot="title">
|
||||
<ai-title title="一村一码" isShowBottomBorder></ai-title>
|
||||
</template>
|
||||
<template #left>
|
||||
<div class="villagecode-left">
|
||||
<div class="villagecode-left__title">
|
||||
<h2>村列表</h2>
|
||||
</div>
|
||||
<div class="addressBook-left__list">
|
||||
<div class="addressBook-left__list--title">
|
||||
<el-input
|
||||
class="addressBook-left__list--search"
|
||||
size="mini"
|
||||
clearable
|
||||
placeholder="请输入地区名称"
|
||||
v-model="unitName"
|
||||
suffix-icon="iconfont iconSearch">
|
||||
</el-input>
|
||||
</div>
|
||||
<el-tree
|
||||
:filter-node-method="filterNode"
|
||||
ref="tree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
:data="areaTree"
|
||||
highlight-current
|
||||
:current-node-key="search.areaId"
|
||||
:default-expanded-keys="defaultExpanded"
|
||||
:default-checked-keys="defaultChecked"
|
||||
@current-change="onTreeChange">
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<ai-search-bar class="search-bar">
|
||||
<template #left>
|
||||
<el-button size="small" type="primary" :disabled="isShowAdd" icon="iconfont iconAdd" @click="toAdd('')">添加活码</el-button>
|
||||
</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="tags" label="标签">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-tags">
|
||||
<el-tag type="info" v-for="(item, index) in row.tags" size="small" :key="index">{{ item }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" width="180px" fixed="right" label="操作" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
width="160"
|
||||
:visible-arrow="false"
|
||||
popper-class="wechat-message__container"
|
||||
trigger="hover">
|
||||
<el-button type="text" slot="reference">二维码</el-button>
|
||||
<div style="font-size: 0;">
|
||||
<img class="message-info__img" :src="row.codeUrl">
|
||||
</div>
|
||||
</el-popover>
|
||||
<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,
|
||||
status: 0,
|
||||
title: '',
|
||||
areaId: ''
|
||||
},
|
||||
defaultExpanded: [],
|
||||
defaultChecked: [],
|
||||
areaTree: [],
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
},
|
||||
currIndex: -1,
|
||||
total: 10,
|
||||
colConfigs: [
|
||||
{prop: 'codeName', label: '名称', align: 'left'},
|
||||
{prop: 'type', label: '二维码类型', align: 'left', formart: v => v === '0' ? '群二维码' : '个人二维码'},
|
||||
{prop: 'createUserName', label: '创建人'},
|
||||
{prop: 'createTime', label: '创建时间'},
|
||||
{slot: 'options', label: '操作'}
|
||||
],
|
||||
areaName: '',
|
||||
unitName: '',
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
|
||||
isShowAdd () {
|
||||
const str = this.search.areaId.substr(this.search.areaId.length - 3)
|
||||
|
||||
return str === '000'
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
unitName (val) {
|
||||
this.$refs.tree.filter(val)
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.search.areaId = this.user.info.areaId
|
||||
this.areaName = this.user.info.areaName
|
||||
this.getTree()
|
||||
this.getList()
|
||||
|
||||
this.$nextTick(() => {
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
getList() {
|
||||
this.instance.post(`/app/appeveryvillagecode/list`, null, {
|
||||
params: {
|
||||
...this.search
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.tableData = res.data.records
|
||||
this.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
filterNode(value, data) {
|
||||
if (!value) return true
|
||||
return data.name.indexOf(value) !== -1
|
||||
},
|
||||
|
||||
onTreeChange (e) {
|
||||
this.search.areaId = e.id
|
||||
this.areaName = e.name
|
||||
this.search.current = 1
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
|
||||
getTree () {
|
||||
this.instance.post(`/admin/area/queryAllArea?id=${this.user.info.areaId}`).then(res => {
|
||||
if (res.code === 0) {
|
||||
let parent = res.data.map(v => {
|
||||
v.label = v.name
|
||||
v.children = []
|
||||
|
||||
return v
|
||||
}).filter(e => !e.parentid)[0]
|
||||
this.defaultExpanded = [parent.id]
|
||||
this.defaultChecked = [parent.id]
|
||||
this.search.areaId = parent.id
|
||||
this.addChild(parent, res.data)
|
||||
this.areaTree = [parent]
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(parent.id)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
addChild (parent, list) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].parentId === parent.id) {
|
||||
parent.children.push(list[i])
|
||||
}
|
||||
}
|
||||
|
||||
if (list.length > 0) {
|
||||
parent['children'].map(v => this.addChild(v, list))
|
||||
}
|
||||
},
|
||||
|
||||
remove(id) {
|
||||
this.$confirm('确定删除该数据?').then(() => {
|
||||
this.instance.post(`/app/appeveryvillagecode/delete?ids=${id}`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('删除成功!')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
toAdd(id) {
|
||||
this.$emit('change', {
|
||||
type: 'Add',
|
||||
params: {
|
||||
areaName: this.areaName,
|
||||
id: id || '',
|
||||
areaId: this.search.areaId
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.villagecode {
|
||||
.table-tags {
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addressBook-left__list {
|
||||
height: calc(100% - 40px);
|
||||
padding: 8px 8px;
|
||||
overflow: auto;
|
||||
|
||||
.addressBook-left__tags--item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 8px 0 16px;
|
||||
color: #222222;
|
||||
|
||||
&.addressBook-left__tags--item-active, &:hover {
|
||||
background: #E8EFFF;
|
||||
color: #2266FF;
|
||||
|
||||
i, span {
|
||||
color: #2266FF;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
color: #8e9ebf;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.addressBook-left__list--title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.addressBook-left__list--search {
|
||||
flex: 1;
|
||||
::v-deep input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button {
|
||||
width: 84px;
|
||||
flex-shrink: 1;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
color: #222222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
::v-deep .el-tree {
|
||||
background: transparent;
|
||||
|
||||
.el-tree-node__expand-icon.is-leaf {
|
||||
color: transparent!important;
|
||||
}
|
||||
|
||||
.el-tree-node__content > .el-tree-node__expand-icon {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.el-tree-node__content {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.el-tree__empty-text {
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.el-tree-node__children .el-tree-node__content {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.el-tree-node__content:hover {
|
||||
background: #E8EFFF;
|
||||
color: #222222;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.is-current > .el-tree-node__content {
|
||||
&:hover {
|
||||
background: #2266FF;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
background: #2266FF;
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.villagecode-left {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: #FAFAFB;
|
||||
|
||||
.villagecode-left__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
background: #E5E5E5;
|
||||
|
||||
h2 {
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.villagecode-left__list {
|
||||
height: calc(100% - 40px);
|
||||
padding: 8px 0;
|
||||
overflow: auto;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 24px;
|
||||
color: #222222;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border-right: 2px solid transparent;
|
||||
background: transparent;
|
||||
|
||||
&:hover {
|
||||
color: #2266FF;
|
||||
background: #E8EFFF;
|
||||
}
|
||||
|
||||
&.left-active {
|
||||
color: #2266FF;
|
||||
border-color: #2266FF;
|
||||
background: #E8EFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .ai-list__content--right {
|
||||
|
||||
.ai-list__content--right-wrapper {
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.message-info__img {
|
||||
font-size: 0;
|
||||
width: 144px;
|
||||
height: 144px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user