page: 📄 消息详情和部分缺陷修复
This commit is contained in:
parent
a9e06b151a
commit
a6eaa41226
|
@ -6,6 +6,21 @@ export const fetchGetMessageList = (data: any) => {
|
|||
return http.request<BaseResult<ResultTable>>('get', `message/getMessageList/${data.currentPage}/${data.pageSize}`, { params: data });
|
||||
};
|
||||
|
||||
/** 系统消息---获取系统消息列表 */
|
||||
export const fetchGetUserMessageList = (data: any) => {
|
||||
return http.request<BaseResult<ResultTable>>('get', `message/noManage/getUserMessageList/${data.currentPage}/${data.pageSize}`, { params: data });
|
||||
};
|
||||
|
||||
/** 系统消息---根据消息id获取接收人信息 */
|
||||
export const fetchGetReceivedUserinfoByMessageId = (data: any) => {
|
||||
return http.request<BaseResult<any>>('get', `messageReceived/noManage/getReceivedUserinfoByMessageId`, { params: data });
|
||||
};
|
||||
|
||||
/** 系统消息---根据消息id查询消息详情 */
|
||||
export const fetchGetMessageDetailById = (data: any) => {
|
||||
return http.request<BaseResult<any>>('get', `message/noManage/getMessageDetailById`, { params: data });
|
||||
};
|
||||
|
||||
/** 系统消息---添加系统消息 */
|
||||
export const fetchAddMessage = (data: any) => {
|
||||
return http.request<BaseResult<object>>('post', 'message/addMessage', { data });
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { computed } from 'vue';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
/**
|
||||
* * 是否默认
|
||||
|
@ -32,6 +33,12 @@ export const enabledOrNotStatus = [
|
|||
{ value: false, label: '禁用' },
|
||||
];
|
||||
|
||||
/** 是否已读 */
|
||||
export const isReadStatus = [
|
||||
{ value: true, label: $t('readAlready') },
|
||||
{ value: false, label: $t('unread') },
|
||||
];
|
||||
|
||||
/**
|
||||
* * 分页默认数组个数
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,7 @@ import { ListItem } from '../data';
|
|||
import { nextTick, PropType, ref } from 'vue';
|
||||
import { useNav } from '@/layout/hooks/useNav';
|
||||
import { deviceDetection } from '@pureadmin/utils';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
defineProps({
|
||||
noticeItem: {
|
||||
|
@ -17,6 +18,7 @@ const descriptionRef = ref(null);
|
|||
const descriptionTooltip = ref(false);
|
||||
const { tooltipEffect } = useNav();
|
||||
const isMobile = deviceDetection();
|
||||
const router = useRouter();
|
||||
|
||||
function hoverTitle() {
|
||||
nextTick(() => {
|
||||
|
@ -39,10 +41,14 @@ function hoverDescription(event, description) {
|
|||
// 当文本宽度大于容器宽度两倍时,代表文本显示超过两行
|
||||
currentWidth > 2 * cellWidth ? (descriptionTooltip.value = true) : (descriptionTooltip.value = false);
|
||||
}
|
||||
|
||||
function goMessageDetail(message: ListItem) {
|
||||
router.push({ path: `/message-detail/${message.messageType}/${message.messageId}` });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]">
|
||||
<div class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]" @click="goMessageDetail(noticeItem)">
|
||||
<el-avatar v-if="noticeItem.cover" :size="30" :src="noticeItem.cover" class="notice-container-avatar" />
|
||||
<div class="notice-container-text">
|
||||
<div class="notice-text-title text-[#000000d9] dark:text-white">
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import { $t } from '@/plugins/i18n';
|
||||
import { ref } from 'vue';
|
||||
import { fetchGetMessageList } from '@/api/v1/message';
|
||||
import { computed, ref } from 'vue';
|
||||
import { fetchGetUserMessageList } from '@/api/v1/message';
|
||||
import { throttle } from '@pureadmin/utils';
|
||||
|
||||
export interface ListItem {
|
||||
messageId: string;
|
||||
cover: string;
|
||||
title: string;
|
||||
datetime: string;
|
||||
type: string;
|
||||
description: string;
|
||||
messageType: string;
|
||||
status?: 'primary' | 'success' | 'warning' | 'info' | 'danger';
|
||||
extra?: string;
|
||||
}
|
||||
|
@ -26,19 +29,21 @@ export const noticesData = ref<TabItem[]>([]);
|
|||
|
||||
/** 获取所有消息 */
|
||||
export const getAllMessageList = async () => {
|
||||
const baseResult = await fetchGetMessageList(form);
|
||||
const datalist = baseResult.data.list;
|
||||
const baseResult = await fetchGetUserMessageList(form);
|
||||
const datalist = baseResult?.data?.list;
|
||||
|
||||
// 通知消息
|
||||
const notifications = datalist
|
||||
.filter(message => message.messageType === 'notifications')
|
||||
.map(message => ({
|
||||
messageId: message.id,
|
||||
cover: message.cover,
|
||||
title: message.title,
|
||||
datetime: message.createTime,
|
||||
description: message.summary,
|
||||
messageType: message.messageType,
|
||||
type: '1',
|
||||
status: message.statusType,
|
||||
status: message.level,
|
||||
extra: message.extra,
|
||||
})) as ListItem[];
|
||||
|
||||
|
@ -46,12 +51,14 @@ export const getAllMessageList = async () => {
|
|||
const notify = datalist
|
||||
.filter(message => message.messageType !== 'notifications' && message.messageType !== 'sys')
|
||||
.map(message => ({
|
||||
messageId: message.id,
|
||||
cover: message.cover,
|
||||
description: message.summary,
|
||||
messageType: message.messageType,
|
||||
title: message.title,
|
||||
datetime: message.createTime,
|
||||
type: '2',
|
||||
status: message.statusType,
|
||||
status: message.level,
|
||||
extra: message.extra,
|
||||
})) as ListItem[];
|
||||
|
||||
|
@ -59,12 +66,14 @@ export const getAllMessageList = async () => {
|
|||
const system = datalist
|
||||
.filter(message => message.messageType === 'sys')
|
||||
.map(message => ({
|
||||
messageId: message.id,
|
||||
cover: message.cover,
|
||||
description: message.summary,
|
||||
messageType: message.messageType,
|
||||
title: message.title,
|
||||
datetime: message.createTime,
|
||||
type: '3',
|
||||
status: message.statusType,
|
||||
status: message.level,
|
||||
extra: message.extra,
|
||||
})) as ListItem[];
|
||||
|
||||
|
@ -74,3 +83,23 @@ export const getAllMessageList = async () => {
|
|||
{ key: '3', name: $t('status.systemMessage'), list: system, emptyText: $t('status.systemMessage') },
|
||||
];
|
||||
};
|
||||
|
||||
// 通知消息数据
|
||||
export const noticesNum = ref(0);
|
||||
export const notices = ref(noticesData);
|
||||
// 选择的消息栏目
|
||||
export const activeKey = ref(noticesData.value[0]?.key);
|
||||
export const getLabel = computed(() => item => item.name + (item.list.length > 0 ? `(${item.list.length})` : ''));
|
||||
|
||||
/** 计算消息数量 */
|
||||
export const computedNoticesNum = throttle(async () => {
|
||||
// 获取所有的消息
|
||||
await getAllMessageList();
|
||||
// 请求成功后将原本条数置为0
|
||||
noticesNum.value = 0;
|
||||
// 整合消息一共多少条
|
||||
notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
// 默认选中的消息类别
|
||||
activeKey.value = noticesData.value[0]?.key;
|
||||
// 定时刷新
|
||||
}, 666);
|
||||
|
|
|
@ -1,42 +1,20 @@
|
|||
<script lang="ts" setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { getAllMessageList, noticesData } from './data';
|
||||
import { onMounted } from 'vue';
|
||||
import { activeKey, computedNoticesNum, getLabel, notices, noticesNum } from './data';
|
||||
import NoticeList from './components/NoticeList.vue';
|
||||
import BellIcon from '@iconify-icons/ep/bell';
|
||||
import { useIntervalFn } from '@vueuse/core';
|
||||
|
||||
const { t } = useI18n();
|
||||
// 通知消息数据
|
||||
const noticesNum = ref(0);
|
||||
const notices = ref(noticesData);
|
||||
// 选择的消息栏目
|
||||
const activeKey = ref(noticesData.value[0]?.key);
|
||||
const getLabel = computed(() => item => item.name + (item.list.length > 0 ? `(${item.list.length})` : ''));
|
||||
|
||||
/** 计算消息数量 */
|
||||
const computedNoticesNum = async () => {
|
||||
// 获取所有的消息
|
||||
await getAllMessageList();
|
||||
// 请求成功后将原本条数置为0
|
||||
noticesNum.value = 0;
|
||||
// 整合消息一共多少条
|
||||
notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
// 默认选中的消息类别
|
||||
activeKey.value = noticesData.value[0]?.key;
|
||||
// 定时刷新
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
computedNoticesNum();
|
||||
// 定时刷新消息内容
|
||||
useIntervalFn(() => computedNoticesNum(), 1000 * 30);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dropdown placement="bottom-end" trigger="click">
|
||||
<span :class="['dropdown-badge', 'navbar-bg-hover', 'select-none', Number(noticesNum) !== 0 && 'mr-[10px]']">
|
||||
<span :class="['dropdown-badge', 'navbar-bg-hover', 'select-none', Number(noticesNum) !== 0 && 'mr-[10px]']" @click="computedNoticesNum">
|
||||
<el-badge :max="99" :value="Number(noticesNum) === 0 ? '' : noticesNum">
|
||||
<span class="header-notice-icon">
|
||||
<IconifyIconOffline :icon="BellIcon" />
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
const { VITE_HIDE_HOME } = import.meta.env;
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
const Layout = () => import('@/layout/index.vue');
|
||||
|
||||
export default {
|
||||
path: "/",
|
||||
name: "Home",
|
||||
component: Layout,
|
||||
redirect: "/welcome",
|
||||
meta: {
|
||||
icon: "ep:home-filled",
|
||||
title: "menus.home",
|
||||
rank: 0
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/welcome",
|
||||
name: "Welcome",
|
||||
component: () => import("@/views/welcome/index.vue"),
|
||||
meta: {
|
||||
title: "menus.home",
|
||||
showLink: VITE_HIDE_HOME !== "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Layout,
|
||||
redirect: '/welcome',
|
||||
meta: {
|
||||
icon: 'ep:home-filled',
|
||||
title: 'menus.home',
|
||||
rank: 0,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/welcome',
|
||||
name: 'Welcome',
|
||||
component: () => import('@/views/welcome/index.vue'),
|
||||
meta: {
|
||||
title: 'menus.home',
|
||||
showLink: VITE_HIDE_HOME !== 'true',
|
||||
},
|
||||
},
|
||||
],
|
||||
} satisfies RouteConfigsTable;
|
||||
|
|
|
@ -34,7 +34,27 @@ export default [
|
|||
meta: {
|
||||
title: 'buttons.accountSettings',
|
||||
showLink: false,
|
||||
rank: 104,
|
||||
rank: 103,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/message-detail',
|
||||
name: 'MessageDetail',
|
||||
component: () => import('@/views/message-management/message-detail/index.vue'),
|
||||
meta: { title: 'message', showLink: false, rank: 104 },
|
||||
children: [
|
||||
{
|
||||
path: '/message-detail/:messageType',
|
||||
name: 'MessageDetailByMessageType',
|
||||
component: () => import('@/views/message-management/message-detail/detail-type.vue'),
|
||||
meta: { title: 'message', showLink: false },
|
||||
},
|
||||
{
|
||||
path: '/message-detail/:messageType/:messageId',
|
||||
name: 'MessageDetailByMessageId',
|
||||
component: () => import('@/views/message-management/message-detail/detail-message-id.vue'),
|
||||
meta: { title: 'message', showLink: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies Array<RouteConfigsTable>;
|
||||
|
|
|
@ -28,6 +28,10 @@ export const useMessageStore = defineStore('messageStore', {
|
|||
content: undefined,
|
||||
// 编辑器类型
|
||||
editorType: undefined,
|
||||
// 消息等级
|
||||
level: undefined,
|
||||
// 消息等级详情
|
||||
extra: undefined,
|
||||
// 0:未读 1:已读
|
||||
status: undefined,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<script lang="ts" setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import 'plus-pro-components/es/components/check-card-group/style/css';
|
||||
import MarkdownPreview from '@/components/Editor/MarkdownPreview.vue';
|
||||
import { fetchGetMessageDetailById } from '@/api/v1/message';
|
||||
import { decode } from 'js-base64';
|
||||
|
||||
const route = useRoute();
|
||||
const messageDetail = ref<any>({});
|
||||
|
||||
/** 获取消息详情 */
|
||||
const getMessageDetail = async () => {
|
||||
// 从路由中获取地址栏信息
|
||||
const messageId = route.params.messageId;
|
||||
|
||||
// 获取消息详情
|
||||
const result = await fetchGetMessageDetailById({ id: messageId });
|
||||
if (result.code !== 200) return;
|
||||
|
||||
// 将消息详情赋值
|
||||
messageDetail.value = result.data;
|
||||
|
||||
// 解码消息内容
|
||||
messageDetail.value.content = decode(messageDetail.value.content);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getMessageDetail();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<header class="flex items-center h-[80px]">
|
||||
<el-avatar :src="messageDetail.cover" size="large" />
|
||||
<div class="content ms-3">
|
||||
<h3>{{ messageDetail.title }}</h3>
|
||||
|
||||
<p>{{ messageDetail.summary }}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<span>
|
||||
<el-text>{{ messageDetail.updateTime }}</el-text>
|
||||
<el-text type="primary"> By:{{ messageDetail.sendNickname }}</el-text>
|
||||
</span>
|
||||
<markdown-preview v-if="messageDetail.editorType === 'markdown'" id="message-detail-markdown" :text="messageDetail.content" />
|
||||
<div v-else v-html="messageDetail.content" />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,37 @@
|
|||
<script lang="ts" setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { PlusCheckCardGroup } from 'plus-pro-components';
|
||||
import 'plus-pro-components/es/components/check-card-group/style/css';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
/** 获取消息详情 */
|
||||
const getMessageDetail = () => {
|
||||
const messageId = route.params.messageId;
|
||||
};
|
||||
const options = [
|
||||
{
|
||||
title: '标题一',
|
||||
value: '0',
|
||||
description: '坚持梦想,成就不凡的自己',
|
||||
avatar: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
|
||||
},
|
||||
{
|
||||
title: '标题二',
|
||||
value: '1',
|
||||
description: '每一次努力,都是成长的契机',
|
||||
avatar: 'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
|
||||
},
|
||||
];
|
||||
const size = ref('default');
|
||||
const dynamicSize = ref();
|
||||
const list = ref('0');
|
||||
onMounted(() => {
|
||||
getMessageDetail();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PlusCheckCardGroup v-model="list" :disabled="size === 'disabled'" :options="options" :size="dynamicSize" />
|
||||
</template>
|
|
@ -0,0 +1,112 @@
|
|||
<script lang="ts" setup>
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { onBeforeMount, ref } from 'vue';
|
||||
import { deviceDetection, useGlobal } from '@pureadmin/utils';
|
||||
import { useDataThemeChange } from '@/layout/hooks/useDataThemeChange';
|
||||
import leftLine from '@iconify-icons/ri/arrow-left-s-line';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { useMessageTypeStore } from '@/store/message/messageType';
|
||||
|
||||
const router = useRouter();
|
||||
const isOpen = ref(!deviceDetection());
|
||||
const { $storage } = useGlobal<GlobalPropertiesApi>();
|
||||
const witchPane = ref('profile');
|
||||
const messageTypeStore = useMessageTypeStore();
|
||||
const route = useRoute();
|
||||
const routerMessageType = ref('');
|
||||
|
||||
/** 点击菜单时 */
|
||||
const onMenuClick = (item: any) => {
|
||||
const messageType = item.messageType;
|
||||
const messageId = item.messageId;
|
||||
routerMessageType.value = messageType;
|
||||
router.push({ path: `/message-detail/${item.messageType}` });
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
messageTypeStore.getAllMessageTypeList();
|
||||
routerMessageType.value = route.params.messageType as string;
|
||||
useDataThemeChange().dataThemeChange($storage.layout?.overallStyle);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container class="h-full">
|
||||
<el-aside
|
||||
v-if="isOpen"
|
||||
:width="deviceDetection() ? '180px' : '240px'"
|
||||
class="pure-account-settings overflow-hidden px-2 dark:!bg-[var(--el-bg-color)] border-r-[1px] border-[var(--pure-border-color)]"
|
||||
>
|
||||
<el-menu :default-active="witchPane" class="pure-account-settings-menu">
|
||||
<!--<el-menu-item class="hover:!transition-all hover:!duration-200 hover:!text-base !h-[50px]" @click="router.go(-1)">-->
|
||||
<el-menu-item class="hover:!transition-all hover:!duration-200 hover:!text-base !h-[50px]" @click="router.push('/')">
|
||||
<div class="flex items-center">
|
||||
<IconifyIconOffline :icon="leftLine" />
|
||||
<span class="ml-2">{{ $t('back') }}</span>
|
||||
</div>
|
||||
</el-menu-item>
|
||||
<el-menu-item v-for="item in messageTypeStore.allMessageTypeList" :key="item.id" :class="routerMessageType === item.messageType ? 'is-active' : ''" :index="item.id" @click="onMenuClick(item)">
|
||||
<div class="flex items-center z-10">
|
||||
<span>{{ item.messageName }}</span>
|
||||
</div>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<RouterView />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.pure-account-settings {
|
||||
background: $menuBg;
|
||||
}
|
||||
|
||||
.pure-account-settings-menu {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
|
||||
.el-menu-item {
|
||||
height: 48px !important;
|
||||
color: $menuText;
|
||||
background-color: transparent !important;
|
||||
transition: color 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: $menuTitleHover !important;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: #fff !important;
|
||||
|
||||
&:hover {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
inset: 0 8px;
|
||||
margin: 4px 0;
|
||||
clear: both;
|
||||
content: '';
|
||||
background: var(--el-color-primary);
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
body[layout] {
|
||||
.el-menu--vertical .is-active {
|
||||
color: #fff !important;
|
||||
transition: color 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,32 @@
|
|||
<script lang="ts" setup>
|
||||
import SplitPane from '@/components/SplitPane';
|
||||
import MessageEditorFrom from '@/views/message-management/message-editing/message-editor-from.vue';
|
||||
import RichEditor from '@/views/message-management/message-editing/rich-editor.vue';
|
||||
import { formState } from '@/views/message-management/message-editing/utils/hooks';
|
||||
import MarkdownEditor from '@/views/message-management/message-editing/markdown-editor.vue';
|
||||
import { settingLR } from '@/views/message-management/message-editing/utils/columns';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<div class="split-pane">
|
||||
<SplitPane :splitSet="settingLR">
|
||||
<template #paneL>
|
||||
<rich-editor v-if="formState.editorType === 'rich'" />
|
||||
<markdown-editor v-else />
|
||||
</template>
|
||||
|
||||
<template #paneR>
|
||||
<message-editor-from />
|
||||
</template>
|
||||
</SplitPane>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.split-pane {
|
||||
width: 100%;
|
||||
height: calc(100vh - 120px);
|
||||
}
|
||||
</style>
|
|
@ -2,7 +2,7 @@
|
|||
import { MdEditor } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import { fetchUploadFile } from '@/api/v1/system';
|
||||
import { formState } from '@/views/messageManagement/messageEditing/utils/hooks';
|
||||
import { formState } from '@/views/message-management/message-editing/utils/hooks';
|
||||
|
||||
/**
|
||||
* * 上传图片
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts" setup>
|
||||
import { beforeUpload, coverUrl, formState, loading, onSearchUserinfo, onUpload, userDataList } from '@/views/messageManagement/messageEditing/utils/hooks';
|
||||
import { beforeUpload, coverUrl, formState, loading, onSearchUserinfo, onUpload, userDataList } from '@/views/message-management/message-editing/utils/hooks';
|
||||
import { onMounted, ref, toRaw } from 'vue';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { rules } from '@/views/messageManagement/messageEditing/utils/columns';
|
||||
import { messageLevel, rules } from '@/views/message-management/message-editing/utils/columns';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { editorTypeList } from '@/views/messageManagement/message/utils/columns';
|
||||
import { editorTypeList } from '@/views/message-management/message/utils/columns';
|
||||
import LoadingSvg from '@/assets/svg/loading.svg';
|
||||
import { useMessageTypeStore } from '@/store/message/messageType';
|
||||
import { encode } from 'js-base64';
|
||||
|
@ -57,6 +57,7 @@ onMounted(() => {
|
|||
messageTypeStore.getAllMessageTypeList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formState" :rules="rules" class="w-[100%] p-5" label-width="auto">
|
||||
<!-- 标题 -->
|
||||
|
@ -117,6 +118,18 @@ onMounted(() => {
|
|||
<el-input v-model="formState.summary" :autosize="{ minRows: 3, maxRows: 6 }" maxlength="200" minlength="10" show-word-limit type="textarea" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息等级 -->
|
||||
<el-form-item :label="$t('level')" prop="level">
|
||||
<el-select v-model="formState.level" :placeholder="$t('level')" clearable filterable remote remote-show-suffix>
|
||||
<el-option v-for="item in messageLevel" :key="item" :label="$t(item)" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息等级简介 -->
|
||||
<el-form-item :label="$t('extra')" prop="extra">
|
||||
<el-input v-model="formState.extra" maxlength="20" minlength="10" show-word-limit type="text" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 提交 -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm(formRef)">{{ $t('submit') }}</el-button>
|
|
@ -2,7 +2,7 @@
|
|||
import { onBeforeUnmount, ref, shallowRef } from 'vue';
|
||||
import '@wangeditor/editor/dist/css/style.css';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
||||
import { formState } from '@/views/messageManagement/messageEditing/utils/hooks';
|
||||
import { formState } from '@/views/message-management/message-editing/utils/hooks';
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
const mode = 'default';
|
|
@ -1,4 +1,6 @@
|
|||
import { $t } from '@/plugins/i18n';
|
||||
import type { ContextProps } from '@/components/SplitPane';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
export const rules = {
|
||||
// 消息标题
|
||||
|
@ -12,3 +14,11 @@ export const rules = {
|
|||
// 消息简介
|
||||
summary: [{ required: true, message: `${$t('input')}${$t('summary')}`, trigger: 'blur' }],
|
||||
};
|
||||
|
||||
export const messageLevel = ['primary', 'success', 'warning', 'info', 'danger'];
|
||||
|
||||
export const settingLR: ContextProps = reactive({
|
||||
minPercent: 20,
|
||||
defaultPercent: 80,
|
||||
split: 'vertical',
|
||||
});
|
|
@ -20,6 +20,8 @@ export const formState = reactive({
|
|||
editorType: 'markdown',
|
||||
status: false,
|
||||
summary: '',
|
||||
level: '',
|
||||
extra: '',
|
||||
cover: '',
|
||||
});
|
||||
const adminUserStore = useAdminUserStore();
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { columns } from '@/views/messageManagement/messageType/utils/columns';
|
||||
import { columns } from '@/views/message-management/message-type/utils/columns';
|
||||
import PureTableBar from '@/components/TableBar/src/bar';
|
||||
import AddFill from '@iconify-icons/ri/add-circle-line';
|
||||
import PureTable from '@pureadmin/table';
|
||||
import { deleteIds, onAdd, onDelete, onDeleteBatch, onSearch, onUpdate } from '@/views/messageManagement/messageType/utils/hooks';
|
||||
import { deleteIds, onAdd, onDelete, onDeleteBatch, onSearch, onUpdate } from '@/views/message-management/message-type/utils/hooks';
|
||||
import Delete from '@iconify-icons/ep/delete';
|
||||
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { rules } from '@/views/messageManagement/messageType/utils/columns';
|
||||
import { FormProps } from '@/views/messageManagement/messageType/utils/types';
|
||||
import { rules } from '@/views/message-management/message-type/utils/columns';
|
||||
import { FormProps } from '@/views/message-management/message-type/utils/types';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { usePublicHooks } from '@/views/hooks';
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import { addDialog } from '@/components/BaseDialog/index';
|
||||
import MessageTypeDialog from '@/views/messageManagement/messageType/message-type-dialog.vue';
|
||||
import MessageTypeDialog from '@/views/message-management/message-type/message-type-dialog.vue';
|
||||
import { useMessageTypeStore } from '@/store/message/messageType';
|
||||
import { h, ref } from 'vue';
|
||||
import { message, messageBox } from '@/utils/message';
|
||||
import type { FormItemProps } from '@/views/messageManagement/messageType/utils/types';
|
||||
import type { FormItemProps } from '@/views/message-management/message-type/utils/types';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import DeleteBatchDialog from '@/components/Table/DeleteBatchDialog.vue';
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { columns, editorTypeList } from '@/views/messageManagement/message/utils/columns';
|
||||
import { columns } from '@/views/message-management/message/utils/columns';
|
||||
import PureTableBar from '@/components/TableBar/src/bar';
|
||||
import PureTable from '@pureadmin/table';
|
||||
import { deleteIds, onDelete, onDeleteBatch, onSearch, onUpdate } from '@/views/messageManagement/message/utils/hooks';
|
||||
import { deleteIds, onDelete, onDeleteBatch, onSearch, onUpdate } from '@/views/message-management/message/utils/hooks';
|
||||
import Delete from '@iconify-icons/ep/delete';
|
||||
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
||||
|
@ -12,6 +12,8 @@ import { $t } from '@/plugins/i18n';
|
|||
import { useMessageStore } from '@/store/message/message';
|
||||
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { messageLevel } from '@/views/message-management/message-editing/utils/columns';
|
||||
import { isReadStatus } from '@/enums/baseConstant';
|
||||
|
||||
const tableRef = ref();
|
||||
const formRef = ref();
|
||||
|
@ -63,31 +65,28 @@ onMounted(() => {
|
|||
<el-input v-model="messageStore.form.title" :placeholder="`${$t('input')}${$t('title')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 发送人昵称 -->
|
||||
<el-form-item :label="$t('sendNickname')" prop="sendNickname">
|
||||
<el-input v-model="messageStore.form.sendNickname" :placeholder="`${$t('input')}${$t('sendNickname')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息类型 -->
|
||||
<el-form-item :label="$t('messageType')" prop="messageType">
|
||||
<el-input v-model="messageStore.form.messageType" :placeholder="`${$t('input')}${$t('messageType')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<el-form-item :label="$t('content')" prop="content">
|
||||
<el-input v-model="messageStore.form.content" :placeholder="`${$t('input')}${$t('content')}`" class="!w-[180px]" clearable />
|
||||
<!-- 消息等级 -->
|
||||
<el-form-item :label="$t('level')" prop="level">
|
||||
<el-select v-model="messageStore.form.level" :placeholder="$t('level')" class="!w-[180px]" clearable filterable remote remote-show-suffix>
|
||||
<el-option v-for="item in messageLevel" :key="item" :label="$t(item)" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 编辑器类型 -->
|
||||
<el-form-item :label="$t('editorType')" prop="editorType">
|
||||
<el-select v-model="messageStore.form.editorType" :placeholder="`${$t('select')}${$t('editorType')}`" class="!w-[180px]" clearable filterable>
|
||||
<el-option v-for="(item, index) in editorTypeList" :key="index" :label="item.label" :navigationBar="false" :value="item.value" />
|
||||
</el-select>
|
||||
<!-- 消息等级简介 -->
|
||||
<el-form-item :label="$t('extra')" prop="extra">
|
||||
<el-input v-model="messageStore.form.extra" class="!w-[180px]" maxlength="20" minlength="10" show-word-limit type="text" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 0:未读 1:已读 -->
|
||||
<el-form-item :label="$t('status')" prop="status">
|
||||
<el-input v-model="messageStore.form.status" :placeholder="`${$t('input')}${$t('status')}`" class="!w-[180px]" clearable />
|
||||
<el-select v-model="messageStore.form.status" :placeholder="$t('status')" class="!w-[180px]" clearable filterable remote remote-show-suffix>
|
||||
<el-option v-for="(item, index) in isReadStatus" :key="index" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
|
@ -0,0 +1,32 @@
|
|||
<script lang="ts" setup>
|
||||
import { MdEditor } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import { fetchUploadFile } from '@/api/v1/system';
|
||||
import { updateMessage } from '@/views/message-management/message/utils/hooks';
|
||||
|
||||
/**
|
||||
* * 上传图片
|
||||
* @param files
|
||||
* @param callback
|
||||
*/
|
||||
const onUploadImg = async (files: any, callback: any) => {
|
||||
// 上传图片等待结果
|
||||
const res = await Promise.all(
|
||||
files.map((file: any) => {
|
||||
return new Promise(async resolve => {
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
form.append('type', 'message');
|
||||
resolve(await fetchUploadFile(form));
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// 插入图片内容
|
||||
callback(res.map(item => item.data.url));
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MdEditor v-model="updateMessage.content" :showToolbarName="true" style="height: calc(100vh - 120px)" @onUploadImg="onUploadImg" />
|
||||
</template>
|
|
@ -0,0 +1,134 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { editorTypeList, rules } from '@/views/message-management/message/utils/columns';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { usePublicHooks } from '@/views/hooks';
|
||||
import { useMessageTypeStore } from '@/store/message/messageType';
|
||||
import LoadingSvg from '@/assets/svg/loading.svg';
|
||||
import { coverUrl, loading, onSearchUserinfo, onUpload, updateMessage, userDataList } from '@/views/message-management/message/utils/hooks';
|
||||
import { messageLevel, settingLR } from '@/views/message-management/message-editing/utils/columns';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
import { beforeUpload } from '@/views/message-management/message-editing/utils/hooks';
|
||||
import SplitPane from '@/components/SplitPane';
|
||||
import RichEditor from '@/views/message-management/message/rich-editor.vue';
|
||||
import MarkdownEditor from '@/views/message-management/message/markdown-editor.vue';
|
||||
|
||||
// 用户是否停用样式
|
||||
const { switchStyle } = usePublicHooks();
|
||||
const formRef = ref<FormInstance>();
|
||||
const messageTypeStore = useMessageTypeStore();
|
||||
|
||||
onMounted(() => {
|
||||
messageTypeStore.getAllMessageTypeList();
|
||||
});
|
||||
|
||||
defineExpose({ formRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never" style="height: calc(100vh - 200px); overflow: auto">
|
||||
<div class="split-pane">
|
||||
<SplitPane :splitSet="settingLR">
|
||||
<template #paneL>
|
||||
<rich-editor v-if="updateMessage.editorType === 'rich'" />
|
||||
<markdown-editor v-else />
|
||||
</template>
|
||||
|
||||
<template #paneR>
|
||||
<el-form ref="formRef" :model="updateMessage" :rules="rules" class="w-[100%] p-5" label-width="auto">
|
||||
<!-- 标题 -->
|
||||
<el-form-item :label="$t('title')" prop="title">
|
||||
<el-input v-model="updateMessage.title" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息类型 -->
|
||||
<el-form-item :label="$t('messageType')" prop="messageType">
|
||||
<el-select v-model="updateMessage.messageType" :placeholder="`${$t('select')}${$t('messageType')}`" clearable filterable>
|
||||
<el-option v-for="(item, index) in messageTypeStore.allMessageTypeList" :key="index" :label="item.messageName" :navigationBar="false" :value="item.messageType" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 接收人ID -->
|
||||
<el-form-item :label="$t('receivedUserIds')" prop="receivedUserIds">
|
||||
<el-select
|
||||
v-model="updateMessage.receivedUserIds"
|
||||
:loading="loading"
|
||||
:placeholder="$t('receivedUserIdTip')"
|
||||
:remote-method="onSearchUserinfo"
|
||||
clearable
|
||||
filterable
|
||||
multiple
|
||||
remote
|
||||
remote-show-suffix
|
||||
>
|
||||
<el-option v-for="item in userDataList" :key="item.id" :label="item.username" :value="item.id" />
|
||||
<template #loading>
|
||||
<el-icon class="is-loading">
|
||||
<LoadingSvg />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 编辑器类型 -->
|
||||
<el-form-item :label="$t('editorType')" prop="editorType">
|
||||
<el-radio-group v-model="updateMessage.editorType">
|
||||
<el-radio v-for="(item, index) in editorTypeList" :key="index" :navigationBar="false" :value="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 是否已读 -->
|
||||
<el-form-item :label="$t('isRead')" prop="status">
|
||||
<el-switch v-model="updateMessage.status" :active-text="$t('readAlready')" :active-value="true" :inactive-text="$t('unread')" :inactive-value="false" :style="switchStyle" inline-prompt />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 封面内容 -->
|
||||
<el-form-item :label="$t('cover')" prop="cover">
|
||||
<el-upload :auto-upload="true" :before-upload="beforeUpload" :http-request="onUpload" :show-file-list="false" accept="image/*" drag>
|
||||
<el-image v-if="coverUrl" :src="coverUrl" fit="cover" lazy>
|
||||
<template #placeholder>
|
||||
<img alt="" src="@/assets/images/tip/loading.gif" />
|
||||
</template>
|
||||
</el-image>
|
||||
<el-icon v-else size="36">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<el-button v-show="coverUrl" link type="primary" @click="coverUrl = ''"> 删除图片</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 简介 -->
|
||||
<el-form-item :label="$t('summary')" prop="summary">
|
||||
<el-input v-model="updateMessage.summary" :autosize="{ minRows: 3, maxRows: 6 }" maxlength="200" minlength="10" show-word-limit type="textarea" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息等级 -->
|
||||
<el-form-item :label="$t('level')" prop="level">
|
||||
<el-select v-model="updateMessage.level" :placeholder="$t('level')" clearable filterable remote remote-show-suffix>
|
||||
<el-option v-for="item in messageLevel" :key="item" :label="$t(item)" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息等级简介 -->
|
||||
<el-form-item :label="$t('extra')" prop="extra">
|
||||
<el-input v-model="updateMessage.extra" maxlength="20" minlength="10" show-word-limit type="text" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</SplitPane>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-upload-dragger) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,58 @@
|
|||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, ref, shallowRef } from 'vue';
|
||||
import '@wangeditor/editor/dist/css/style.css';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { updateMessage } from '@/views/message-management/message/utils/hooks';
|
||||
|
||||
const mode = 'default';
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef();
|
||||
const toolbarConfig: any = { excludeKeys: 'fullScreen' };
|
||||
const editorConfig = { placeholder: '请输入内容...', MENU_CONF: {} };
|
||||
const token = ref(getToken().token);
|
||||
|
||||
editorConfig.MENU_CONF['uploadImage'] = {
|
||||
// 服务端上传地址,根据实际业务改写
|
||||
server: '/api/files/upload',
|
||||
// form-data 的 fieldName,根据实际业务改写
|
||||
fieldName: 'file',
|
||||
// 选择文件时的类型限制,根据实际业务改写
|
||||
allowedFileTypes: ['image/png', 'image/jpg', 'image/jpeg'],
|
||||
meta: { type: 'message' },
|
||||
headers: { token: token.value },
|
||||
// 自定义插入图片
|
||||
customInsert(res: any, insertFn) {
|
||||
// res.data.url是后端返回的图片地址,根据实际业务改写
|
||||
if (res.data.url) {
|
||||
// const form = new FormData();
|
||||
// form.append('file', file);
|
||||
// form.append('type', 'message');
|
||||
// await fetchUploadFile(form)
|
||||
setTimeout(() => {
|
||||
// insertFn插入图片进编辑器
|
||||
insertFn(res.data.url);
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const handleCreated = editor => {
|
||||
// 记录 editor 实例,重要!
|
||||
editorRef.value = editor;
|
||||
};
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="wangeditor">
|
||||
<Toolbar :defaultConfig="toolbarConfig" :editor="editorRef" :mode="mode" style="border-bottom: 1px solid #ccc" />
|
||||
<Editor v-model="updateMessage.content" :defaultConfig="editorConfig" :mode="mode" style="height: 500px; overflow-y: hidden" @onCreated="handleCreated" />
|
||||
</div>
|
||||
</template>
|
|
@ -1,6 +1,7 @@
|
|||
import { reactive } from 'vue';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import type { FormRules } from 'element-plus';
|
||||
import { ElTag } from 'element-plus';
|
||||
|
||||
// 表格列
|
||||
export const columns: TableColumnList = [
|
||||
|
@ -8,28 +9,28 @@ export const columns: TableColumnList = [
|
|||
{ type: 'index', index: (index: number) => index + 1, label: '序号', width: 60 },
|
||||
// 消息标题
|
||||
{ label: $t('title'), prop: 'title' },
|
||||
// 接收人用户ID
|
||||
{
|
||||
label: $t('receivedUserIds'),
|
||||
prop: 'receivedUserIds',
|
||||
formatter({ receivedUserIds }) {
|
||||
return !receivedUserIds ? $t('all') : $t('portion');
|
||||
},
|
||||
},
|
||||
// 发送人昵称
|
||||
{ label: $t('sendNickname'), prop: 'sendNickname' },
|
||||
// 消息类型
|
||||
{ label: $t('messageType'), prop: 'messageType' },
|
||||
// 消息内容
|
||||
{ label: $t('content'), prop: 'content' },
|
||||
// 编辑器类型
|
||||
{ label: $t('editorType'), prop: 'editorType' },
|
||||
// 编辑器类型
|
||||
{
|
||||
label: $t('level'),
|
||||
prop: 'level',
|
||||
formatter({ level }) {
|
||||
return <ElTag type={level}>{$t(level)}</ElTag>;
|
||||
},
|
||||
},
|
||||
// 编辑器类型
|
||||
{ label: $t('extra'), prop: 'extra' },
|
||||
// 0:未读 1:已读
|
||||
{
|
||||
label: $t('status'),
|
||||
prop: 'status',
|
||||
formatter({ status }) {
|
||||
return status ? $t('readAlready') : $t('unread');
|
||||
return status ? <ElTag type={'info'}>{$t('readAlready')}</ElTag> : <ElTag type={'primary'}>{$t('unread')}</ElTag>;
|
||||
},
|
||||
},
|
||||
{ label: $t('table.updateTime'), prop: 'updateTime', sortable: true, width: 160 },
|
|
@ -1,20 +1,51 @@
|
|||
import { addDialog } from '@/components/BaseDialog/index';
|
||||
import MessageDialog from '@/views/messageManagement/message/message-dialog.vue';
|
||||
import MessageDialog from '@/views/message-management/message/message-dialog.vue';
|
||||
import { useMessageStore } from '@/store/message/message';
|
||||
import { h, ref } from 'vue';
|
||||
import { h, reactive, ref, toRaw } from 'vue';
|
||||
import { message, messageBox } from '@/utils/message';
|
||||
import type { FormItemProps } from '@/views/messageManagement/message/utils/types';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import DeleteBatchDialog from '@/components/Table/DeleteBatchDialog.vue';
|
||||
import { useAdminUserStore } from '@/store/system/adminUser';
|
||||
import { fetchGetReceivedUserinfoByMessageId } from '@/api/v1/message';
|
||||
import { decode, encode } from 'js-base64';
|
||||
import type { UploadRequestOptions } from 'element-plus';
|
||||
import { fetchUploadFile } from '@/api/v1/system';
|
||||
|
||||
export const formRef = ref();
|
||||
// 删除ids
|
||||
export const deleteIds = ref([]);
|
||||
// 用户信息列表
|
||||
export const userDataList = ref();
|
||||
// 更新时封面图片
|
||||
export const coverUrl = ref('');
|
||||
// 搜索用户加载
|
||||
export const loading = ref(false);
|
||||
export const updateMessage = reactive({
|
||||
// 消息标题
|
||||
title: undefined,
|
||||
// 封面
|
||||
cover: undefined,
|
||||
// 接收人用户ID
|
||||
receivedUserIds: undefined,
|
||||
// 发送人用户ID
|
||||
sendUserId: undefined,
|
||||
// 发送人昵称
|
||||
sendNickName: undefined,
|
||||
// 消息类型
|
||||
messageType: undefined,
|
||||
// 消息内容
|
||||
content: undefined,
|
||||
// 简介
|
||||
summary: undefined,
|
||||
// 消息等级
|
||||
level: undefined,
|
||||
// 消息等级类型
|
||||
extra: undefined,
|
||||
// 编辑器类型
|
||||
editorType: undefined,
|
||||
// 0:未读 1:已读
|
||||
status: undefined,
|
||||
});
|
||||
const messageStore = useMessageStore();
|
||||
const adminUserStore = useAdminUserStore();
|
||||
|
||||
|
@ -25,39 +56,55 @@ export async function onSearch() {
|
|||
messageStore.loading = false;
|
||||
}
|
||||
|
||||
/** 搜索 */
|
||||
export const onSearchUserinfo = async (keyword: string) => {
|
||||
loading.value = true;
|
||||
userDataList.value = await adminUserStore.queryUser({ keyword });
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* * 更新系统消息
|
||||
* @param row
|
||||
*/
|
||||
export async function onUpdate(row: any) {
|
||||
await onSearchUserinfo('');
|
||||
// 将表格数据合并到更新数据中
|
||||
Object.assign(updateMessage, row);
|
||||
|
||||
// 解码内容
|
||||
updateMessage.content = decode(row.content);
|
||||
|
||||
// 获取当前消息内容和接收者信息
|
||||
const result = await fetchGetReceivedUserinfoByMessageId({ messageId: row.id });
|
||||
userDataList.value = result.data.map((item: any) => ({
|
||||
id: item.receivedUserId,
|
||||
nickname: item.nickname,
|
||||
username: item.username,
|
||||
}));
|
||||
updateMessage.receivedUserIds = result.data.map((item: any) => item.receivedUserId);
|
||||
|
||||
// 设置封面图片
|
||||
coverUrl.value = row.cover;
|
||||
|
||||
addDialog({
|
||||
title: `${$t('modify')}${$t('message')}`,
|
||||
width: '30%',
|
||||
props: {
|
||||
formInline: {
|
||||
title: row.title,
|
||||
receivedUserIds: row.receivedUserIds ? row.receivedUserIds.split(',') : row.receivedUserIds,
|
||||
sendUserId: row.sendUserId,
|
||||
sendNickName: row.sendNickName,
|
||||
messageType: row.messageType,
|
||||
content: row.content,
|
||||
editorType: row.editorType,
|
||||
status: row.status,
|
||||
},
|
||||
},
|
||||
width: '85%',
|
||||
draggable: true,
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(MessageDialog, { ref: formRef }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const form = options.props.formInline as FormItemProps;
|
||||
beforeSure: done => {
|
||||
formRef.value.formRef.validate(async (valid: any) => {
|
||||
if (!valid) return;
|
||||
|
||||
const result = await messageStore.updateMessage({ ...form, id: row.id });
|
||||
// 将更新的信息进行编码
|
||||
const data = toRaw(updateMessage);
|
||||
data.content = encode(data.content);
|
||||
|
||||
// 更新消息内容
|
||||
const result = await messageStore.updateMessage({ ...data, id: row.id });
|
||||
if (!result) return;
|
||||
Object.assign(updateMessage, {});
|
||||
done();
|
||||
await onSearch();
|
||||
});
|
||||
|
@ -113,9 +160,10 @@ export const onDeleteBatch = async () => {
|
|||
});
|
||||
};
|
||||
|
||||
/** 搜索 */
|
||||
export const onSearchUserinfo = async (keyword: string) => {
|
||||
loading.value = true;
|
||||
userDataList.value = await adminUserStore.queryUser({ keyword });
|
||||
loading.value = false;
|
||||
/** 上传时 */
|
||||
export const onUpload = async (options: UploadRequestOptions) => {
|
||||
const data = { file: options.file, type: 'message' };
|
||||
const result: any = await fetchUploadFile(data);
|
||||
coverUrl.value = result.data.url;
|
||||
updateMessage.cover = result.data.filepath;
|
||||
};
|
|
@ -10,6 +10,10 @@ export interface FormItemProps {
|
|||
messageType: string;
|
||||
// 消息内容
|
||||
content: string;
|
||||
// 消息等级
|
||||
level: string;
|
||||
// 消息等级类型
|
||||
extra: string;
|
||||
// 编辑器类型
|
||||
editorType: string;
|
||||
// 0:未读 1:已读
|
|
@ -1,100 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { editorTypeList, rules } from '@/views/messageManagement/message/utils/columns';
|
||||
import { FormProps } from '@/views/messageManagement/message/utils/types';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
import { usePublicHooks } from '@/views/hooks';
|
||||
import { useMessageTypeStore } from '@/store/message/messageType';
|
||||
import LoadingSvg from '@/assets/svg/loading.svg';
|
||||
import { loading, onSearchUserinfo, userDataList } from '@/views/messageManagement/message/utils/hooks';
|
||||
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
formInline: () => ({
|
||||
// 消息标题
|
||||
title: undefined,
|
||||
// 接收人用户ID
|
||||
receivedUserIds: undefined,
|
||||
// 发送人用户ID
|
||||
sendUserId: undefined,
|
||||
// 发送人昵称
|
||||
sendNickName: undefined,
|
||||
// 消息类型
|
||||
messageType: undefined,
|
||||
// 消息内容
|
||||
content: undefined,
|
||||
// 编辑器类型
|
||||
editorType: undefined,
|
||||
// 0:未读 1:已读
|
||||
status: undefined,
|
||||
}),
|
||||
});
|
||||
// 用户是否停用样式
|
||||
const { switchStyle } = usePublicHooks();
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = ref(props.formInline);
|
||||
const messageTypeStore = useMessageTypeStore();
|
||||
|
||||
onMounted(() => {
|
||||
messageTypeStore.getAllMessageTypeList();
|
||||
});
|
||||
|
||||
defineExpose({ formRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto">
|
||||
<!-- 消息标题 -->
|
||||
<el-form-item :label="$t('title')" prop="title">
|
||||
<el-input v-model="form.title" :placeholder="$t('input') + $t('title')" autocomplete="off" maxlength="100" type="text" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 接收人用户ID -->
|
||||
<el-form-item :label="$t('receivedUserIds')" prop="receivedUserIds">
|
||||
<el-select v-model="form.receivedUserIds" :loading="loading" :placeholder="$t('receivedUserIdTip')" :remote-method="onSearchUserinfo" clearable filterable multiple remote remote-show-suffix>
|
||||
<el-option v-for="item in userDataList" :key="item.id" :label="item.username" :value="item.id" />
|
||||
<template #loading>
|
||||
<el-icon class="is-loading">
|
||||
<LoadingSvg />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 发送人用户ID -->
|
||||
<el-form-item :label="$t('sendUserId')" prop="sendUserId">
|
||||
<el-select v-model="form.sendUserId" :loading="loading" :placeholder="$t('select') + $t('sendUserId')" :remote-method="onSearchUserinfo" clearable filterable remote remote-show-suffix>
|
||||
<el-option v-for="item in userDataList" :key="item.id" :label="item.username" :value="item.id" />
|
||||
<template #loading>
|
||||
<el-icon class="is-loading">
|
||||
<LoadingSvg />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息类型 -->
|
||||
<el-form-item :label="$t('messageType')" prop="messageType">
|
||||
<el-select v-model="form.messageType" :placeholder="`${$t('select')}${$t('messageType')}`" clearable filterable>
|
||||
<el-option v-for="(item, index) in messageTypeStore.allMessageTypeList" :key="index" :label="item.messageName" :navigationBar="false" :value="item.messageType" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 编辑器类型 -->
|
||||
<el-form-item :label="$t('editorType')" prop="editorType">
|
||||
<el-select v-model="form.editorType" :placeholder="`${$t('select')}${$t('editorType')}`" clearable filterable>
|
||||
<el-option v-for="(item, index) in editorTypeList" :key="index" :label="item.label" :navigationBar="false" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<el-form-item :label="$t('content')" prop="content">
|
||||
<el-input v-model="form.content" :placeholder="$t('input') + $t('content')" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 0:未读 1:已读 -->
|
||||
<el-form-item :label="$t('status')" prop="status">
|
||||
<el-switch v-model="form.status" :active-text="$t('readAlready')" :active-value="true" :inactive-text="$t('unread')" :inactive-value="false" :style="switchStyle" inline-prompt />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
|
@ -1,38 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import splitpane, { ContextProps } from '@/components/SplitPane';
|
||||
import { reactive } from 'vue';
|
||||
import MessageEditorFrom from '@/views/messageManagement/messageEditing/message-editor-from.vue';
|
||||
import RichEditor from '@/views/messageManagement/messageEditing/rich-editor.vue';
|
||||
import { formState } from '@/views/messageManagement/messageEditing/utils/hooks';
|
||||
import MarkdownEditor from '@/views/messageManagement/messageEditing/markdown-editor.vue';
|
||||
|
||||
const settingLR: ContextProps = reactive({
|
||||
minPercent: 20,
|
||||
defaultPercent: 80,
|
||||
split: 'vertical',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<div class="split-pane">
|
||||
<splitpane :splitSet="settingLR">
|
||||
<template #paneL>
|
||||
<RichEditor v-if="formState.editorType === 'rich'" />
|
||||
<markdown-editor v-else />
|
||||
</template>
|
||||
|
||||
<template #paneR>
|
||||
<message-editor-from />
|
||||
</template>
|
||||
</splitpane>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.split-pane {
|
||||
width: 100%;
|
||||
height: calc(100vh - 120px);
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue