Browse Source

合并 develop分支代码

master
JinxChen 2 years ago
parent
commit
b2e2279a83
20 changed files with 1143 additions and 117 deletions
  1. +64
    -1
      README.md
  2. +4
    -2
      package.json
  3. +81
    -0
      src/api/wechat-fans.js
  4. +19
    -3
      src/components/TTable/TTable.vue
  5. +1
    -1
      src/layout/components/Sidebar/Logo.vue
  6. +3
    -3
      src/main.js
  7. +41
    -4
      src/router/index.js
  8. +1
    -1
      src/settings.js
  9. +3
    -2
      src/styles/index.scss
  10. +1
    -1
      src/utils/get-page-title.js
  11. +2
    -2
      src/utils/model.js
  12. +84
    -0
      src/utils/request-wx-fans.js
  13. +1
    -1
      src/views/dashboard/index.vue
  14. +68
    -4
      src/views/message-manage/main/add-articles/index.vue
  15. +382
    -71
      src/views/message-manage/main/add-mass/index.vue
  16. +236
    -20
      src/views/message-manage/main/mass-list/index.vue
  17. +77
    -0
      src/views/message-manage/main/send-details/index.vue
  18. +31
    -0
      src/views/traffic-statistics/main/index.vue
  19. +43
    -0
      src/views/traffic-statistics/main/ssjl-statistics/index.vue
  20. +1
    -1
      vue.config.js

+ 64
- 1
README.md View File

@@ -1,7 +1,7 @@
<!--
* @Date: 2021-11-29 11:14:13
* @LastEditors: JinxChen
* @LastEditTime: 2022-09-08 14:24:09
* @LastEditTime: 2023-01-05 14:55:26
* @FilePath: \TelpoUserManageAdmin\README.md
* @description:
-->
@@ -66,3 +66,66 @@ fix
`2022.8.8`
feat
- 增加 收单系统,众筹拼单系统,消息管理和系统管理路由配置


## v1.0.1
`2022.9.8`
feat
- 增加 流量统计功能


## v1.0.2
`2022.9.9`
feat
- 增加 群发列表功能

## v1.0.3
`2022.9.14`
feat
- 添加文章
- 富文本编辑器更换为 wangeditor/editor-for-vue


## v1.0.4
`2022.9.17`
feat
- 添加文章
- 增加 富文本一些基本配置
- 添加群发
- 增加 接口对接
- 群发列表
- 增加 接口对接


## v1.0.5
`2022.9.19`
fix
- 添加群发
- 群发列表
- 修改 接口数据结构



## v1.0.6
`2022.9.20`
fix
- 添加群发
- 群发列表
- 修复 分页异常的问题
- 修复 预览异常的问题


## v1.0.7
`2022.9.21`
feat
- 添加群发
- 增加 动态创建消息模板
- 群发列表
- 修复 分页异常的问题

## v1.0.8
`2023.1.5`
feat
- 添加群发
- 取消 模板列表为空时显示默认模板id
- 增加 正式环境的接口地址配置

+ 4
- 2
package.json View File

