feat: 圈子数据统计对接

This commit is contained in:
kaizheng(郑凯) 2025-02-15 20:10:05 +08:00
parent 7cf28d106b
commit a5cbfa84a5
13 changed files with 928 additions and 242 deletions

View File

@ -58,6 +58,7 @@
"moment": "^2.30.1",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"numeral": "^2.0.6",
"path-to-regexp": "8.0.0",
"qrcodejs2": "^0.0.2",
"qs": "^6.10.1",
@ -76,7 +77,7 @@
"vuex": "3.1.0",
"wangeditor": "^4.7.11",
"webpack": "^4.47.0",
"xlsx": "^0.18.5"
"xlsx": "^0.14.1"
},
"devDependencies": {
"@babel/parser": "^7.7.4",

View File

@ -143,3 +143,13 @@ export function setNotice(data) {
data
});
}
// 后台查询交易圈统计
export function getCircleStatistics(data) {
debugger;
return request({
url: "/admin/group/collect/query",
method: "post",
data
});
}

View File

@ -6,6 +6,7 @@ import "normalize.css/normalize.css";
import Element from "element-ui";
import Vue2Editor from "vue2-editor";
import "@/utils/filters";
// 数据字典
import dict from "./components/Dict";

View File

@ -1,25 +1,30 @@
/* eslint-disable */
require('script-loader!file-saver');
import XLSX from 'xlsx'
require("script-loader!file-saver");
import * as XLSX from "xlsx";
function generateArray(table) {
var out = [];
var rows = table.querySelectorAll('tr');
var rows = table.querySelectorAll("tr");
var ranges = [];
for (var R = 0; R < rows.length; ++R) {
var outRow = [];
var row = rows[R];
var columns = row.querySelectorAll('td');
var columns = row.querySelectorAll("td");
for (var C = 0; C < columns.length; ++C) {
var cell = columns[C];
var colspan = cell.getAttribute('colspan');
var rowspan = cell.getAttribute('rowspan');
var colspan = cell.getAttribute("colspan");
var rowspan = cell.getAttribute("rowspan");
var cellValue = cell.innerText;
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
//Skip ranges
ranges.forEach(function (range) {
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
ranges.forEach(function(range) {
if (
R >= range.s.r &&
R <= range.e.r &&
outRow.length >= range.s.c &&
outRow.length <= range.e.c
) {
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
}
});
@ -38,19 +43,18 @@ function generateArray(table) {
c: outRow.length + colspan - 1
}
});
};
}
//Handle Value
outRow.push(cellValue !== "" ? cellValue : null);
//Handle Colspan
if (colspan)
for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
out.push(outRow);
}
return [out, ranges];
};
}
function datenum(v, date1904) {
if (date1904) v += 1462;
@ -85,18 +89,18 @@ function sheet_from_array_of_arrays(data, opts) {
r: R
});
if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b';
if (typeof cell.v === "number") cell.t = "n";
else if (typeof cell.v === "boolean") cell.t = "b";
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.t = "n";
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
} else cell.t = 's';
} else cell.t = "s";
ws[cell_ref] = cell;
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
if (range.s.c < 10000000) ws["!ref"] = XLSX.utils.encode_range(range);
return ws;
}
@ -109,7 +113,7 @@ function Workbook() {
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
return buf;
}
@ -127,21 +131,24 @@ export function export_table_to_excel(id) {
/* add ranges to worksheet */
// ws['!cols'] = ['apple', 'banan'];
ws['!merges'] = ranges;
ws["!merges"] = ranges;
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookType: "xlsx",
bookSST: false,
type: 'binary'
type: "binary"
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), "test.xlsx")
saveAs(
new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}),
"test.xlsx"
);
}
export function export_json_to_excel({
@ -149,11 +156,11 @@ export function export_json_to_excel({
data,
filename,
autoWidth = true,
bookType= 'xlsx'
bookType = "xlsx"
} = {}) {
/* original data */
filename = filename || 'excel-list'
data = [...data]
filename = filename || "excel-list";
data = [...data];
data.unshift(header);
var ws_name = "SheetJS";
var wb = new Workbook(),
@ -161,34 +168,35 @@ export function export_json_to_excel({
if (autoWidth) {
/*设置worksheet每列的最大宽度*/
const colWidth = data.map(row => row.map(val => {
/*先判断是否为null/undefined*/
if (val == null) {
return {
'wch': 10
};
}
/*再判断是否为中文*/
else if (val.toString().charCodeAt(0) > 255) {
return {
'wch': val.toString().length * 2
};
} else {
return {
'wch': val.toString().length
};
}
}))
const colWidth = data.map(row =>
row.map(val => {
/*先判断是否为null/undefined*/
if (val == null) {
return {
wch: 10
};
} else if (val.toString().charCodeAt(0) > 255) {
/*再判断是否为中文*/
return {
wch: val.toString().length * 2
};
} else {
return {
wch: val.toString().length
};
}
})
);
/*以第一行为初始值*/
let result = colWidth[0];
for (let i = 1; i < colWidth.length; i++) {
for (let j = 0; j < colWidth[i].length; j++) {
if (result[j]['wch'] < colWidth[i][j]['wch']) {
result[j]['wch'] = colWidth[i][j]['wch'];
if (result[j]["wch"] < colWidth[i][j]["wch"]) {
result[j]["wch"] = colWidth[i][j]["wch"];
}
}
}
ws['!cols'] = result;
ws["!cols"] = result;
}
/* add worksheet to workbook */
@ -198,9 +206,12 @@ export function export_json_to_excel({
var wbout = XLSX.write(wb, {
bookType: bookType,
bookSST: false,
type: 'binary'
type: "binary"
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), `${filename}.${bookType}`);
saveAs(
new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}),
`${filename}.${bookType}`
);
}

259
src/utils/filters.js Normal file
View File

@ -0,0 +1,259 @@
import Vue from "vue";
import numeral from "numeral";
import dayjs from "dayjs";
// 格式化和操作数字
Vue.filter("numberFormat", (value, format) => {
if (value == null) return "--";
return numeral(value || 0).format(format);
});
// 格式时间
Vue.filter("dateFilter", (value, format = "YYYY-MM-DD") => {
if (value == null) return "--";
return dayjs(value).format(format);
});
Vue.filter("getUserStatus", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "已启用";
case 2:
return "已禁用";
case 3:
return "已冻结";
}
});
Vue.filter("getRiskLevel", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "低风险";
case 2:
return "中低风险";
case 3:
return "中风险";
case 4:
return "中高风险";
case 5:
return "高风险";
}
});
Vue.filter("getProductType", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "观点包";
case 2:
return "观点";
case 7:
return "投资组合";
case 8:
return "股票池";
case 9:
return "套餐";
case 21:
return "增值产品";
case 22:
return "三方-课程";
case 23:
return "EFT专区";
case 24:
return "选股工具";
case 25:
return "小飞机理财";
case 31:
return "投资课堂";
case 32:
return "课程";
}
});
Vue.filter("getStatus", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "待提交";
case 2:
return "待一级审核";
case 11:
return "待二级审核";
case 12:
return "待三级审核";
case 3:
return "已上架";
case 4:
return "已驳回";
case 5:
return "已下架";
}
});
Vue.filter("getCommissionStatus", value => {
// if (value == null) return '--'
switch (value) {
case 0:
return "无信用账号";
case 1:
return "成功";
case -1:
return "失败";
case 2:
return "查询失败";
default:
return "--";
}
});
Vue.filter("getAuthType", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "公开可见";
case 2:
return "登录手机可见";
case 3:
return "登录资金账号可见";
case 4:
return "付费可见";
case 5:
return "仅供签约客户查看";
}
});
Vue.filter("getPeriodType", value => {
if (value == null) return "--";
switch (value) {
case 0:
return "免费永久,单品提拥";
case 7:
return "周";
case 14:
return "月";
case 16:
return "季度";
case 19:
return "半年";
case 20:
return "年";
case 99:
return "收费永久,单篇收费";
case 98:
return "收费永久,整体提拥";
}
});
Vue.filter("getCustomRiskLevel", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "C1保守型";
case 2:
return "C2谨慎型";
case 3:
return "C3稳健型";
case 4:
return "C4积极型";
case 5:
return "C5激进型";
}
});
Vue.filter("getPayType", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "支付宝";
case 5:
return "微信";
case 13:
return "提佣支付";
case 14:
return "保证金";
}
});
Vue.filter("getOrderStatus", (value, type) => {
if (value == null) return "--";
if (type === 13) {
switch (value) {
case 70:
return "签约失败";
case 95:
return "解约中";
case 100:
return "已解约";
case 180:
return "新订单";
case 220:
return "已签约";
}
} else {
switch (value) {
case 220:
return "支付成功";
case 70:
return "支付取消";
case 95:
return "退款中";
case 100:
return "退款成功";
case 180:
return "新订单";
case 80:
return "已过期";
case 90:
return "已关闭";
}
}
});
Vue.filter("getRefundStatus", (value, type) => {
if (value == null) return "--";
let mould = "";
if (type === 13) {
mould = "解约";
} else {
mould = "退款";
}
switch (value) {
case 180:
return `申请${mould}`;
case 200:
return `${mould}审核通过`;
case 210:
return `${mould}失败`;
case 220:
return `${mould}`;
}
});
Vue.filter("getChannel", value => {
if (value == null) return "--";
switch (value) {
case 1:
return "APP";
case 2:
return "小程序";
}
});
Vue.filter("formatPhone", phone => {
// console.log(phone)
// if(!phone) return phone
// if (typeof phone == 'number') {
// phone = phone.toString();
// }
// return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
return phone;
});
Vue.filter("formatRatio", (value, unit = true, num = 2) => {
if (value == null || value == "--") {
return "";
}
return (Number(value) * 100).toFixed(num) + (unit ? "%" : "");
});

