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_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
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,11 @@
<script lang="ts" setup>
import { ref } from 'vue';
import WxLocation from '#/views/mp/components/wx-location';
import WxMusic from '#/views/mp/components/wx-music';
import WxNews from '#/views/mp/components/wx-news';
import WxVideoPlayer from '#/views/mp/components/wx-video-play';
import WxVoicePlayer from '#/views/mp/components/wx-voice-play';
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';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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