目录代码整合
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