Преглед изворни кода

:sparkles: 添加新特性。 新增微信公众号管理

冷冷 пре 6 година
родитељ
комит
ec6e87298f

+ 2 - 0
package.json

@@ -12,6 +12,7 @@
     "axios": "^0.18.0",
     "babel-polyfill": "^6.26.0",
     "classlist-polyfill": "^1.2.0",
+    "clipboard": "^2.0.4",
     "element-ui": "^2.4.11",
     "js-cookie": "^2.2.0",
     "net": "^1.0.2",
@@ -21,6 +22,7 @@
     "stompjs": "^2.3.3",
     "vue": "^2.5.16",
     "vue-axios": "^2.1.2",
+    "vue-clipboard2": "^0.3.0",
     "vue-cron": "^1.0.9",
     "vue-json-editor": "^1.2.3",
     "vue-json-tree-view": "^2.1.4",

+ 70 - 0
src/api/mp/wxaccount.js

@@ -0,0 +1,70 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/mp/wxaccount/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/mp/wxaccount',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/mp/wxaccount/' + id,
+    method: 'get'
+  })
+}
+
+export function generateQr(appid) {
+  return request({
+    url: '/mp/wxaccount/qr/' + appid,
+    method: 'post'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/mp/wxaccount/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/mp/wxaccount',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function fetchAccountList() {
+  return request({
+    url: '/mp/wxaccount/list',
+    method: 'get'
+  })
+}

+ 63 - 0
src/api/mp/wxaccountfans.js

@@ -0,0 +1,63 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/mp/wxaccountfans/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/mp/wxaccountfans',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function sync(appId) {
+  return request({
+    url: '/mp/wxaccountfans/sync/' + appId,
+    method: 'post'
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/mp/wxaccountfans/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/mp/wxaccountfans/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/mp/wxaccountfans',
+    method: 'put',
+    data: obj
+  })
+}

+ 80 - 0
src/api/mp/wxfansmsg.js

@@ -0,0 +1,80 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/mp/wxfansmsg/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/mp/wxfansmsg',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/mp/wxfansmsg/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/mp/wxfansmsg/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/mp/wxfansmsg',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function fetchResList(query) {
+  return request({
+    url: '/mp/wxfansmsgres/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addResObj(obj) {
+  return request({
+    url: '/mp/wxfansmsgres',
+    method: 'post',
+    data: obj
+  })
+}
+
+
+export function delResObj(id) {
+  return request({
+    url: '/mp/wxfansmsgres/' + id,
+    method: 'delete'
+  })
+}

+ 56 - 0
src/api/mp/wxmenu.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/mp/wxmenu/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/mp/wxmenu',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/mp/wxmenu/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/mp/wxmenu/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/mp/wxmenu',
+    method: 'put',
+    data: obj
+  })
+}

+ 115 - 0
src/const/crud/mp/wxaccount.js

@@ -0,0 +1,115 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  column: [
+    {
+      label: '主键',
+      prop: 'id',
+      hide: true,
+      editDisabled: true,
+      addVisdiplay: false
+    },
+    {
+      label: '名称',
+      prop: 'name',
+      rules: [{
+        required: true,
+        message: '请输入名称',
+        trigger: 'blur'
+      }]
+    },
+    {
+      label: '微信号',
+      prop: 'account',
+      overHidden: true,
+      placeholder: '公众号设置->最下边原生ID',
+      rules: [{
+        required: true,
+        message: '请输入微信号',
+        trigger: 'blur'
+      }]
+    },
+    {
+      label: 'appid',
+      prop: 'appid',
+      overHidden: true,
+      rules: [{
+        required: true,
+        message: '请输入appid',
+        trigger: 'blur'
+      }]
+    },
+    {
+      label: '密钥',
+      prop: 'appsecret',
+      overHidden: true,
+      rules: [{
+        required: true,
+        message: '请输入密钥',
+        trigger: 'blur'
+      }]
+    },
+    {
+      label: '协议域名',
+      prop: 'url',
+      hide: true,
+      placeholder: 'scheme://domain',
+      rules: [{
+        required: true,
+        message: '输入不合法',
+        trigger: 'blur'
+      }]
+    },
+    {
+      label: 'token',
+      prop: 'token',
+      overHidden: true,
+      rules: [{
+        required: true,
+        message: '请输入token',
+        trigger: 'blur'
+      }]
+    },
+    {
+      label: '加密密钥',
+      prop: 'aeskey',
+      overHidden: true,
+      rules: [{
+        required: true,
+        message: '请输入加密密钥',
+        trigger: 'blur'
+      }]
+    },
+    {
+      label: '图片',
+      prop: 'qrUrl',
+      type: 'upload',
+      imgWidth: 60,
+      imgFullscreen: true,
+      editVisdiplay: false,
+      addVisdiplay: false,
+      listType: 'picture-img'
+    }
+  ]
+}

+ 99 - 0
src/const/crud/mp/wxaccountfans.js

@@ -0,0 +1,99 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  viewBtn: true,
+  addBtn: false,
+  editBtn: false,
+  align: 'center',
+  column: [
+    {
+      label: '主键',
+      prop: 'id',
+      hide: false
+    },
+    {
+      label: '公众号',
+      prop: 'wxAccountName'
+    },
+    {
+      label: '昵称',
+      prop: 'nickname',
+      overHidden: true
+    },
+    {
+      label: '用户标识',
+      prop: 'openid',
+      overHidden: true
+    },
+    {
+      label: '性别',
+      prop: 'gender',
+      type: 'select',
+      dicUrl: '/admin/dict/type/gender',
+    },
+    {
+      label: '语言',
+      prop: 'language',
+      hide: true
+    },
+    {
+      label: '国家',
+      prop: 'country',
+      hide: true
+    },
+    {
+      label: '省份',
+      prop: 'province',
+      hide: true
+    },
+    {
+      label: '城市',
+      prop: 'city'
+    },
+    {
+      label: '订阅状态',
+      prop: 'subscribeStatus',
+      type: 'select',
+      dicUrl: '/admin/dict/type/subscribe',
+    },
+    {
+      label: '订阅时间',
+      prop: 'subscribeTime'
+    },
+    {
+      label: '备注',
+      prop: 'remark',
+      hide: true
+    },
+    {
+      label: '头像地址',
+      prop: 'headimgUrl',
+      type: 'upload',
+      imgWidth: 60,
+      imgFullscreen: true,
+      editVisdiplay: false,
+      addVisdiplay: false,
+      listType: 'picture-img'
+    }
+  ]
+}

+ 109 - 0
src/const/crud/mp/wxfansmsg.js

@@ -0,0 +1,109 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  viewBtn: true,
+  addBtn: false,
+  editBtn: false,
+  align: 'center',
+  column: [
+    {
+      label: '主键',
+      prop: 'id',
+      hide: false
+    },
+    {
+      label: '公众号',
+      prop: 'wxAccountName'
+    },
+    {
+      label: '用户标识',
+      prop: 'openid',
+      overHidden: true
+    },
+    {
+      label: '昵称',
+      prop: 'nickname'
+    },
+    {
+      label: '消息类型',
+      prop: 'msgType'
+    },
+    {
+      label: '内容',
+      prop: 'content'
+    },
+    {
+      label: '已回复',
+      prop: 'isRes',
+      type: 'select',
+      dicUrl: '/admin/dict/type/response_type',
+      search: true
+    },
+    {
+      label: '创建时间',
+      prop: 'createTime'
+    },
+    {
+      label: '头像地址',
+      prop: 'headimgUrl',
+      type: 'upload',
+      imgWidth: 60,
+      imgFullscreen: true,
+      editVisdiplay: false,
+      addVisdiplay: false,
+      listType: 'picture-img'
+    }
+  ]
+}
+
+
+export const tableResOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  viewBtn: true,
+  editBtn: false,
+  delBtn: true,
+  menuAlign: 'center',
+  align: 'center',
+  column: [
+    {
+      label: '回复人',
+      prop: 'userName',
+      addVisdiplay: false,
+    },
+    {
+      label: '回复内容',
+      prop: 'resContent',
+      type:'textarea',
+      span: 24,
+      minRows: 5
+    },
+    {
+      label: '时间',
+      prop: 'createTime',
+      addVisdiplay: false,
+    }
+  ]
+}

