style: 代码风格统一

This commit is contained in:
hw
2025-11-03 11:18:45 +08:00
parent 7cc72018ea
commit 81f85c200e
38 changed files with 353 additions and 275 deletions

View File

@@ -4,7 +4,7 @@ VITE_PORT=5777
VITE_BASE=/ VITE_BASE=/
# 请求路径 # 请求路径
VITE_BASE_URL=http://127.0.0.1:48080 VITE_BASE_URL=http://47.103.66.220:48080
# 接口地址 # 接口地址
VITE_GLOB_API_URL=http://47.103.66.220:48080/admin-api VITE_GLOB_API_URL=http://47.103.66.220:48080/admin-api
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务 # 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Menu, RawMenu } from './components/types'; import type { Menu, RawMenu } from './modules/types';
import { ref } from 'vue'; import { ref } from 'vue';
@@ -15,10 +15,10 @@ import {
import * as MpMenuApi from '#/api/mp/menu'; import * as MpMenuApi from '#/api/mp/menu';
import * as UtilsTree from '#/utils/tree'; import * as UtilsTree from '#/utils/tree';
import WxAccountSelect from '#/views/mp/components/wx-account-select/main.vue';
import { Level, MENU_NOT_SELECTED } from '#/views/mp/menu/data'; import { Level, MENU_NOT_SELECTED } from '#/views/mp/menu/data';
import MenuEditor from '#/views/mp/menu/modules/menu-editor.vue'; import MenuEditor from '#/views/mp/menu/modules/menu-editor.vue';
import MenuPreviewer from '#/views/mp/menu/modules/menu-previewer.vue'; import MenuPreviewer from '#/views/mp/menu/modules/menu-previewer.vue';
import WxAccountSelect from '#/views/mp/modules/wx-account-select/main.vue';
defineOptions({ name: 'MpMenu' }); defineOptions({ name: 'MpMenu' });
@@ -57,14 +57,14 @@ const tempSelfObj = ref<{
const dialogNewsVisible = ref(false); // 跳转图文时的素材选择弹窗 const dialogNewsVisible = ref(false); // 跳转图文时的素材选择弹窗
/** 侦听公众号变化 */ /** 侦听公众号变化 */
const onAccountChanged = (id: number, name: string) => { function onAccountChanged(id: number, name: string) {
accountId.value = id; accountId.value = id;
accountName.value = name; accountName.value = name;
getList(); getList();
}; }
/** 查询并转换菜单 */ /** 查询并转换菜单 */
const getList = async () => { async function getList() {
loading.value = true; loading.value = true;
try { try {
const data = await MpMenuApi.getMenuList(accountId.value); const data = await MpMenuApi.getMenuList(accountId.value);
@@ -73,16 +73,16 @@ const getList = async () => {
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { function handleQuery() {
resetForm(); resetForm();
getList(); getList();
}; }
// 将后端返回的 menuList转换成前端的 menuList /** 将后端返回的 menuList转换成前端的 menuList */
const menuListToFrontend = (list: any[]) => { function menuListToFrontend(list: any[]) {
if (!list) return []; if (!list) return [];
const result: RawMenu[] = []; const result: RawMenu[] = [];
@@ -107,10 +107,10 @@ const menuListToFrontend = (list: any[]) => {
result.push(menu as RawMenu); result.push(menu as RawMenu);
}); });
return result; return result;
}; }
// 重置表单,清空表单数据 /** 重置表单,清空表单数据 */
const resetForm = () => { function resetForm() {
// 菜单操作 // 菜单操作
activeIndex.value = MENU_NOT_SELECTED; activeIndex.value = MENU_NOT_SELECTED;
parentIndex.value = -1; parentIndex.value = -1;
@@ -120,11 +120,11 @@ const resetForm = () => {
activeMenu.value = {}; activeMenu.value = {};
tempSelfObj.value = { grand: Level.Undefined, x: 0, y: 0 }; tempSelfObj.value = { grand: Level.Undefined, x: 0, y: 0 };
dialogNewsVisible.value = false; dialogNewsVisible.value = false;
}; }
// ======================== 菜单操作 ======================== // ======================== 菜单操作 ========================
// 一级菜单点击事件 /** 一级菜单点击事件 */
const menuClicked = (parent: Menu, x: number) => { function menuClicked(parent: Menu, x: number) {
// 右侧的表单相关 // 右侧的表单相关
showRightPanel.value = true; // 右边菜单 showRightPanel.value = true; // 右边菜单
activeMenu.value = parent; // 这个如果放在顶部flag 会没有。因为重新赋值了。 activeMenu.value = parent; // 这个如果放在顶部flag 会没有。因为重新赋值了。
@@ -135,10 +135,10 @@ const menuClicked = (parent: Menu, x: number) => {
// 左侧的选中 // 左侧的选中
activeIndex.value = `${x}`; // 菜单选中样式 activeIndex.value = `${x}`; // 菜单选中样式
parentIndex.value = x; // 二级菜单显示标志 parentIndex.value = x; // 二级菜单显示标志
}; }
// 二级菜单点击事件 /** 二级菜单点击事件 */
const subMenuClicked = (child: Menu, x: number, y: number) => { function subMenuClicked(child: Menu, x: number, y: number) {
// 右侧的表单相关 // 右侧的表单相关
showRightPanel.value = true; // 右边菜单 showRightPanel.value = true; // 右边菜单
activeMenu.value = child; // 将点击的数据放到临时变量,对象有引用作用 activeMenu.value = child; // 将点击的数据放到临时变量,对象有引用作用
@@ -149,10 +149,10 @@ const subMenuClicked = (child: Menu, x: number, y: number) => {
// 左侧的选中 // 左侧的选中
activeIndex.value = `${x}-${y}`; activeIndex.value = `${x}-${y}`;
}; }
// 删除当前菜单 /** 删除当前菜单 */
const onDeleteMenu = async () => { async function onDeleteMenu() {
try { try {
await confirm('确定要删除吗?'); await confirm('确定要删除吗?');
if (tempSelfObj.value.grand === Level.Parent) { if (tempSelfObj.value.grand === Level.Parent) {
@@ -175,10 +175,11 @@ const onDeleteMenu = async () => {
} catch { } catch {
// //
} }
}; }
// ======================== 菜单编辑 ======================== // ======================== 菜单编辑 ========================
const onSave = async () => { /** 保存菜单 */
async function onSave() {
try { try {
await confirm('确定要保存吗?'); await confirm('确定要保存吗?');
const loadingInstance = ElLoading.service({ const loadingInstance = ElLoading.service({
@@ -194,9 +195,10 @@ const onSave = async () => {
} catch { } catch {
// //
} }
}; }
const onClear = async () => { /** 清空菜单 */
async function onClear() {
try { try {
await confirm('确定要删除吗?'); await confirm('确定要删除吗?');
const loadingInstance = ElLoading.service({ const loadingInstance = ElLoading.service({
@@ -212,10 +214,10 @@ const onClear = async () => {
} catch { } catch {
// //
} }
}; }
// 将前端的 menuList转换成后端接收的 menuList /** 将前端的 menuList转换成后端接收的 menuList */
const menuListToBackend = () => { function menuListToBackend() {
const result: any[] = []; const result: any[] = [];
menuList.value.forEach((item) => { menuList.value.forEach((item) => {
const menu = menuToBackend(item); const menu = menuToBackend(item);
@@ -231,11 +233,11 @@ const menuListToBackend = () => {
}); });
}); });
return result; return result;
}; }
// 将前端的 menu转换成后端接收的 menu /** 将前端的 menu转换成后端接收的 menu */
// TODO: @芋艿需要根据后台API删除不需要的字段 // TODO: @芋艿需要根据后台API删除不需要的字段
const menuToBackend = (menu: any) => { function menuToBackend(menu: any) {
const result = { const result = {
...menu, ...menu,
children: undefined, // 不处理子节点 children: undefined, // 不处理子节点
@@ -254,7 +256,7 @@ const menuToBackend = (menu: any) => {
result.replyHqMusicUrl = menu.reply.hqMusicUrl; result.replyHqMusicUrl = menu.reply.hqMusicUrl;
return result; return result;
}; }
</script> </script>
<template> <template>

View File

@@ -14,11 +14,11 @@ import {
ElSelect, ElSelect,
} from 'element-plus'; } from 'element-plus';
import WxMaterialSelect from '#/views/mp/components/wx-material-select'; import WxMaterialSelect from '#/views/mp/modules/wx-material-select';
import WxNews from '#/views/mp/components/wx-news'; import WxNews from '#/views/mp/modules/wx-news';
import WxReplySelect from '#/views/mp/components/wx-reply'; import WxReplySelect from '#/views/mp/modules/wx-reply';
import menuOptions from '../components/menuOptions'; import menuOptions from './menuOptions';
const props = defineProps<{ const props = defineProps<{
accountId: number; accountId: number;
@@ -51,7 +51,8 @@ watch(menu, () => {
}); });
// ======================== 菜单编辑(素材选择) ======================== // ======================== 菜单编辑(素材选择) ========================
const selectMaterial = (item: any) => { /** 选择素材 */
function selectMaterial(item: any) {
const articleId = item.articleId; const articleId = item.articleId;
const articles = item.content.newsItem; const articles = item.content.newsItem;
// 提示,针对多图文 // 提示,针对多图文
@@ -71,12 +72,13 @@ const selectMaterial = (item: any) => {
url: article.url, url: article.url,
}); });
}); });
}; }
const deleteMaterial = () => { /** 删除素材 */
function deleteMaterial() {
delete menu.value.articleId; delete menu.value.articleId;
delete menu.value.replyArticles; delete menu.value.replyArticles;
}; }
</script> </script>
<template> <template>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Menu } from '../components/types'; import type { Menu } from './types';
import { computed } from 'vue'; import { computed } from 'vue';
@@ -25,8 +25,8 @@ const menuList = computed<Menu[]>({
set: (val) => emit('update:modelValue', val), set: (val) => emit('update:modelValue', val),
}); });
// 添加横向一级菜单 /** 添加横向一级菜单 */
const addMenu = () => { function addMenu() {
const index = menuList.value.length; const index = menuList.value.length;
const menu = { const menu = {
name: '菜单名称', name: '菜单名称',
@@ -39,10 +39,10 @@ const addMenu = () => {
}; };
menuList.value[index] = menu; menuList.value[index] = menu;
menuClicked(menu, index - 1); menuClicked(menu, index - 1);
}; }
// 添加横向二级菜单parent 表示要操作的父菜单 /** 添加横向二级菜单parent 表示要操作的父菜单 */
const addSubMenu = (i: number, parent: any) => { function addSubMenu(i: number, parent: any) {
const subMenuKeyLength = parent.children.length; // 获取二级菜单key长度 const subMenuKeyLength = parent.children.length; // 获取二级菜单key长度
const addButton = { const addButton = {
name: '子菜单名称', name: '子菜单名称',
@@ -54,15 +54,17 @@ const addSubMenu = (i: number, parent: any) => {
}; };
parent.children[subMenuKeyLength] = addButton; parent.children[subMenuKeyLength] = addButton;
subMenuClicked(parent.children[subMenuKeyLength], i, subMenuKeyLength); subMenuClicked(parent.children[subMenuKeyLength], i, subMenuKeyLength);
}; }
const menuClicked = (parent: Menu, x: number) => { /** 一级菜单点击 */
function menuClicked(parent: Menu, x: number) {
emit('menuClicked', parent, x); emit('menuClicked', parent, x);
}; }
const subMenuClicked = (child: Menu, x: number, y: number) => { /** 二级菜单点击 */
function subMenuClicked(child: Menu, x: number, y: number) {
emit('submenuClicked', child, x, y); emit('submenuClicked', child, x, y);
}; }
/** /**
* 处理一级菜单展开后被拖动,激活(展开)原来活动的一级菜单 * 处理一级菜单展开后被拖动,激活(展开)原来活动的一级菜单
@@ -71,13 +73,13 @@ const subMenuClicked = (child: Menu, x: number, y: number) => {
* @param options.oldIndex - 一级菜单拖动前的位置 * @param options.oldIndex - 一级菜单拖动前的位置
* @param options.newIndex - 一级菜单拖动后的位置 * @param options.newIndex - 一级菜单拖动后的位置
*/ */
const onParentDragEnd = ({ function onParentDragEnd({
oldIndex, oldIndex,
newIndex, newIndex,
}: { }: {
newIndex: number; newIndex: number;
oldIndex: number; oldIndex: number;
}) => { }) {
// 二级菜单没有展开,直接返回 // 二级菜单没有展开,直接返回
if (props.activeIndex === '__MENU_NOT_SELECTED__') { if (props.activeIndex === '__MENU_NOT_SELECTED__') {
return; return;
@@ -95,7 +97,7 @@ const onParentDragEnd = ({
if (parent && newParentIndex !== -1) { if (parent && newParentIndex !== -1) {
emit('menuClicked', parent, newParentIndex); emit('menuClicked', parent, newParentIndex);
} }
}; }
/** /**
* 处理二级菜单展开后被拖动,激活被拖动的菜单 * 处理二级菜单展开后被拖动,激活被拖动的菜单
@@ -103,7 +105,7 @@ const onParentDragEnd = ({
* @param options - 拖动参数对象 * @param options - 拖动参数对象
* @param options.newIndex - 二级菜单拖动后的位置 * @param options.newIndex - 二级菜单拖动后的位置
*/ */
const onChildDragEnd = ({ newIndex }: { newIndex: number }) => { function onChildDragEnd({ newIndex }: { newIndex: number }) {
const x = props.parentIndex; const x = props.parentIndex;
const y = newIndex; const y = newIndex;
const children = menuList.value[x]?.children; const children = menuList.value[x]?.children;
@@ -113,7 +115,7 @@ const onChildDragEnd = ({ newIndex }: { newIndex: number }) => {
emit('submenuClicked', child, x, y); emit('submenuClicked', child, x, y);
} }
} }
}; }
</script> </script>
<template> <template>

View File

@@ -26,7 +26,8 @@ const account: MpAccountApi.AccountSimple = reactive({
const accountList = ref<MpAccountApi.AccountSimple[]>([]); const accountList = ref<MpAccountApi.AccountSimple[]>([]);
const handleQuery = async () => { /** 查询公众号列表 */
async function handleQuery() {
accountList.value = await getSimpleAccountList(); accountList.value = await getSimpleAccountList();
if (accountList.value.length === 0) { if (accountList.value.length === 0) {
message.error('未配置公众号,请在【公众号管理 -> 账号管理】菜单,进行配置'); message.error('未配置公众号,请在【公众号管理 -> 账号管理】菜单,进行配置');
@@ -43,9 +44,10 @@ const handleQuery = async () => {
emit('change', account.id, account.name); emit('change', account.id, account.name);
} }
} }
}; }
const onChanged = (id?: number) => { /** 公众号变化 */
function onChanged(id?: number) {
const found = accountList.value.find( const found = accountList.value.find(
(v: MpAccountApi.AccountSimple) => v.id === id, (v: MpAccountApi.AccountSimple) => v.id === id,
); );
@@ -53,7 +55,7 @@ const onChanged = (id?: number) => {
account.name = found.name; account.name = found.name;
emit('change', account.id, account.name); emit('change', account.id, account.name);
} }
}; }
/** 初始化 */ /** 初始化 */
onMounted(() => { onMounted(() => {

View File

@@ -12,9 +12,9 @@ import { formatTime } from '@vben/utils';
import * as MpDraftApi from '#/api/mp/draft'; import * as MpDraftApi from '#/api/mp/draft';
import * as MpFreePublishApi from '#/api/mp/freePublish'; import * as MpFreePublishApi from '#/api/mp/freePublish';
import * as MpMaterialApi from '#/api/mp/material'; import * as MpMaterialApi from '#/api/mp/material';
import WxNews from '#/views/mp/components/wx-news'; import WxNews from '#/views/mp/modules/wx-news';
import WxVideoPlayer from '#/views/mp/components/wx-video-play'; import WxVideoPlayer from '#/views/mp/modules/wx-video-play';
import WxVoicePlayer from '#/views/mp/components/wx-voice-play'; import WxVoicePlayer from '#/views/mp/modules/wx-voice-play';
import { NewsType } from './types'; import { NewsType } from './types';
@@ -46,11 +46,13 @@ const queryParams = reactive({
accountId: props.accountId, accountId: props.accountId,
}); });
const selectMaterialFun = (item: any) => { /** 选择素材 */
function selectMaterialFun(item: any) {
emit('selectMaterial', item); emit('selectMaterial', item);
}; }
const getPage = async () => { /** 获取分页数据 */
async function getPage() {
loading.value = true; loading.value = true;
try { try {
if (props.type === 'news' && props.newsType === NewsType.Published) { if (props.type === 'news' && props.newsType === NewsType.Published) {
@@ -66,18 +68,20 @@ const getPage = async () => {
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; }
const getMaterialPageFun = async () => { /** 获取素材分页 */
async function getMaterialPageFun() {
const data = await MpMaterialApi.getMaterialPage({ const data = await MpMaterialApi.getMaterialPage({
...queryParams, ...queryParams,
type: props.type, type: props.type,
}); });
list.value = data.list; list.value = data.list;
total.value = data.total; total.value = data.total;
}; }
const getFreePublishPageFun = async () => { /** 获取已发布图文分页 */
async function getFreePublishPageFun() {
const data = await MpFreePublishApi.getFreePublishPage(queryParams); const data = await MpFreePublishApi.getFreePublishPage(queryParams);
data.list.forEach((item: any) => { data.list.forEach((item: any) => {
const articles = item.content.newsItem; const articles = item.content.newsItem;
@@ -87,9 +91,10 @@ const getFreePublishPageFun = async () => {
}); });
list.value = data.list; list.value = data.list;
total.value = data.total; total.value = data.total;
}; }
const getDraftPageFun = async () => { /** 获取草稿图文分页 */
async function getDraftPageFun() {
const data = await MpDraftApi.getDraftPage(queryParams); const data = await MpDraftApi.getDraftPage(queryParams);
data.list.forEach((draft: any) => { data.list.forEach((draft: any) => {
const articles = draft.content.newsItem; const articles = draft.content.newsItem;
@@ -99,7 +104,7 @@ const getDraftPageFun = async () => {
}); });
list.value = data.list; list.value = data.list;
total.value = data.total; total.value = data.total;
}; }
onMounted(async () => { onMounted(async () => {
getPage(); getPage();

View File

@@ -1,11 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import WxLocation from '#/views/mp/components/wx-location'; import WxLocation from '#/views/mp/modules/wx-location';
import WxMusic from '#/views/mp/components/wx-music'; import WxMusic from '#/views/mp/modules/wx-music';
import WxNews from '#/views/mp/components/wx-news'; import WxNews from '#/views/mp/modules/wx-news';
import WxVideoPlayer from '#/views/mp/components/wx-video-play'; import WxVideoPlayer from '#/views/mp/modules/wx-video-play';
import WxVoicePlayer from '#/views/mp/components/wx-voice-play'; import WxVoicePlayer from '#/views/mp/modules/wx-voice-play';
import { MsgType } from '../types'; import { MsgType } from '../types';
import MsgEvent from './MsgEvent.vue'; import MsgEvent from './MsgEvent.vue';

View File

@@ -8,7 +8,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { User } from './types'; import type { User } from './types';
import type { Reply } from '#/views/mp/components/wx-reply'; import type { Reply } from '#/views/mp/modules/wx-reply';
import { nextTick, onMounted, reactive, ref, unref } from 'vue'; import { nextTick, onMounted, reactive, ref, unref } from 'vue';
@@ -17,7 +17,7 @@ import { ElMessage } from 'element-plus';
import { getMessagePage, sendMessage } from '#/api/mp/message'; import { getMessagePage, sendMessage } from '#/api/mp/message';
import { getUser } from '#/api/mp/user'; import { getUser } from '#/api/mp/user';
import profile from '#/assets/imgs/profile.jpg'; import profile from '#/assets/imgs/profile.jpg';
import WxReplySelect, { ReplyType } from '#/views/mp/components/wx-reply'; import WxReplySelect, { ReplyType } from '#/views/mp/modules/wx-reply';
import MsgList from './components/MsgList.vue'; import MsgList from './components/MsgList.vue';
@@ -76,8 +76,8 @@ onMounted(async () => {
refreshChange(); refreshChange();
}); });
// /** 执行发送 */
const sendMsg = async () => { async function sendMsg() {
if (!unref(reply)) { if (!unref(reply)) {
return; return;
} }
@@ -104,14 +104,16 @@ const sendMsg = async () => {
// //
replySelectRef.value?.clear(); replySelectRef.value?.clear();
}; }
const loadMore = () => { /** 加载更多 */
function loadMore() {
queryParams.pageNo++; queryParams.pageNo++;
getPage(queryParams, null); getPage(queryParams, null);
}; }
const getPage = async (page: any, params: any = null) => { /** 获取分页数据 */
async function getPage(page: any, params: any = null) {
loading.value = true; loading.value = true;
const dataTemp = await getMessagePage( const dataTemp = await getMessagePage(
Object.assign( Object.assign(
@@ -147,19 +149,20 @@ const getPage = async (page: any, params: any = null) => {
msgDivRef.value.scrollHeight - scrollHeight - 100; msgDivRef.value.scrollHeight - scrollHeight - 100;
} }
} }
}; }
const refreshChange = () => { /** 刷新消息 */
function refreshChange() {
getPage(queryParams); getPage(queryParams);
}; }
/** 定位到消息底部 */ /** 定位到消息底部 */
const scrollToBottom = async () => { async function scrollToBottom() {
await nextTick(); await nextTick();
if (msgDivRef.value) { if (msgDivRef.value) {
msgDivRef.value.scrollTop = msgDivRef.value.scrollHeight; msgDivRef.value.scrollTop = msgDivRef.value.scrollHeight;
} }
}; }
</script> </script>
<template> <template>

View File

@@ -5,15 +5,20 @@ import type { Reply } from './types';
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
// import { getAccessToken } from '#/utils/auth'; import { IconifyIcon } from '@vben/icons';
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import { ElMessage } from 'element-plus'; import {
ElButton,
ElCol,
ElDialog,
ElMessage,
ElRow,
ElUpload,
} from 'element-plus';
// import WxMaterialSelect from '#/views/mp/components/wx-material-select';
import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload'; import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload';
import WxMaterialSelect from '#/views/mp/modules/wx-material-select';
//
const props = defineProps<{ const props = defineProps<{
modelValue: Reply; modelValue: Reply;
@@ -41,10 +46,13 @@ const uploadData = reactive({
introduction: '', introduction: '',
}); });
const beforeImageUpload = (rawFile: UploadRawFile) => /** 图片上传前校验 */
useBeforeUpload(UploadType.Image, 2)(rawFile); function beforeImageUpload(rawFile: UploadRawFile) {
return useBeforeUpload(UploadType.Image, 2)(rawFile);
}
const onUploadSuccess = (res: any) => { /** 上传成功 */
function onUploadSuccess(res: any) {
if (res.code !== 0) { if (res.code !== 0) {
message.error(`上传出错:${res.msg}`); message.error(`上传出错:${res.msg}`);
return false; return false;
@@ -57,22 +65,24 @@ const onUploadSuccess = (res: any) => {
// //
selectMaterial(res.data); selectMaterial(res.data);
}; }
const onDelete = () => { /** 删除图片 */
function onDelete() {
reply.value.mediaId = null; reply.value.mediaId = null;
reply.value.url = null; reply.value.url = null;
reply.value.name = null; reply.value.name = null;
}; }
const selectMaterial = (item: any) => { /** 选择素材 */
function selectMaterial(item: any) {
showDialog.value = false; showDialog.value = false;
// reply.value.type = 'image' // reply.value.type = 'image'
reply.value.mediaId = item.mediaId; reply.value.mediaId = item.mediaId;
reply.value.url = item.url; reply.value.url = item.url;
reply.value.name = item.name; reply.value.name = item.name;
}; }
</script> </script>
<template> <template>
@@ -81,20 +91,20 @@ const selectMaterial = (item: any) => {
<div class="select-item" v-if="reply.url"> <div class="select-item" v-if="reply.url">
<img class="material-img" :src="reply.url" /> <img class="material-img" :src="reply.url" />
<p class="item-name" v-if="reply.name">{{ reply.name }}</p> <p class="item-name" v-if="reply.name">{{ reply.name }}</p>
<el-row class="ope-row" justify="center"> <ElRow class="ope-row" justify="center">
<el-button type="danger" circle @click="onDelete"> <ElButton type="danger" circle @click="onDelete">
<Icon icon="ep:delete" /> <IconifyIcon icon="ep:delete" />
</el-button> </ElButton>
</el-row> </ElRow>
</div> </div>
<!-- 情况二未做完上述操作 --> <!-- 情况二未做完上述操作 -->
<el-row v-else style="text-align: center" align="middle"> <ElRow v-else style="text-align: center" align="middle">
<!-- 选择素材 --> <!-- 选择素材 -->
<el-col :span="12" class="col-select"> <ElCol :span="12" class="col-select">
<el-button type="success" @click="showDialog = true"> <ElButton type="success" @click="showDialog = true">
素材库选择 <Icon icon="ep:circle-check" /> 素材库选择 <IconifyIcon icon="ep:circle-check" />
</el-button> </ElButton>
<el-dialog <ElDialog
title="选择图片" title="选择图片"
v-model="showDialog" v-model="showDialog"
width="90%" width="90%"
@@ -106,11 +116,11 @@ const selectMaterial = (item: any) => {
:account-id="reply.accountId" :account-id="reply.accountId"
@select-material="selectMaterial" @select-material="selectMaterial"
/> />
</el-dialog> </ElDialog>
</el-col> </ElCol>
<!-- 文件上传 --> <!-- 文件上传 -->
<el-col :span="12" class="col-add"> <ElCol :span="12" class="col-add">
<el-upload <ElUpload
:action="UPLOAD_URL" :action="UPLOAD_URL"
:headers="HEADERS" :headers="HEADERS"
multiple multiple
@@ -120,7 +130,7 @@ const selectMaterial = (item: any) => {
:before-upload="beforeImageUpload" :before-upload="beforeImageUpload"
:on-success="onUploadSuccess" :on-success="onUploadSuccess"
> >
<el-button type="primary">上传图片</el-button> <ElButton type="primary">上传图片</ElButton>
<template #tip> <template #tip>
<span> <span>
<div class="el-upload__tip"> <div class="el-upload__tip">
@@ -128,9 +138,9 @@ const selectMaterial = (item: any) => {
</div> </div>
</span> </span>
</template> </template>
</el-upload> </ElUpload>
</el-col> </ElCol>
</el-row> </ElRow>
</div> </div>
</template> </template>

View File

@@ -5,13 +5,22 @@ import type { Reply } from './types';
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import { ElMessage } from 'element-plus'; import {
ElButton,
ElCol,
ElDialog,
ElInput,
ElMessage,
ElRow,
ElUpload,
} from 'element-plus';
// import { getAccessToken } from '@/utils/auth'
import WxMaterialSelect from '#/views/mp/components/wx-material-select';
import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload'; import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload';
// import { getAccessToken } from '@/utils/auth'
import WxMaterialSelect from '#/views/mp/modules/wx-material-select';
// //
@@ -41,10 +50,13 @@ const uploadData = reactive({
introduction: '', introduction: '',
}); });
const beforeImageUpload = (rawFile: UploadRawFile) => /** 图片上传前校验 */
useBeforeUpload(UploadType.Image, 2)(rawFile); function beforeImageUpload(rawFile: UploadRawFile) {
return useBeforeUpload(UploadType.Image, 2)(rawFile);
}
const onUploadSuccess = (res: any) => { /** 上传成功 */
function onUploadSuccess(res: any) {
if (res.code !== 0) { if (res.code !== 0) {
message.error(`上传出错:${res.msg}`); message.error(`上传出错:${res.msg}`);
return false; return false;
@@ -57,33 +69,34 @@ const onUploadSuccess = (res: any) => {
// //
selectMaterial(res.data); selectMaterial(res.data);
}; }
const selectMaterial = (item: any) => { /** 选择素材 */
function selectMaterial(item: any) {
showDialog.value = false; showDialog.value = false;
reply.value.thumbMediaId = item.mediaId; reply.value.thumbMediaId = item.mediaId;
reply.value.thumbMediaUrl = item.url; reply.value.thumbMediaUrl = item.url;
}; }
</script> </script>
<template> <template>
<div> <div>
<el-row align="middle" justify="center"> <ElRow align="middle" justify="center">
<el-col :span="6"> <ElCol :span="6">
<el-row align="middle" justify="center" class="thumb-div"> <ElRow align="middle" justify="center" class="thumb-div">
<el-col :span="24"> <ElCol :span="24">
<el-row align="middle" justify="center"> <ElRow align="middle" justify="center">
<img <img
style="width: 100px" style="width: 100px"
v-if="reply.thumbMediaUrl" v-if="reply.thumbMediaUrl"
:src="reply.thumbMediaUrl" :src="reply.thumbMediaUrl"
/> />
<icon v-else icon="ep:plus" /> <IconifyIcon v-else icon="ep:plus" />
</el-row> </ElRow>
<el-row align="middle" justify="center" style="margin-top: 2%"> <ElRow align="middle" justify="center" style="margin-top: 2%">
<div class="thumb-but"> <div class="thumb-but">
<el-upload <ElUpload
:action="UPLOAD_URL" :action="UPLOAD_URL"
:headers="HEADERS" :headers="HEADERS"
multiple multiple
@@ -94,22 +107,22 @@ const selectMaterial = (item: any) => {
:on-success="onUploadSuccess" :on-success="onUploadSuccess"
> >
<template #trigger> <template #trigger>
<el-button type="primary" link>本地上传</el-button> <ElButton type="primary" link>本地上传</ElButton>
</template> </template>
<el-button <ElButton
type="primary" type="primary"
link link
@click="showDialog = true" @click="showDialog = true"
style="margin-left: 5px" style="margin-left: 5px"
> >
素材库选择 素材库选择
</el-button> </ElButton>
</el-upload> </ElUpload>
</div> </div>
</el-row> </ElRow>
</el-col> </ElCol>
</el-row> </ElRow>
<el-dialog <ElDialog
title="选择图片" title="选择图片"
v-model="showDialog" v-model="showDialog"
width="80%" width="80%"
@@ -121,17 +134,17 @@ const selectMaterial = (item: any) => {
:account-id="reply.accountId" :account-id="reply.accountId"
@select-material="selectMaterial" @select-material="selectMaterial"
/> />
</el-dialog> </ElDialog>
</el-col> </ElCol>
<el-col :span="18"> <ElCol :span="18">
<el-input v-model="reply.title" placeholder="请输入标题" /> <ElInput v-model="reply.title" placeholder="请输入标题" />
<div style="margin: 20px 0"></div> <div style="margin: 20px 0"></div>
<el-input v-model="reply.description" placeholder="请输入描述" /> <ElInput v-model="reply.description" placeholder="请输入描述" />
</el-col> </ElCol>
</el-row> </ElRow>
<div style="margin: 20px 0"></div> <div style="margin: 20px 0"></div>
<el-input v-model="reply.musicUrl" placeholder="请输入音乐链接" /> <ElInput v-model="reply.musicUrl" placeholder="请输入音乐链接" />
<div style="margin: 20px 0"></div> <div style="margin: 20px 0"></div>
<el-input v-model="reply.hqMusicUrl" placeholder="请输入高质量音乐链接" /> <ElInput v-model="reply.hqMusicUrl" placeholder="请输入高质量音乐链接" />
</div> </div>
</template> </template>

View File

@@ -1,10 +1,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { NewsType, Reply } from './types'; import type { Reply } from './types';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import WxMaterialSelect from '#/views/mp/components/wx-material-select'; import { IconifyIcon } from '@vben/icons';
import WxNews from '#/views/mp/components/wx-news';
import { ElButton, ElCol, ElDialog, ElRow } from 'element-plus';
import WxMaterialSelect from '#/views/mp/modules/wx-material-select';
import WxNews from '#/views/mp/modules/wx-news';
import { NewsType } from './types';
const props = defineProps<{ const props = defineProps<{
modelValue: Reply; modelValue: Reply;
@@ -20,46 +26,48 @@ const reply = computed<Reply>({
const showDialog = ref(false); const showDialog = ref(false);
const selectMaterial = (item: any) => { /** 选择素材 */
function selectMaterial(item: any) {
showDialog.value = false; showDialog.value = false;
reply.value.articles = item.content.newsItem; reply.value.articles = item.content.newsItem;
}; }
const onDelete = () => { /** 删除图文 */
function onDelete() {
reply.value.articles = []; reply.value.articles = [];
}; }
</script> </script>
<template> <template>
<div> <div>
<el-row> <ElRow>
<div <div
class="select-item" class="select-item"
v-if="reply.articles && reply.articles.length > 0" v-if="reply.articles && reply.articles.length > 0"
> >
<WxNews :articles="reply.articles" /> <WxNews :articles="reply.articles" />
<el-col class="ope-row"> <ElCol class="ope-row">
<el-button type="danger" circle @click="onDelete"> <ElButton type="danger" circle @click="onDelete">
<Icon icon="ep:delete" /> <IconifyIcon icon="ep:delete" />
</el-button> </ElButton>
</el-col> </ElCol>
</div> </div>
<!-- 选择素材 --> <!-- 选择素材 -->
<el-col :span="24" v-if="!reply.content"> <ElCol :span="24" v-if="!reply.content">
<el-row style="text-align: center" align="middle"> <ElRow style="text-align: center" align="middle">
<el-col :span="24"> <ElCol :span="24">
<el-button type="success" @click="showDialog = true"> <ElButton type="success" @click="showDialog = true">
{{ {{
newsType === NewsType.Published newsType === NewsType.Published
? '选择已发布图文' ? '选择已发布图文'
: '选择草稿箱图文' : '选择草稿箱图文'
}} }}
<Icon icon="ep:circle-check" /> <IconifyIcon icon="ep:circle-check" />
</el-button> </ElButton>
</el-col> </ElCol>
</el-row> </ElRow>
</el-col> </ElCol>
<el-dialog <ElDialog
title="选择图文" title="选择图文"
v-model="showDialog" v-model="showDialog"
width="90%" width="90%"
@@ -72,8 +80,8 @@ const onDelete = () => {
:news-type="newsType" :news-type="newsType"
@select-material="selectMaterial" @select-material="selectMaterial"
/> />
</el-dialog> </ElDialog>
</el-row> </ElRow>
</div> </div>
</template> </template>

View File

@@ -1,6 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed } from 'vue';
import { ElInput } from 'element-plus';
const props = defineProps<{ const props = defineProps<{
modelValue?: null | string; modelValue?: null | string;
}>(); }>();
@@ -20,7 +22,7 @@ const content = computed({
</script> </script>
<template> <template>
<el-input <ElInput
type="textarea" type="textarea"
:rows="5" :rows="5"
placeholder="请输入内容" placeholder="请输入内容"

View File

@@ -5,13 +5,22 @@ import type { Reply } from './types';
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import { ElMessage } from 'element-plus'; import {
ElButton,
ElCol,
ElDialog,
ElInput,
ElMessage,
ElRow,
ElUpload,
} from 'element-plus';
import WxMaterialSelect from '#/views/mp/components/wx-material-select';
import WxVideoPlayer from '#/views/mp/components/wx-video-play';
import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload'; import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload';
import WxMaterialSelect from '#/views/mp/modules/wx-material-select';
import WxVideoPlayer from '#/views/mp/modules/wx-video-play';
const props = defineProps<{ const props = defineProps<{
modelValue: Reply; modelValue: Reply;
@@ -40,10 +49,13 @@ const uploadData = reactive({
introduction: '', introduction: '',
}); });
const beforeVideoUpload = (rawFile: UploadRawFile) => /** 视频上传前校验 */
useBeforeUpload(UploadType.Video, 10)(rawFile); function beforeVideoUpload(rawFile: UploadRawFile) {
return useBeforeUpload(UploadType.Video, 10)(rawFile);
}
const onUploadSuccess = (res: any) => { /** 上传成功 */
function onUploadSuccess(res: any) {
if (res.code !== 0) { if (res.code !== 0) {
message.error(`上传出错:${res.msg}`); message.error(`上传出错:${res.msg}`);
return false; return false;
@@ -55,10 +67,10 @@ const onUploadSuccess = (res: any) => {
uploadData.introduction = ''; uploadData.introduction = '';
selectMaterial(res.data); selectMaterial(res.data);
}; }
/** 选择素材后设置 */ /** 选择素材后设置 */
const selectMaterial = (item: any) => { function selectMaterial(item: any) {
showDialog.value = false; showDialog.value = false;
reply.value.mediaId = item.mediaId; reply.value.mediaId = item.mediaId;
@@ -72,33 +84,33 @@ const selectMaterial = (item: any) => {
if (item.introduction) { if (item.introduction) {
reply.value.description = item.introduction || ''; reply.value.description = item.introduction || '';
} }
}; }
</script> </script>
<template> <template>
<div> <div>
<el-row> <ElRow>
<el-input <ElInput
v-model="reply.title" v-model="reply.title"
class="input-margin-bottom" class="input-margin-bottom"
placeholder="请输入标题" placeholder="请输入标题"
/> />
<el-input <ElInput
class="input-margin-bottom" class="input-margin-bottom"
v-model="reply.description" v-model="reply.description"
placeholder="请输入描述" placeholder="请输入描述"
/> />
<el-row class="ope-row" justify="center"> <ElRow class="ope-row" justify="center">
<WxVideoPlayer v-if="reply.url" :url="reply.url" /> <WxVideoPlayer v-if="reply.url" :url="reply.url" />
</el-row> </ElRow>
<el-col> <ElCol>
<el-row style="text-align: center" align="middle"> <ElRow style="text-align: center" align="middle">
<!-- 选择素材 --> <!-- 选择素材 -->
<el-col :span="12"> <ElCol :span="12">
<el-button type="success" @click="showDialog = true"> <ElButton type="success" @click="showDialog = true">
素材库选择 <Icon icon="ep:circle-check" /> 素材库选择 <IconifyIcon icon="ep:circle-check" />
</el-button> </ElButton>
<el-dialog <ElDialog
title="选择视频" title="选择视频"
v-model="showDialog" v-model="showDialog"
width="90%" width="90%"
@@ -110,11 +122,11 @@ const selectMaterial = (item: any) => {
:account-id="reply.accountId" :account-id="reply.accountId"
@select-material="selectMaterial" @select-material="selectMaterial"
/> />
</el-dialog> </ElDialog>
</el-col> </ElCol>
<!-- 文件上传 --> <!-- 文件上传 -->
<el-col :span="12"> <ElCol :span="12">
<el-upload <ElUpload
:action="UPLOAD_URL" :action="UPLOAD_URL"
:headers="HEADERS" :headers="HEADERS"
multiple multiple
@@ -124,14 +136,14 @@ const selectMaterial = (item: any) => {
:before-upload="beforeVideoUpload" :before-upload="beforeVideoUpload"
:on-success="onUploadSuccess" :on-success="onUploadSuccess"
> >
<el-button type="primary"> <ElButton type="primary">
新建视频 <Icon icon="ep:upload" /> 新建视频 <IconifyIcon icon="ep:upload" />
</el-button> </ElButton>
</el-upload> </ElUpload>
</el-col> </ElCol>
</el-row> </ElRow>
</el-col> </ElCol>
</el-row> </ElRow>
</div> </div>
</template> </template>

View File

@@ -5,13 +5,21 @@ import type { Reply } from './types';
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import { ElMessage } from 'element-plus'; import {
ElButton,
ElCol,
ElDialog,
ElMessage,
ElRow,
ElUpload,
} from 'element-plus';
import WxMaterialSelect from '#/views/mp/components/wx-material-select';
import WxVoicePlayer from '#/views/mp/components/wx-voice-play';
import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload'; import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload';
import WxMaterialSelect from '#/views/mp/modules/wx-material-select';
import WxVoicePlayer from '#/views/mp/modules/wx-voice-play';
// //
@@ -41,10 +49,13 @@ const uploadData = reactive({
introduction: '', introduction: '',
}); });
const beforeVoiceUpload = (rawFile: UploadRawFile) => /** 语音上传前校验 */
useBeforeUpload(UploadType.Voice, 10)(rawFile); function beforeVoiceUpload(rawFile: UploadRawFile) {
return useBeforeUpload(UploadType.Voice, 10)(rawFile);
}
const onUploadSuccess = (res: any) => { /** 上传成功 */
function onUploadSuccess(res: any) {
if (res.code !== 0) { if (res.code !== 0) {
message.error(`上传出错:${res.msg}`); message.error(`上传出错:${res.msg}`);
return false; return false;
@@ -57,43 +68,45 @@ const onUploadSuccess = (res: any) => {
// //
selectMaterial(res.data); selectMaterial(res.data);
}; }
const onDelete = () => { /** 删除语音 */
function onDelete() {
reply.value.mediaId = null; reply.value.mediaId = null;
reply.value.url = null; reply.value.url = null;
reply.value.name = null; reply.value.name = null;
}; }
const selectMaterial = (item: Reply) => { /** 选择素材 */
function selectMaterial(item: Reply) {
showDialog.value = false; showDialog.value = false;
// reply.value.type = ReplyType.Voice // reply.value.type = ReplyType.Voice
reply.value.mediaId = item.mediaId; reply.value.mediaId = item.mediaId;
reply.value.url = item.url; reply.value.url = item.url;
reply.value.name = item.name; reply.value.name = item.name;
}; }
</script> </script>
<template> <template>
<div> <div>
<div class="select-item2" v-if="reply.url"> <div class="select-item2" v-if="reply.url">
<p class="item-name">{{ reply.name }}</p> <p class="item-name">{{ reply.name }}</p>
<el-row class="ope-row" justify="center"> <ElRow class="ope-row" justify="center">
<WxVoicePlayer :url="reply.url" /> <WxVoicePlayer :url="reply.url" />
</el-row> </ElRow>
<el-row class="ope-row" justify="center"> <ElRow class="ope-row" justify="center">
<el-button type="danger" circle @click="onDelete"> <ElButton type="danger" circle @click="onDelete">
<Icon icon="ep:delete" /> <IconifyIcon icon="ep:delete" />
</el-button> </ElButton>
</el-row> </ElRow>
</div> </div>
<el-row v-else style="text-align: center"> <ElRow v-else style="text-align: center">
<!-- 选择素材 --> <!-- 选择素材 -->
<el-col :span="12" class="col-select"> <ElCol :span="12" class="col-select">
<el-button type="success" @click="showDialog = true"> <ElButton type="success" @click="showDialog = true">
素材库选择<Icon icon="ep:circle-check" /> 素材库选择<IconifyIcon icon="ep:circle-check" />
</el-button> </ElButton>
<el-dialog <ElDialog
title="选择语音" title="选择语音"
v-model="showDialog" v-model="showDialog"
width="90%" width="90%"
@@ -105,11 +118,11 @@ const selectMaterial = (item: Reply) => {
:account-id="reply.accountId" :account-id="reply.accountId"
@select-material="selectMaterial" @select-material="selectMaterial"
/> />
</el-dialog> </ElDialog>
</el-col> </ElCol>
<!-- 文件上传 --> <!-- 文件上传 -->
<el-col :span="12" class="col-add"> <ElCol :span="12" class="col-add">
<el-upload <ElUpload
:action="UPLOAD_URL" :action="UPLOAD_URL"
:headers="HEADERS" :headers="HEADERS"
multiple multiple
@@ -119,15 +132,15 @@ const selectMaterial = (item: Reply) => {
:before-upload="beforeVoiceUpload" :before-upload="beforeVoiceUpload"
:on-success="onUploadSuccess" :on-success="onUploadSuccess"
> >
<el-button type="primary">点击上传</el-button> <ElButton type="primary">点击上传</ElButton>
<template #tip> <template #tip>
<div class="el-upload__tip"> <div class="el-upload__tip">
格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s 格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s
</div> </div>
</template> </template>
</el-upload> </ElUpload>
</el-col> </ElCol>
</el-row> </ElRow>
</div> </div>
</template> </template>

View File

@@ -12,6 +12,10 @@ import type { Reply } from './components/types';
import { computed, ref, unref, watch } from 'vue'; import { computed, ref, unref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { ElRow, ElTabPane, ElTabs } from 'element-plus';
import TabImage from './components/TabImage.vue'; import TabImage from './components/TabImage.vue';
import TabMusic from './components/TabMusic.vue'; import TabMusic from './components/TabMusic.vue';
import TabNews from './components/TabNews.vue'; import TabNews from './components/TabNews.vue';
@@ -70,9 +74,9 @@ watch(
); );
/** 清除除了`type`, `accountId`的字段 */ /** 清除除了`type`, `accountId`的字段 */
const clear = () => { function clear() {
reply.value = createEmptyReply(reply); reply.value = createEmptyReply(reply);
}; }
defineExpose({ defineExpose({
clear, clear,
@@ -80,57 +84,57 @@ defineExpose({
</script> </script>
<template> <template>
<el-tabs type="border-card" v-model="currentTab"> <ElTabs type="border-card" v-model="currentTab">
<!-- 类型 1文本 --> <!-- 类型 1文本 -->
<el-tab-pane :name="ReplyType.Text"> <ElTabPane :name="ReplyType.Text">
<template #label> <template #label>
<el-row align="middle"><Icon icon="ep:document" /> 文本</el-row> <ElRow align="middle"><IconifyIcon icon="ep:document" /> 文本</ElRow>
</template> </template>
<TabText v-model="reply.content" /> <TabText v-model="reply.content" />
</el-tab-pane> </ElTabPane>
<!-- 类型 2图片 --> <!-- 类型 2图片 -->
<el-tab-pane :name="ReplyType.Image"> <ElTabPane :name="ReplyType.Image">
<template #label> <template #label>
<el-row align="middle"> <ElRow align="middle">
<Icon icon="ep:picture" class="mr-5px" /> 图片 <IconifyIcon icon="ep:picture" class="mr-5px" /> 图片
</el-row> </ElRow>
</template> </template>
<TabImage v-model="reply" /> <TabImage v-model="reply" />
</el-tab-pane> </ElTabPane>
<!-- 类型 3语音 --> <!-- 类型 3语音 -->
<el-tab-pane :name="ReplyType.Voice"> <ElTabPane :name="ReplyType.Voice">
<template #label> <template #label>
<el-row align="middle"><Icon icon="ep:phone" /> 语音</el-row> <ElRow align="middle"><IconifyIcon icon="ep:phone" /> 语音</ElRow>
</template> </template>
<TabVoice v-model="reply" /> <TabVoice v-model="reply" />
</el-tab-pane> </ElTabPane>
<!-- 类型 4视频 --> <!-- 类型 4视频 -->
<el-tab-pane :name="ReplyType.Video"> <ElTabPane :name="ReplyType.Video">
<template #label> <template #label>
<el-row align="middle"><Icon icon="ep:share" /> 视频</el-row> <ElRow align="middle"><IconifyIcon icon="ep:share" /> 视频</ElRow>
</template> </template>
<TabVideo v-model="reply" /> <TabVideo v-model="reply" />
</el-tab-pane> </ElTabPane>
<!-- 类型 5图文 --> <!-- 类型 5图文 -->
<el-tab-pane :name="ReplyType.News"> <ElTabPane :name="ReplyType.News">
<template #label> <template #label>
<el-row align="middle"><Icon icon="ep:reading" /> 图文</el-row> <ElRow align="middle"><IconifyIcon icon="ep:reading" /> 图文</ElRow>
</template> </template>
<TabNews v-model="reply" :news-type="newsType" /> <TabNews v-model="reply" :news-type="newsType" />
</el-tab-pane> </ElTabPane>
<!-- 类型 6音乐 --> <!-- 类型 6音乐 -->
<el-tab-pane :name="ReplyType.Music"> <ElTabPane :name="ReplyType.Music">
<template #label> <template #label>
<el-row align="middle"><Icon icon="ep:service" />音乐</el-row> <ElRow align="middle"><IconifyIcon icon="ep:service" />音乐</ElRow>
</template> </template>
<TabMusic v-model="reply" /> <TabMusic v-model="reply" />
</el-tab-pane> </ElTabPane>
</el-tabs> </ElTabs>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>