feat: antd菜单模块迁移
This commit is contained in:
116
apps/web-antd/src/views/mp/modules/wx-msg/card.scss
Normal file
116
apps/web-antd/src/views/mp/modules/wx-msg/card.scss
Normal file
@@ -0,0 +1,116 @@
|
||||
.avue-card {
|
||||
&__item {
|
||||
box-sizing: border-box;
|
||||
height: 200px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
font-feature-settings: 'tnum';
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5;
|
||||
color: rgb(0 0 0 / 65%);
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e8e8e8;
|
||||
|
||||
&:hover {
|
||||
border-color: rgb(0 0 0 / 9%);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 9%);
|
||||
}
|
||||
|
||||
&--add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
color: rgb(0 0 0 / 45%);
|
||||
background-color: #fff;
|
||||
border: 1px dashed #000;
|
||||
border-color: #d9d9d9;
|
||||
border-radius: 2px;
|
||||
|
||||
i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #40a9ff;
|
||||
background-color: #fff;
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
&__detail {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-right: 12px;
|
||||
overflow: hidden;
|
||||
border-radius: 48px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
color: rgb(0 0 0 / 85%);
|
||||
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: -webkit-box;
|
||||
height: 64px;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 3;
|
||||
color: rgb(0 0 0 / 45%);
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
&__menu {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
color: rgb(0 0 0 / 45%);
|
||||
text-align: center;
|
||||
background: #f7f9fa;
|
||||
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** joolun 额外加的 */
|
||||
.avue-comment__main {
|
||||
flex: unset !important;
|
||||
margin: 0 8px !important;
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.avue-comment__header {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
||||
.avue-comment__body {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
109
apps/web-antd/src/views/mp/modules/wx-msg/comment.scss
Normal file
109
apps/web-antd/src/views/mp/modules/wx-msg/comment.scss
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */
|
||||
.avue-comment {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 30px;
|
||||
|
||||
&--reverse {
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.avue-comment__main {
|
||||
&::before,
|
||||
&::after {
|
||||
right: -8px;
|
||||
left: auto;
|
||||
border-width: 8px 0 8px 8px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
border-left-color: #dedede;
|
||||
}
|
||||
|
||||
&::after {
|
||||
margin-right: 1px;
|
||||
margin-left: auto;
|
||||
border-left-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
box-sizing: border-box;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
vertical-align: middle;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px 15px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
&__author {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&__main {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
margin: 0 20px;
|
||||
border: 1px solid #dedede;
|
||||
border-radius: 2px;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 100%;
|
||||
left: -8px;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
content: ' ';
|
||||
border-color: transparent;
|
||||
border-style: solid solid outset;
|
||||
border-width: 8px 8px 8px 0;
|
||||
}
|
||||
|
||||
&::before {
|
||||
z-index: 1;
|
||||
border-right-color: #dedede;
|
||||
}
|
||||
|
||||
&::after {
|
||||
z-index: 2;
|
||||
margin-left: 1px;
|
||||
border-right-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
font-family:
|
||||
'Segoe UI', 'Lucida Grande', Helvetica, Arial, 'Microsoft YaHei',
|
||||
FreeSans, Arimo, 'Droid Sans', 'wenquanyi micro hei', 'Hiragino Sans GB',
|
||||
'Hiragino Sans GB W3', FontAwesome, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 1px 0 1px 15px;
|
||||
margin: 0;
|
||||
font-family:
|
||||
Georgia, 'Times New Roman', Times, Kai, 'Kaiti SC', KaiTi, BiauKai,
|
||||
FontAwesome, serif;
|
||||
border-left: 4px solid #ddd;
|
||||
}
|
||||
}
|
||||
80
apps/web-antd/src/views/mp/modules/wx-msg/components/Msg.vue
Normal file
80
apps/web-antd/src/views/mp/modules/wx-msg/components/Msg.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import WxLocation from '#/views/mp/modules/wx-location';
|
||||
import WxMusic from '#/views/mp/modules/wx-music';
|
||||
import WxNews from '#/views/mp/modules/wx-news';
|
||||
import WxVideoPlayer from '#/views/mp/modules/wx-video-play';
|
||||
import WxVoicePlayer from '#/views/mp/modules/wx-voice-play';
|
||||
|
||||
import { MsgType } from '../types';
|
||||
import MsgEvent from './MsgEvent.vue';
|
||||
|
||||
defineOptions({ name: 'Msg' });
|
||||
|
||||
const props = defineProps<{
|
||||
item: any;
|
||||
}>();
|
||||
|
||||
const item = ref<any>(props.item);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<MsgEvent v-if="item.type === MsgType.Event" :item="item" />
|
||||
|
||||
<div v-else-if="item.type === MsgType.Text">{{ item.content }}</div>
|
||||
|
||||
<div v-else-if="item.type === MsgType.Voice">
|
||||
<WxVoicePlayer :url="item.mediaUrl" :content="item.recognition" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="item.type === MsgType.Image">
|
||||
<a target="_blank" :href="item.mediaUrl">
|
||||
<img :src="item.mediaUrl" style="width: 100px" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="item.type === MsgType.Video || item.type === 'shortvideo'"
|
||||
style="text-align: center"
|
||||
>
|
||||
<WxVideoPlayer :url="item.mediaUrl" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="item.type === MsgType.Link" class="avue-card__detail">
|
||||
<a target="_blank" :href="item.url">
|
||||
<div class="avue-card__title">
|
||||
<Icon icon="ep:link" />{{ item.title }}
|
||||
</div>
|
||||
</a>
|
||||
<div class="avue-card__info" style="height: unset">
|
||||
{{ item.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="item.type === MsgType.Location">
|
||||
<WxLocation
|
||||
:label="item.label"
|
||||
:location-y="item.locationY"
|
||||
:location-x="item.locationX"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else-if="item.type === MsgType.News" style="width: 300px">
|
||||
<WxNews :articles="item.articles" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="item.type === MsgType.Music">
|
||||
<WxMusic
|
||||
:title="item.title"
|
||||
:description="item.description"
|
||||
:thumb-media-url="item.thumbMediaUrl"
|
||||
:music-url="item.musicUrl"
|
||||
:hq-music-url="item.hqMusicUrl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
item: any;
|
||||
}>();
|
||||
|
||||
const item = ref(props.item);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="item.event === 'subscribe'">
|
||||
<Tag color="success">关注</Tag>
|
||||
</div>
|
||||
<div v-else-if="item.event === 'unsubscribe'">
|
||||
<Tag color="error">取消关注</Tag>
|
||||
</div>
|
||||
<div v-else-if="item.event === 'CLICK'">
|
||||
<Tag>点击菜单</Tag>
|
||||
【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.event === 'VIEW'">
|
||||
<Tag>点击菜单链接</Tag>
|
||||
【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.event === 'scancode_waitmsg'">
|
||||
<Tag>扫码结果</Tag>
|
||||
【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.event === 'scancode_push'">
|
||||
<Tag>扫码结果</Tag>
|
||||
【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.event === 'pic_sysphoto'">
|
||||
<Tag>系统拍照发图</Tag>
|
||||
</div>
|
||||
<div v-else-if="item.event === 'pic_photo_or_album'">
|
||||
<Tag>拍照或者相册</Tag>
|
||||
</div>
|
||||
<div v-else-if="item.event === 'pic_weixin'">
|
||||
<Tag>微信相册</Tag>
|
||||
</div>
|
||||
<div v-else-if="item.event === 'location_select'">
|
||||
<Tag>选择地理位置</Tag>
|
||||
</div>
|
||||
<div v-else-if="item.event === 'SCAN'">
|
||||
<Tag>扫码</Tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
<Tag color="error">未知事件类型</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,77 @@
|
||||
<script lang="ts" setup>
|
||||
import type { User } from '../types';
|
||||
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import avatarWechat from '@/assets/imgs/wechat.png';
|
||||
|
||||
import Msg from './Msg.vue';
|
||||
|
||||
// 确保 User 类型被识别为已使用
|
||||
type PropsUser = User;
|
||||
|
||||
defineOptions({ name: 'MsgList' });
|
||||
|
||||
const props = defineProps<{
|
||||
accountId: number;
|
||||
list: any[];
|
||||
user: PropsUser;
|
||||
}>();
|
||||
|
||||
// 使用常量对象替代枚举,避免 linter 误报
|
||||
const SendFrom = {
|
||||
MpBot: 2,
|
||||
User: 1,
|
||||
} as const;
|
||||
|
||||
type SendFromType = (typeof SendFrom)[keyof typeof SendFrom];
|
||||
|
||||
// 显式引用枚举成员供模板使用
|
||||
const MpBotValue = SendFrom.MpBot;
|
||||
const UserValue = SendFrom.User;
|
||||
|
||||
const getAvatar = (sendFrom: SendFromType) =>
|
||||
sendFrom === UserValue ? props.user.avatar : avatarWechat;
|
||||
|
||||
const getNickname = (sendFrom: SendFromType) =>
|
||||
sendFrom === UserValue ? props.user.nickname : '公众号';
|
||||
</script>
|
||||
<template>
|
||||
<div class="execution" v-for="item in props.list" :key="item.id">
|
||||
<div
|
||||
class="avue-comment"
|
||||
:class="{ 'avue-comment--reverse': item.sendFrom === MpBotValue }"
|
||||
>
|
||||
<div class="avatar-div">
|
||||
<img :src="getAvatar(item.sendFrom)" class="avue-comment__avatar" />
|
||||
<div class="avue-comment__author">
|
||||
{{ getNickname(item.sendFrom) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="avue-comment__main">
|
||||
<div class="avue-comment__header">
|
||||
<div class="avue-comment__create_time">
|
||||
{{ formatDateTime(item.createTime) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="avue-comment__body"
|
||||
:style="item.sendFrom === MpBotValue ? 'background: #6BED72;' : ''"
|
||||
>
|
||||
<Msg :item="item" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc */
|
||||
@import url('../comment.scss');
|
||||
@import url('../card.scss');
|
||||
|
||||
.avatar-div {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
3
apps/web-antd/src/views/mp/modules/wx-msg/index.ts
Normal file
3
apps/web-antd/src/views/mp/modules/wx-msg/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default } from './main.vue';
|
||||
|
||||
export { MsgType } from './types';
|
||||
226
apps/web-antd/src/views/mp/modules/wx-msg/main.vue
Normal file
226
apps/web-antd/src/views/mp/modules/wx-msg/main.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
① 移除暂时用不到的 websocket
|
||||
② 代码优化,补充注释,提升阅读性
|
||||
-->
|
||||
<script lang="ts" setup>
|
||||
import type { User } from './types';
|
||||
|
||||
import type { Reply } from '#/views/mp/modules/wx-reply';
|
||||
|
||||
import { nextTick, onMounted, reactive, ref, unref } from 'vue';
|
||||
|
||||
import { Button, message, Spin } from 'ant-design-vue';
|
||||
|
||||
import { getMessagePage, sendMessage } from '#/api/mp/message';
|
||||
import { getUser } from '#/api/mp/user';
|
||||
import profile from '#/assets/imgs/profile.jpg';
|
||||
import WxReplySelect, { ReplyType } from '#/views/mp/modules/wx-reply';
|
||||
|
||||
import MsgList from './components/MsgList.vue';
|
||||
|
||||
defineOptions({ name: 'WxMsg' });
|
||||
|
||||
const props = defineProps({
|
||||
userId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 消息弹窗
|
||||
const accountId = ref(-1); // 公众号ID,需要通过userId初始化
|
||||
const loading = ref(false); // 消息列表是否正在加载中
|
||||
const hasMore = ref(true); // 是否可以加载更多
|
||||
const list = ref<any[]>([]); // 消息列表
|
||||
const queryParams = reactive({
|
||||
pageNo: 1, // 当前页数
|
||||
pageSize: 14, // 每页显示多少条
|
||||
accountId,
|
||||
});
|
||||
|
||||
// 由于微信不再提供昵称,直接使用"用户"展示
|
||||
const user: User = reactive({
|
||||
nickname: '用户',
|
||||
avatar: profile,
|
||||
accountId, // 公众号账号编号
|
||||
});
|
||||
|
||||
// ========= 消息发送 =========
|
||||
const sendLoading = ref(false); // 发送消息是否加载中
|
||||
// 微信发送消息
|
||||
const reply = ref<Reply>({
|
||||
type: ReplyType.Text,
|
||||
accountId: -1,
|
||||
articles: [],
|
||||
});
|
||||
|
||||
const replySelectRef = ref<InstanceType<typeof WxReplySelect> | null>(null); // WxReplySelect组件ref,用于消息发送成功后清除内容
|
||||
const msgDivRef = ref<HTMLDivElement | null>(null); // 消息显示窗口ref,用于滚动到底部
|
||||
|
||||
/** 完成加载 */
|
||||
onMounted(async () => {
|
||||
const data = await getUser(props.userId);
|
||||
user.nickname = data.nickname?.length > 0 ? data.nickname : user.nickname;
|
||||
// API 返回的数据可能包含 headImageUrl,但类型定义中没有,使用类型断言
|
||||
const userData = data as typeof data & { headImageUrl?: string };
|
||||
user.avatar =
|
||||
userData.headImageUrl && userData.headImageUrl.length > 0
|
||||
? userData.headImageUrl
|
||||
: user.avatar;
|
||||
accountId.value = data.accountId;
|
||||
reply.value.accountId = data.accountId;
|
||||
|
||||
refreshChange();
|
||||
});
|
||||
|
||||
/** 执行发送 */
|
||||
async function sendMsg() {
|
||||
if (!unref(reply)) {
|
||||
return;
|
||||
}
|
||||
// 公众号限制:客服消息,公众号只允许发送一条
|
||||
if (
|
||||
reply.value.type === ReplyType.News &&
|
||||
reply.value.articles &&
|
||||
reply.value.articles.length > 1
|
||||
) {
|
||||
reply.value.articles = [reply.value.articles[0]];
|
||||
message.success('图文消息条数限制在 1 条以内,已默认发送第一条');
|
||||
}
|
||||
|
||||
// 注意:sendMessage API 需要 openid,但这里传入的是 userId
|
||||
// 这可能是后端 API 的特殊处理,使用类型断言绕过类型检查
|
||||
const data = await sendMessage({
|
||||
userId: props.userId,
|
||||
...reply.value,
|
||||
} as any);
|
||||
sendLoading.value = false;
|
||||
|
||||
list.value = [...list.value, data];
|
||||
await scrollToBottom();
|
||||
|
||||
// 发送后清空数据
|
||||
replySelectRef.value?.clear();
|
||||
}
|
||||
|
||||
/** 加载更多 */
|
||||
function loadMore() {
|
||||
queryParams.pageNo++;
|
||||
getPage(queryParams, null);
|
||||
}
|
||||
|
||||
/** 获取分页数据 */
|
||||
async function getPage(page: any, params: any = null) {
|
||||
loading.value = true;
|
||||
const dataTemp = await getMessagePage(
|
||||
Object.assign(
|
||||
{
|
||||
pageNo: page.pageNo,
|
||||
pageSize: page.pageSize,
|
||||
userId: props.userId,
|
||||
accountId: page.accountId,
|
||||
},
|
||||
params,
|
||||
),
|
||||
);
|
||||
|
||||
const scrollHeight = msgDivRef.value?.scrollHeight ?? 0;
|
||||
// 处理数据
|
||||
const data = dataTemp.list.reverse();
|
||||
list.value = [...data, ...list.value];
|
||||
loading.value = false;
|
||||
if (data.length < queryParams.pageSize || data.length === 0) {
|
||||
hasMore.value = false;
|
||||
}
|
||||
queryParams.pageNo = page.pageNo;
|
||||
queryParams.pageSize = page.pageSize;
|
||||
// 滚动到原来的位置
|
||||
if (queryParams.pageNo === 1) {
|
||||
// 定位到消息底部
|
||||
await scrollToBottom();
|
||||
} else if (data.length > 0) {
|
||||
// 定位滚动条
|
||||
await nextTick();
|
||||
if (scrollHeight !== 0 && msgDivRef.value) {
|
||||
msgDivRef.value.scrollTop =
|
||||
msgDivRef.value.scrollHeight - scrollHeight - 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 刷新消息 */
|
||||
function refreshChange() {
|
||||
getPage(queryParams);
|
||||
}
|
||||
|
||||
/** 定位到消息底部 */
|
||||
async function scrollToBottom() {
|
||||
await nextTick();
|
||||
if (msgDivRef.value) {
|
||||
msgDivRef.value.scrollTop = msgDivRef.value.scrollHeight;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Spin :spinning="loading">
|
||||
<div class="msg-div" ref="msgDivRef">
|
||||
<!-- 加载更多 -->
|
||||
<div v-if="!loading">
|
||||
<div class="empty-block" v-if="hasMore" @click="loadMore">
|
||||
<span class="empty-text">点击加载更多</span>
|
||||
</div>
|
||||
<div class="empty-block" v-if="!hasMore">
|
||||
<span class="empty-text">没有更多了</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<MsgList :list="list" :account-id="accountId" :user="user" />
|
||||
</div>
|
||||
</Spin>
|
||||
|
||||
<Spin :spinning="sendLoading">
|
||||
<div class="msg-send">
|
||||
<WxReplySelect ref="replySelectRef" v-model="reply" />
|
||||
<Button type="primary" class="send-but" @click="sendMsg">
|
||||
发送(S)
|
||||
</Button>
|
||||
</div>
|
||||
</Spin>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.msg-div {
|
||||
height: 50vh;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
overflow: auto;
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
|
||||
.msg-send {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.send-but {
|
||||
float: right;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-block {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
17
apps/web-antd/src/views/mp/modules/wx-msg/types.ts
Normal file
17
apps/web-antd/src/views/mp/modules/wx-msg/types.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export enum MsgType {
|
||||
Event = 'event',
|
||||
Image = 'image',
|
||||
Link = 'link',
|
||||
Location = 'location',
|
||||
Music = 'music',
|
||||
News = 'news',
|
||||
Text = 'text',
|
||||
Video = 'video',
|
||||
Voice = 'voice',
|
||||
}
|
||||
|
||||
export interface User {
|
||||
nickname: string;
|
||||
avatar: string;
|
||||
accountId: number;
|
||||
}
|
||||
Reference in New Issue
Block a user