+ 79 - 0
src/const/crud/mp/wxmenu.js

@@ -0,0 +1,79 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  column: [
+	  {
+      label: '主键',
+      prop: 'id'
+    },
+	  {
+      label: '父ID',
+      prop: 'parentId'
+    },
+	  {
+      label: '菜单名称',
+      prop: 'menuName'
+    },
+	  {
+      label: '菜单类型 1文本消息;2图文消息;3网址链接;4小程序',
+      prop: 'menuType'
+    },
+	  {
+      label: '菜单等级',
+      prop: 'menuLevel'
+    },
+	  {
+      label: '模板ID',
+      prop: 'tplId'
+    },
+	  {
+      label: '菜单URL',
+      prop: 'menuUrl'
+    },
+	  {
+      label: '排序',
+      prop: 'menuSort'
+    },
+	  {
+      label: '微信账号ID',
+      prop: 'wxAccountId'
+    },
+	  {
+      label: '小程序appid',
+      prop: 'miniprogramAppid'
+    },
+	  {
+      label: '小程序页面路径',
+      prop: 'miniprogramPagepath'
+    },
+	  {
+      label: '创建时间',
+      prop: 'createTime'
+    },
+	  {
+      label: '更新时间',
+      prop: 'updateTime'
+    },
+  ]
+}

+ 1 - 1
src/page/login/thirdlogin.vue

@@ -14,7 +14,7 @@
       <span class="container"
             :style="{backgroundColor:'#8dc349'}">
         <i icon-class="qq"
-           class="iconfont icon-qq"></i>
+           class="iconfont icon-qq1"></i>
       </span>
       <p class="title">QQ</p>
     </div>

+ 4 - 13
src/views/daemon/job-manage/index.vue

@@ -137,19 +137,10 @@
     startJobRa,
     startJobsRa
   } from '@/api/daemon/sys-job'
