feat: 插件管理 消息管理 优化

This commit is contained in:
dataeaseShu 2022-08-23 17:42:43 +08:00
parent 3377a0b6db
commit 7b859dfe46
8 changed files with 786 additions and 850 deletions

View File

@ -34,7 +34,7 @@ export default {
return backPath || backName || backTo
},
needInnerPadding() {
return ['sys-task-email', 'system-dept', 'system-dept-form', 'system-auth', 'sys-appearance', 'system-param', 'system-template', "sys-task-dataset"].includes(this.$route.name)
return ['sys-task-email', 'system-dept', 'system-dept-form', 'system-auth', 'sys-appearance', 'system-param', 'system-template', "sys-task-dataset", "sys-msg-web-all", "system-plugin"].includes(this.$route.name)
}
}
}

View File

@ -25,6 +25,7 @@
</template>
<script>
import { log } from '@antv/g2plot/lib/utils';
import tableBody from "./tableBody";
export default {
components: { tableBody },

View File

@ -2169,7 +2169,7 @@ export default {
i18n_msg_type_ds_invalid: '数据源失效',
i18n_msg_type_all: '全部类型',
channel_inner_msg: '站内消息',
channel_email_msg: '邮件'
channel_email_msg: '邮件提醒'
},
denumberrange: {
label: '数值区间',

View File

@ -1,191 +1,328 @@
<template>
<layout-content v-loading="$store.getters.loadingMap[$store.getters.currentPath]">
<de-layout-content
:header="$t('消息列表')"
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
>
<div class="organization">
<el-tabs v-model="tabActive" @tab-click="changeTab">
<el-tab-pane :label="$t('未读消息')" name="unread"> </el-tab-pane>
<el-tab-pane :label="$t('已读消息')" name="readed"> </el-tab-pane>
<el-tab-pane :label="$t('全部消息')" name="allMsg"> </el-tab-pane>
</el-tabs>
<div class="tabs-container">
<div class="msg-cont">
<el-row class="top-operate">
<el-col :span="12">
<template v-if="tabActive === 'unread'">
<deBtn secondary @click="allMarkReaded">{{
$t("webmsg.all_mark_readed")
}}</deBtn>
<deBtn
secondary
:disabled="multipleSelection.length === 0"
@click="markReaded"
>{{ $t("webmsg.mark_readed") }}</deBtn
>
</template>
<deBtn
v-if="tabActive === 'readed'"
secondary
:disabled="multipleSelection.length === 0"
@click="deleteBatch"
>{{ $t("commons.delete") }}</deBtn
>
&nbsp;
</el-col>
<el-col class="right-user" :span="12">
<el-select
class="name-email-search"
v-model="selectType"
size="small"
@change="typeChange"
>
<el-option
v-for="(item, index) in $store.getters.msgTypes.filter(
(type) => type.pid <= 0
)"
:key="index"
:label="$t('webmsg.' + item.typeName)"
:value="item.msgTypeId"
></el-option>
</el-select>
</el-col>
</el-row>
<div class="table-container" :key="tabActive">
<grid-table
:key="tabActive"
:tableData="data"
:multipleSelection="multipleSelection"
:columns="[]"
:pagination="paginationConfig"
@selection-change="handleSelectionChange"
@sort-change="sortChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<el-table-column type="selection" width="55" />
<el-radio-group v-model="selectType" style="margin-bottom: 15px;" @change="typeChange">
<el-radio-button v-for="(item,index) in $store.getters.msgTypes.filter(type => type.pid <= 0)" :key="index" class="de-msg-radio-class" :label="item.msgTypeId">{{ $t('webmsg.' + item.typeName) }}</el-radio-button>
<el-table-column prop="content" :label="$t('webmsg.content')">
<template slot-scope="scope">
<span style="display: flex; flex: 1">
<span>
<svg-icon
v-if="!scope.row.status"
icon-class="unread-msg"
style="color: red"
/>
<svg-icon v-else icon-class="readed-msg" />
</span>
<span
style="margin-left: 6px"
class="de-msg-a"
@click="toDetail(scope.row)"
>
{{ scope.row.content }}
</span>
</span>
</template>
</el-table-column>
</el-radio-group>
<complex-table
:data="data"
:columns="columns"
:pagination-config="paginationConfig"
@select="select"
@search="search"
@sort-change="sortChange"
>
<el-table-column
prop="createTime"
sortable="custom"
:label="$t('webmsg.sned_time')"
width="180"
>
<template slot-scope="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="content" :label="$t('webmsg.content')">
<template slot-scope="scope">
<span style="display: flex;flex: 1;">
<span>
<svg-icon v-if="!scope.row.status" icon-class="unread-msg" style="color: red;" />
<svg-icon v-else icon-class="readed-msg" />
</span>
<span style="margin-left: 6px;" class="de-msg-a" @click="toDetail(scope.row)">
{{ scope.row.content }}
</span>
</span>
</template>
</el-table-column>
<el-table-column prop="createTime" sortable="custom" :label="$t('webmsg.sned_time')" width="180">
<template slot-scope="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="typeId" sortable="custom" :label="$t('webmsg.type')" width="140">
<template slot-scope="scope">
<span>{{ getTypeName(scope.row.typeId) }}</span>
</template>
</el-table-column>
</complex-table>
</layout-content>
<el-table-column
prop="typeId"
sortable="custom"
:label="$t('webmsg.type')"
width="140"
>
<template slot-scope="scope">
<span>{{ getTypeName(scope.row.typeId) }}</span>
</template>
</el-table-column>
</grid-table>
</div>
</div>
</div>
</div>
</de-layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import ComplexTable from '@/components/business/complex-table'
import { query, updateStatus } from '@/api/system/msg'
import { msgTypes, getTypeName, loadMsgTypes } from '@/utils/webMsg'
import bus from '@/utils/bus'
import { addOrder, formatOrders } from '@/utils/index'
import { mapGetters } from 'vuex'
import DeLayoutContent from "@/components/business/DeLayoutContent";
import GridTable from "@/components/gridTable/index.vue";
import { query, updateStatus, batchRead, allRead, batchDelete } from '@/api/system/msg'
import { msgTypes, getTypeName, loadMsgTypes } from "@/utils/webMsg";
import bus from "@/utils/bus";
import { addOrder, formatOrders } from "@/utils/index";
import msgCfm from "@/components/msgCfm/index";
import { mapGetters } from "vuex";
export default {
components: {
LayoutContent,
ComplexTable
DeLayoutContent,
GridTable,
},
mixins: [msgCfm],
data() {
return {
multipleSelection: [],
tabActive: "unread",
selectType: -1,
msgTypes: msgTypes,
data: [],
allTypes: [{ name: 'mysql', type: 'jdbc' }, { name: 'sqlServer', type: 'jdbc' }],
allTypes: [
{ name: "mysql", type: "jdbc" },
{ name: "sqlServer", type: "jdbc" },
],
columns: [],
paginationConfig: {
currentPage: 1,
pageSize: 10,
total: 0
total: 0,
},
orderConditions: []
}
orderConditions: [],
};
},
computed: {
...mapGetters([
'permission_routes'
])
...mapGetters(["permission_routes"]),
},
mounted() {
this.search()
this.search();
},
created() {
//
loadMsgTypes()
loadMsgTypes();
},
methods: {
select(selection) {
changeTab(val) {
this.initSearch();
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleSizeChange(pageSize) {
this.paginationConfig.currentPage = 1;
this.paginationConfig.pageSize = pageSize;
this.search();
},
handleCurrentChange(currentPage) {
this.paginationConfig.currentPage = currentPage;
this.search();
},
initSearch() {
this.handleCurrentChange(1);
},
allMarkReaded() {
allRead().then(res => {
this.openMessageSuccess('webmsg.mark_success');
bus.$emit('refresh-top-notification')
this.initSearch()
})
},
markReaded() {
const param = this.multipleSelection.map(item => item.msgId)
batchRead(param).then(res => {
this.openMessageSuccess('webmsg.mark_success');
bus.$emit('refresh-top-notification')
this.initSearch()
})
},
deleteBatch() {
const param = this.multipleSelection.map(item => item.msgId)
batchDelete(param).then(res => {
this.openMessageSuccess('commons.delete_success');
this.initSearch()
})
},
search() {
const param = {}
const param = {};
if (this.selectType >= 0) {
param.type = this.selectType
param.type = this.selectType;
}
if (this.orderConditions.length === 0) {
param.orders = ['create_time desc ']
param.orders = ["create_time desc "];
} else {
param.orders = formatOrders(this.orderConditions)
param.orders = formatOrders(this.orderConditions);
}
const { currentPage, pageSize } = this.paginationConfig
query(currentPage, pageSize, param).then(response => {
this.data = response.data.listObject
this.paginationConfig.total = response.data.itemCount
})
if (this.tabActive !== "allMsg") {
param.status = this.tabActive === "readed";
}
const { currentPage, pageSize } = this.paginationConfig;
query(currentPage, pageSize, param).then((response) => {
this.data = response.data.listObject;
this.paginationConfig.total = response.data.itemCount;
});
},
getTypeName(value) {
return this.$t('webmsg.' + getTypeName(value))
return this.$t("webmsg." + getTypeName(value));
},
typeChange(value) {
this.search()
typeChange() {
this.initSearch();
},
toDetail(row) {
const param = { ...{ msgNotification: true, msgType: row.typeId, sourceParam: row.param }}
const param = {
...{
msgNotification: true,
msgType: row.typeId,
sourceParam: row.param,
},
};
if (this.hasPermissionRoute(row.router)) {
this.$router.push({ name: row.router, params: param })
row.status || this.setReaded(row)
return
this.$router.push({ name: row.router, params: param });
row.status || this.setReaded(row);
return;
}
this.$warning(this.$t('commons.no_target_permission'))
this.$warning(this.$t("commons.no_target_permission"));
},
hasPermissionRoute(name, permission_routes) {
permission_routes = permission_routes || this.permission_routes
permission_routes = permission_routes || this.permission_routes;
for (let index = 0; index < permission_routes.length; index++) {
const route = permission_routes[index]
if (route.name && route.name === name) return true
if (route.children && this.hasPermissionRoute(name, route.children)) return true
const route = permission_routes[index];
if (route.name && route.name === name) return true;
if (route.children && this.hasPermissionRoute(name, route.children))
return true;
}
return false
return false;
},
//
setReaded(row) {
updateStatus(row.msgId).then(res => {
bus.$emit('refresh-top-notification')
this.search()
})
updateStatus(row.msgId).then((res) => {
bus.$emit("refresh-top-notification");
this.search();
});
},
sortChange({ column, prop, order }) {
this.orderConditions = []
this.orderConditions = [];
if (!order) {
this.search()
return
this.search();
return;
}
if (prop === 'createTime') {
prop = 'create_time'
if (prop === "createTime") {
prop = "create_time";
}
if (prop === 'typeId') {
prop = 'type_id'
if (prop === "typeId") {
prop = "type_id";
}
addOrder({ field: prop, value: order }, this.orderConditions)
this.search()
}
}
}
addOrder({ field: prop, value: order }, this.orderConditions);
this.search();
},
},
};
</script>
<style lang="scss" scoped>
.de-msg-radio-class {
padding: 0 5px;
::v-deep .el-radio-button__inner {
border-radius: 4px 4px 4px 4px !important;
border-left: 1px solid #dcdfe6 !important;
padding: 10px 10px;
}
::v-deep .el-radio-button__orig-radio:checked+.el-radio-button__inner {
color: #fff;
// background-color: #0a7be0;
// border-color: #0a7be0;
-webkit-box-shadow: 0px 0 0 0 #0a7be0;
box-shadow: 0px 0 0 0 #0a7be0;
}
}
.de-msg-a:hover {
text-decoration: underline !important;
color: #0a7be0 !important;
cursor: pointer !important;
text-decoration: underline !important;
color: #0a7be0 !important;
cursor: pointer !important;
}
.table-container {
height: calc(100% - 50px);
}
.top-operate {
margin-bottom: 16px;
.right-user {
text-align: right;
display: flex;
align-items: center;
justify-content: flex-end;
}
.name-email-search {
width: 240px;
}
}
</style>
<style scoped lang="scss">
.organization {
height: 100%;
background-color: var(--MainBG, #f5f6f7);
.tabs-container {
height: calc(100% - 48px);
background: var(--ContentBG, #ffffff);
overflow-x: auto;
.msg-cont {
padding: 24px;
height: 100%;
box-sizing: border-box;
}
}
}
</style>

View File

@ -1,221 +0,0 @@
<template>
<layout-content v-loading="$store.getters.loadingMap[$store.getters.currentPath]">
<el-radio-group v-model="selectType" style="margin-bottom: 15px;" @change="typeChange">
<el-radio-button v-for="(item,index) in $store.getters.msgTypes.filter(type => type.pid <= 0)" :key="index" class="de-msg-radio-class" :label="item.msgTypeId">{{ $t('webmsg.' + item.typeName) }}</el-radio-button>
</el-radio-group>
<complex-table
:data="data"
:columns="columns"
:hide-columns="true"
:pagination-config="paginationConfig"
:search-config="searchConfig"
@select="select"
@search="search"
@selection-change="handleSelectionChange"
@sort-change="sortChange"
>
<template #toolbar>
<el-button :disabled="multipleSelection.length === 0" @click="deleteBatch">{{ $t('commons.delete') }}</el-button>
</template>
<el-table-column
type="selection"
width="55"
/>
<el-table-column prop="content" :label="$t('webmsg.content')">
<template slot-scope="scope">
<span style="display: flex;flex: 1;">
<span>
<svg-icon v-if="!scope.row.status" icon-class="unread-msg" style="color: red;" />
<svg-icon v-else icon-class="readed-msg" />
</span>
<span style="margin-left: 6px;" class="de-msg-a" @click="toDetail(scope.row)">
{{ scope.row.content }}
</span>
</span>
</template>
</el-table-column>
<el-table-column prop="createTime" sortable="custom" :label="$t('webmsg.sned_time')" width="180">
<template slot-scope="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="readTime" sortable="custom" :label="$t('webmsg.read_time')" width="180">
<template slot-scope="scope">
<span>{{ scope.row.readTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="typeId" sortable="custom" :label="$t('webmsg.type')" width="140">
<template slot-scope="scope">
<span>{{ getTypeName(scope.row.typeId) }}</span>
</template>
</el-table-column>
</complex-table>
</layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import ComplexTable from '@/components/business/complex-table'
import { query, batchDelete } from '@/api/system/msg'
import { msgTypes, getTypeName, loadMsgTypes } from '@/utils/webMsg'
import { addOrder, formatOrders } from '@/utils/index'
import { mapGetters } from 'vuex'
export default {
components: {
LayoutContent,
ComplexTable
},
data() {
return {
selectType: -1,
msgTypes: msgTypes,
data: [],
allTypes: [{ name: 'mysql', type: 'jdbc' }, { name: 'sqlServer', type: 'jdbc' }],
columns: [],
orderConditions: [],
paginationConfig: {
currentPage: 1,
pageSize: 10,
total: 0
},
multipleSelection: [],
searchConfig: {
useQuickSearch: false,
useComplexSearch: false
}
}
},
computed: {
...mapGetters([
'permission_routes'
])
},
mounted() {
this.search()
},
created() {
//
loadMsgTypes()
},
methods: {
select(selection) {
},
search() {
const param = {}
param.status = true
if (this.selectType >= 0) {
param.type = this.selectType
}
if (this.orderConditions.length === 0) {
param.orders = [' create_time desc ']
} else {
param.orders = formatOrders(this.orderConditions)
}
const { currentPage, pageSize } = this.paginationConfig
query(currentPage, pageSize, param).then(response => {
this.data = response.data.listObject
this.paginationConfig.total = response.data.itemCount
})
},
getTypeName(value) {
return this.$t('webmsg.' + getTypeName(value))
},
typeChange(value) {
this.search()
},
toDetail(row) {
const param = { ...{ msgNotification: true, msgType: row.typeId, sourceParam: row.param }}
// this.$router.push({ name: row.router, params: param })
if (this.hasPermissionRoute(row.router)) {
this.$router.push({ name: row.router, params: param })
return
}
this.$warning(this.$t('commons.no_target_permission'))
},
hasPermissionRoute(name, permission_routes) {
permission_routes = permission_routes || this.permission_routes
for (let index = 0; index < permission_routes.length; index++) {
const route = permission_routes[index]
if (route.name && route.name === name) return true
if (route.children && this.hasPermissionRoute(name, route.children)) return true
}
return false
},
sortChange({ column, prop, order }) {
this.orderConditions = []
if (!order) {
this.search()
return
}
if (prop === 'createTime') {
prop = 'create_time'
}
if (prop === 'readTime') {
prop = 'read_time'
}
if (prop === 'typeId') {
prop = 'type_id'
}
addOrder({ field: prop, value: order }, this.orderConditions)
this.search()
},
deleteBatch() {
if (this.multipleSelection.length === 0) {
this.$warning(this.$t('webmsg.please_select'))
return
}
const param = this.multipleSelection.map(item => item.msgId)
batchDelete(param).then(res => {
this.$success(this.$t('commons.delete_success'))
this.search()
})
},
handleSelectionChange(val) {
this.multipleSelection = val
}
}
}
</script>
<style lang="scss" scoped>
.de-msg-radio-class {
padding: 0 5px;
::v-deep .el-radio-button__inner {
border-radius: 4px 4px 4px 4px !important;
border-left: 1px solid #dcdfe6 !important;
padding: 10px 10px;
}
::v-deep .el-radio-button__orig-radio:checked+.el-radio-button__inner {
color: #fff;
/* background-color: #0a7be0;
border-color: #0a7be0; */
-webkit-box-shadow: 0px 0 0 0 #0a7be0;
box-shadow: 0px 0 0 0 #0a7be0;
}
}
.de-msg-a:hover {
text-decoration: underline !important;
color: #0a7be0 !important;
cursor: pointer !important;
}
</style>

View File

@ -1,13 +1,19 @@
<template xmlns:el-col="http://www.w3.org/1999/html">
<layout-content :header="$t('webmsg.receive_manage')">
<de-layout-content :header="$t('消息接收管理')">
<el-col>
<el-row class="tree-head">
<span style="float: left;padding-left: 10px">{{ $t('webmsg.type') }}</span>
<span v-for="channel in msg_channels" :key="channel.msgChannelId" class="auth-span">
<span style="float: left;">{{
$t("webmsg.type")
}}</span>
<span
v-for="channel in msg_channels"
:key="channel.msgChannelId"
class="auth-span"
>
{{ $t(channel.channelName) }}
</span>
</el-row>
<el-row style="margin-top: 5px">
<el-row class="msg-setting" style="margin-top: 5px">
<el-tree
:props="defaultProps"
:data="treeData"
@ -18,235 +24,281 @@
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<span>
<span style="margin-left: 6px">{{ $t('webmsg.' + data.name) }}</span>
<span style="margin-left: 6px">{{
$t("webmsg." + data.name)
}}</span>
</span>
<span @click.stop>
<div>
<span v-for="channel in msg_channels" :key="channel.msgChannelId" class="auth-span">
<el-checkbox v-if="data.children && data.children.length > 0" v-model="data.check_all_map[channel.msgChannelId]" :indeterminate="data.indeterminate_map[channel.msgChannelId]" @change="parentBoxChange(node, channel)" />
<el-checkbox v-else v-model="data.check_map[channel.msgChannelId]" @change="childBoxChange(node, channel)" />
</span>
</div></span>
<span
v-for="channel in msg_channels"
:key="channel.msgChannelId"
class="auth-span-check"
>
<el-checkbox
v-if="data.children && data.children.length > 0"
v-model="data.check_all_map[channel.msgChannelId]"
:indeterminate="
data.indeterminate_map[channel.msgChannelId]
"
@change="parentBoxChange(node, channel)"
/>
<el-checkbox
v-else
v-model="data.check_map[channel.msgChannelId]"
@change="childBoxChange(node, channel)"
/>
</span></div
></span>
</span>
</el-tree>
</el-row>
</el-col>
</layout-content>
</de-layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import { treeList, channelList, settingList, updateSetting, batchUpdate } from '@/api/system/msg'
import DeLayoutContent from "@/components/business/DeLayoutContent";
import {
treeList,
channelList,
settingList,
updateSetting,
batchUpdate,
} from "@/api/system/msg";
export default {
name: 'LazyTree',
components: { LayoutContent },
name: "LazyTree",
components: { DeLayoutContent },
data() {
return {
treeData: [],
defaultProps: {
children: 'children',
label: 'name',
id: 'id'
children: "children",
label: "name",
id: "id",
},
highlightCurrent: true,
msg_channels: [],
setting_data: {}
}
},
computed: {
},
mounted() {
setting_data: {},
};
},
computed: {},
mounted() {},
beforeCreate() {
// this.loadChannelData()
channelList().then(res => {
this.msg_channels = res.data
})
channelList().then((res) => {
this.msg_channels = res.data;
});
},
created() {
this.loadSettingData(this.loadTreeData)
this.loadSettingData(this.loadTreeData);
},
methods: {
//
loadTreeData() {
treeList().then(res => {
const datas = res.data
datas.forEach(data => this.formatTreeNode(data))
this.treeData = datas
})
treeList().then((res) => {
const datas = res.data;
datas.forEach((data) => this.formatTreeNode(data));
this.treeData = datas;
});
},
formatTreeNode(node) {
if (node.children && node.children.length > 0) {
node.check_all_map = {}
node.indeterminate_map = {}
node.indeterminate_number_map = {}
const kidSize = node.children.length
node.children.forEach(kid => {
this.formatTreeNode(kid)
const isLeaf = !kid.children || kid.children.length === 0
const tempMap = isLeaf ? kid.check_map : kid.indeterminate_map
node.check_all_map = {};
node.indeterminate_map = {};
node.indeterminate_number_map = {};
const kidSize = node.children.length;
node.children.forEach((kid) => {
this.formatTreeNode(kid);
const isLeaf = !kid.children || kid.children.length === 0;
const tempMap = isLeaf ? kid.check_map : kid.indeterminate_map;
for (const key in tempMap) {
if (Object.hasOwnProperty.call(tempMap, key)) {
const element = tempMap[key]
node.indeterminate_number_map[key] = node.indeterminate_number_map[key] || 0
const element = tempMap[key];
node.indeterminate_number_map[key] =
node.indeterminate_number_map[key] || 0;
if (element) {
node.indeterminate_number_map[key]++
node.indeterminate_number_map[key]++;
}
if (node.indeterminate_number_map[key] === kidSize && (isLeaf || kid.check_all_map[key])) {
node.check_all_map[key] = true
node.indeterminate_map[key] = false
if (
node.indeterminate_number_map[key] === kidSize &&
(isLeaf || kid.check_all_map[key])
) {
node.check_all_map[key] = true;
node.indeterminate_map[key] = false;
} else if (node.indeterminate_number_map[key] > 0) {
node.check_all_map[key] = false
node.indeterminate_map[key] = true
node.check_all_map[key] = false;
node.indeterminate_map[key] = true;
}
}
}
})
});
} else {
node.check_map = {}
this.msg_channels.forEach(channel => {
node.check_map[channel.msgChannelId] = this.checkBoxStatus(node, channel)
})
node.check_map = {};
this.msg_channels.forEach((channel) => {
node.check_map[channel.msgChannelId] = this.checkBoxStatus(
node,
channel
);
});
// this.checkBoxStatus(node, )
}
},
//
loadChannelData() {
channelList().then(res => {
this.msg_channels = res.data
})
channelList().then((res) => {
this.msg_channels = res.data;
});
},
//
loadSettingData(callBack) {
// this.setting_data = {}
const temp_setting_data = {}
settingList().then(res => {
const lists = res.data
lists.forEach(item => {
const key = item.typeId + ''
const temp_setting_data = {};
settingList().then((res) => {
const lists = res.data;
lists.forEach((item) => {
const key = item.typeId + "";
if (!Object.keys(temp_setting_data).includes(key)) {
temp_setting_data[key] = []
temp_setting_data[key] = [];
}
temp_setting_data[key].push(item)
})
this.setting_data = temp_setting_data
callBack && callBack()
})
temp_setting_data[key].push(item);
});
this.setting_data = temp_setting_data;
callBack && callBack();
});
},
checkBoxStatus(node, channel) {
// const nodeId = node.data.id
const nodeId = node.id
return this.setting_data[nodeId] && this.setting_data[nodeId].some(item => item.channelId === channel.msgChannelId && item.enable)
const nodeId = node.id;
return (
this.setting_data[nodeId] &&
this.setting_data[nodeId].some(
(item) => item.channelId === channel.msgChannelId && item.enable
)
);
},
nodeClick(data, node) {
},
nodeClick(data, node) {},
getAllKidId(node, ids) {
if (node.children && node.children.length > 0) {
node.children.forEach(item => this.getAllKidId(item, ids))
node.children.forEach((item) => this.getAllKidId(item, ids));
} else {
ids.push(node.id)
ids.push(node.id);
}
},
parentBoxChange(node, channel) {
const typeIds = []
this.getAllKidId(node.data, typeIds)
const channelId = channel.msgChannelId
const typeIds = [];
this.getAllKidId(node.data, typeIds);
const channelId = channel.msgChannelId;
const data = node.data
const enable = data.check_all_map && data.check_all_map[channelId]
node.data.check_all_map[channelId] = enable
node.data.indeterminate_map[channelId] = false
node.data.children.forEach(item => {
item.check_map = item.check_map || {}
item.check_map[channelId] = enable
})
const data = node.data;
const enable = data.check_all_map && data.check_all_map[channelId];
node.data.check_all_map[channelId] = enable;
node.data.indeterminate_map[channelId] = false;
node.data.children.forEach((item) => {
item.check_map = item.check_map || {};
item.check_map[channelId] = enable;
});
const param = {
typeIds: typeIds,
channelId: channelId,
enable
}
batchUpdate(param).then(res => {
this.loadSettingData(this.loadTreeData)
})
enable,
};
batchUpdate(param).then((res) => {
this.loadSettingData(this.loadTreeData);
});
},
childBoxChange(node, channel) {
const channelId = channel.msgChannelId
const parent = node.parent
const channelId = channel.msgChannelId;
const parent = node.parent;
if (parent) {
const data = parent.data
const kids = data.children
const kidSize = kids.length
let index = 0
kids.forEach(kid => {
const data = parent.data;
const kids = data.children;
const kidSize = kids.length;
let index = 0;
kids.forEach((kid) => {
if (kid.check_map[channelId]) {
index++
index++;
}
})
});
if (index === kidSize) {
node.parent.data.check_all_map[channelId] = true
node.parent.data.indeterminate_map[channelId] = false
node.parent.data.check_all_map[channelId] = true;
node.parent.data.indeterminate_map[channelId] = false;
} else if (index > 0) {
node.parent.data.check_all_map[channelId] = false
node.parent.data.indeterminate_map[channelId] = true
node.parent.data.check_all_map[channelId] = false;
node.parent.data.indeterminate_map[channelId] = true;
} else {
node.parent.data.check_all_map[channelId] = false
node.parent.data.indeterminate_map[channelId] = false
node.parent.data.check_all_map[channelId] = false;
node.parent.data.indeterminate_map[channelId] = false;
}
// this.formatTreeNode(node.parent.data)
}
const param = {
typeId: node.data.id,
channelId: channelId
channelId: channelId,
};
updateSetting(param).then((res) => {
this.loadSettingData(this.loadTreeData);
});
},
},
};
</script>
<style scoped lang="scss">
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-left: 8px;
padding-right: 32px;
}
.tree-main {
overflow-y: auto;
}
.tree-head {
height: 46px;
line-height: 46px;
border-bottom: 1px solid var(--TableBorderColor, #e6e6e6);
border-top: 1px solid var(--TableBorderColor, #e6e6e6);
background-color: var(--SiderBG, #f7f8fa);
font-size: 12px;
color: var(--TableColor, #3d4d66);
font-family: PingFang SC;
font-size: 14px;
font-weight: 500;
padding: 0 12px
}
.auth-span {
float: right;
margin-left: 24px;
}
.auth-span-check {
float: right;
margin-left: 64px;
}
</style>
<style lang="scss">
.msg-setting {
.el-tree-node__content {
height: 46px;
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
&:hover {
background-color: var(--deWhiteHover, #3370ff) !important;
.custom-tree-node {
color: var(--primary, #3370ff);
}
updateSetting(param).then(res => {
this.loadSettingData(this.loadTreeData)
})
}
}
}
</script>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-left: 8px;
}
.tree-main{
overflow-y: auto;
}
.tree-head{
height: 30px;
line-height: 30px;
border-bottom: 1px solid var(--TableBorderColor, #e6e6e6);
background-color: var(--SiderBG, #f7f8fa);
font-size: 12px;
color: var(--TableColor, #3d4d66) ;
}
.auth-span{
float: right;
width:50px;
margin-right: 30px
}
.highlights-text {
color: #faaa39 !important;
}
</style>
</style>

View File

@ -1,229 +0,0 @@
<template>
<layout-content v-loading="$store.getters.loadingMap[$store.getters.currentPath]">
<el-radio-group v-model="selectType" style="margin-bottom: 15px;" @change="typeChange">
<el-radio-button v-for="(item,index) in $store.getters.msgTypes.filter(type => type.pid <= 0)" :key="index" class="de-msg-radio-class" :label="item.msgTypeId">{{ $t('webmsg.' + item.typeName) }}</el-radio-button>
</el-radio-group>
<complex-table
:data="data"
:columns="columns"
:hide-columns="true"
:pagination-config="paginationConfig"
:search-config="searchConfig"
@select="select"
@search="search"
@selection-change="handleSelectionChange"
@sort-change="sortChange"
>
<template #toolbar>
<el-button :disabled="multipleSelection.length === 0" @click="markReaded">{{ $t('webmsg.mark_readed') }}</el-button>
<el-button @click="allMarkReaded">{{ $t('webmsg.all_mark_readed') }}</el-button>
</template>
<el-table-column
type="selection"
width="55"
/>
<el-table-column prop="content" :label="$t('webmsg.content')">
<template slot-scope="scope">
<span style="display: flex;flex: 1;">
<span>
<svg-icon v-if="!scope.row.status" icon-class="unread-msg" style="color: red;" />
<svg-icon v-else icon-class="readed-msg" />
</span>
<span style="margin-left: 6px;" class="de-msg-a" @click="toDetail(scope.row)">
{{ scope.row.content }}
</span>
</span>
</template>
</el-table-column>
<el-table-column prop="createTime" sortable="custom" :label="$t('webmsg.sned_time')" width="180">
<template slot-scope="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="typeId" sortable="custom" :label="$t('webmsg.type')" width="140">
<template slot-scope="scope">
<span>{{ getTypeName(scope.row.typeId) }}</span>
</template>
</el-table-column>
</complex-table>
</layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import ComplexTable from '@/components/business/complex-table'
import { query, updateStatus, batchRead, allRead } from '@/api/system/msg'
import { msgTypes, getTypeName, loadMsgTypes } from '@/utils/webMsg'
import bus from '@/utils/bus'
import { addOrder, formatOrders } from '@/utils/index'
import { mapGetters } from 'vuex'
export default {
components: {
LayoutContent,
ComplexTable
},
data() {
return {
selectType: -1,
msgTypes: msgTypes,
data: [],
allTypes: [{ name: 'mysql', type: 'jdbc' }, { name: 'sqlServer', type: 'jdbc' }],
columns: [],
paginationConfig: {
currentPage: 1,
pageSize: 10,
total: 0
},
searchConfig: {
useQuickSearch: false,
useComplexSearch: false
},
multipleSelection: [],
orderConditions: []
}
},
computed: {
...mapGetters([
'permission_routes'
])
},
mounted() {
this.search()
},
created() {
//
loadMsgTypes()
},
methods: {
select(selection) {
},
search() {
const param = {}
param.status = false
if (this.selectType >= 0) {
param.type = this.selectType
}
if (this.orderConditions.length === 0) {
param.orders = [' create_time desc ']
} else {
param.orders = formatOrders(this.orderConditions)
}
const { currentPage, pageSize } = this.paginationConfig
query(currentPage, pageSize, param).then(response => {
this.data = response.data.listObject
this.paginationConfig.total = response.data.itemCount
})
},
getTypeName(value) {
return this.$t('webmsg.' + getTypeName(value))
},
typeChange(value) {
this.search()
},
toDetail(row) {
const param = { ...{ msgNotification: true, msgType: row.typeId, sourceParam: row.param }}
if (this.hasPermissionRoute(row.router)) {
this.$router.push({ name: row.router, params: param })
this.setReaded(row)
return
}
this.$warning(this.$t('commons.no_target_permission'))
},
hasPermissionRoute(name, permission_routes) {
permission_routes = permission_routes || this.permission_routes
for (let index = 0; index < permission_routes.length; index++) {
const route = permission_routes[index]
if (route.name && route.name === name) return true
if (route.children && this.hasPermissionRoute(name, route.children)) return true
}
return false
},
//
setReaded(row) {
updateStatus(row.msgId).then(res => {
bus.$emit('refresh-top-notification')
this.search()
})
},
allMarkReaded() {
allRead().then(res => {
this.$success(this.$t('webmsg.mark_success'))
bus.$emit('refresh-top-notification')
this.search()
})
},
markReaded() {
if (this.multipleSelection.length === 0) {
this.$warning(this.$t('webmsg.please_select'))
return
}
const param = this.multipleSelection.map(item => item.msgId)
batchRead(param).then(res => {
this.$success(this.$t('webmsg.mark_success'))
bus.$emit('refresh-top-notification')
this.search()
})
},
handleSelectionChange(val) {
this.multipleSelection = val
},
sortChange({ column, prop, order }) {
this.orderConditions = []
if (!order) {
this.search()
return
}
if (prop === 'createTime') {
prop = 'create_time'
}
if (prop === 'typeId') {
prop = 'type_id'
}
addOrder({ field: prop, value: order }, this.orderConditions)
this.search()
}
}
}
</script>
<style lang="scss" scoped>
.de-msg-radio-class {
padding: 0 5px;
::v-deep .el-radio-button__inner {
border-radius: 4px 4px 4px 4px !important;
border-left: 1px solid #dcdfe6 !important;
padding: 10px 10px;
}
::v-deep .el-radio-button__orig-radio:checked+.el-radio-button__inner {
color: #fff;
/* background-color: #0a7be0;
border-color: #0a7be0; */
-webkit-box-shadow: 0px 0 0 0 #0a7be0;
box-shadow: 0px 0 0 0 #0a7be0;
}
}
.de-msg-a:hover {
text-decoration: underline !important;
color: #0a7be0 !important;
cursor: pointer !important;
}
</style>

View File

@ -1,165 +1,361 @@
<template>
<layout-content v-loading="$store.getters.loadingMap[$store.getters.currentPath]">
<complex-table
:data="data"
:columns="columns"
:search-config="searchConfig"
:pagination-config="paginationConfig"
@search="search"
>
<template #toolbar>
<el-upload
v-permission="['plugin:upload']"
:action="baseUrl+'api/plugin/upload'"
:multiple="false"
:show-file-list="false"
:file-list="fileList"
accept=".zip"
:before-upload="beforeUpload"
:on-success="uploadSuccess"
:on-error="uploadFail"
name="file"
:headers="headers"
<de-layout-content
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
>
<div class="top-install">
<el-input
placeholder="通过插件名称搜索"
size="small"
prefix-icon="el-icon-search"
v-model="name"
clearable
@blur="search"
>
</el-input>
<el-upload
v-permission="['plugin:upload']"
:action="baseUrl + 'api/plugin/upload'"
:multiple="false"
:show-file-list="false"
:file-list="fileList"
accept=".zip"
:before-upload="beforeUpload"
:on-success="uploadSuccess"
:on-error="uploadFail"
name="file"
:headers="headers"
>
<deBtn
:icon="!uploading ? 'el-icon-upload2' : 'el-icon-loading'"
type="primary"
:disabled="uploading"
>
<el-button size="mini" type="primary" :disabled="uploading">
<span v-if="!uploading" style="font-size: 12px;">{{ $t('plugin.local_install') }}</span>
<span v-if="uploading" style="font-size: 12px;"><i class="el-icon-loading" /> {{ $t('dataset.uploading') }}</span>
</el-button>
</el-upload>
</template>
<el-table-column prop="name" :label="$t('plugin.name')" />
<!-- <el-table-column prop="free" :label="$t('plugin.free')">
<template v-slot:default="scope">
<span>{{ scope.row.free ? '是' : '否' }}</span>
</template>
</el-table-column> -->
<el-table-column prop="cost" :label="$t('plugin.cost')" />
<el-table-column :show-overflow-tooltip="true" prop="descript" :label="$t('plugin.descript')" />
<el-table-column prop="version" :label="$t('plugin.version')" />
<el-table-column prop="creator" :label="$t('plugin.creator')" />
<el-table-column prop="installTime" :label="$t('plugin.install_time')">
<template v-slot:default="scope">
<span>{{ scope.row.installTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<fu-table-operations :buttons="buttons" :label="$t('commons.operating')" fix />
</complex-table>
</layout-content>
{{ $t(!uploading ? "plugin.local_install" : "dataset.uploading") }}
</deBtn>
</el-upload>
</div>
<div v-if="!data.length" class="plugin-cont">
<el-empty style="width: 100%" description="没有找到相关内容"></el-empty>
</div>
<div v-else class="plugin-cont">
<div v-for="ele in data" :key="ele.pluginId" class="de-card-plugin">
<div class="card-info">
<div class="info-top">
<img
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
alt=""
/>
<p class="title">{{ ele.descript }}</p>
<el-tooltip
class="item"
effect="dark"
:content="ele.descript"
placement="top"
>
<p class="tips">{{ ele.name }}</p>
</el-tooltip>
</div>
<div class="info-left">
<p class="list name" v-for="item in listName" :key="item">
{{ item }}
</p>
</div>
<div class="info-right">
<p class="list value" v-for="item in listValue" :key="item">
<template v-if="item === 'cost' && !ele.cost">
<el-tag size="mini" type="success">免费</el-tag>
</template>
<template v-else>
{{ ele[item] }}
</template>
</p>
</div>
</div>
<div class="card-method">
<el-upload
v-permission="['plugin:upload']"
:action="baseUrl + 'api/plugin/upload'"
:multiple="false"
:show-file-list="false"
:file-list="fileList"
accept=".zip"
:before-upload="beforeUpload"
:on-success="uploadSuccess"
:on-error="uploadFail"
name="file"
:headers="headers"
>
<div class="btn-plugin"><i class="el-icon-more"></i>更新</div>
</el-upload>
<el-divider direction="vertical"></el-divider>
<div
:class="[{ 'is-disable': btnDisabled(ele) }]"
v-show="checkPermission(['plugin:uninstall'])"
@click="del(ele)"
class="btn-plugin"
>
<i class="el-icon-more"></i>卸载
</div>
</div>
</div>
</div>
</de-layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import ComplexTable from '@/components/business/complex-table'
import DeLayoutContent from "@/components/business/DeLayoutContent";
import { checkPermission } from '@/utils/permission'
import { formatCondition, formatQuickCondition } from '@/utils/index'
import { pluginLists, uninstall } from '@/api/system/plugin'
import { getToken } from '@/utils/auth'
import { checkPermission } from "@/utils/permission";
import { formatCondition, formatQuickCondition } from "@/utils/index";
import { pluginLists, uninstall } from "@/api/system/plugin";
import { getToken } from "@/utils/auth";
import msgCfm from "@/components/msgCfm/index";
export default {
components: { ComplexTable, LayoutContent },
components: { DeLayoutContent },
mixins: [msgCfm],
data() {
return {
header: '',
columns: [],
buttons: [
// {
// label: this.$t('commons.delete'), icon: 'el-icon-delete', type: 'danger', click: this.del,
// show: checkPermission(['user:del'])
// }
{
label: this.$t('plugin.un_install'), icon: 'el-icon-delete', type: 'danger', click: this.del,
show: checkPermission(['plugin:uninstall']),
disabled: this.btnDisabled
}
],
searchConfig: {
useQuickSearch: true,
quickPlaceholder: this.$t('role.search_by_name'),
components: [
{ field: 'name', label: this.$t('plugin.name'), component: 'DeComplexInput' }
]
},
paginationConfig: {
currentPage: 1,
pageSize: 10,
total: 0
},
listName: ["费用", "开发者", "版本", "安装时间"],
name: "",
listValue: ["cost", "creator", "version", "installTime"],
data: [],
uploading: false,
baseUrl: process.env.VUE_APP_BASE_API,
fileList: [],
headers: { Authorization: getToken() }
}
headers: { Authorization: getToken() },
};
},
mounted() {
this.search()
this.search();
this.bindKey();
},
destroyed() {
this.unBindKey();
},
methods: {
search(condition) {
condition = formatQuickCondition(condition, 'name')
const temp = formatCondition(condition)
const param = temp || {}
const { currentPage, pageSize } = this.paginationConfig
pluginLists(currentPage, pageSize, param).then(response => {
this.data = response.data.listObject
this.paginationConfig.total = response.data.itemCount
})
entryKey(event) {
const keyCode = event.keyCode;
if (keyCode === 13) {
this.search();
}
},
bindKey() {
document.addEventListener("keypress", this.entryKey);
},
unBindKey() {
document.removeEventListener("keypress", this.entryKey);
},
search() {
const param = {};
if (this.name) {
param.conditions = [
{
field: "name",
operator: "like",
value: this.name,
},
];
}
pluginLists(0, 0, param).then((response) => {
this.data = response.data.listObject;
this.data.forEach((ele) => {
if (ele.installTime) {
ele.installTime = new Date(ele.installTime).format(
"yyyy-MM-dd hh:mm:ss"
);
}
if (ele.cost) {
ele.cost = ele.cost.toLocaleString();
}
});
});
},
beforeUpload(file) {
this.uploading = true
this.uploading = true;
},
uploadFail(response, file, fileList) {
const msg = response && response.message || '安装失败'
const msg = (response && response.message) || "安装失败";
try {
const result = JSON.parse(msg)
const result = JSON.parse(msg);
if (result && result.message) {
this.$error(result.message)
this.uploading = false
this.$error(result.message);
this.uploading = false;
}
return
return;
} catch (e) {
console.error(e)
console.error(e);
}
this.$error(msg)
this.uploading = false
this.$error(msg);
this.uploading = false;
},
uploadSuccess(response, file, fileList) {
this.uploading = false
this.search()
this.uploading = false;
this.search();
},
del(row) {
this.$confirm(this.$t('plugin.uninstall_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
uninstall(row.pluginId).then(res => {
this.search()
this.$success(this.$t('plugin.un_install_success'))
}).catch(() => {
this.$error(this.$t('plugin.un_install_error'))
})
}).catch(() => {
this.$info(this.$t('plugin.uninstall_cancel'))
})
const options = {
title: "确定卸载该插件?",
content: "卸载并重启服务器之后才能生效",
confirmButtonText: this.$t('卸载'),
type: "primary",
cb: () => {
uninstall(row.pluginId)
.then((res) => {
this.search();
this.openMessageSuccess("plugin.un_install_success");
})
.catch(() => {
this.$error(this.$t("plugin.un_install_error"));
});
},
};
this.handlerConfirm(options);
},
btnDisabled(row) {
return row.pluginId < 4
}
return row.pluginId < 4;
},
},
};
</script>
<style lang="scss">
.top-install {
position: absolute;
top: 24px;
right: 24px;
display: flex;
align-items: center;
justify-content: flex-end;
.el-input {
margin-right: 12px;
}
.el-input__inner {
background: #ffffff !important;
}
}
</script>
<style scoped>
.plugin-cont {
height: 100%;
display: flex;
flex-wrap: wrap;
background-color: var(--MainBG, #f5f6f7);
overflow-y: auto;
}
.de-card-plugin {
width: 270px;
height: 230px;
background: #ffffff;
border: 1px solid #dee0e3;
border-radius: 4px;
margin: 0 24px 24px 0;
&:hover {
box-shadow: 0px 6px 24px rgba(31, 35, 41, 0.08);
}
.card-method {
border-top: 1px solid #dee0e3;
display: flex;
align-items: center;
padding: 9px 30px 10px 30px;
width: 100%;
justify-content: space-around;
box-sizing: border-box;
.btn-plugin {
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
/* identical to box height, or 157% */
display: flex;
align-items: center;
letter-spacing: -0.1px;
/* Neutral/600 */
color: #646a73;
i {
font-size: 13px;
margin-right: 5.33px;
}
}
}
.card-info {
width: 100%;
height: 188px;
padding: 12px;
padding-bottom: 4px;
box-sizing: border-box;
.info-top {
margin-bottom: 12px;
overflow: hidden;
img {
float: left;
box-sizing: border-box;
width: 40px;
height: 40px;
background: #ffffff;
border: 1px solid #dee0e3;
border-radius: 4px;
}
.title {
width: 190px;
height: 22px;
float: left;
font-family: "PingFang SC";
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 22px;
color: #000000;
margin: -2px 0 0 6px;
}
.tips {
max-width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
float: left;
height: 20px;
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 20px;
color: #646a73;
margin: 2px 0 0 6px;
}
}
.list {
padding-bottom: 8px;
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
margin: 0;
color: #646a73;
}
.info-left {
display: inline-block;
.name {
color: #646a73;
}
}
.info-right {
display: inline-block;
margin-left: 12px;
.value {
color: #1f2329;
}
}
}
}
</style>