View File

@ -27,7 +27,6 @@ export default {
}
},
mounted() {
this.initChart();
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize();
@ -44,10 +43,13 @@ export default {
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, "macarons");
this.chart.setOption(this.option);
drawChart() {
if (this.chart) {
this.chart.setOption(this.option, true);
} else {
this.chart = echarts.init(this.$el, "macarons");
this.chart.setOption(this.option);
}
}
}
};

View File

@ -1,75 +1,129 @@
<template>
<div class="module-content">
<h4>成员</h4>
<h4>内容</h4>
<ul class="data-list">
<li>
<p>总动态</p>
<h5>100</h5>
<p>今日老师发布内容</p>
<h5>{{ currentData.advisorGroupContent }}</h5>
</li>
<li>
<p>今日新增动态书</p>
<h5>500</h5>
<p>今日助教发布内容数</p>
<h5>{{ currentData.assistantGroupContent }}</h5>
</li>
<li>
<p>今日用户互动内容数</p>
<h5>{{ currentData.customerGroupContent }}</h5>
</li>
<li>
<p>今日老师私聊内容数</p>
<h5>{{ currentData.advisorPrivateContent }}</h5>
</li>
<li>
<p>今日助教私聊内容数</p>
<h5>{{ currentData.assistantPrivateContent }}</h5>
</li>
<li>
<p>今日用户私聊内容数</p>
<h5>{{ currentData.customerPrivateContent }}</h5>
</li>
</ul>
<div class="chart-header">
<h5>内容趋势</h5>
<el-form :inline="true" size="mini" :model="formInline" class="my-form">
<el-form-item label="">
<el-select v-model="value" placeholder="请选择">
<el-form-item label="" prop="timeType">
<el-select
v-model="timeType"
class="filter-item"
style="width: 100px"
>
<el-option
v-for="item in options"
:key="item.value"
v-for="item in [
{ label: '总数据', id: 1 },
{ label: '年', id: 2 },
{ label: '月', id: 3 },
{ label: '周', id: 4 },
{ label: '日', id: 5 }
]"
:key="item.id"
:label="item.label"
:value="item.value"
>
</el-option>
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="">
<el-form-item label="" prop="username">
<el-date-picker
v-model="value1"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
>
</el-date-picker>
v-if="timeType === 2"
v-model="time"
type="year"
placeholder="选择年"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100px"
/>
<el-date-picker
v-else-if="timeType === 3"
v-model="time"
type="month"
placeholder="选择月"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100px"
/>
<el-date-picker
v-else-if="timeType === 4"
v-model="time"
type="week"
format="yyyy 第 WW 周"
placeholder="选择周"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100px"
:picker-options="{ firstDayOfWeek: 1 }"
/>
<el-date-picker
v-else-if="timeType === 5"
v-model="time"
type="date"
placeholder="选择日期"
style="width: 100px"
/>
</el-form-item>
<el-form-item label="">
<el-button type="primary" @click="onSubmit">搜索</el-button>
<el-button>导出</el-button>
<el-button @click="toExport">导出</el-button>
</el-form-item>
</el-form>
</div>
<LineChart :option="echartOption"></LineChart>
<LineChart ref="lineChartRef" :option="echartOption" />
</div>
</template>
<script>
import LineChart from "./LineChart";
import { getCircleStatistics } from "@/api/circle";
import dayjs from "dayjs";
export default {
components: { LineChart },
data() {
return {
options: [
{
value: "1",
label: "日"
},
{
value: "2",
label: "月"
},
{
value: "3",
label: "年"
}
],
formInline: {
groupId: this.$route.query.id,
startDate: "",
endDate: ""
},
timeType: 1,
time: "",
currentData: {},
dataList: [],
echartOption: {
tooltip: {
trigger: "axis"
},
legend: {
data: ["Email", "Union Ads"]
data: [
"老师发布内容数",
"助教发布内容数",
"用户互动内容数",
"老师私聊内容数",
"助教私聊内容数",
"用户私聊内容数"
]
},
grid: {
top: "40px",
@ -86,21 +140,169 @@ export default {
xAxis: {
type: "category",
boundaryGap: false,
data: ["Mon", "Tue"]
data: []
},
yAxis: {
type: "value"
},
series: [
{
name: "Email",
type: "line",
stack: "Total",
data: [120, 132, 101, 134, 90, 230, 210]
}
]
series: []
}
};
},
watch: {
timeType(value) {
this.time = this.formInline.startDate = this.formInline.endDate = "";
},
time(value) {
this.formInline.startDate = value;
if (value === null || value === "") {
return (this.formInline.endDate = null);
}
if (this.formInline.startDate !== null) {
this.formInline.startDate = dayjs(this.formInline.startDate).format(
"YYYY-MM-DD HH:mm:ss"
);
}
if (this.timeType == 2) {
this.formInline.endDate = dayjs(value)
.add(1, "year")
.format("YYYY-MM-DD HH:mm:ss");
} else if (this.timeType == 3) {
this.formInline.endDate = dayjs(value)
.add(1, "month")
.format("YYYY-MM-DD HH:mm:ss");
} else if (this.timeType == 4) {
this.formInline.startDate = dayjs(value)
.subtract(1, "day")
.format("YYYY-MM-DD HH:mm:ss");
this.formInline.endDate = dayjs(value)
.add(6, "day")
.format("YYYY-MM-DD HH:mm:ss");
} else if (this.timeType == 5) {
this.formInline.endDate = dayjs(value)
.add(1, "day")
.format("YYYY-MM-DD HH:mm:ss");
}
}
},
created() {
this.getData();
},
methods: {
async getData() {
const ret = await getCircleStatistics(this.formInline);
if (ret && ret.data) {
this.dataList = ret.data;
if (
!this.formInline.endDate ||
this.formInline.endDate === dayjs().format("YYYY-MM-DD")
) {
this.currentData = ret.data[ret.data.length - 1];
}
const xAxisData = [];
const seriesData = {
advisorGroupContent: {
name: "老师发布内容数",
type: "line",
stack: "Total",
data: []
},
assistantGroupContent: {
name: "助教发布内容数",
type: "line",
stack: "Total",
data: []
},
customerGroupContent: {
name: "用户互动内容数",
type: "line",
stack: "Total",
data: []
},
advisorPrivateContent: {
name: "老师私聊内容数",
type: "line",
stack: "Total",
data: []
},
assistantPrivateContent: {
name: "助教私聊内容数",
type: "line",
stack: "Total",
data: []
},
customerPrivateContent: {
name: "用户私聊内容数",
type: "line",
stack: "Total",
data: []
}
};
this.dataList.forEach(item => {
for (const key in seriesData) {
seriesData[key].data.push(item[key]);
}
xAxisData.push(item.date);
});
this.echartOption.xAxis.data = [];
this.echartOption.xAxis.data = xAxisData;
this.echartOption.series = [];
for (const key in seriesData) {
this.echartOption.series.push(seriesData[key]);
}
this.$nextTick(() => {
this.$refs.lineChartRef.drawChart();
});
}
},
onSubmit() {
this.getData();
},
formatJson(filterVal, jsonData) {
return jsonData.map(v =>
filterVal.map(j => {
return v[j];
})
);
},
async toExport() {
this.downloadLoading = true;
const { data, code, message } = await getCircleStatistics(
this.formInline
);
if (code === 0) {
import("@/utils/export2Excel").then(excel => {
const tHeader = [
"日期",
"老师发布内容数",
"助教发布内容数",
"用户互动内容数",
"老师私聊内容数",
"助教私聊内容数",
"用户私聊内容数"
];
const filterVal = [
"date",
"advisorGroupContent",
"assistantGroupContent",
"customerGroupContent",
"advisorPrivateContent",
"assistantPrivateContent",
"customerPrivateContent"
];
const excelData = this.formatJson(filterVal, data);
excel.export_json_to_excel({
header: tHeader,
data: excelData,
filename: "圈子内容统计"
});
console.log("list=>", data);
});
} else {
this.$message.error(message);
}
this.downloadLoading = false;
}
}
};
</script>

View File

@ -4,80 +4,121 @@
<ul class="data-list">
<li>
<p>总成员数</p>
<h5>100</h5>
<h5>{{ currentData.totalMembers }}</h5>
</li>
<li>
<p>今日访问成员数</p>
<h5>500</h5>
<h5>{{ currentData.visitedMembers }}</h5>
</li>
<li>
<p>今日新增成员数</p>
<h5>500</h5>
<h5>{{ currentData.newMembers }}</h5>
</li>
<li>
<p>今日动态成员数</p>
<h5>500</h5>
<p>今日发互动成员数</p>
<h5>{{ currentData.interactionMembers }}</h5>
</li>
<li>
<p>今日发私聊成员数</p>
<h5>{{ currentData.privateChatMembers }}</h5>
</li>
</ul>
<div class="chart-header">
<h5>成员趋势</h5>
<el-form :inline="true" size="mini" :model="formInline" class="my-form">
<el-form-item label="">
<el-select v-model="value" placeholder="请选择">
<el-form-item label="" prop="timeType">
<el-select
v-model="timeType"
class="filter-item"
style="width: 100px"
>
<el-option
v-for="item in options"
:key="item.value"
v-for="item in [
{ label: '总数据', id: 1 },
{ label: '年', id: 2 },
{ label: '月', id: 3 },
{ label: '周', id: 4 },
{ label: '日', id: 5 }
]"
:key="item.id"
:label="item.label"
:value="item.value"
>
</el-option>
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="">
<el-form-item label="" prop="username">
<el-date-picker
v-model="value1"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
>
</el-date-picker>
v-if="timeType === 2"
v-model="time"
type="year"
placeholder="选择年"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100px"
/>
<el-date-picker
v-else-if="timeType === 3"
v-model="time"
type="month"
placeholder="选择月"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100px"
/>
<el-date-picker
v-else-if="timeType === 4"
v-model="time"
type="week"
format="yyyy 第 WW 周"
placeholder="选择周"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100px"
:picker-options="{ firstDayOfWeek: 1 }"
/>
<el-date-picker
v-else-if="timeType === 5"
v-model="time"
type="date"
placeholder="选择日期"
style="width: 100px"
/>
</el-form-item>
<el-form-item label="">
<el-button type="primary" @click="onSubmit">搜索</el-button>
<el-button>导出</el-button>
<el-button @click="toExport">导出</el-button>
</el-form-item>
</el-form>
</div>
<LineChart :option="echartOption"></LineChart>
<LineChart ref="lineChartRef" :option="echartOption" />
</div>
</template>
<script>
import LineChart from "./LineChart";
import { getCircleStatistics } from "@/api/circle";
import dayjs from "dayjs";
export default {
components: { LineChart },
data() {
return {
options: [
{
value: "1",
label: "日"
},
{
value: "2",
label: "月"
},
{
value: "3",
label: "年"
}
],
formInline: {
groupId: this.$route.query.id,
startDate: "",
endDate: ""
},
timeType: 1,
time: "",
currentData: {},
dataList: [],
echartOption: {
tooltip: {
trigger: "axis"
},
legend: {
data: ["Email", "Union Ads"]
data: [
"总成员数",
"访问成员数",
"新增成员数",
"发互动成员数",
"发私聊成员数"
]
},
grid: {
top: "40px",
@ -94,27 +135,161 @@ export default {
xAxis: {
type: "category",
boundaryGap: false,
data: ["Mon", "Tue"]
data: []
},
yAxis: {
type: "value"
},
series: [
{
name: "Email",
type: "line",
stack: "Total",
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: "Union Ads",
type: "line",
stack: "Total",
data: [220, 182, 191, 234, 290, 330, 310]
}
]
series: []
}
};
},
watch: {
timeType(value) {
this.time = this.formInline.startDate = this.formInline.endDate = "";
},
time(value) {
this.formInline.startDate = value;
if (value === null || value === "") {
return (this.formInline.endDate = null);
}
if (this.formInline.startDate !== null) {
this.formInline.startDate = dayjs(this.formInline.startDate).format(
"YYYY-MM-DD HH:mm:ss"
);
}
if (this.timeType == 2) {
this.formInline.endDate = dayjs(value)
.add(1, "year")
.format("YYYY-MM-DD HH:mm:ss");
} else if (this.timeType == 3) {
this.formInline.endDate = dayjs(value)
.add(1, "month")
.format("YYYY-MM-DD HH:mm:ss");
} else if (this.timeType == 4) {
this.formInline.startDate = dayjs(value)
.subtract(1, "day")
.format("YYYY-MM-DD HH:mm:ss");
this.formInline.endDate = dayjs(value)
.add(6, "day")
.format("YYYY-MM-DD HH:mm:ss");
} else if (this.timeType == 5) {
this.formInline.endDate = dayjs(value)
.add(1, "day")
.format("YYYY-MM-DD HH:mm:ss");
}
}
},
created() {
this.getData();
},
methods: {
async getData() {
const ret = await getCircleStatistics(this.formInline);
if (ret && ret.data) {
this.dataList = ret.data;
if (
!this.formInline.endDate ||
this.formInline.endDate === dayjs().format("YYYY-MM-DD")
) {
this.currentData = ret.data[ret.data.length - 1];
}
const xAxisData = [];
const seriesData = {
totalMembers: {
name: "总成员数",
type: "line",
stack: "Total",
data: []
},
visitedMembers: {
name: "访问成员数",
type: "line",
stack: "Total",
data: []
},
newMembers: {
name: "新增成员数",
type: "line",
stack: "Total",
data: []
},
interactionMembers: {
name: "发互动成员数",
type: "line",
stack: "Total",
data: []
},
privateChatMembers: {
name: "发私聊成员数",
type: "line",
stack: "Total",
data: []
}
};
this.dataList.forEach(item => {
for (const key in seriesData) {
seriesData[key].data.push(item[key]);
}
xAxisData.push(item.date);
});
this.echartOption.xAxis.data = [];
this.echartOption.xAxis.data = xAxisData;
this.echartOption.series = [];
for (const key in seriesData) {
this.echartOption.series.push(seriesData[key]);
}
this.$nextTick(() => {
this.$refs.lineChartRef.drawChart();
});
}
},
onSubmit() {
this.getData();
},
formatJson(filterVal, jsonData) {
return jsonData.map(v =>
filterVal.map(j => {
return v[j];
})
);
},
async toExport() {
this.downloadLoading = true;
const { data, code, message } = await getCircleStatistics(
this.formInline
);
if (code === 0) {
import("@/utils/export2Excel").then(excel => {
const tHeader = [
"日期",
"总成员数",
"访问成员数",
"新增成员数",
"发互动成员数",
"发私聊成员数"
];
const filterVal = [
"date",
"totalMembers",
"visitedMembers",
"newMembers",
"interactionMembers",
"privateChatMembers"
];
const excelData = this.formatJson(filterVal, data);
excel.export_json_to_excel({
header: tHeader,
data: excelData,
filename: "圈子成员统计"
});
console.log("list=>", data);
});
} else {
this.$message.error(message);
}
this.downloadLoading = false;
}
}
};
</script>
@ -151,6 +326,7 @@ export default {
padding: 0;
font-size: 24px;
line-height: 40px;
height: 40px;
}
}
}

View File

@ -1,57 +1,59 @@
<template>
<div class="module-content">
<h4>学员列表</h4>
<el-form
:inline="true"
size="mini"
:model="formInline"
class="demo-form-inline"
>
<el-form-item label="">
<el-input
v-model="formInline.user"
placeholder="请输入uid或者昵称"
></el-input>
</el-form-item>
<el-form-item label="">
<el-select v-model="formInline.region" placeholder="选择在期状态">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item label="">
<el-select v-model="formInline.region" placeholder="选择禁言状态">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">搜索</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="头像" width="180">
<template slot-scope="scope">
<img
src=""
style="max-width: 50x;max-height: 50px;"
alt=""
srcset=""
<div class="table-area">
<el-form
:inline="true"
size="mini"
:model="formInline"
class="demo-form-inline"
>
<el-form-item label="">
<el-input
v-model="formInline.user"
placeholder="请输入uid或者昵称"
/>
</template>
</el-table-column>
<el-table-column prop="name" label="uid" width="180"> </el-table-column>
<el-table-column prop="address" label="加入时间"> </el-table-column>
<el-table-column prop="address" label="到期时间"> </el-table-column>
<el-table-column prop="address" label="状态"> </el-table-column>
<el-table-column prop="address" label="禁言状态"> </el-table-column>
<el-table-column fixed="right" label="操作" width="100">
<template slot-scope="scope">
<el-button type="text" size="small">私聊</el-button>
<el-button type="text" size="small">禁言</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item label="">
<el-select v-model="formInline.region" placeholder="选择在期状态">
<el-option label="区域一" value="shanghai" />
<el-option label="区域二" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="">
<el-select v-model="formInline.region" placeholder="选择禁言状态">
<el-option label="区域一" value="shanghai" />
<el-option label="区域二" value="beijing" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">搜索</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="头像" width="180">
<template slot-scope="scope">
<img
src=""
style="max-width: 50x;max-height: 50px;"
alt=""
srcset=""
>
</template>
</el-table-column>
<el-table-column prop="name" label="uid" width="180" />
<el-table-column prop="address" label="加入时间" />
<el-table-column prop="address" label="到期时间" />
<el-table-column prop="address" label="状态" />
<el-table-column prop="address" label="禁言状态" />
<el-table-column fixed="right" label="操作" width="100">
<template slot-scope="scope">
<el-button type="text" size="small">私聊</el-button>
<el-button type="text" size="small">禁言</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
@ -61,7 +63,21 @@ export default {
formInline: {},
tableData: [{}]
};
},
methods: {
onSubmit() {}
}
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.module-content {
margin-bottom: 20px;
h4 {
margin: 0;
font-size: 18px;
}
}
.table-area {
padding: 20px;
}
</style>