-  import {
-    tableLogOption,
-    tableOption
-  } from '@/const/crud/daemon/sys-job'
-  import {
-    remote
-  } from '@/api/admin/dict'
-  import {
-    mapGetters
-  } from 'vuex'
-  import {
-    cron
-  } from 'vue-cron'
+  import {tableLogOption, tableOption} from '@/const/crud/daemon/sys-job'
+  import {remote} from '@/api/admin/dict'
+  import {mapGetters} from 'vuex'
+  import {cron} from 'vue-cron'
 
   export default {
     components: {cron},

+ 198 - 0
src/views/mp/wxaccount/index.vue

@@ -0,0 +1,198 @@
+<!--
+  -    Copyright (c) 2018-2025, lengleng All rights reserved.
+  -
+  - Redistribution and use in source and binary forms, with or without
+  - modification, are permitted provided that the following conditions are met:
+  -
+  - Redistributions of source code must retain the above copyright notice,
+  - this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright
+  - notice, this list of conditions and the following disclaimer in the
+  - documentation and/or other materials provided with the distribution.
+  - Neither the name of the pig4cloud.com developer nor the names of its
+  - contributors may be used to endorse or promote products derived from
+  - this software without specific prior written permission.
+  - Author: lengleng (wangiegie@gmail.com)
+  -->
+<template>
+  <div class="execution">
+    <basic-container>
+      <avue-crud ref="crud"
+                 :page="page"
+                 :data="tableData"
+                 :permission="permissionList"
+                 :table-loading="tableLoading"
+                 :option="tableOption"
+                 @on-load="getList"
+                 @refresh-change="refreshChange"
+                 @row-update="handleUpdate"
+                 @row-save="handleSave"
+                 @row-del="rowDel">
+        <template slot-scope="scope"
+                  slot="menu">
+          <el-button type="text" v-if="permissionList.addBtn"
+                     icon="el-icon-upload"
+                     size="mini"
+                     plain
+                     @click="access(scope.row,scope.index)">
+            接入
+          </el-button>
+          <el-button type="text" v-if="permissionList.addBtn"
+                     icon="el-icon-check"
+                     size="mini"
+                     plain
+                     @click="generateQr(scope.row,scope.index)">二维码
+          </el-button>
+        </template>
+      </avue-crud>
+    </basic-container>
+    <el-dialog title="接入" :visible.sync="dialogFormVisible">
+      <el-row :gutter="20">
+        <el-col :span="6">服务器地址(URL)</el-col>
+        <el-col :span="12">{{wxurl}}</el-col>
+        <el-col :span="6">
+          <el-button id="btn" @click="copyLink" type="primary" size="small" style="float: right;">点击复制</el-button>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import {addObj, delObj, fetchList, generateQr, getObj, putObj} from '@/api/mp/wxaccount'
+  import {tableOption} from '@/const/crud/mp/wxaccount'
+  import {mapGetters} from 'vuex'
+  import Clipboard from 'clipboard'
+
+  export default {
+    name: 'wxaccount',
+    data() {
+      return {
+        wxurl: '',
+        dialogFormVisible: false,
+        tableData: [],
+        page: {
+          total: 0, // 总页数
+          currentPage: 1, // 当前页数
+          pageSize: 20 // 每页显示多少条
+        },
+        tableLoading: false,
+        tableOption: tableOption
+      }
+    },
+    computed: {
+      ...mapGetters(['permissions']),
+      permissionList() {
+        return {
+          addBtn: this.vaildData(this.permissions.mp_wxaccount_add, false),
+          delBtn: this.vaildData(this.permissions.mp_wxaccount_del, false),
+          editBtn: this.vaildData(this.permissions.mp_wxaccount_edit, false)
+        };
+      }
+    },
+    methods: {
+      getList(page, params) {
+        this.tableLoading = true
+        fetchList(Object.assign({
+          current: page.currentPage,
+          size: page.pageSize
+        }, params)).then(response => {
+          this.tableData = response.data.data.records
+          this.page.total = response.data.data.total
+          this.tableLoading = false
+        })
+      },
+      rowDel: function (row, index) {
+        var _this = this
+        this.$confirm('是否确认删除ID为' + row.id, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(function () {
+          return delObj(row.id)
+        }).then(data => {
+          _this.tableData.splice(index, 1)
+          _this.$message({
+            showClose: true,
+            message: '删除成功',
+            type: 'success'
+          })
+          this.getList(this.page)
+        }).catch(function (err) {
+        })
+      },
+      /**
+       * @title 数据更新
+       * @param row 为当前的数据
+       * @param index 为当前更新数据的行数
+       * @param done 为表单关闭函数
+       *
+       **/
+      handleUpdate: function (row, index, done) {
+        putObj(row).then(data => {
+          this.tableData.splice(index, 1, Object.assign({}, row))
+          this.$message({
+            showClose: true,
+            message: '修改成功',
+            type: 'success'
+          })
+          done()
+          this.getList(this.page)
+        })
+      },
+      /**
+       * @title 数据添加
+       * @param row 为当前的数据
+       * @param done 为表单关闭函数
+       *
+       **/
+      handleSave: function (row, done) {
+        row.qrUrl = undefined
+        addObj(row).then(data => {
+          this.tableData.push(Object.assign({}, row))
+          this.$message({
+            showClose: true,
+            message: '添加成功',
+            type: 'success'
+          })
+          done()
+          this.getList(this.page)
+        })
+      },
+
+      refreshChange() {
+        this.getList(this.page)
+      },
+      generateQr: function (row) {
+        generateQr(row.appid).then(data => {
+          this.$message({
+            showClose: true,
+            message: '获取成功',
+            type: 'success'
+          })
+          this.getList(this.page)
+        })
+      },
+      access: function (row) {
+        this.dialogFormVisible = true
+        this.wxurl = row.url + "/mp/" + row.appid + "/portal"
+      },
+      copyLink() {
+        let clipboard = new Clipboard('#btn', {
+          target: () => document.querySelector('#target')
+        })
+        //复制成功执行的回调
+        clipboard.on('success', (e) => {
+          this.$message({
+            showClose: true,
+            message: '复制成功',
+            type: 'success'
+          })
+        });
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 197 - 0
src/views/mp/wxaccountfans/index.vue

@@ -0,0 +1,197 @@
+<!--
+  -    Copyright (c) 2018-2025, lengleng All rights reserved.
+  -
+  - Redistribution and use in source and binary forms, with or without
+  - modification, are permitted provided that the following conditions are met:
+  -
+  - Redistributions of source code must retain the above copyright notice,
+  - this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright
+  - notice, this list of conditions and the following disclaimer in the
+  - documentation and/or other materials provided with the distribution.
+  - Neither the name of the pig4cloud.com developer nor the names of its
+  - contributors may be used to endorse or promote products derived from
+  - this software without specific prior written permission.
+  - Author: lengleng (wangiegie@gmail.com)
+  -->
+<template>
+  <div class="execution">
+    <basic-container>
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <div class="grid-content bg-purple">
+            <el-input v-model="q.nickname" placeholder="请输入昵称"></el-input>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="grid-content bg-purple">
+            <el-select v-model="q.account" placeholder="请选择公众号">
+              <el-option
+                v-for="item in wxAccountList"
+                :key="item.appid"
+                :label="item.name"
+                :value="item.appid">
+              </el-option>
+            </el-select>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="grid-content bg-purple">
+            <el-button type="primary" size="small" icon="el-icon-search" @click="search">搜索</el-button>
+            <el-button type="primary" size="small" icon="el-icon-sort" @click="asyncFans">同步</el-button>
+          </div>
+        </el-col>
+      </el-row>
+      <avue-crud ref="crud"
+                 :page="page"
+                 :data="tableData"
+                 :permission="permissionList"
+                 :table-loading="tableLoading"
+                 :option="tableOption"
+                 @on-load="getList"
+                 @refresh-change="refreshChange"
+                 @row-update="handleUpdate"
+                 @row-save="handleSave"
+                 @row-del="rowDel">
+      </avue-crud>
+    </basic-container>
+  </div>
+</template>
+
+<script>
+  import {addObj, delObj, fetchList, getObj, putObj, sync} from '@/api/mp/wxaccountfans'
+  import {fetchAccountList} from '@/api/mp/wxaccount'
+  import {tableOption} from '@/const/crud/mp/wxaccountfans'
+  import {mapGetters} from 'vuex'
+
+  export default {
+    name: 'wxaccountfans',
+    data() {
+      return {
+        q: {},
+        tableData: [],
+        wxAccountList: [],
+        page: {
+          total: 0, // 总页数
+          currentPage: 1, // 当前页数
+          pageSize: 20 // 每页显示多少条
+        },
+        tableLoading: false,
+        tableOption: tableOption
+      }
+    },
+    created() {
+      this.getAccountList()
+    },
+    computed: {
+      ...mapGetters(['permissions']),
+      permissionList() {
+        return {
+          addBtn: this.vaildData(this.permissions.mp_wxaccountfans_add, false),
+          delBtn: this.vaildData(this.permissions.mp_wxaccountfans_del, false),
+          editBtn: this.vaildData(this.permissions.mp_wxaccountfans_edit, false)
+        };
+      }
+    },
+    methods: {
+      getAccountList() {
+        fetchAccountList().then(response => {
+          this.wxAccountList = response.data.data
+        })
+      },
+      getList(page, params) {
+        this.tableLoading = true
+        fetchList(Object.assign({
+          current: page.currentPage,
+          size: page.pageSize
+        }, params)).then(response => {
+          this.tableData = response.data.data.records
+          this.page.total = response.data.data.total
+          this.tableLoading = false
+        })
+      },
+      rowDel: function (row, index) {
+        var _this = this
+        this.$confirm('是否确认删除ID为' + row.id, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(function () {
+          return delObj(row.id)
+        }).then(data => {
+          _this.tableData.splice(index, 1)
+          _this.$message({
+            showClose: true,
+            message: '删除成功',
+            type: 'success'
+          })
+          this.getList(this.page)
+        }).catch(function (err) {
+        })
+      },
+      /**
+       * @title 数据更新
+       * @param row 为当前的数据
+       * @param index 为当前更新数据的行数
+       * @param done 为表单关闭函数
+       *
+       **/
+      handleUpdate: function (row, index, done) {
+        putObj(row).then(data => {
+          this.tableData.splice(index, 1, Object.assign({}, row))
+          this.$message({
+            showClose: true,
+            message: '修改成功',
+            type: 'success'
+          })
+          done()
+          this.getList(this.page)
+        })
+      },
+      /**
+       * @title 数据添加
+       * @param row 为当前的数据
+       * @param done 为表单关闭函数
+       *
+       **/
+      handleSave: function (row, done) {
+        addObj(row).then(data => {
+          this.tableData.push(Object.assign({}, row))
+          this.$message({
+            showClose: true,
+            message: '添加成功',
+            type: 'success'
+          })
+          done()
+          this.getList(this.page)
+        })
+      },
+      /**
+       * 刷新回调
+       */
+      refreshChange() {
+        this.getList(this.page)
+      },
+      search() {
+        this.getList(this.page, this.q)
+      },
+      asyncFans() {
+        if (this.q.account) {
+          sync(this.q.account).then(data => {
+            this.$message({
+              showClose: true,
+              message: '已开始从微信同步粉丝信息,建议等待后查询',
+              type: 'success'
+            })
+          })
+          this.getList(this.page)
+        } else {
+          this.$message.error('请选择公众号')
+        }
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 189 - 0
src/views/mp/wxfansmsg/index.vue

@@ -0,0 +1,189 @@
+<!--
+  -    Copyright (c) 2018-2025, lengleng All rights reserved.
+  -
+  - Redistribution and use in source and binary forms, with or without
+  - modification, are permitted provided that the following conditions are met:
+  -
+  - Redistributions of source code must retain the above copyright notice,
+  - this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright
+  - notice, this list of conditions and the following disclaimer in the
+  - documentation and/or other materials provided with the distribution.
+  - Neither the name of the pig4cloud.com developer nor the names of its
+  - contributors may be used to endorse or promote products derived from
+  - this software without specific prior written permission.
+  - Author: lengleng (wangiegie@gmail.com)
+  -->
+<template>
+  <div class="execution">
+    <basic-container>
+      <avue-crud ref="crud"
+                 :page="page"
+                 :data="tableData"
+                 :permission="permissionList"
+                 :table-loading="tableLoading"
+                 :option="tableOption"
+                 @on-load="getList"
+                 @refresh-change="refreshChange"
+                 @row-save="handleSave"
+                 @row-del="rowDel">
+        <template slot-scope="scope"
+                  slot="menu">
+          <el-button type="text" v-if="permissionList.addBtn"
+                     icon="el-icon-check"
+                     size="mini"
+                     plain
+                     @click="reply(scope.row,scope.index)">回复
+          </el-button>
+        </template>
+      </avue-crud>
+      <el-dialog title="回复消息" :visible.sync="dialogFormVisible">
+        <avue-crud ref="crudLog"
+                   :page="pageRes"
+                   :data="tableResData"
+                   :option="tableResOption"
+                   @row-save="handleResSave"
+                   @row-del="rowResDel">
+        </avue-crud>
+      </el-dialog>
+    </basic-container>
+  </div>
+</template>
+
+<script>
+  import {addObj, addResObj, delObj, delResObj, fetchList, fetchResList, getObj, putObj} from '@/api/mp/wxfansmsg'
+  import {tableOption, tableResOption} from '@/const/crud/mp/wxfansmsg'
+  import {mapGetters} from 'vuex'
+
+  export default {
+    name: 'wxfansmsg',
+    data() {
+      return {
+        dialogFormVisible: false,
+        tableData: [],
+        tableResData: [],
+        page: {
+          total: 0, // 总页数
+          currentPage: 1, // 当前页数
+          pageSize: 20 // 每页显示多少条
+        },
+        pageRes: {
+          total: 0, // 总页数
+          currentPage: 1, // 当前页数
+          pageSize: 20 // 每页显示多少条
+        },
+        tableLoading: false,
+        tableOption: tableOption,
+        tableResOption: tableResOption,
+        msgId: undefined
+      }
+    },
+    computed: {
+      ...mapGetters(['permissions']),
+      permissionList() {
+        return {
+          addBtn: this.vaildData(this.permissions.mp_wxfansmsg_add, false),
+          delBtn: this.vaildData(this.permissions.mp_wxfansmsg_del, false),
+          editBtn: this.vaildData(this.permissions.mp_wxfansmsg_edit, false)
+        };
+      }
+    },
+    methods: {
+      getList(page, params) {
+        this.tableLoading = true
+        fetchList(Object.assign({
+          current: page.currentPage,
+          size: page.pageSize
+        }, params)).then(response => {
+          this.tableData = response.data.data.records
+          this.page.total = response.data.data.total
+          this.tableLoading = false
+        })
+      },
+      getResList(pageRes, params) {
+        fetchResList(Object.assign({
+          current: pageRes.currentPage,
+          size: pageRes.pageSize
+        }, params)).then(response => {
+          this.tableResData = response.data.data.records
+          this.pageRes.total = response.data.data.total
+        })
+      },
+      rowDel: function (row, index) {
+        var _this = this
+        this.$confirm('是否确认删除ID为' + row.id, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(function () {
+          return delObj(row.id)
+        }).then(data => {
+          _this.tableData.splice(index, 1)
+          _this.$message({
+            showClose: true,
+            message: '删除成功',
+            type: 'success'
+          })
+          this.getList(this.page)
+        }).catch(function (err) {
+        })
+      },
+      handleSave: function (row, done) {
+        addObj(row).then(data => {
+          this.tableResData.push(Object.assign({}, row))
+          this.$message({
+            showClose: true,
+            message: '添加成功',
+            type: 'success'
+          })
+          done()
+          this.getList(this.page)
+        })
+      },
+      rowResDel: function (row, index) {
+        var _this = this
+        this.$confirm('是否确认删除ID为' + row.id, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(function () {
+          return delResObj(row.id)
+        }).then(data => {
+          _this.$message({
+            showClose: true,
+            message: '删除成功',
+            type: 'success'
+          })
+          this.getResList(this.pageRes, {fansMsgId: this.msgId})
+        }).catch(function (err) {
+        })
+      },
+      handleResSave: function (row, done) {
+        row.fansMsgId = this.msgId
+        addResObj(this.filterForm(row)).then(data => {
+          this.$message({
+            showClose: true,
+            message: '发送成功',
+            type: 'success'
+          })
+          done()
+          this.getResList(this.pageRes, {fansMsgId: this.msgId})
+        })
+      },
+      /**
+       * 刷新回调
+       */
+      refreshChange() {
+        this.getList(this.page)
+      },
+      reply: function (row) {
+        this.msgId = row.id
+        this.dialogFormVisible = true
+        this.getResList(this.pageRes, {fansMsgId: this.msgId})
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 158 - 0
src/views/mp/wxmenu/index.vue

@@ -0,0 +1,158 @@
+<!--
+  -    Copyright (c) 2018-2025, lengleng All rights reserved.
+  -
+  - Redistribution and use in source and binary forms, with or without
+  - modification, are permitted provided that the following conditions are met:
+  -
+  - Redistributions of source code must retain the above copyright notice,
+  - this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright
+  - notice, this list of conditions and the following disclaimer in the
+  - documentation and/or other materials provided with the distribution.
+  - Neither the name of the pig4cloud.com developer nor the names of its
+  - contributors may be used to endorse or promote products derived from
+  - this software without specific prior written permission.
+  - Author: lengleng (wangiegie@gmail.com)
+  -->
+<template>
+    <div class="execution">
+        <basic-container>
+            <avue-crud ref="crud"
+                       :page="page"
+                       :data="tableData"
+                       :permission="permissionList"
+                       :table-loading="tableLoading"
+                       :option="tableOption"
+                       @on-load="getList"
+                       @refresh-change="refreshChange"
+                       @row-update="handleUpdate"
+                       @row-save="handleSave"
+                       @row-del="rowDel">
+            </avue-crud>
+        </basic-container>
+    </div>
+</template>
+
+<script>
+    import {
+        fetchList,
+        getObj,
+        addObj,
+        putObj,
+        delObj
+    } from '@/api/mp/wxmenu'
+    import {
+        tableOption
+    } from '@/const/crud/mp/wxmenu'
+    import {
+        mapGetters
+    } from 'vuex'
+
+    export default {
+        name: 'wxmenu',
+        data() {
+            return {
+                tableData: [],
+                page: {
+                    total: 0, // 总页数
+                    currentPage: 1, // 当前页数
+                    pageSize: 20 // 每页显示多少条
+                },
+                tableLoading: false,
+                tableOption: tableOption
+            }
+        },
+        created() {
+        },
+        mounted: function () {
+        },
+        computed: {
+            ...mapGetters(['permissions']),
+            permissionList() {
+                return {
+                    addBtn: this.vaildData(this.permissions.mp_wxmenu_add, false),
+                    delBtn: this.vaildData(this.permissions.mp_wxmenu_del, false),
+                    editBtn: this.vaildData(this.permissions.mp_wxmenu_edit, false)
+                };
+            }
+        },
+        methods: {
+            getList(page, params) {
+                this.tableLoading = true
+                fetchList(Object.assign({
+                    current: page.currentPage,
+                    size: page.pageSize
+                }, params)).then(response => {
+                    this.tableData = response.data.data.records
+                    this.page.total = response.data.data.total
+                    this.tableLoading = false
+                })
+            },
+            rowDel: function (row, index) {
+                var _this = this
+                this.$confirm('是否确认删除ID为' + row.id, '提示', {
+                    confirmButtonText: '确定',
+                    cancelButtonText: '取消',
+                    type: 'warning'
+                }).then(function () {
+                    return delObj(row.id)
+                }).then(data => {
+                    _this.tableData.splice(index, 1)
+                    _this.$message({
+                        showClose: true,
+                        message: '删除成功',
+                        type: 'success'
+                    })
+                    this.getList(this.page)
+                }).catch(function (err) {
+                })
+            },
+            /**
+             * @title 数据更新
+             * @param row 为当前的数据
+             * @param index 为当前更新数据的行数
+             * @param done 为表单关闭函数
+             *
+             **/
+            handleUpdate: function (row, index, done) {
+                putObj(row).then(data => {
+                    this.tableData.splice(index, 1, Object.assign({}, row))
+                    this.$message({
+                        showClose: true,
+                        message: '修改成功',
+                        type: 'success'
+                    })
+                    done()
+                    this.getList(this.page)
+                })
+            },
+            /**
+             * @title 数据添加
+             * @param row 为当前的数据
+             * @param done 为表单关闭函数
+             *
+             **/
+            handleSave: function (row, done) {
+                addObj(row).then(data => {
+                    this.tableData.push(Object.assign({}, row))
+                    this.$message({
+                        showClose: true,
+                        message: '添加成功',
+                        type: 'success'
+                    })
+                    done()
+                    this.getList(this.page)
+                })
+            },
+            /**
+             * 刷新回调
+             */
+            refreshChange() {
+                this.getList(this.page)
+            }
+        }
+    }
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 8 - 1
vue.config.js

@@ -26,7 +26,7 @@ module.exports = {
   // 配置转发代理
   devServer: {
     disableHostCheck: true,
-    port:8080,
+    port: 8080,
     proxy: {
       '/auth': {
         target: url,
@@ -56,6 +56,13 @@ module.exports = {
           '^/gen': '/gen'
         }
       },
+      '/mp': {
+        target: url,
+        ws: true,
+        pathRewrite: {
+          '^/mp': '/mp'
+        }
+      },
       '/daemon': {
         target: url,
         ws: true,