feat: add hosted message

This commit is contained in:
engigu
2025-01-01 17:30:54 +08:00
parent 344ace9020
commit 42eb0b2946
18 changed files with 506 additions and 19 deletions
+16 -1
View File
@@ -69,7 +69,7 @@ const CONSTANT = {
},
{
type: 'Custom',
label: '自定义',
label: '自定义推送',
inputs: [
{ subLabel: 'webhook地址', value: '', col: 'webhook', desc: "自定义webhook地址" },
{ subLabel: '请求体', value: '', col: 'body', desc: "请求体, text内容请使用 TEXT 进行占位\n例如:{\"message\": \"TEXT\", \"foo\": \"bar\"}", isTextArea: true },
@@ -103,6 +103,21 @@ const CONSTANT = {
{ value: '', col: 'to_account', desc: "要发送的OpenId(登录微信公众号后台查看)" },
],
},
{
type: 'MessageNest',
label: '自托管消息',
inputs: [
{ subLabel: '渠道名', value: '', col: 'name', desc: "想要设置的渠道名字" },
],
tips: {
text: "自托管消息说明", desc: "站点本身会作为消息接收站点,接收、展示推送过来的消息。"
},
taskInsRadios: [
{ subLabel: 'text', content: 'text' },
],
taskInsInputs: [
],
},
],
API_VIEW_DATA: [
{ label: "curl", class: "language-shell line-numbers", code: "", func: ApiStrGenerate.getCurlString },
+5
View File
@@ -41,6 +41,11 @@ const router = createRouter({
path: '/settings',
name: 'settings',
component: () => import('../views/tabsTools/settings/settings.vue')
},
{
path: '/hostedMessage',
name: 'hostedmessage',
component: () => import('../views/tabsTools/hostedMessage/hostedMessage.vue')
},
{
path: '/:catchAll(.*)',
+5 -4
View File
@@ -35,10 +35,11 @@ export default {
const menuData = reactive([
{ id: '0', title: '数据统计', path: '/statistic' },
{ id: '1', title: '发信日志', path: '/sendlogs' },
{ id: '2', title: '定时发信', path: '/cronmessages' },
{ id: '3', title: '发信任务', path: '/sendtasks' },
{ id: '4', title: '发信渠道', path: '/sendways' },
{ id: '5', title: '设置', path: '/settings' },
{ id: '2', title: '托管消息', path: '/hostedmessage' },
{ id: '3', title: '定时发信', path: '/cronmessages' },
{ id: '4', title: '发信任务', path: '/sendtasks' },
{ id: '5', title: '发信渠道', path: '/sendways' },
{ id: '6', title: '设置', path: '/settings' },
]);
const checkIsLogin = () => {
@@ -0,0 +1,206 @@
<template>
<div class="main-center-body">
<div class="container">
<div class="search-input-sendways">
<el-input v-model="search" size="small" placeholder="根据关键字搜索相应托管消息" @change="filterFunc()" />
</div>
<div class="search-box">
<el-text class="refresh-time" v-if="refreshText" size="small">{{ refreshText }}</el-text>
<el-input-number class="refresh-box" v-model="refreshSec" size="small" :step="2" :min="5"
controls-position="right" />
<el-switch v-model="refreshSwitch" class="ml-2" width="80" inline-prompt active-text="自动刷新" inactive-text="自动刷新"
@click="clickFreshSwitch" />
</div>
<hr />
<div ref="refContainer">
<el-table :data="tableData" stripe empty-text="托管消息为空" :row-style="rowStyle()">
<el-table-column label="ID" prop="id" width="85px" />
<el-table-column label="消息标题" prop="title" show-overflow-tooltip width="150px" />
<el-table-column label="托管消息" prop="content">
<template #default="scope">
<el-tooltip enterable placement="top">
<template #content>
<div v-html="formatLogDisplayHtml(scope)"></div>
</template>
<span class="log-overflow">{{ scope.row.content }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="发送时间" prop="created_on" width="160px" />
<el-table-column label="详情" prop="status" width="120px" fixed="right">
<template #default="scope">
<el-button link size="small" style="margin-right: 10px;" type="primary"
@click="drawer = true; logText = formatLogDisplayHtml(scope)">查看消息</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-drawer v-model="drawer" :with-header="false">
<el-text v-html="logText" size="small"></el-text>
</el-drawer>
<div class="pagination-block">
<el-pagination layout="prev, pager, next" :total="total" :page-size="pageSize"
@current-change="handPageChange" />
<el-text class="total-tip" size="small">每页{{ pageSize }}{{ total }}</el-text>
</div>
</div>
</div>
</template>
<script>
import { reactive, toRefs, onMounted, watch, ref } from 'vue'
import { request } from '../../../api/api'
import { copyToClipboard } from '../../../util/clipboard.js';
import { useRoute } from 'vue-router';
import { CONSTANT } from '@/constant'
import { usePageState } from '@/store/page_sate.js';
import { CommonUtils } from "@/util/commonUtils.js";
export default {
components: {
},
setup() {
const pageState = usePageState();
const router = useRoute();
const state = reactive({
search: '',
refreshText: '',
refreshSwitch: false,
refreshSec: 20,
refreshIntervalFuncList: [],
optionValue: '',
logText: '',
drawer: false,
tableData: [],
total: CONSTANT.TOTAL,
pageSize: pageState.siteConfigData.pagesize,
currPage: CONSTANT.PAGE,
});
const TransHtml = (raw) => {
if (raw) {
return raw.replace(/\n/g, '<br />')
}
return ''
}
const formatLogDisplayHtml = (scope) => {
let content = TransHtml(scope.row.content);
return content;
}
const handPageChange = async (pageNum) => {
state.currPage = pageNum;
await queryListData(pageNum, state.pageSize);
}
const rowStyle = () => {
return {
'font-size': '13px',
}
}
const clickFreshSwitch = () => {
if (state.refreshSwitch) {
let flag = setInterval(async function () {
await filterFunc();
state.refreshText = `自动刷新于:${CommonUtils.getCurrentTimeStr()}`;
}, state.refreshSec * 1000);
state.refreshIntervalFuncList.push(flag);
} else {
state.refreshIntervalFuncList.forEach(intervalId => {
clearInterval(intervalId);
});
state.refreshIntervalFuncList = [];
state.refreshText = '';
}
}
const filterFunc = async () => {
await queryListData(state.currPage, state.pageSize, state.search, state.optionValue);
}
const queryListData = async (page, size, name = '', query = '') => {
let params = { page: page, size: size, text: name, query: query };
const rsp = await request.get('/hostedmessages/list', { params: params });
state.tableData = await rsp.data.data.lists;
state.total = await rsp.data.data.total;
}
onMounted(async () => {
state.search = router.query.name;
await queryListData(1, state.pageSize, router.query.name, router.query.taskid, router.query.query);
});
return {
...toRefs(state), TransHtml, clickFreshSwitch,
rowStyle, handPageChange, filterFunc, copyToClipboard, formatLogDisplayHtml
};
}
}
</script>
<style scoped>
hr {
color: #FAFCFF;
background-color: #FAFCFF;
border-color: #FAFCFF;
}
.container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
max-width: 1000px;
width: 100%;
/* margin-top: -10vh; */
}
.search-box {
float: right;
margin-top: -2px;
}
.refresh-box {
width: 80px;
margin-right: 5px;
}
.refresh-time {
margin-right: 10px;
}
.pagination-block {
margin-top: 15px;
display: flex;
justify-content: flex-end;
}
.total-tip {
display: inline-block;
}
.search-input-sendways {
width: 200px;
display: inline-flex;
}
.log-overflow {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
</style>
@@ -1,8 +1,8 @@
<template>
<div class="main-center-body">
<div class="container">
<el-row :gutter="16">
<el-col :span="6">
<el-row :gutter="18">
<el-col :span="4">
<div class="statistic-card">
<el-statistic :value="data.message_total_num">
<template #title>
@@ -13,7 +13,18 @@
</el-statistic>
</div>
</el-col>
<el-col :span="6">
<el-col :span="5">
<div class="statistic-card">
<el-statistic :value="data.hosted_message_total_num">
<template #title>
<div style="display: inline-flex; align-items: center">
托管消息数
</div>
</template>
</el-statistic>
</div>
</el-col>
<el-col :span="5">
<div class="statistic-card">
<el-statistic :value="data.today_total_num">
<template #title>
@@ -24,7 +35,7 @@
</el-statistic>
</div>
</el-col>
<el-col :span="6">
<el-col :span="5">
<div class="statistic-card">
<el-statistic :value="data.today_succ_num">
<template #title>
@@ -35,7 +46,7 @@
</el-statistic>
</div>
</el-col>
<el-col :span="6">
<el-col :span="5">
<div class="statistic-card">
<el-statistic :value="data.today_failed_num" :value-style="formatFailedNumStyle()">
<template #title>