@@ -16,6 +16,8 @@
"test:ci": "npm run lint && npm run test:unit"
},
"dependencies": {
"@wangeditor/editor": "^5.1.15",
"@wangeditor/editor-for-vue": "^1.0.1",
"axios": "0.18.1",
"core-js": "3.6.5",
"element-ui": "2.13.2",
@@ -23,7 +25,7 @@
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"vue": "2.6.10",
"vue": "2.6.14",
"vue-router": "3.0.6",
"vuex": "3.1.0",
"xlsx": "0.14.1"
@@ -52,7 +54,7 @@
"serve-static": "1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",
"vue-template-compiler": "2.6.10"
"vue-template-compiler": "2.6.14"
},
"browserslist": [
"> 1%",


+ 81
- 0
src/api/wechat-fans.js View File

@@ -0,0 +1,81 @@
/*
* @Date: 2022-09-15 16:40:39
* @LastEditors: JinxChen
* @LastEditTime: 2022-09-20 11:25:01
* @FilePath: \TelpoUserManageAdmin\src\api\wechat-fans.js
* @description:
*/
import requestWxFans from '@/utils/request-wx-fans';
export const APIWechatFans = {
SelfGroups, //获取自定义分组
getGroupSender, //获取群发列表
addGroupSender, //添加模板消息群发
getArticlesGroup, //获取文章列表
getGroupSenderDetails, //获取单个群发记录
deleteGroupSender, //删除单个群发记录
getTemplateMsgList, //获取模板消息列表
getTemplateById, //根据模板id获取对应的模板消息
};
export default APIWechatFans;


function SelfGroups(data) {
return requestWxFans({
url: '/SelfGroups',
method: 'get',
params: data,
});
}

function getGroupSender(data) {
return requestWxFans({
url: '/GroupSender',
method: 'get',
params: data,
});
}

function getArticlesGroup(data) {
return requestWxFans({
url: '/Articles',
method: 'get',
params: data,
});
}

function addGroupSender(params) {
return requestWxFans({
url: '/GroupSender',
method: 'post',
data: params,
});
}


function getGroupSenderDetails(subject_id, data) {
return requestWxFans({
url: `/GroupSender/${subject_id}/details`,
method: 'get',
params: data,
});
}

function deleteGroupSender(id) {
return requestWxFans({
url: `/GroupSender/${id}`,
method: 'delete',
});
}
function getTemplateMsgList(){
return requestWxFans({
url: `/GroupSender/TemplateMsgTemplates`,
method: 'get',
});
}

function getTemplateById(id){
return requestWxFans({
url: `/GroupSender/TemplateMsgTemplates/${id}`,
method: 'get',
});
}

+ 19
- 3
src/components/TTable/TTable.vue View File

@@ -1,14 +1,14 @@
<!--
* @Date: 2021-11-30 17:19:51
* @LastEditors: JinxChen
* @LastEditTime: 2022-08-16 16:13:27
* @LastEditTime: 2022-09-21 14:31:06
* @FilePath: \TelpoUserManageAdmin\src\components\TTable\TTable.vue
* @description: 封装通用的table组件
-->
<template>
<div class="app-container">
<!-- 表格 -->
<el-table :data="tableData" border fit highlight-current-row @sort-change="sortChange" :height="400" >
<el-table :data="tableData" border fit highlight-current-row @sort-change="sortChange" :empty-text="emptyText" :height="400" >
<template v-for="column in columns">
<!-- 标题 -->
<el-table-column
@@ -16,9 +16,16 @@
:prop="column.prop"
:label="column.title"
:fixed="column.fixed"
v-if="!column.action"
v-if="!column.action && !column.isCustom"
height="400"
/>
<el-table-column :label="column.title" :show-overflow-tooltip="true" :key="column.prop" v-else-if="column.isCustom" fixed="right" min-width="100">
<template slot-scope="scope">
<div>
<p @click="handleClick(scope.row, column.fnName)" class="details-underline">{{scope.row.template_content}}</p>
</div>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column :label="column.title" :key="column.prop" v-else fixed="right" min-width="100">
<template slot-scope="scope">
@@ -61,6 +68,9 @@ export default {
type: Array,
default: () => []
},
emptyText: {
type: String,
},
btnType: {
type: String
}
@@ -94,4 +104,10 @@ export default {
.el-table {
font-size: 14px;
}
.details-underline {
text-decoration: underline;
&:hover {
cursor: pointer;
}
}
</style>

+ 1
- 1
src/layout/components/Sidebar/Logo.vue View File

@@ -31,7 +31,7 @@ export default {
},
data() {
return {
title: '用户运营管理系统',
title: '用户运营管理平台',
logo: require('@/assets/telpo.png')
}
}


+ 3
- 3
src/main.js View File

@@ -1,8 +1,8 @@
/*
* @Date: 2021-11-30 15:35:16
* @LastEditors: JinxChen
* @LastEditTime: 2022-08-06 10:25:02
* @FilePath: \telpoAdminTemplate\src\main.js
* @LastEditTime: 2022-09-17 15:12:15
* @FilePath: \TelpoUserManageAdmin\src\main.js
* @description:
*/
import Vue from 'vue'
@@ -10,7 +10,7 @@ import Vue from 'vue'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import 'element-ui/lib/theme-chalk/index.css';
/* import locale from 'element-ui/lib/locale/lang/en' */ // lang i18n

import '@/styles/index.scss' // global css


+ 41
- 4
src/router/index.js View File

@@ -72,6 +72,7 @@ export const constantRoutes = [{
component: Layout,
redirect: '/collection-order-list',
name: 'collection-order-system',
hidden: true,
meta: {
title: '收单系统',
icon: 'el-icon-s-help'
@@ -89,6 +90,29 @@ export const constantRoutes = [{
]
},

// 流量统计 todo
{
path: '/traffic-statistics',
component: Layout,
redirect: '/ssjl-statistics',
name: 'traffic-statistics',
hidden: false,
meta: {
title: '流量统计',
icon: 'el-icon-data-line'
},
children: [
{
path: '/ssjl-statistics',
name: 'ssjl-statistics',
component: () => import('@/views/traffic-statistics/main/ssjl-statistics/index'),
meta: {
title: '家长端流量统计', //收单列表
icon: 'el-icon-office-building'
}
},
]
},
// 众筹拼单系统
{
path: 'crowd-funding-otrder-system',
@@ -168,7 +192,7 @@ export const constantRoutes = [{
redirect: '/wechat-fans',
name: 'message-manage',
meta: {
title: '消息管理',
title: '消息发送系统',
icon: 'el-icon-message-solid'
},
children: [
@@ -179,7 +203,8 @@ export const constantRoutes = [{
meta: {
title: '公众号粉丝',
icon: 'el-icon-user-solid'
}
},
hidden: true
},

{
@@ -189,7 +214,8 @@ export const constantRoutes = [{
meta: {
title: '添加文章',
icon: 'el-icon-edit'
}
},
hidden: true
},

{
@@ -211,6 +237,16 @@ export const constantRoutes = [{
icon: 'el-icon-document'
}
},
{
path: '/send-details',
name: 'send-details',
component: () => import('@/views/message-manage/main/send-details/index'),
meta: {
title: '发送明细',
icon: 'el-icon-document'
},
hidden: true
},

{
path: '/unsubscribe-list',
@@ -219,7 +255,8 @@ export const constantRoutes = [{
meta: {
title: '退订列表',
icon: 'el-icon-notebook-2'
}
},
hidden: true
},

]


+ 1
- 1
src/settings.js View File

@@ -7,7 +7,7 @@
*/
module.exports = {

title: '用户运营管理系统',
title: '用户运营管理平台',

/**
* @type {boolean} true | false


+ 3
- 2
src/styles/index.scss View File

@@ -64,14 +64,15 @@ div:focus {
// main-container global css
.home-container {
height: 600px;
width: 100%;
position: relative;
margin: 60px 20px 0 20px;
padding: 10px 0;
padding: 10px 10px;
border: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
.top-container {
display: inline-block;
padding: 0 20px 0 20px;
padding: 0 20px 0 0;
span {
font-size: 14px;
}


+ 1
- 1
src/utils/get-page-title.js View File

@@ -7,7 +7,7 @@
*/
import defaultSettings from '@/settings'

const title = defaultSettings.title || '用户运营管理系统'
const title = defaultSettings.title || '用户运营管理平台'

export default function getPageTitle(pageTitle) {
if (pageTitle) {


+ 2
- 2
src/utils/model.js View File

@@ -1,8 +1,8 @@
/*
* @Date: 2021-11-30 15:09:25
* @LastEditors: JinxChen
* @LastEditTime: 2022-08-08 11:10:59
* @LastEditTime: 2022-12-12 11:35:33
* @FilePath: \TelpoUserManageAdmin\src\utils\model.js
* @description: 版本号
*/
export const VersionModel = '1.0.0';
export const VersionModel = '1.0.8';

+ 84
- 0
src/utils/request-wx-fans.js View File

@@ -0,0 +1,84 @@
/*
* @Date: 2021-12-08 15:59:46
* @LastEditors: JinxChen
* @LastEditTime: 2023-01-05 14:53:02
* @FilePath: \TelpoUserManageAdmin\src\utils\request-wx-fans.js
* @description:
*/
import axios from 'axios'
import {
Message
} from 'element-ui'
import store from '@/store'
/* import { getToken } from '@/utils/auth' */

// create an axios instance
// 区分正式和测试环境,注意 :http://localhost:9528/ 是你本地项目的启动地址
const currentUrl = window.location.href.split('#'); // 当前url,因项目部署方式不同,用当前url区分正式环境和测试环境
const testUrlList = [
'http://ping.ssjlai.com',
'http://localhost:9528'
];
const proUrl = 'http://47.116.67.214:8766/api'; //正式环境接口地址
const isTest = testUrlList.some(item => { return currentUrl[0].indexOf(item) > -1; });
const service = axios.create({
baseURL: process.env === 'test' || process.env === 'development' ? 'http://ping.ssjlai.com:8008/api/' : isTest ? 'http://ping.ssjlai.com:8008/api/': proUrl,
});

// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent

/* if (store.getters.token) {
config.headers['AuthToken'] = store.getters.token;
} */
return config;
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)

// response interceptor
service.interceptors.response.use(
response => {
const res = response.data;
return res;
// todo 拦截暂时去掉
/* if (res.code === 106) {
setTimeout(() => {
store.dispatch('user/resetToken').then(() => {
Message({
message: '登录过期,请您重新登录!',
type: 'error',
duration: 1500
})
location.reload()
})
}, 1000)
} else if (res.code === 200000) {
return res
} else {
Message({
message: res.message || '出错了,请联系管理员!',
type: 'error',
duration: 3 * 1000
})
return Promise.reject(new Error(res.message))
}
},
error => {
console.log('err:', error)
Message({
message: error.message,
type: 'error',
duration: 3 * 1000
})
return Promise.reject(error)
} */}
);

export default service;

+ 1
- 1
src/views/dashboard/index.vue View File

@@ -7,7 +7,7 @@
-->
<template>
<div class="dashboard-container">
<div class="dashboard-text">欢迎来到用户运营管理系统</div>
<div class="dashboard-text">欢迎来到用户运营管理平台</div>
</div>
</template>



+ 68
- 4
src/views/message-manage/main/add-articles/index.vue View File

@@ -1,7 +1,7 @@
<!--
* @Date: 2022-08-08 10:09:45
* @LastEditors: JinxChen
* @LastEditTime: 2022-08-09 16:44:50
* @LastEditTime: 2022-09-16 16:39:27
* @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\add-articles\index.vue
* @description: 添加文章
-->
@@ -55,8 +55,29 @@

<!-- 富文本编辑器 -->
<el-form-item label="文章内容:">
<tinymce v-model="form.articleContent" :height="5" menubar="false" />
<!-- <tinymce v-model="form.articleContent" :height="5" menubar="false" /> -->
<!-- <p>文章内容:{{form.articleContent}}</p> -->
<div style="border: 1px solid #ccc;">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editor"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 350px; overflow-y: hidden;"
v-model="form.articleContent"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="onCreated"
@onChange="onChange"
@onDestroyed="onDestroyed"
@onMaxLength="onMaxLength"
@onFocus="onFocus"
@onBlur="onBlur"
@customPaste="customPaste"
/>
</div>
</el-form-item>

<!-- 保存 -->
@@ -70,10 +91,11 @@

<script>
import TopMenu from "@/components/TopMenu/index";
import Tinymce from '@/components/Tinymce'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
/* import Tinymce from '@/components/Tinymce' */
export default {
name: 'wechat-fans',
components: { TopMenu, Tinymce },
components: { TopMenu, /* Tinymce */ Editor, Toolbar },
data(){
return {
buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}}
@@ -95,6 +117,18 @@ export default {
],
// 图片列表
fileList: [],
editor: null,
toolbarConfig: { },
mode: 'default', // or 'simple'
// 菜单配置
editorConfig: {
MENU_CONF: {
uploadImage: {
server: '', //上传图片地址 todo 待后端提供

}
}
}

}
},
@@ -103,7 +137,36 @@ export default {
this.clearFiles();
console.log("本地的上传图片列表", this.fileList);
},
beforeDestroy() {
const editor = this.editor
if (editor == null) return
editor.destroy() // 组件销毁时,及时销毁编辑器
},
methods: {
onCreated(editor) {
this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
},
onChange(editor) { console.log('onChange', editor.children) },
onDestroyed(editor) { console.log('onDestroyed', editor) },
onMaxLength(editor) { console.log('onMaxLength', editor) },
onFocus(editor) { console.log('onFocus', editor) },
onBlur(editor) { console.log('onBlur', editor) },
customPaste(editor, event, callback) {
console.log('ClipboardEvent 粘贴事件对象', event)
// const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
// const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
// const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)

// 自定义插入内容
editor.insertText('')

// 返回 false ,阻止默认粘贴行为
event.preventDefault()
callback(false) // 返回值(注意,vue 事件的返回值,不能用 return)

// 返回 true ,继续默认的粘贴行为
// callback(true)
},
// 清空本地上传的图片列表
clearFiles() {},
// 删除上传的图片
@@ -151,6 +214,7 @@ export default {
}
</script>

<style src="@wangeditor/editor/dist/css/style.css"></style>
<style scoped lang="scss">
.home-container {
position: relative;


+ 382
- 71
src/views/message-manage/main/add-mass/index.vue View File

@@ -1,7 +1,7 @@
<!--
* @Date: 2022-08-08 10:09:47
* @LastEditors: JinxChen
* @LastEditTime: 2022-08-10 09:43:22
* @LastEditTime: 2023-01-05 14:49:38
* @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\add-mass\index.vue
* @description: 添加群发
-->
@@ -10,54 +10,56 @@
<div class="home-container">
<div class="form-container">
<!-- 表单 -->
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="消息主题:" size="small">
<el-input v-model="form.msgTheme" class="input-width-400" clearable ></el-input>
<el-form ref="form" :model="form" label-width="100px" :rules="rules">
<el-form-item label="消息主题:" size="small" prop="subject">
<el-input v-model="form.subject" class="input-width-400" clearable ></el-input>
</el-form-item>
<el-form-item label="内容模板:" size="small">

<p>{{firstData}}</p>
<el-input v-model="form.contentTitle" class="input-width-600" clearable ></el-input>

<p>学习账号{{keyword1Data}}</p>
<el-input v-model="form.studyAccount" class="input-width-400" clearable ></el-input>

<p>更新内容{{keyword2Data}}</p>
<el-input v-model="form.updateCon" class="input-width-600" clearable ></el-input>

<p>{{remarkData}}</p>
<el-input v-model="form.contentDetails" class="input-width-600" clearable ></el-input>
</el-form-item>

<el-form-item label="内容链接:" size="small" class="inline-form-item">
<el-input v-model="form.contentUrl" placeholder="请选择文章或者输入外部URL" class="input-width-400"></el-input>
<el-select v-model="article" placeholder="选择文章" clearable filterable @change="onSelectArt">
<el-option :label="item.label" :value="item.label" v-for="(item, index) in articleList" :key="index"/>
<el-form-item label="选择模板:" size="small" prop="tempId">
<!-- 动态生成模板 -->
<el-select v-model="form.tempId" placeholder="选择模板" clearable filterable @change="onSelectTemp">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in templateMsgList" :key="index"/>
</el-select>
</el-form-item>
<el-form-item label="群发对象:" size="small">
<el-select v-model="form.group" placeholder="选择分组" clearable filterable @change="onSelectMass">
<el-option :label="item.label" :value="item.label" v-for="(item, index) in groupList" :key="index"/>
<el-form-item label="模板内容:" size="small" required>
<el-form-item v-for="(item, index) in templateMsgCon" :key="index">
<el-form-item>
<p>{{item.name}}</p>
<el-input v-model="templateInput[item.key]" class="input-width-600" clearable type="textarea" autosize></el-input>
</el-form-item>
</el-form-item>
</el-form-item>
<el-form-item label="内容链接:" size="small" class="inline-form-item" required>
<el-form-item prop="articleContent" >
<el-input v-model="form.articleContent" placeholder="请输入正确的外部URL" class="input-width-400" clearable @clear="onClearUrl"></el-input>
<!-- <el-select v-model="article" placeholder="选择文章" clearable filterable @change="onSelectArt" @clear="onClearSelect">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in articleList" :key="index"/>
</el-select> -->
</el-form-item>
</el-form-item>

<el-form-item label="群发对象:" size="small" prop="selfGroupId">
<el-select v-model="form.selfGroupId" placeholder="选择分组" clearable filterable @change="onSelectMass">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in groupList" :key="index"/>
</el-select>
<el-button type="primary" @click="onCheckList">查看名单</el-button>
<el-button type="primary" @click="onCheckList" disabled>查看名单</el-button>
</el-form-item>
<div class="footer-container">
<el-button type="primary" v-for="(item, index) in footBtnList" :key="index" @click="item.click">{{item.name}}</el-button>
</div>
</el-form>
</div>
<!-- 底部操作按钮 -->
<div class="footer-container">
<el-button type="primary" v-for="(item, index) in footBtnList" :key="index" @click="item.click">{{item.name}}</el-button>
</div>
<!-- 预览dialog -->
<el-dialog title="预览信息" :visible.sync="previewShow">
<div class="preview-container">
<div class="top">
<p>{{form.contentTitle}}</p>
<p>学习账号:{{form.studyAccount}}</p>
<p>更新内容:{{form.updateCon}}</p>
<p>更新时间:{{updateTime}}</p>
<p>{{form.contentDetails}}</p>
<p>{{templateInput.first}}</p>
<p v-for="(item, index) in previewData" :key="index">
{{item.name}}:{{item.value}}
</p>
<p>{{templateInput.remark}}</p>
</div>
<div class="fot">
<div class="fot" @click="onOpenUrl">
<p>详情</p>
<p>></p>
</div>
@@ -70,42 +72,47 @@
</template>

<script>
//import TopMenu from "@/components/TopMenu/index";
import { initTime } from '@/utils/index.js';
/* import './index.scss' */
import { APIWechatFans } from '@/api/wechat-fans.js';
export default {
name: 'add-mass',
components: { },
data(){
const checkUrl = (rule, value, callback) => {
let urlrReg = /^((https?|ftp):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})(\/\w\.-]*)*\/?/;
if(urlrReg.test(value)) {
callback();
} else if (this.form.articleId !== '') {
callback();
} else {
callback(`请输入正确的网址`);
}
};
return {
buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}}
firstData: '{{first.DATA}}:',
keyword1Data: '{{keyword1DATA}}:',
keyword2Data: '{{keyword2DATA}}:',
keyword3Data: '{{keyword3DATA}}:',
remarkData: '{{remarkDATA}}:',
form: {
msgTheme: '' || '财商学习通知',
contentTitle: '' || '家长您好,您孩子所需的财商学习内容有更新', //内容模板标题
studyAccount: '' || 'csds', //学习账号
updateCon: '' || '财商36问', //更新内容
contentDetails: '' || '请点击内容查看详情', //内容详情
contentUrl: '', //内容连接
group: '', //分组
subject: '' /* || '财商学习通知' */, //发送主题
templateId: 1, //模板id
articleContent: '', //文章内容
articleId: '', //文章id
selfGroupId: '', //分组id
first: '' /* || '家长您好,您孩子所需的财商学习内容有更新' */, //内容模板标题
keyword1: '' /* || 'csds' */, //学习账号
keyword2: '' /* || '财商36问' */, //更新内容
keyword3: '',
remark: '' /* || '请点击内容查看详情' */, //内容详情
tempId: '' /* || '-kOhlwJPhO7v3WW3rAdmNs76lR7ZpQEJtbVSY8Y35Eg' */, //模板id
},
article: '', //选择文章
articleList: [
{ label: '文章1', value: '文章1' },
{ label: '文章2', value: '文章2' },
{ label: '文章3', value: '文章3' },
{ label: '文章4', value: '文章4' },
],
// 文章列表
articleList: [],
// 用户分组
groupList: [
{ label: '分组1', value: '分组2' },
{ label: '分组2', value: '分组2' },
{ label: '分组3', value: '分组3' },
{ label: '分组4', value: '分组4' },
],
groupList: [],
// 底部操作按钮
footBtnList: [
{ name: '立即发送', click: () => { this.onSend()}},
@@ -115,27 +122,264 @@ export default {
],
// 预览dialog
previewShow: false,
// 要预览的页面字段
previewData: [],
// form验证
rules: {
subject: [
{ required: true, message: '请输入消息主题', trigger: 'blur' },
],
tempId: [
{ required: true, message: '请选择模板消息', trigger: 'blur' },
],
/* first: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
],
keyword1: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
],
keyword2: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
],
keyword3: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
],
keyword4: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
],
keyword5: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
],
remark: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
], */
articleContent: [
{ required: true, validator: checkUrl, message: '请输入正确的外部URL', trigger: 'blur' },
],
selfGroupId: [
{ required: true, message: '请选择群发分组', trigger: 'blur' },
],
},
// 模板消息列表
templateMsgList: [],
// 模板内容
templateMsgCon: [
{
key: 'first',
name: '{{first.DATA}}',
},
{
key: 'keyword1',
name: '消息类别:{{keyword1.DATA}}',
},
{
key: 'keyword2',
name: '通知用户:{{keyword2.DATA}}',
},
{
key: 'keyword3',
name: '通知内容:{{keyword3.DATA}}',
},
{
key: 'remark',
name: '{{remark.DATA}}'
}
],
// 动态生成的input
templateInput: {},
tempData: [] || [
{first: 'first', remark: 'remark'}
]
}
},
computed: {
// 初始弹窗更新时间
updateTime() {
return initTime(new Date(), 'ymdh'); //更新时间
}
initUpdateTime() {
return initTime(new Date(), 'ymdhm'); //更新时间
},
},
activated() {
this.getSelfGroups();
this.getArticlesGroup();
this.getTemplateMsgList();
},
mounted() {
},
created() {
this.getSelfGroups();
this.getTemplateMsgList();
this.getArticlesGroup();
},
mounted() {},
created() {},
methods: {
// 获取自定义分组
getSelfGroups() {
let reqBody = {
page_index: 1,
page_size: 10
}
APIWechatFans.SelfGroups(reqBody)
.then(res => {
let data = res.data;
this.groupList = data.rows.map(item => {
return {
label: item.name,
value: item.id
}
})
console.log("data", data);
})
},
// 获取文章列表
getArticlesGroup() {
let reqBody = {
page_index: 1,
page_size: 100000
}
APIWechatFans.getArticlesGroup(reqBody)
.then(res => {
let data = res.data;
console.log("文章data", data);
this.articleList = data.rows.map(item => {
return {
label: item.title,
value: item.id
}
})
})
},
// 查看白名单
onCheckList() {},
// objectToArray
objectToArray(obj) {
let arr = [];
for(let i in obj) {
// arr.push (obj[i] ) //返回属性值
arr.push(obj[i]) //返回键名
};
return arr;
},
// 立即发送
onSend() {},
onSend() {
this.tempData.push(this.templateInput);
console.log("tempData", JSON.stringify(this.templateInput));
// 拼接所需字段
let reqBody = {
send_now: true, //true 是立即发送, false是保存
subject: this.form.subject,
template_id: this.form.tempId,
self_group_id: this.form.selfGroupId,
article_id: this.form.articleId || 0,
mptemplate_content: JSON.stringify(this.templateInput),
url: this.form.articleContent
};
this.$refs['form'].validate((valid) => {
if(valid) {
const isCanSave = this.checkTemplateInput();
if(!isCanSave) {
this.$message({
type: "error",
message: "请完善模板内容"
});
return
}
this.addGroupSender(reqBody, '群发');
} else {
this.$message({
type: "error",
message: "请完善群发消息"
});
}
});
},
// 验证动态模板
checkTemplateInput() {
console.log("templateInput", this.templateInput);
// 获取模板输入框的实际数量
let tempInputLen = this.templateMsgCon.length;
console.log("tempInputLen", tempInputLen);
// 计算模板输入的数量
let tempLen = (Object.keys(this.templateInput).length);
if( tempLen !== tempInputLen) {
return false
} else {
return true
}
},
// 保存
onSave() {
// 定义接口所需字段
let reqBody = {
send_now: false, //true 是立即发送, false是保存
subject: this.form.subject, //消息主题
template_id: this.form.tempId, // 模板id
self_group_id: this.form.selfGroupId, //发送分组id
article_id: this.form.articleId || 0, //文章id
mptemplate_content: JSON.stringify(this.templateInput), //模板消息内容
url: this.form.articleContent, //文章链接
};
this.$refs['form'].validate((valid) => {
if(valid) {
const isCanSave = this.checkTemplateInput();
if(!isCanSave) {
this.$message({
type: "error",
message: "请完善模板内容"
});
return
}
this.addGroupSender(reqBody, '保存');
} else {
this.$message({
type: "error",
message: "请完善群发消息"
});
}
});
},
// 添加群发
addGroupSender(reqBody, action) {
const loading = this.$loading({
text: `${action}中`
});
APIWechatFans.addGroupSender(reqBody)
.then(res => {
console.log("res", res);
if(res.code === 20000) {
this.$message({
type: "success",
message: `${action}成功!`
});
} else {
this.$message({
type: "error",
message: `${action}失败!${res.message}`
});
}
}).catch(error => {
this.$message({
type: "error",
message: '出错了!请联系管理员。'
});
}).finally(() => {
loading.close();
})
},
// 预览
onPreview() {
const isCanSave = this.checkTemplateInput();
if(!isCanSave) {
this.$message({
type: "error",
message: "请先完善模板内容"
});
return
}
this.previewShow = true;
this.tempData.push(this.templateInput);
let previewArr = this.objectToArray(this.tempData[0]).slice(1, this.objectToArray(this.tempData[0]).length - 1);
this.previewData.forEach((value, index) => {
value['value'] = previewArr[index];
});
},
// 预览确认
onConfirm() {
@@ -144,16 +388,76 @@ export default {
// 选择文章
onSelectArt(value) {
if(value) {
this.form.contentUrl = value;
console.log("this.form.contentUrl", this.form.contentUrl);
this.form.articleId = value;
this.form.articleContent = this.articleList.filter(item => {
return item.value === value;
}).map(item => {
return item.label;
}).toString();
}
},
// 选择群发对象分组
onSelectMass(value) {
if(value) {
this.form.group = value;
console.log("this.form.group", this.form.group);
this.form.selfGroupId = value;
console.log("选择的对象分组", value);
}
},
// 获取消息模板列表
getTemplateMsgList() {
APIWechatFans.getTemplateMsgList()
.then(res => {
let telpData = res.data;
this.templateMsgList = telpData.map(item => {
return {
label: item.title,
value: item.template_id
}
});
// 默认显示第一个模板id
this.getTemplateMsgListById(this.templateMsgList[0].value);
})
},
// 根据模板id获取对应模板消息
getTemplateMsgListById(id) {
APIWechatFans.getTemplateById(id)
.then(res => {
console.log("res", res);
let data = res.data;
let splitData = data.content.split('\\n');
this.templateMsgCon = splitData.map(item => {
return {
name: item,
key: item.substring( item.indexOf("{{") + 2, item.indexOf(".DATA"))
}
});
this.previewData = splitData.splice(1, splitData.length - 2 ).map(item => {
return {
name: item.substring( 0, item.indexOf(":")),
key: item.substring( item.indexOf("{{") + 2, item.indexOf(".DATA"))
}
});
})
},
// 选择模板消息
onSelectTemp(value) {
console.log("选择了模板消息", value);
this.form.tempId = value;
this.templateInput = {};
this.getTemplateMsgListById(value);
},
// 清空url填写
onClearUrl() {
this.form.articleContent = '';
if( this.form.articleId ) {
this.form.articleId = '';
}
},
onClearSelect() {
this.form.articleContent = '';
},
onOpenUrl() {
window.open(this.form.articleContent);
}
}
}
@@ -161,18 +465,21 @@ export default {

<style scoped lang="scss">
.home-container {
height: 600px;
padding: 0 20px;
//overflow-y: scroll;
overflow-x: hidden;
border: none;
box-shadow: none;
overflow-y: scroll;
.form-container {
height: 500px;
height: 600px;
padding: 5px 0;
border: 1px solid #d8dce5;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
//align-items: center;
overflow: scroll;
p {
margin: 5px 0;
}
@@ -187,7 +494,7 @@ export default {
}
}
.footer-container {
height: 100px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
@@ -211,6 +518,7 @@ export default {
flex-direction: column;
padding: 0 40px;
border-bottom: 1px solid #d8dce5 ;
overflow-y: scroll;
}
.fot {
width: 100%;
@@ -218,6 +526,9 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
&:hover {
cursor: pointer;
}
}
}
.preview-btn {


+ 236
- 20
src/views/message-manage/main/mass-list/index.vue View File

@@ -1,38 +1,254 @@
<!--
* @Date: 2022-08-08 10:09:50
* @LastEditors: JinxChen
* @LastEditTime: 2022-08-08 16:08:15
* @LastEditTime: 2022-09-21 14:34:33
* @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\mass-list\index.vue
* @description: 群发列表
-->

<template>
<div class="app-container">
<!-- 顶部内容 -->
<div class="top-container">
<TopMenu :buttonList="buttonList"/>
</div>
<div class="home-container">
<!-- 顶部内容 -->
<div class="top-container">
<!-- 搜索 -->
<el-input
:placeholder="placeholder"
v-model="searchParams.subject"
class="search-input"
clearable
@clear="onClear"
/>
<el-button icon="el-icon-search" @click="onSearch" type="primary" class="search-btn">搜索</el-button>
</div>
<TTable :tableData="dataList" :columns="columns" @details="onDetails" @delete="onDelete" @update="onUpdate" @openUrl="openUrl"></TTable>
<!-- 分页 -->
<pagination
v-show="total > 0"
ref="pages"
:total="total"
:page.sync="searchParams.page"
:limit.sync="searchParams.limit"
@pagination="pageChange"
/>
<el-dialog
title="发送明细"
:visible.sync="isDetailsShow"
center
top="5vh"
@close="onCloseDetail"
>
<TTable :tableData="detailsDataList" :columns="detailsColumns" :emptyText="errorMessage"></TTable>
<!-- <p v-else-if="detailsDataList.length <= 0">{{errorMessage}}</p> -->
<!-- 分页 -->
<pagination
v-show="detailsTotal > 0"
ref="pages"
:total="detailsTotal"
:page.sync="detailsParams.page"
:limit.sync="detailsParams.limit"
@pagination="pageDetailsChange"
/>
</el-dialog>
</div>
</template>

<script>
import TopMenu from "@/components/TopMenu/index";
import TTable from "@/components/TTable/TTable";
import Pagination from "@/components/Pagination";
import { APIWechatFans } from '@/api/wechat-fans.js';
export default {
name: 'mass-list',
components: { TopMenu },
data(){
return {
buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}}
}
},
mounted() {},
created() {},
methods: {
name: "mass-list",
components: { TTable, Pagination },
data() {
return {
buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}}
searchParams: {
subject: "",
page: 1,
limit: 10
},
placeholder: "可输入消息主题",
dataList: [],
// 群发详细数组
detailsDataList: [],
detailsColumns: [
{ prop: "nickname", title: "发送对象", fixed: "left" },
{ prop: "status", title: "发送状态" },
{ prop: "send_time", title: "发送时间" },
],
columns: [
{ prop: "subject", title: "消息主题", fixed: "left" },
{ prop: "template_content", isCustom: true, title: "内容摘要", fnName: 'openUrl' },
{ prop: "self_group_name", title: "发送分组" },
{ prop: "create_time", title: "创建时间" },
{
action: true,
title: "操作",
fixed: "right",
actions: [
/* {
fnName: "update",
title: "修改",
type: "primary",
icon: "el-icon-edit",
size: "small"
}, */
{
fnName: "delete",
title: "删除",
type: "danger",
icon: "el-icon-delete",
size: "small"
},
{
fnName: "details",
title: "发送明细",
type: "success",
icon: "el-icon-tickets",
size: "small"
}
/* { fnName: "delete", title: "删除", type: "danger" , icon: 'el-icon-delete', size: 'small'}, */
]
}
],
total: 0,
isDetailsShow: false,
detailsTotal: 0,
detailsParams: {
page: 1,
limit: 10
},
errorMessage: '', //详情错误信息
// 群发明细ID
massDetailsId: ''

}
}
};
},
mounted() {

},
activated() {
this.getGroupSender();
},
created() {
this.getGroupSender();
},
methods: {
//获取群发列表
getGroupSender() {
let reqBody = {
subject: this.searchParams.subject,
page_index: this.searchParams.page,
page_size: this.searchParams.limit
};
APIWechatFans.getGroupSender(reqBody)
.then(res => {
console.log("res::", res);
this.dataList = res.data.rows.map(item => {
item.template_content = item.template_content ? JSON.parse(item.template_content).remark : '';
return item
});
this.total = res.data.totals;
})
},
// 搜索
onSearch() {
this.getGroupSender();
},
// 清空输入框
onClear() {
this.searchParams.subject = '';
this.getGroupSender();
},
onUpdate() {

},
onDelete(value) {
let that = this;
this.$confirm("是否删除?", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
APIWechatFans.deleteGroupSender(value.id)
.then(res => {
if(res.code === 20000) {
this.$message({
type: "success",
message: "删除成功"
});
this.getGroupSender();
} else {
this.$message({
type: "error",
message: `删除失败!${error.message}`
});
}
})
})
},
// 群发明细
onDetails(value) {
console.log("value", value);
//this.$router.push({name: 'send-details'});
this.isDetailsShow = true;
this.massDetailsId = value.id;
this.getGroupSenderDetails(value.id);
},
// 获取单个群发明细
getGroupSenderDetails(id) {
let reqBody = {
page_index: 1,
page_size: 10,
};
APIWechatFans.getGroupSenderDetails(id,reqBody)
.then(res => {
console.log("res", res);
if(res.data !== null) {
this.detailsDataList = res.data.rows.map(item => {
item.status = item.status === 1 ? '发送成功' : '发送失败';
return item
});
this.detailsTotal = res.data.totals;
} else {
this.errorMessage = res.message;
}
})
console.log("reqBody", reqBody);
},
// 关闭群发明细
onCloseDetail() {
this.getGroupSender();
},
// 分页发生变化
pageChange() {
if (this.searchParams.subject !== "") {
this.searchParams.subject = "";
this.getGroupSender();
} else {
this.getGroupSender();
}
},
// 群发明细发生变化
pageDetailsChange() {
this.getGroupSenderDetails(this.massDetailsId);
},
// 打开链接
openUrl(value) {
window.open(value.url);
console.log("value", value.url);
}
}
};
</script>

<style scoped lang="scss">

.search-input {
width: 350px;
margin-left: 20px;
}
.search-btn {
margin-left: 20px;
width: 100px;
}
</style>

+ 77
- 0
src/views/message-manage/main/send-details/index.vue View File

@@ -0,0 +1,77 @@
<!--
* @Date: 2022-08-08 10:09:50
* @LastEditors: JinxChen
* @LastEditTime: 2022-09-09 14:54:02
* @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\send-details\index.vue
* @description: 发送明细
-->

<template>
<div class="home-container">
<!-- 顶部内容 -->
<!-- <div class="top-container">
<el-input
:placeholder="placeholder"
v-model="searchParams.inputValue"
class="search-input"
/>
<el-button icon="el-icon-search" @click="onSearch" type="primary" class="search-btn">搜索</el-button>
</div> -->
<TTable :tableData="dataList" :columns="columns"></TTable>
<!-- 分页 -->
<pagination
v-show="total > 0"
ref="pages"
:total="total"
:page.sync="searchParams.page"
:limit.sync="searchParams.limit"
/>
</div>
</template>

<script>
import TTable from "@/components/TTable/TTable";
import Pagination from "@/components/Pagination";
export default {
name: "send-details",
components: { TTable, Pagination },
data() {
return {
buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}}
searchParams: {
inputValue: "",
page: 1,
limit: 10
},
placeholder: "可输入消息主题",
dataList: [
{ sendUser: '张三', sendStatus: '成功', sendTime: '2022.09.09 17:35' },
{ sendUser: '李四', sendStatus: '成功', sendTime: '2022.09.09 17:35' },
{ sendUser: '王五', sendStatus: '失败', sendTime: '2022.09.09 17:35' },
],
columns: [
{ prop: "sendUser", title: "发送对象", fixed: "left" },
{ prop: "sendStatus", title: "发送状态" },
{ prop: "sendTime", title: "发送时间" },
],
total: 1
};
},
mounted() {},
created() {},
methods: {
onSearch() {}
}
};
</script>

<style scoped lang="scss">
.search-input {
width: 350px;
margin-left: 20px;
}
.search-btn {
margin-left: 20px;
width: 100px;
}
</style>

+ 31
- 0
src/views/traffic-statistics/main/index.vue View File

@@ -0,0 +1,31 @@
<!--
* @Date: 2022-08-08 10:14:16
* @LastEditors: JinxChen
* @LastEditTime: 2022-09-08 15:54:08
* @FilePath: \TelpoUserManageAdmin\src\views\traffic-statistics\main\index.vue
* @description: 家长端流量统计
-->

<template>
<div style="padding:30px;">
<el-alert :closable="false" title="menu 1">
<router-view />
</el-alert>
</div>
</template>


<script>
export default {
name:'',
data(){
return {

}
}
}
</script>

<style scoped>

</style>

+ 43
- 0
src/views/traffic-statistics/main/ssjl-statistics/index.vue View File

@@ -0,0 +1,43 @@
<!--
* @Date: 2022-09-08 15:55:10
* @LastEditors: JinxChen
* @LastEditTime: 2022-09-08 16:11:22
* @FilePath: \TelpoUserManageAdmin\src\views\traffic-statistics\main\ssjl-statistics\index.vue
* @description:
-->
<template>
<div class="app-container">
<div class="home-container">
<p>账号: <el-input v-model="account" ></el-input></p>
<p>密码: <el-input v-model="password" ></el-input></p>
<el-button type="success" icon="el-icon-paperclip" @click="onJumpBaidu">点击跳转百度统计平台</el-button>
</div>
</div>
</template>

<script>
export default {
name: 'ssjl-statistics',
data(){
return {
hrefUrl: 'https://tongji.baidu.com/web5/welcome/login',
account: 'ssjlai',
password: 'Ssjlai_123456'
}
},
methods: {
onJumpBaidu() {
window.open(this.hrefUrl);
}
}
}
</script>

<style scoped lang="scss">
.home-container {
.el-input {
width: 200px;
}
}

</style>

+ 1
- 1
vue.config.js View File

@@ -6,7 +6,7 @@ function resolve(dir) {
return path.join(__dirname, dir)
}

const name = defaultSettings.title || '用户运营管理系统' // page title
const name = defaultSettings.title || '用户运营管理平台' // page title

// If your port is set to 80,
// use administrator privileges to execute the command line.


Loading…
Cancel
Save