View File

@ -11,9 +11,9 @@
<img
v-if="item.userType === 1"
:src="item.advisor ? item.advisor.avatar : defaultAvatar.teacher"
/>
<img v-else-if="item.userType === 3" :src="defaultAvatar.assistant" />
<img v-else-if="item.userType === 2" :src="defaultAvatar.student" />
>
<img v-else-if="item.userType === 3" :src="defaultAvatar.assistant">
<img v-else-if="item.userType === 2" :src="defaultAvatar.student">
<div class="news-info">
<div class="news-info-top">
<div class="news-user">
@ -21,32 +21,30 @@
<label
v-if="[1, 3].includes(item.userType)"
:class="[item.userType === 1 ? 'blue' : 'orange']"
>{{ item.userType === 1 ? "老师" : "助教" }}</label
>
<span><i>1</i>/4</span>
<span>已读</span>
>{{ item.userType === 1 ? "老师" : "助教" }}</label>
<div v-if="item.userType !== 2">
<span><i v-if="item.readCount">{{ item.readCount }}</i><i v-if="item.totalCount">{{ item.totalCount }}</i></span>
<span>已读</span>
</div>
</div>
<div class="news-opt">
<el-link
v-if="type !== 5"
type="success"
@click="setMessageRecommend(item)"
>{{
item.isRecommend === 1 ? "取消精选" : "设为精选"
}}</el-link
>
>{{
item.isRecommend === 1 ? "取消精选" : "设为精选"
}}</el-link>
<el-link
v-if="item.userType === 2 && type !== 5"
type="success"
@click="setReplyMsg(item)"
>引用</el-link
>
>引用</el-link>
<el-link
v-if="item.userType === 2 && item.status === 1"
type="success"
@click="updateMessageStatus(item)"
>通过审核</el-link
>
>通过审核</el-link>
</div>
</div>
<p>{{ item.createTime }}</p>
@ -54,7 +52,7 @@
</div>
<div class="new-content">
<p v-if="item.contentType === 1">{{ item.content }}</p>
<img v-else :src="item.content" alt="" />
<img v-else :src="item.content" alt="">
</div>
</li>
</ul>
@ -177,7 +175,7 @@ export default {
this.loading = false;
});
if (ret && ret.code === 0) {
let retList = ret.data.list;
const retList = ret.data.list;
this.list = this.list.concat(ret.data.list);
retList.forEach(msg => {
this.msgIdsObj[msg.id] = msg;

View File

@ -13,7 +13,7 @@
>
<li v-for="(item, index) in list" :key="index">
<div class="new-header">
<img :src="defaultAvatar.student" />
<img :src="defaultAvatar.student">
<div class="news-info">
<div class="news-info-top">
<div class="news-user">
@ -21,30 +21,32 @@
<label
v-if="[1, 3].includes(item.userType)"
:class="[item.userType === 1 ? 'blue' : 'orange']"
>{{ item.userType === 1 ? "老师" : "助教" }}</label
>
<span><i>1</i>/4</span>
<span>已读</span>
>{{ item.userType === 1 ? "老师" : "助教" }}</label>
<div v-if="item.userType !== 2">
<span><i v-if="item.readCount">{{ item.readCount }}</i><i v-if="item.totalCount">{{ item.totalCount }}</i></span>
<span>已读</span>
</div>
</div>
</div>
<p>{{ item.createTime }}</p>
</div>
<div class="news-opt">
<div class="flex">
<el-link type="success" @click="setReplyMsg(item)"
>引用</el-link
>
<el-link
type="success"
@click="setReplyMsg(item)"
>引用</el-link>
<el-link
v-if="item.userType === 2 && item.status === 1"
type="success"
@click="updateMessageStatus(item)"
>通过审核</el-link
>
>通过审核</el-link>
</div>
<div class="flex">
<el-link type="success" @click="toPrivateChat(item)"
>私聊</el-link
>
<el-link
type="success"
@click="toPrivateChat(item)"
>私聊</el-link>
<el-link type="success" @click="prohibition(item)">{{
item.isForbidden === 1 ? "取消禁言" : "禁言"
@ -54,7 +56,7 @@
</div>
<div class="new-content">
<p v-if="item.contentType === 1">{{ item.content }}</p>
<img v-else :src="item.content" alt="" />
<img v-else :src="item.content" alt="">
</div>
</li>
</ul>
@ -156,7 +158,7 @@ export default {
this.loading = false;
});
if (ret && ret.code === 0) {
let retList = ret.data.list;
const retList = ret.data.list;
this.list = this.list.concat(retList);
retList.forEach(msg => {
this.msgIdsObj[msg.id] = msg;

View File

@ -6,6 +6,9 @@ export default {
stompClient: null
};
},
beforeDestroy() {
this.stompClient && this.stompClient.deactivate();
},
methods: {
async getConnectConfig(id) {
const res = await rLiveWsConfig({ id, type: 9 });
@ -19,8 +22,8 @@ export default {
process.env.NODE_ENV === "development"
? "ws://8.138.144.54:8080/tgim/chat"
: `${location.protocol === "http:" ? "ws://" : "wss://"}${
location.host
}/tgim/chat`;
location.host
}/tgim/chat`;
// 后端群组消息
const groupChatTopic = `/admin/group/topic/${id}`;
// 私聊消息

View File

@ -91,8 +91,13 @@
v-if="tougu || yunyin || zhujiao || yinxiao"
type="text"
size="mini"
@click="addTag(scope.row, 1)"
@click="addTag(scope.row, 0)"
>查看</el-button>
<el-button
type="text"
size="mini"
@click="$router.push(`/circle/data?id=${scope.row.id}`)"
>数据</el-button>
<el-button
v-if="
(tougu || zhujiao || yunyin) &&