mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-02-02 00:25:22 +08:00
前端和后端源码,合并到一个git仓库中,方便用户下载,避免前后端不匹配的问题
This commit is contained in:
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<a-list item-layout="horizontal" :data-source="messageList">
|
||||
<template #loadMore>
|
||||
<div
|
||||
v-if="messageList && messageList.length > 0 && !loadEndStatus && !loadingMoreStatus"
|
||||
:style="{ textAlign: 'center', marginTop: '12px', height: '32px', lineHeight: '32px' }"
|
||||
>
|
||||
<a-button @click="onLoadMore">加载更多</a-button>
|
||||
</div>
|
||||
<div
|
||||
v-if="messageList && messageList.length > 0 && loadEndStatus"
|
||||
:style="{ textAlign: 'center', marginTop: '12px', height: '32px', lineHeight: '32px' }"
|
||||
>
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<template #actions>
|
||||
<a-rate :value="item.starFlag=='1'?1:0" :count="1" @click="clickStar(item)" style="cursor: pointer" disabled />
|
||||
</template>
|
||||
|
||||
<a-list-item-meta :description="item.createTime">
|
||||
<template #title>
|
||||
<div style="position: relative">
|
||||
<!-- <span style="display: inline-block; position: absolute; left: -16px">
|
||||
<exclamation-outlined v-if="noRead(item)" title="未读消息" style="color: red" />
|
||||
</span>-->
|
||||
|
||||
<span>{{ getMsgCategory(item) }}</span>
|
||||
<span v-if="item.busType == 'bpm'" class="bpm-cuiban-content" v-html="item.msgContent">
|
||||
</span>
|
||||
<a-tooltip v-else>
|
||||
<template #title>
|
||||
<div v-html="item.msgContent"></div>
|
||||
</template>
|
||||
{{ item.titile }}
|
||||
</a-tooltip>
|
||||
|
||||
<a @click="showMessageDetail(item)" style="margin-left: 16px">查看详情</a>
|
||||
</div>
|
||||
</template>
|
||||
<template #avatar>
|
||||
<template v-if="item.busType=='email'">
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><mail-outlined style="font-size: 16px" title="未读消息"/></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><mail-outlined style="font-size: 16px"/></a-avatar>
|
||||
</template>
|
||||
|
||||
<template v-else-if="item.busType=='bpm_task'">
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><interaction-outlined style="font-size: 16px" title="未读消息"/></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><interaction-outlined style="font-size: 16px"/></a-avatar>
|
||||
</template>
|
||||
|
||||
<template v-else-if="item.busType=='bpm'">
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><alert-outlined style="font-size: 16px" title="未读消息"/></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><alert-outlined style="font-size: 16px"/></a-avatar>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><bell-filled style="font-size: 16px" title="未读消息"/></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><bell-filled style="font-size: 16px" /></a-avatar>
|
||||
</template>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { FilterOutlined, CloseOutlined, BellFilled, ExclamationOutlined, MailOutlined,InteractionOutlined, AlertOutlined } from '@ant-design/icons-vue';
|
||||
import { useSysMessage, useMessageHref } from './useSysMessage';
|
||||
|
||||
|
||||
export default {
|
||||
name: 'SysMessageList',
|
||||
components: {
|
||||
FilterOutlined,
|
||||
CloseOutlined,
|
||||
BellFilled,
|
||||
ExclamationOutlined,
|
||||
MailOutlined,
|
||||
InteractionOutlined,
|
||||
AlertOutlined
|
||||
},
|
||||
props:{
|
||||
star: {
|
||||
type:Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits:['close', 'detail'],
|
||||
setup(props, {emit}){
|
||||
const { messageList,loadEndStatus,loadingMoreStatus,onLoadMore,noRead, getMsgCategory, searchParams, reset, loadData, updateStarMessage } = useSysMessage();
|
||||
|
||||
function reload(params){
|
||||
let { fromUser, rangeDateKey, rangeDate } = params;
|
||||
searchParams.fromUser = fromUser||'';
|
||||
searchParams.rangeDateKey = rangeDateKey||'';
|
||||
searchParams.rangeDate = rangeDate||[];
|
||||
if(props.star===true){
|
||||
searchParams.starFlag = '1'
|
||||
}else{
|
||||
searchParams.starFlag = ''
|
||||
}
|
||||
reset();
|
||||
loadData();
|
||||
}
|
||||
|
||||
function clickStar(item){
|
||||
console.log(item)
|
||||
updateStarMessage(item);
|
||||
if(item.starFlag=='1'){
|
||||
item.starFlag = '0'
|
||||
}else{
|
||||
item.starFlag = '1'
|
||||
}
|
||||
}
|
||||
|
||||
const { goPage } = useMessageHref(emit);
|
||||
|
||||
function showMessageDetail(record){
|
||||
record.readFlag = '1'
|
||||
goPage(record);
|
||||
emit('close', record.id)
|
||||
}
|
||||
|
||||
return {
|
||||
messageList,
|
||||
loadEndStatus,
|
||||
loadingMoreStatus,
|
||||
onLoadMore,
|
||||
noRead,
|
||||
getMsgCategory,
|
||||
reload,
|
||||
clickStar,
|
||||
showMessageDetail
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.msg-no-read{
|
||||
:deep(.ant-badge-dot){
|
||||
top: 5px;
|
||||
right: 3px;
|
||||
}
|
||||
}
|
||||
:deep(.bpm-cuiban-content) p{
|
||||
display: inherit;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/** QQYUN-5688 【样式】鼠标放上去怎么不是手 */
|
||||
:deep(.ant-rate-disabled){
|
||||
.ant-rate-star{
|
||||
cursor: pointer !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,523 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
:canFullscreen="false"
|
||||
:draggable="false"
|
||||
:closable="false"
|
||||
@register="registerModal"
|
||||
wrapClassName="sys-msg-modal"
|
||||
:width="800"
|
||||
:footer="null"
|
||||
destroyOnClose
|
||||
>
|
||||
<template #title>
|
||||
<div class="sys-msg-modal-title">
|
||||
<div class="title"></div>
|
||||
<div class="ant-tabs-nav-wrap">
|
||||
<div class="ant-tabs-nav-scroll">
|
||||
<div class="ant-tabs-nav ant-tabs-nav-animated">
|
||||
<div>
|
||||
<div
|
||||
@click="(e) => handleChangeTab(e, 'all')"
|
||||
role="tab"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab"
|
||||
:class="{ 'ant-tabs-tab-active': activeKey == 'all' }"
|
||||
>
|
||||
全部消息
|
||||
</div>
|
||||
<div
|
||||
@click="(e) => handleChangeTab(e, 'star')"
|
||||
role="tab"
|
||||
aria-disabled="false"
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab"
|
||||
:class="{ 'ant-tabs-tab-active': activeKey == 'star' }"
|
||||
>
|
||||
标星消息
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
|
||||
:style="{
|
||||
transform: activeKey == 'all' ? 'translate3d(130px, 0px, 0px)' : 'translate3d(215px, 0px, 0px)',
|
||||
display: 'block',
|
||||
width: '88px',
|
||||
height: '1px'
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 头部图标 -->
|
||||
<div class="icon-right">
|
||||
<div class="icons">
|
||||
<a-popover placement="bottomRight" :overlayStyle="{ width: '400px' }" trigger="click" v-model:open="showSearch">
|
||||
<template #content>
|
||||
<div>
|
||||
<span class="search-label">回复、提到我的人?:</span>
|
||||
<span style="display: inline-block;">
|
||||
<div v-if="searchParams.fromUser" class="selected-user">
|
||||
<span>{{searchParams.realname}}</span>
|
||||
<span class="clear-user-icon"><close-outlined style="font-size: 12px" @click="clearSearchParamsUser"/></span>
|
||||
</div>
|
||||
<a-button v-else type="dashed" shape="circle" @click="openSelectPerson">
|
||||
<plus-outlined />
|
||||
</a-button>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<div class="search-date">
|
||||
<div class="date-label">时间:</div>
|
||||
<div class="date-tags">
|
||||
<div class="tags-container">
|
||||
<div v-for="item in dateTags" :class="item.active == true ? 'tag active' : 'tag'" @click="handleClickDateTag(item)">{{
|
||||
item.text
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="cust-range-date" v-if="showRangeDate">
|
||||
<a-range-picker v-model:value="searchRangeDate" @change="handleChangeSearchDate" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<span v-if="conditionStr" class="anticon filtera">
|
||||
<filter-outlined />
|
||||
<span style="font-size:12px;margin-left: 3px">{{conditionStr}}</span>
|
||||
<span style="display: flex;margin:0 5px;"><close-outlined style="font-size: 12px" @click="clearAll"/></span>
|
||||
</span>
|
||||
<filter-outlined v-else/>
|
||||
</a-popover>
|
||||
<close-outlined @click="closeModal" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="sys-message-card">
|
||||
<a-tabs :activeKey="activeKey" center @tabClick="handleChangePanel" animated>
|
||||
<template #renderTabBar>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<a-tab-pane tab="全部消息" key="all" forceRender>
|
||||
<sys-message-list ref="allMessageRef" @close="hrefThenClose" @detail="showDetailModal"/>
|
||||
</a-tab-pane>
|
||||
|
||||
<!-- 标星 -->
|
||||
<a-tab-pane tab="标星消息" key="star" forceRender>
|
||||
<sys-message-list ref="starMessageRef" star @close="hrefThenClose" @detail="showDetailModal"/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</BasicModal>
|
||||
|
||||
<user-select-modal isRadioSelection :showButton="false" labelKey="realname" rowKey="username" @register="regModal" @getSelectResult="getSelectedUser"></user-select-modal>
|
||||
|
||||
<DetailModal @register="registerDetail" :zIndex="1001"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BasicModal, useModalInner, useModal } from '/@/components/Modal';
|
||||
import { FilterOutlined, CloseOutlined, BellFilled, ExclamationOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import JSelectUser from '/@/components/Form/src/jeecg/components/JSelectUser.vue';
|
||||
import { ref, unref, reactive, computed } from 'vue';
|
||||
// import SysMessageList from './SysMessageList.vue'
|
||||
import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue'
|
||||
import DetailModal from '/@/views/monitor/mynews/DetailModal.vue';
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
|
||||
export default {
|
||||
name: 'SysMessageModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
FilterOutlined,
|
||||
CloseOutlined,
|
||||
BellFilled,
|
||||
ExclamationOutlined,
|
||||
JSelectUser,
|
||||
SysMessageList: createAsyncComponent(() => import('./SysMessageList.vue')),
|
||||
UserSelectModal,
|
||||
PlusOutlined,
|
||||
DetailModal
|
||||
},
|
||||
emits:['register', 'refresh'],
|
||||
setup(_p, {emit}) {
|
||||
const allMessageRef = ref();
|
||||
const starMessageRef = ref();
|
||||
const activeKey = ref('all');
|
||||
function handleChangeTab(e, key) {
|
||||
activeKey.value = key;
|
||||
loadData();
|
||||
}
|
||||
function handleChangePanel(key) {
|
||||
activeKey.value = key;
|
||||
}
|
||||
|
||||
// 查询区域存储值
|
||||
const searchParams = reactive({
|
||||
fromUser: '',
|
||||
realname: '',
|
||||
rangeDateKey: '7day',
|
||||
rangeDate: [],
|
||||
});
|
||||
|
||||
function loadData(){
|
||||
let params = {
|
||||
fromUser: searchParams.fromUser,
|
||||
rangeDateKey: searchParams.rangeDateKey,
|
||||
rangeDate: searchParams.rangeDate,
|
||||
}
|
||||
if(activeKey.value == 'all'){
|
||||
getRefPromise(allMessageRef).then(() => {
|
||||
allMessageRef.value.reload(params);
|
||||
});
|
||||
}else{
|
||||
starMessageRef.value.reload(params);
|
||||
}
|
||||
}
|
||||
|
||||
//useModalInner
|
||||
const [registerModal, { closeModal }] = useModalInner(async (data) => {
|
||||
//每次弹窗打开 加载最新的数据
|
||||
loadData();
|
||||
});
|
||||
|
||||
const showSearch = ref(false);
|
||||
|
||||
function handleChangeSearchPerson(value, a) {
|
||||
console.log('选择改变', value, a);
|
||||
showSearch.value = true;
|
||||
}
|
||||
|
||||
const dateTags = reactive([
|
||||
{ key: 'jt', text: '今天', active: false },
|
||||
{ key: 'zt', text: '昨天', active: false },
|
||||
{ key: 'qt', text: '前天', active: false },
|
||||
{ key: 'bz', text: '本周', active: false },
|
||||
{ key: 'sz', text: '上周', active: false },
|
||||
{ key: 'by', text: '本月', active: false },
|
||||
{ key: 'sy', text: '上月', active: false },
|
||||
{ key: '7day', text: '七日', active: true },
|
||||
{ key: 'zdy', text: '自定义', active: false },
|
||||
]);
|
||||
function handleClickDateTag(item) {
|
||||
for (let a of dateTags) {
|
||||
if(a.key != item.key){
|
||||
a.active = false;
|
||||
}
|
||||
}
|
||||
item.active = !item.active;
|
||||
if(item.active == false){
|
||||
searchParams.rangeDateKey = ''
|
||||
}else{
|
||||
searchParams.rangeDateKey = item.key;
|
||||
}
|
||||
if (item.key == 'zdy') {
|
||||
// 自定义日期查询走的是 handleChangeSearchDate
|
||||
if(item.active == false){
|
||||
searchParams.rangeDate = []
|
||||
loadData();
|
||||
}
|
||||
}else{
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
const showRangeDate = computed(() => {
|
||||
let temp = dateTags.filter((i) => i.active == true);
|
||||
if (temp && temp.length > 0) {
|
||||
if (temp[0].text == '自定义') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const searchRangeDate = ref([]);
|
||||
function handleChangeSearchDate(_value, dateStringArray) {
|
||||
searchParams.rangeDate = [...dateStringArray]
|
||||
loadData();
|
||||
}
|
||||
|
||||
function hrefThenClose(id){
|
||||
emit('refresh', id)
|
||||
// closeModal();
|
||||
}
|
||||
|
||||
// 有查询条件值的时候显示该字符串
|
||||
const conditionStr = computed(()=>{
|
||||
const { fromUser, rangeDateKey, realname } = searchParams;
|
||||
if(!fromUser && !rangeDateKey){
|
||||
return ''
|
||||
}
|
||||
let arr = [];
|
||||
if(fromUser){
|
||||
arr.push(realname)
|
||||
}
|
||||
if(rangeDateKey){
|
||||
let rangDates = dateTags.filter(item=>item.key == rangeDateKey);
|
||||
if(rangDates && rangDates.length>0){
|
||||
arr.push(rangDates[0].text)
|
||||
}
|
||||
}
|
||||
return arr.join('、')
|
||||
});
|
||||
|
||||
//注册model
|
||||
const [regModal, { openModal }] = useModal();
|
||||
|
||||
function getSelectedUser(options, value){
|
||||
if(options && options.length>0){
|
||||
searchParams.fromUser = value
|
||||
searchParams.realname = options[0].label;
|
||||
}
|
||||
}
|
||||
|
||||
function openSelectPerson(){
|
||||
openModal(true, {})
|
||||
}
|
||||
|
||||
function clearSearchParamsUser(){
|
||||
searchParams.fromUser = ''
|
||||
searchParams.realname = ''
|
||||
}
|
||||
|
||||
function getRefPromise(componentRef) {
|
||||
return new Promise((resolve) => {
|
||||
(function next() {
|
||||
let ref = componentRef.value;
|
||||
if (ref) {
|
||||
resolve(ref);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
next();
|
||||
}, 100);
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
function clearAll(){
|
||||
searchParams.fromUser='';
|
||||
searchParams.realname='';
|
||||
searchParams.rangeDateKey='';
|
||||
searchParams.rangeDate=[];
|
||||
for (let a of dateTags) {
|
||||
a.active = false;
|
||||
}
|
||||
loadData();
|
||||
}
|
||||
|
||||
const [registerDetail, { openModal: openDetailModal }] = useModal();
|
||||
function showDetailModal(record){
|
||||
openDetailModal(true, {record: unref(record), isUpdate: true})
|
||||
}
|
||||
return {
|
||||
conditionStr,
|
||||
regModal,
|
||||
getSelectedUser,
|
||||
openSelectPerson,
|
||||
clearSearchParamsUser,
|
||||
clearAll,
|
||||
|
||||
registerModal,
|
||||
activeKey,
|
||||
handleChangePanel,
|
||||
handleChangeTab,
|
||||
|
||||
showSearch,
|
||||
searchParams,
|
||||
handleChangeSearchPerson,
|
||||
dateTags,
|
||||
handleClickDateTag,
|
||||
showRangeDate,
|
||||
searchRangeDate,
|
||||
handleChangeSearchDate,
|
||||
closeModal,
|
||||
hrefThenClose,
|
||||
|
||||
allMessageRef,
|
||||
starMessageRef,
|
||||
registerDetail,
|
||||
showDetailModal
|
||||
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@keyframes move22{
|
||||
0%{ transform:translateY(0px); }
|
||||
100%{transform:translateY(600px);}
|
||||
}
|
||||
|
||||
|
||||
.sys-msg-modal {
|
||||
.ant-modal-header {
|
||||
padding-bottom: 0;
|
||||
padding-top: 6px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.ant-modal-body {
|
||||
height: 550px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ant-modal {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: calc(100% - 600px);
|
||||
/* animation-name: move22;
|
||||
animation-duration:0.8s;
|
||||
animation-timing-function:ease;*/
|
||||
}
|
||||
}
|
||||
.sys-msg-modal-title {
|
||||
.title {
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
}
|
||||
.icon-right {
|
||||
display: inline-block;
|
||||
width: 220px;
|
||||
vertical-align: top;
|
||||
|
||||
.icons {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
> span.anticon {
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
> span.filtera{
|
||||
//background-color: #d3eafd;
|
||||
background-color: #eff1f2;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
height: 27px;
|
||||
padding-top: 7px;
|
||||
margin-top: 3px;
|
||||
line-height: 25px;
|
||||
//color: #2196f3;
|
||||
display: flex;
|
||||
|
||||
>span.anticon{
|
||||
height: auto;
|
||||
line-height: 9px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
.ant-tabs-nav-wrap {
|
||||
display: inline-block;
|
||||
width: calc(100% - 340px);
|
||||
.ant-tabs-tab {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
font-size: 14px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ant-tabs-tab {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
font-size: 14px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ant-tabs-tab+.ant-tabs-tab {
|
||||
margin: 0 0 0 32px;
|
||||
}
|
||||
.ant-tabs-ink-bar {
|
||||
background: @primary-color;
|
||||
}
|
||||
}
|
||||
.ant-tabs-nav-scroll {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.sys-message-card {
|
||||
}
|
||||
|
||||
.search-label {
|
||||
font-weight: 500;
|
||||
font-size: 14px !important;
|
||||
color: #757575 !important;
|
||||
display: inline-block;
|
||||
margin-right: 15px !important;
|
||||
}
|
||||
.search-date {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
margin-top: 15px;
|
||||
.date-label {
|
||||
margin-top: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 14px !important;
|
||||
color: #757575 !important;
|
||||
margin-right: 15px !important;
|
||||
}
|
||||
|
||||
.date-tags {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
-ms-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
-ms-flex: 1;
|
||||
.tags-container {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex-wrap: wrap;
|
||||
.tag {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 17px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
background-color: #d3eafd !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-user{
|
||||
background: #f5f5f5;
|
||||
padding: 2px 14px;
|
||||
border-radius: 30px;
|
||||
.clear-user-icon{
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,239 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { getDictItemsByCode } from '/@/utils/dict/index';
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
import { useTabs } from '/@/hooks/web/useTabs';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import {useMessage} from "/@/hooks/web/useMessage";
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
const queryMessageList = (params) => {
|
||||
const url = '/sys/annountCement/vue3List';
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取消息列表数据
|
||||
*/
|
||||
export function useSysMessage() {
|
||||
const { createMessage } = useMessage();
|
||||
const rangeDateArray = getDictItemsByCode('rangeDate');
|
||||
console.log('+++++++++++++++++++++');
|
||||
console.log('rangeDateArray', rangeDateArray);
|
||||
console.log('+++++++++++++++++++++');
|
||||
|
||||
const messageList = ref<any[]>([]);
|
||||
const pageNo = ref(1)
|
||||
let pageSize = 10;
|
||||
|
||||
const searchParams = reactive({
|
||||
fromUser: '',
|
||||
rangeDateKey: '',
|
||||
rangeDate: [],
|
||||
starFlag: ''
|
||||
});
|
||||
|
||||
|
||||
function getQueryParams() {
|
||||
let { fromUser, rangeDateKey, rangeDate, starFlag } = searchParams;
|
||||
let params = {
|
||||
fromUser,
|
||||
starFlag,
|
||||
rangeDateKey,
|
||||
beginDate: '',
|
||||
endDate: '',
|
||||
pageNo: pageNo.value,
|
||||
pageSize
|
||||
};
|
||||
if (rangeDateKey == 'zdy') {
|
||||
params.beginDate = rangeDate[0]+' 00:00:00';
|
||||
params.endDate = rangeDate[1]+' 23:59:59';
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
// 数据是否加载完了
|
||||
const loadEndStatus = ref(false);
|
||||
|
||||
//请求数据
|
||||
async function loadData() {
|
||||
if(loadEndStatus.value === true){
|
||||
return;
|
||||
}
|
||||
let params = getQueryParams();
|
||||
const data = await queryMessageList(params);
|
||||
console.log('获取结果', data);
|
||||
if(!data || data.length<=0){
|
||||
loadEndStatus.value = true;
|
||||
return;
|
||||
}
|
||||
if(data.length<pageSize){
|
||||
loadEndStatus.value = true;
|
||||
}
|
||||
pageNo.value = pageNo.value+1
|
||||
let temp:any[] = messageList.value;
|
||||
temp.push(...data);
|
||||
messageList.value = temp;
|
||||
}
|
||||
|
||||
//重置
|
||||
function reset(){
|
||||
messageList.value = []
|
||||
pageNo.value = 1;
|
||||
loadEndStatus.value = false;
|
||||
}
|
||||
|
||||
//标星
|
||||
async function updateStarMessage(item){
|
||||
const url = '/sys/sysAnnouncementSend/edit';
|
||||
let starFlag = '1';
|
||||
if(item.starFlag==starFlag){
|
||||
starFlag = '0'
|
||||
}
|
||||
const params = {
|
||||
starFlag,
|
||||
id: item.sendId
|
||||
}
|
||||
//update-begin-author:taoyan date:2023-3-6 for: QQYUN-4491【应用】一些小问题 4、标星不需要提示吧
|
||||
const data:any = await defHttp.put({url, params}, {isTransformResponse: false});
|
||||
if(data.success === true){
|
||||
}else{
|
||||
createMessage.warning(data.message)
|
||||
}
|
||||
//update-end-author:taoyan date:2023-3-6 for: QQYUN-4491【应用】一些小问题 4、标星不需要提示吧
|
||||
}
|
||||
|
||||
|
||||
const loadingMoreStatus = ref(false);
|
||||
async function onLoadMore() {
|
||||
loadingMoreStatus.value = true;
|
||||
await loadData();
|
||||
loadingMoreStatus.value = false;
|
||||
}
|
||||
|
||||
function noRead(item) {
|
||||
if (item.readFlag === '1') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 消息类型
|
||||
function getMsgCategory(item) {
|
||||
if(item.busType=='email'){
|
||||
return '邮件提醒:';
|
||||
} else if(item.busType=='bpm'){
|
||||
return '流程催办:';
|
||||
} else if(item.busType=='bpm_cc'){
|
||||
return '流程抄送:';
|
||||
}else if(item.busType=='bpm_task'){
|
||||
return '流程任务:';
|
||||
} else if (item.msgCategory == '2') {
|
||||
return '系统消息:';
|
||||
} else if (item.msgCategory == '1') {
|
||||
return '通知公告:';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
return {
|
||||
messageList,
|
||||
reset,
|
||||
loadData,
|
||||
loadEndStatus,
|
||||
searchParams,
|
||||
updateStarMessage,
|
||||
|
||||
onLoadMore,
|
||||
noRead,
|
||||
getMsgCategory,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于消息跳转
|
||||
*/
|
||||
export function useMessageHref(emit, props){
|
||||
const messageHrefArray: any[] = getDictItemsByCode('messageHref');
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const rt = useRoute();
|
||||
const { close: closeTab, closeSameRoute } = useTabs();
|
||||
// const defaultPath = '/monitor/mynews';
|
||||
//const bpmPath = '/task/handle/'
|
||||
|
||||
async function goPage(record, openModalFun?){
|
||||
if(!record.busType || record.busType == 'msg_node'){
|
||||
if(!openModalFun){
|
||||
// 从首页的消息通知跳转
|
||||
await goPageFromOuter(record);
|
||||
}else{
|
||||
// 从消息页面列表点击详情查看 直接打开modal
|
||||
openModalFun()
|
||||
}
|
||||
}else{
|
||||
await goPageWithBusType(record)
|
||||
}
|
||||
/* busId: "1562035005173587970"
|
||||
busType: "email"
|
||||
openPage: "modules/eoa/email/modals/EoaEmailInForm"
|
||||
openType: "component"*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据busType不同跳转不同页面
|
||||
* @param record
|
||||
*/
|
||||
async function goPageWithBusType(record){
|
||||
const { busType, busId, msgAbstract } = record;
|
||||
let temp = messageHrefArray.filter(item=>item.value === busType);
|
||||
if(!temp || temp.length==0){
|
||||
console.error('当前业务类型不识别', busType);
|
||||
return;
|
||||
}
|
||||
let path = temp[0].text;
|
||||
path = path.replace('{DETAIL_ID}', busId)
|
||||
//固定参数 detailId 用于查询表单数据
|
||||
let query:any = {
|
||||
detailId: busId
|
||||
};
|
||||
// 额外参数处理
|
||||
if(msgAbstract){
|
||||
try {
|
||||
let json = JSON.parse(msgAbstract);
|
||||
Object.keys(json).map(k=>{
|
||||
query[k] = json[k]
|
||||
});
|
||||
}catch (e) {
|
||||
console.error('msgAbstract参数不是JSON格式', msgAbstract)
|
||||
}
|
||||
}
|
||||
// 跳转路由
|
||||
appStore.setMessageHrefParams(query);
|
||||
if(rt.path.indexOf(path)>=0){
|
||||
await closeTab();
|
||||
await router.replace({ path: path, query:{ time: new Date().getTime() } });
|
||||
}else{
|
||||
closeSameRoute(path)
|
||||
await router.push({ path: path });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从首页的消息通知跳转消息列表打开modal
|
||||
* @param record
|
||||
*/
|
||||
async function goPageFromOuter(record){
|
||||
//没有定义业务类型 直接跳转我的消息页面
|
||||
emit('detail', record)
|
||||
}
|
||||
|
||||
return {
|
||||
goPage
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<BasicDrawer @register="registerModal" title="详情" :width="600" v-bind="$attrs" @ok="closeDrawer">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { formSchemas } from './manage.data';
|
||||
|
||||
// 声明 emits
|
||||
const emit = defineEmits(['register']);
|
||||
// 注册 form
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// 注册 modal
|
||||
const [registerModal, { closeDrawer }] = useDrawerInner(async (data) => {
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...data.record });
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,5 @@
|
||||
//noinspection LessUnresolvedVariable
|
||||
@prefix-cls: ~'@{namespace}-message-manage';
|
||||
|
||||
.@{prefix-cls} {
|
||||
}
|
||||
129
jeecgboot-vue3/src/views/system/message/manage/index.vue
Normal file
129
jeecgboot-vue3/src/views/system/message/manage/index.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-dropdown v-if="showBatchBtn">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onDeleteBatch">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作</span>
|
||||
<Icon icon="mdi:chevron-down"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<ManageDrawer @register="registerDrawer" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="message-manage">
|
||||
import { unref, computed } from 'vue';
|
||||
import { ActionItem, BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import ManageDrawer from './ManageDrawer.vue';
|
||||
import { Api, list, deleteBatch } from './manage.api';
|
||||
import { columns, searchFormSchema } from './manage.data';
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext } = useListPage({
|
||||
designScope: 'message-manage',
|
||||
tableProps: {
|
||||
title: '消息中心模板列表数据',
|
||||
api: list,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
url: Api.exportXls,
|
||||
name: '消息中心模板列表',
|
||||
},
|
||||
importConfig: {
|
||||
url: Api.importXls,
|
||||
success: () => reload(),
|
||||
},
|
||||
});
|
||||
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload, setLoading }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
const showBatchBtn = computed(() => selectedRowKeys.value.length > 0);
|
||||
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
|
||||
function onDetail(record) {
|
||||
openDrawer(true, { record: record });
|
||||
}
|
||||
|
||||
function onDelete(record) {
|
||||
if (record) {
|
||||
doDeleteDepart([record.id], false);
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteBatch() {
|
||||
try {
|
||||
await doDeleteDepart(selectedRowKeys);
|
||||
selectedRowKeys.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ids 批量删除
|
||||
* @param idListRef array
|
||||
* @param confirm 是否显示确认提示框
|
||||
*/
|
||||
async function doDeleteDepart(idListRef, confirm = true) {
|
||||
const idList = unref(idListRef);
|
||||
if (idList.length > 0) {
|
||||
try {
|
||||
setLoading(true);
|
||||
await deleteBatch({ ids: idList.join(',') }, confirm);
|
||||
await reload();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [{ label: '详情', onClick: onDetail.bind(null, record) }];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认要删除吗?',
|
||||
confirm: onDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import 'index';
|
||||
</style>
|
||||
52
jeecgboot-vue3/src/views/system/message/manage/manage.api.ts
Normal file
52
jeecgboot-vue3/src/views/system/message/manage/manage.api.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { unref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
export enum Api {
|
||||
list = '/sys/message/sysMessage/list',
|
||||
delete = '/sys/message/sysMessage/delete',
|
||||
deleteBatch = '/sys/message/sysMessage/deleteBatch',
|
||||
exportXls = 'sys/message/sysMessage/exportXls',
|
||||
importXls = 'sys/message/sysMessage/importExcel',
|
||||
save = '/sys/message/sysMessage/add',
|
||||
edit = '/sys/message/sysMessage/edit',
|
||||
}
|
||||
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
* @param confirm
|
||||
*/
|
||||
export const deleteBatch = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要删除吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或者更改消息模板
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
if (unref(isUpdate)) {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
}
|
||||
};
|
||||
134
jeecgboot-vue3/src/views/system/message/manage/manage.data.ts
Normal file
134
jeecgboot-vue3/src/views/system/message/manage/manage.data.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '消息标题',
|
||||
dataIndex: 'esTitle',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: '发送内容',
|
||||
dataIndex: 'esContent',
|
||||
width: 200,
|
||||
// slots: { customRender: 'esContent' },
|
||||
},
|
||||
{
|
||||
title: '接收人',
|
||||
dataIndex: 'esReceiver',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: '发送次数',
|
||||
dataIndex: 'esSendNum',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '发送状态',
|
||||
dataIndex: 'esSendStatus_dictText',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '发送时间',
|
||||
dataIndex: 'esSendTime',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: '发送方式',
|
||||
dataIndex: 'esType_dictText',
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '消息标题',
|
||||
field: 'esTitle',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '发送状态',
|
||||
field: 'esSendStatus',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'msgSendStatus',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '发送方式',
|
||||
field: 'esType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'messageType',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchemas: FormSchema[] = [
|
||||
{
|
||||
label: 'ID',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '消息标题',
|
||||
field: 'esTitle',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送内容',
|
||||
field: 'esContent',
|
||||
component: 'InputTextArea',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送参数',
|
||||
field: 'esParam',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
|
||||
{
|
||||
label: '接收人',
|
||||
field: 'esReceiver',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送方式',
|
||||
field: 'esType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { disabled: true, dictCode: 'messageType' },
|
||||
},
|
||||
{
|
||||
label: '发送时间',
|
||||
field: 'esSendTime',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送状态',
|
||||
field: 'esSendStatus',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { disabled: true, dictCode: 'msgSendStatus' },
|
||||
},
|
||||
{
|
||||
label: '发送次数',
|
||||
field: 'esSendNum',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送失败原因',
|
||||
field: 'esResult',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'remark',
|
||||
component: 'InputTextArea',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" :title="title" :width="800" v-bind="$attrs" @ok="onSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { formSchemas } from './template.data';
|
||||
import { saveOrUpdate } from './template.api';
|
||||
|
||||
// 声明 emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const title = ref<string>('');
|
||||
const isUpdate = ref<boolean>(false);
|
||||
// 注册 form
|
||||
//update-begin---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能------------
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema, setProps }] = useForm({
|
||||
//update-end---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能--------------z
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// 注册 modal
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
|
||||
isUpdate.value = unref(data.isUpdate);
|
||||
title.value = unref(data.title);
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...data.record });
|
||||
// 隐藏底部时禁用整个表单
|
||||
setProps({ disabled: !data?.showFooter })
|
||||
});
|
||||
|
||||
//表单提交事件
|
||||
async function onSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
// 提交表单
|
||||
await saveOrUpdate(values, isUpdate);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" title="发送测试" :width="800" v-bind="$attrs" @ok="onSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { sendTestFormSchemas } from './template.data';
|
||||
import { sendMessageTest } from './template.api';
|
||||
|
||||
// 声明 emits
|
||||
const emit = defineEmits(['register']);
|
||||
// 注册 form
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: sendTestFormSchemas,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// 注册 modal
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...unref(data.record) });
|
||||
});
|
||||
|
||||
//表单提交事件
|
||||
async function onSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
// 提交表单
|
||||
await sendMessageTest(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,5 @@
|
||||
//noinspection LessUnresolvedVariable
|
||||
@prefix-cls: ~'@{namespace}-message-template';
|
||||
|
||||
.@{prefix-cls} {
|
||||
}
|
||||
209
jeecgboot-vue3/src/views/system/message/template/index.vue
Normal file
209
jeecgboot-vue3/src/views/system/message/template/index.vue
Normal file
@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="onAdd">新增</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||
<a-dropdown v-if="showBatchBtn">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onDeleteBatch">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作</span>
|
||||
<Icon icon="mdi:chevron-down"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<TemplateModal @register="registerModal" @success="reload" />
|
||||
<TemplateTestModal @register="registerTestModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="message-template">
|
||||
import { unref, computed, toRaw } from 'vue';
|
||||
import { ActionItem, BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import TemplateModal from './TemplateModal.vue';
|
||||
import TemplateTestModal from './TemplateTestModal.vue';
|
||||
import { Api, saveOrUpdate, list, deleteBatch } from './template.api';
|
||||
import { columns, searchFormSchema } from './template.data';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, onExportXls, onImportXls, tableContext } = useListPage({
|
||||
designScope: 'message-template',
|
||||
tableProps: {
|
||||
title: '消息中心模板列表数据',
|
||||
api: list,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
url: Api.exportXls,
|
||||
name: '消息中心模板列表',
|
||||
},
|
||||
importConfig: {
|
||||
url: Api.importXls,
|
||||
success: () => reload(),
|
||||
},
|
||||
});
|
||||
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload, setLoading }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerTestModal, testModal] = useModal();
|
||||
const showBatchBtn = computed(() => selectedRowKeys.value.length > 0);
|
||||
|
||||
function onAdd() {
|
||||
openModal(true, {
|
||||
title: '新增消息模板',
|
||||
isUpdate: false,
|
||||
showFooter: true,
|
||||
record: {},
|
||||
});
|
||||
}
|
||||
|
||||
function onEdit(record) {
|
||||
if (record.useStatus === '1') {
|
||||
createMessage.warning('此模板已被应用,禁止编辑!');
|
||||
return;
|
||||
}
|
||||
openModal(true, {
|
||||
title: '修改消息模板',
|
||||
isUpdate: true,
|
||||
record: record,
|
||||
showFooter: true,
|
||||
});
|
||||
}
|
||||
|
||||
function onDelete(record) {
|
||||
if (record) {
|
||||
//update-begin-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
if(record.useStatus == '1'){
|
||||
createMessage.warning('该模板已被应用禁止删除!');
|
||||
return;
|
||||
}
|
||||
//update-end-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
doDeleteDepart([record.id], false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ids 批量删除
|
||||
* @param idListRef array
|
||||
* @param confirm 是否显示确认提示框
|
||||
*/
|
||||
async function doDeleteDepart(idListRef, confirm = true) {
|
||||
const idList = unref(idListRef);
|
||||
if (idList.length > 0) {
|
||||
try {
|
||||
setLoading(true);
|
||||
await deleteBatch({ ids: idList.join(',') }, confirm);
|
||||
await reload();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteBatch() {
|
||||
try {
|
||||
//update-begin-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
let arr = toRaw(selectedRows.value);
|
||||
let temp = arr.filter(item=>item.useStatus=='1')
|
||||
if(temp.length>0){
|
||||
createMessage.warning('选中的模板已被应用禁止删除!');
|
||||
return;
|
||||
}
|
||||
//update-end-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
await doDeleteDepart(selectedRowKeys);
|
||||
selectedRowKeys.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息测试
|
||||
function onSendTest(record) {
|
||||
testModal.openModal(true, { record });
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
//update-begin---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能------------
|
||||
return [{ label: '查看', onClick: handleDetail.bind(null, record)}, { label: '编辑', onClick: onEdit.bind(null, record) }];
|
||||
//update-end---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能------------
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{ label: '应用', onClick: handleUse.bind(null, record) },
|
||||
{ label: '停用', onClick: handleNotUse.bind(null, record) },
|
||||
{ label: '发送测试', onClick: onSendTest.bind(null, record) },
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认要删除吗?',
|
||||
confirm: onDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用
|
||||
*/
|
||||
async function handleUse(record) {
|
||||
let param = { id: record.id, useStatus: '1' };
|
||||
await saveOrUpdate(param, true);
|
||||
await reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用
|
||||
*/
|
||||
async function handleNotUse(record) {
|
||||
let param = { id: record.id, useStatus: '0' };
|
||||
await saveOrUpdate(param, true);
|
||||
await reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
* @param record
|
||||
*/
|
||||
function handleDetail(record) {
|
||||
openModal(true,{
|
||||
title: "消息模板详情",
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
record:record
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import 'index';
|
||||
</style>
|
||||
@ -0,0 +1,60 @@
|
||||
import { unref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
export enum Api {
|
||||
list = '/sys/message/sysMessageTemplate/list',
|
||||
delete = '/sys/message/sysMessageTemplate/delete',
|
||||
deleteBatch = '/sys/message/sysMessageTemplate/deleteBatch',
|
||||
exportXls = 'sys/message/sysMessageTemplate/exportXls',
|
||||
importXls = 'sys/message/sysMessageTemplate/importExcel',
|
||||
save = '/sys/message/sysMessageTemplate/add',
|
||||
edit = '/sys/message/sysMessageTemplate/edit',
|
||||
// 发送测试
|
||||
send = '/sys/message/sysMessageTemplate/sendMsg',
|
||||
}
|
||||
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
* @param confirm
|
||||
*/
|
||||
export const deleteBatch = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要删除吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或者更改消息模板
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
if (unref(isUpdate)) {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送消息测试
|
||||
* @param params
|
||||
*/
|
||||
export const sendMessageTest = (params) => defHttp.post({ url: Api.send, params });
|
||||
@ -0,0 +1,185 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { rules } from '/@/utils/helper/validator';
|
||||
import { filterDictTextByCache } from '/@/utils/dict/JDictSelectUtil';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '模板标题',
|
||||
dataIndex: 'templateName',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '模板编码',
|
||||
dataIndex: 'templateCode',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '通知模板',
|
||||
dataIndex: 'templateContent',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '模板类型',
|
||||
dataIndex: 'templateType',
|
||||
width: 100,
|
||||
customRender: ({ text }) => filterDictTextByCache('msgType', text),
|
||||
},
|
||||
{
|
||||
title: '是否应用',
|
||||
dataIndex: 'useStatus',
|
||||
width: 90,
|
||||
customRender: function ({ text }) {
|
||||
if (text == '1') {
|
||||
return '是';
|
||||
} else {
|
||||
return '否';
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '模板标题',
|
||||
field: 'templateName',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '模板编码',
|
||||
field: 'templateCode',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '模板类型',
|
||||
field: 'templateType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'msgType',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchemas: FormSchema[] = [
|
||||
{
|
||||
label: 'ID',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '模板标题',
|
||||
field: 'templateName',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '模板编码',
|
||||
field: 'templateCode',
|
||||
component: 'Input',
|
||||
dynamicRules: ({ model, schema }) => {
|
||||
return [ ...rules.duplicateCheckRule('sys_sms_template', 'template_code', model, schema, true)];
|
||||
},
|
||||
// 编辑模式下不可修改编码
|
||||
dynamicDisabled: (params) => !!params.values.id,
|
||||
},
|
||||
{
|
||||
label: '模板类型',
|
||||
field: 'templateType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'msgType',
|
||||
placeholder: '请选择模板类型',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '是否应用',
|
||||
field: 'useStatus',
|
||||
component: 'JSwitch',
|
||||
componentProps: {
|
||||
options: ['1', '0'],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'InputTextArea',
|
||||
componentProps: {
|
||||
autoSize: {
|
||||
minRows: 8,
|
||||
maxRows: 8,
|
||||
},
|
||||
},
|
||||
ifShow: ({ values }) => {
|
||||
return !['2', '4', '5'].includes(values.templateType);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'JEditor',
|
||||
ifShow: ({ values }) => {
|
||||
return ['2', '4'].includes(values.templateType);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'JMarkdownEditor',
|
||||
ifShow: ({ values }) => {
|
||||
return ['5'].includes(values.templateType);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const sendTestFormSchemas: FormSchema[] = [
|
||||
{
|
||||
label: '模板编码',
|
||||
field: 'templateCode',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '模板标题',
|
||||
field: 'templateName',
|
||||
component: 'Input',
|
||||
componentProps: { disabled: true },
|
||||
},
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'InputTextArea',
|
||||
componentProps: { disabled: true, rows: 5 },
|
||||
},
|
||||
{
|
||||
label: '测试数据',
|
||||
field: 'testData',
|
||||
component: 'InputTextArea',
|
||||
required: true,
|
||||
helpMessage: 'JSON数据',
|
||||
defaultValue: '{}',
|
||||
componentProps: {
|
||||
placeholder: '请输入JSON格式测试数据',
|
||||
rows: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '消息类型',
|
||||
field: 'msgType',
|
||||
component: 'JDictSelectTag',
|
||||
required: true,
|
||||
defaultValue:'system',
|
||||
componentProps: { dictCode: 'messageType',type:'radio' },
|
||||
},
|
||||
{
|
||||
label: '消息接收方',
|
||||
field: 'receiver',
|
||||
required: true,
|
||||
component: 'JSelectUser',
|
||||
componentProps: {
|
||||
labelKey: 'username',
|
||||
rowKey: 'username',
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user