fix: todo修复
This commit is contained in:
3
apps/web-ele/src/views/mp/components/wx-reply/index.ts
Normal file
3
apps/web-ele/src/views/mp/components/wx-reply/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { createEmptyReply, type Reply, ReplyType } from './types';
|
||||
|
||||
export { default } from './wx-reply.vue';
|
||||
161
apps/web-ele/src/views/mp/components/wx-reply/tab-image.vue
Normal file
161
apps/web-ele/src/views/mp/components/wx-reply/tab-image.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<script lang="ts" setup>
|
||||
import type { UploadRawFile } from 'element-plus';
|
||||
|
||||
import type { Reply } from './types';
|
||||
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElCol,
|
||||
ElDialog,
|
||||
ElMessage,
|
||||
ElRow,
|
||||
ElUpload,
|
||||
} from 'element-plus';
|
||||
|
||||
import { UploadType, useBeforeUpload } from '#/utils/useUpload';
|
||||
import MaterialSelect from '#/views/mp/components/wx-material-select/wx-material-select.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Reply;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: Reply): void;
|
||||
}>();
|
||||
|
||||
const message = ElMessage;
|
||||
|
||||
const UPLOAD_URL = `${import.meta.env.VITE_BASE_URL}/admin-api/mp/material/upload-temporary`;
|
||||
const HEADERS = { Authorization: `Bearer ${useAccessStore().accessToken}` };
|
||||
const reply = computed<Reply>({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
const showDialog = ref(false);
|
||||
const fileList = ref([]);
|
||||
const uploadData = reactive({
|
||||
accountId: reply.value.accountId,
|
||||
type: 'image',
|
||||
title: '',
|
||||
introduction: '',
|
||||
});
|
||||
|
||||
/** 图片上传前校验 */
|
||||
function beforeImageUpload(rawFile: UploadRawFile) {
|
||||
return useBeforeUpload(UploadType.Image, 2)(rawFile);
|
||||
}
|
||||
|
||||
/** 上传成功 */
|
||||
function onUploadSuccess(res: any) {
|
||||
if (res.code !== 0) {
|
||||
message.error(`上传出错:${res.msg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清空上传时的各种数据
|
||||
fileList.value = [];
|
||||
uploadData.title = '';
|
||||
uploadData.introduction = '';
|
||||
|
||||
// 上传好的文件,本质是个素材,所以可以进行选中
|
||||
selectMaterial(res.data);
|
||||
}
|
||||
|
||||
/** 删除图片 */
|
||||
function onDelete() {
|
||||
reply.value.mediaId = null;
|
||||
reply.value.url = null;
|
||||
reply.value.name = null;
|
||||
}
|
||||
|
||||
/** 选择素材 */
|
||||
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>
|
||||
<div>
|
||||
<!-- 情况一:已经选择好素材、或者上传好图片 -->
|
||||
<div
|
||||
class="mx-auto mb-[10px] w-[280px] border border-[#eaeaea] p-[10px]"
|
||||
v-if="reply.url"
|
||||
>
|
||||
<img class="w-full" :src="reply.url" />
|
||||
<p
|
||||
class="overflow-hidden text-ellipsis whitespace-nowrap text-center text-xs"
|
||||
v-if="reply.name"
|
||||
>
|
||||
{{ reply.name }}
|
||||
</p>
|
||||
<ElRow class="pt-[10px] text-center" justify="center">
|
||||
<ElButton type="danger" circle @click="onDelete">
|
||||
<IconifyIcon icon="ep:delete" />
|
||||
</ElButton>
|
||||
</ElRow>
|
||||
</div>
|
||||
<!-- 情况二:未做完上述操作 -->
|
||||
<ElRow v-else class="text-center" align="middle">
|
||||
<!-- 选择素材 -->
|
||||
<ElCol
|
||||
:span="12"
|
||||
class="h-[160px] w-[49.5%] border border-[rgb(234,234,234)] py-[50px]"
|
||||
>
|
||||
<ElButton type="success" @click="showDialog = true">
|
||||
素材库选择 <IconifyIcon icon="ep:circle-check" />
|
||||
</ElButton>
|
||||
<ElDialog
|
||||
title="选择图片"
|
||||
v-model="showDialog"
|
||||
width="90%"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<MaterialSelect
|
||||
type="image"
|
||||
:account-id="reply.accountId"
|
||||
@select-material="selectMaterial"
|
||||
/>
|
||||
</ElDialog>
|
||||
</ElCol>
|
||||
<!-- 文件上传 -->
|
||||
<ElCol
|
||||
:span="12"
|
||||
class="float-right h-[160px] w-[49.5%] border border-[rgb(234,234,234)] py-[50px]"
|
||||
>
|
||||
<ElUpload
|
||||
:action="UPLOAD_URL"
|
||||
:headers="HEADERS"
|
||||
multiple
|
||||
:limit="1"
|
||||
:file-list="fileList"
|
||||
:data="uploadData"
|
||||
:before-upload="beforeImageUpload"
|
||||
:on-success="onUploadSuccess"
|
||||
>
|
||||
<ElButton type="primary">上传图片</ElButton>
|
||||
<template #tip>
|
||||
<span>
|
||||
<div class="text-center leading-[18px]">
|
||||
支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
</ElUpload>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
150
apps/web-ele/src/views/mp/components/wx-reply/tab-music.vue
Normal file
150
apps/web-ele/src/views/mp/components/wx-reply/tab-music.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<script lang="ts" setup>
|
||||
import type { UploadRawFile } from 'element-plus';
|
||||
|
||||
import type { Reply } from './types';
|
||||
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElCol,
|
||||
ElDialog,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElRow,
|
||||
ElUpload,
|
||||
} from 'element-plus';
|
||||
|
||||
import { UploadType, useBeforeUpload } from '#/utils/useUpload';
|
||||
// import { getAccessToken } from '@/utils/auth'
|
||||
import MaterialSelect from '#/views/mp/components/wx-material-select/wx-material-select.vue';
|
||||
|
||||
// 设置上传的请求头部
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Reply;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: Reply): void;
|
||||
}>();
|
||||
|
||||
const message = ElMessage;
|
||||
|
||||
const UPLOAD_URL = `${import.meta.env.VITE_BASE_URL}/admin-api/mp/material/upload-temporary`;
|
||||
const HEADERS = { Authorization: `Bearer ${useAccessStore().accessToken}` };
|
||||
const reply = computed<Reply>({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
const showDialog = ref(false);
|
||||
const fileList = ref([]);
|
||||
const uploadData = reactive({
|
||||
accountId: reply.value.accountId,
|
||||
type: 'thumb', // 音乐类型为thumb
|
||||
title: '',
|
||||
introduction: '',
|
||||
});
|
||||
|
||||
/** 图片上传前校验 */
|
||||
function beforeImageUpload(rawFile: UploadRawFile) {
|
||||
return useBeforeUpload(UploadType.Image, 2)(rawFile);
|
||||
}
|
||||
|
||||
/** 上传成功 */
|
||||
function onUploadSuccess(res: any) {
|
||||
if (res.code !== 0) {
|
||||
message.error(`上传出错:${res.msg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清空上传时的各种数据
|
||||
fileList.value = [];
|
||||
uploadData.title = '';
|
||||
uploadData.introduction = '';
|
||||
|
||||
// 上传好的文件,本质是个素材,所以可以进行选中
|
||||
selectMaterial(res.data);
|
||||
}
|
||||
|
||||
/** 选择素材 */
|
||||
function selectMaterial(item: any) {
|
||||
showDialog.value = false;
|
||||
|
||||
reply.value.thumbMediaId = item.mediaId;
|
||||
reply.value.thumbMediaUrl = item.url;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ElRow align="middle" justify="center">
|
||||
<ElCol :span="6">
|
||||
<ElRow align="middle" justify="center" class="inline-block text-center">
|
||||
<ElCol :span="24">
|
||||
<ElRow align="middle" justify="center">
|
||||
<img
|
||||
class="w-[100px]"
|
||||
v-if="reply.thumbMediaUrl"
|
||||
:src="reply.thumbMediaUrl"
|
||||
/>
|
||||
<IconifyIcon v-else icon="ep:plus" />
|
||||
</ElRow>
|
||||
<ElRow align="middle" justify="center" class="mt-[2%]">
|
||||
<div>
|
||||
<ElUpload
|
||||
:action="UPLOAD_URL"
|
||||
:headers="HEADERS"
|
||||
multiple
|
||||
:limit="1"
|
||||
:file-list="fileList"
|
||||
:data="uploadData"
|
||||
:before-upload="beforeImageUpload"
|
||||
:on-success="onUploadSuccess"
|
||||
>
|
||||
<template #trigger>
|
||||
<ElButton type="primary" link>本地上传</ElButton>
|
||||
</template>
|
||||
<ElButton
|
||||
type="primary"
|
||||
link
|
||||
@click="showDialog = true"
|
||||
class="ml-[5px]"
|
||||
>
|
||||
素材库选择
|
||||
</ElButton>
|
||||
</ElUpload>
|
||||
</div>
|
||||
</ElRow>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElDialog
|
||||
title="选择图片"
|
||||
v-model="showDialog"
|
||||
width="80%"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<MaterialSelect
|
||||
type="image"
|
||||
:account-id="reply.accountId"
|
||||
@select-material="selectMaterial"
|
||||
/>
|
||||
</ElDialog>
|
||||
</ElCol>
|
||||
<ElCol :span="18">
|
||||
<ElInput v-model="reply.title" placeholder="请输入标题" />
|
||||
<div class="my-5"></div>
|
||||
<ElInput v-model="reply.description" placeholder="请输入描述" />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<div class="my-5"></div>
|
||||
<ElInput v-model="reply.musicUrl" placeholder="请输入音乐链接" />
|
||||
<div class="my-5"></div>
|
||||
<ElInput v-model="reply.hqMusicUrl" placeholder="请输入高质量音乐链接" />
|
||||
</div>
|
||||
</template>
|
||||
88
apps/web-ele/src/views/mp/components/wx-reply/tab-news.vue
Normal file
88
apps/web-ele/src/views/mp/components/wx-reply/tab-news.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Reply } from './types';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElButton, ElCol, ElDialog, ElRow } from 'element-plus';
|
||||
|
||||
import MaterialSelect from '#/views/mp/components/wx-material-select/wx-material-select.vue';
|
||||
import News from '#/views/mp/components/wx-news/wx-news.vue';
|
||||
|
||||
import { NewsType } from '../wx-material-select/types';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Reply;
|
||||
newsType: NewsType;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: Reply): void;
|
||||
}>();
|
||||
const reply = computed<Reply>({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
const showDialog = ref(false);
|
||||
|
||||
/** 选择素材 */
|
||||
function selectMaterial(item: any) {
|
||||
showDialog.value = false;
|
||||
reply.value.articles = item.content.newsItem;
|
||||
}
|
||||
|
||||
/** 删除图文 */
|
||||
function onDelete() {
|
||||
reply.value.articles = [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ElRow>
|
||||
<div
|
||||
class="mx-auto mb-[10px] w-[280px] border border-[#eaeaea] p-[10px]"
|
||||
v-if="reply.articles && reply.articles.length > 0"
|
||||
>
|
||||
<News :articles="reply.articles" />
|
||||
<ElCol class="pt-[10px] text-center">
|
||||
<ElButton type="danger" circle @click="onDelete">
|
||||
<IconifyIcon icon="ep:delete" />
|
||||
</ElButton>
|
||||
</ElCol>
|
||||
</div>
|
||||
<!-- 选择素材 -->
|
||||
<ElCol :span="24" v-if="!reply.content">
|
||||
<ElRow class="text-center" align="middle">
|
||||
<ElCol :span="24">
|
||||
<ElButton type="success" @click="showDialog = true">
|
||||
{{
|
||||
newsType === NewsType.Published
|
||||
? '选择已发布图文'
|
||||
: '选择草稿箱图文'
|
||||
}}
|
||||
<IconifyIcon icon="ep:circle-check" />
|
||||
</ElButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElCol>
|
||||
<ElDialog
|
||||
title="选择图文"
|
||||
v-model="showDialog"
|
||||
width="90%"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<MaterialSelect
|
||||
type="news"
|
||||
:account-id="reply.accountId"
|
||||
:news-type="newsType"
|
||||
@select-material="selectMaterial"
|
||||
/>
|
||||
</ElDialog>
|
||||
</ElRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
32
apps/web-ele/src/views/mp/components/wx-reply/tab-text.vue
Normal file
32
apps/web-ele/src/views/mp/components/wx-reply/tab-text.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { ElInput } from 'element-plus';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: null | string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: null | string): void;
|
||||
(e: 'input', v: null | string): void;
|
||||
}>();
|
||||
|
||||
const content = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val: null | string) => {
|
||||
emit('update:modelValue', val);
|
||||
emit('input', val);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElInput
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="请输入内容"
|
||||
v-model="content"
|
||||
class="w-full"
|
||||
/>
|
||||
</template>
|
||||
146
apps/web-ele/src/views/mp/components/wx-reply/tab-video.vue
Normal file
146
apps/web-ele/src/views/mp/components/wx-reply/tab-video.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<script lang="ts" setup>
|
||||
import type { UploadRawFile } from 'element-plus';
|
||||
|
||||
import type { Reply } from './types';
|
||||
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElCol,
|
||||
ElDialog,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElRow,
|
||||
ElUpload,
|
||||
} from 'element-plus';
|
||||
|
||||
import { UploadType, useBeforeUpload } from '#/utils/useUpload';
|
||||
import MaterialSelect from '#/views/mp/components/wx-material-select/wx-material-select.vue';
|
||||
import VideoPlayer from '#/views/mp/components/wx-video-play/wx-video-play.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Reply;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: Reply): void;
|
||||
}>();
|
||||
|
||||
const message = ElMessage;
|
||||
|
||||
const UPLOAD_URL = `${import.meta.env.VITE_BASE_URL}/admin-api/mp/material/upload-temporary`;
|
||||
const HEADERS = { Authorization: `Bearer ${useAccessStore().accessToken}` };
|
||||
|
||||
const reply = computed<Reply>({
|
||||
get: () => props.modelValue,
|
||||
set: (val: Reply) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
const showDialog = ref(false);
|
||||
const fileList = ref([]);
|
||||
const uploadData = reactive({
|
||||
accountId: reply.value.accountId,
|
||||
type: 'video',
|
||||
title: '',
|
||||
introduction: '',
|
||||
});
|
||||
|
||||
/** 视频上传前校验 */
|
||||
function beforeVideoUpload(rawFile: UploadRawFile) {
|
||||
return useBeforeUpload(UploadType.Video, 10)(rawFile);
|
||||
}
|
||||
|
||||
/** 上传成功 */
|
||||
function onUploadSuccess(res: any) {
|
||||
if (res.code !== 0) {
|
||||
message.error(`上传出错:${res.msg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清空上传时的各种数据
|
||||
fileList.value = [];
|
||||
uploadData.title = '';
|
||||
uploadData.introduction = '';
|
||||
|
||||
selectMaterial(res.data);
|
||||
}
|
||||
|
||||
/** 选择素材后设置 */
|
||||
function selectMaterial(item: any) {
|
||||
showDialog.value = false;
|
||||
|
||||
reply.value.mediaId = item.mediaId;
|
||||
reply.value.url = item.url;
|
||||
reply.value.name = item.name;
|
||||
|
||||
// title、introduction:从 item 到 tempObjItem,因为素材里有 title、introduction
|
||||
if (item.title) {
|
||||
reply.value.title = item.title || '';
|
||||
}
|
||||
if (item.introduction) {
|
||||
reply.value.description = item.introduction || '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ElRow>
|
||||
<ElInput v-model="reply.title" class="mb-[2%]" placeholder="请输入标题" />
|
||||
<ElInput
|
||||
class="mb-[2%]"
|
||||
v-model="reply.description"
|
||||
placeholder="请输入描述"
|
||||
/>
|
||||
<ElRow class="w-full pt-[10px] text-center" justify="center">
|
||||
<VideoPlayer v-if="reply.url" :url="reply.url" />
|
||||
</ElRow>
|
||||
<ElCol>
|
||||
<ElRow class="text-center" align="middle">
|
||||
<!-- 选择素材 -->
|
||||
<ElCol :span="12">
|
||||
<ElButton type="success" @click="showDialog = true">
|
||||
素材库选择 <IconifyIcon icon="ep:circle-check" />
|
||||
</ElButton>
|
||||
<ElDialog
|
||||
title="选择视频"
|
||||
v-model="showDialog"
|
||||
width="90%"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<MaterialSelect
|
||||
type="video"
|
||||
:account-id="reply.accountId"
|
||||
@select-material="selectMaterial"
|
||||
/>
|
||||
</ElDialog>
|
||||
</ElCol>
|
||||
<!-- 文件上传 -->
|
||||
<ElCol :span="12">
|
||||
<ElUpload
|
||||
:action="UPLOAD_URL"
|
||||
:headers="HEADERS"
|
||||
multiple
|
||||
:limit="1"
|
||||
:file-list="fileList"
|
||||
:data="uploadData"
|
||||
:before-upload="beforeVideoUpload"
|
||||
:on-success="onUploadSuccess"
|
||||
>
|
||||
<ElButton type="primary">
|
||||
新建视频 <IconifyIcon icon="ep:upload" />
|
||||
</ElButton>
|
||||
</ElUpload>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
160
apps/web-ele/src/views/mp/components/wx-reply/tab-voice.vue
Normal file
160
apps/web-ele/src/views/mp/components/wx-reply/tab-voice.vue
Normal file
@@ -0,0 +1,160 @@
|
||||
<script lang="ts" setup>
|
||||
import type { UploadRawFile } from 'element-plus';
|
||||
|
||||
import type { Reply } from './types';
|
||||
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElCol,
|
||||
ElDialog,
|
||||
ElMessage,
|
||||
ElRow,
|
||||
ElUpload,
|
||||
} from 'element-plus';
|
||||
|
||||
import { UploadType, useBeforeUpload } from '#/utils/useUpload';
|
||||
import MaterialSelect from '#/views/mp/components/wx-material-select/wx-material-select.vue';
|
||||
import VoicePlayer from '#/views/mp/components/wx-voice-play/wx-voice-play.vue';
|
||||
|
||||
// 设置上传的请求头部
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Reply;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: Reply): void;
|
||||
}>();
|
||||
|
||||
const message = ElMessage;
|
||||
|
||||
const UPLOAD_URL = `${import.meta.env.VITE_BASE_URL}/admin-api/mp/material/upload-temporary`;
|
||||
const HEADERS = { Authorization: `Bearer ${useAccessStore().accessToken}` };
|
||||
const reply = computed<Reply>({
|
||||
get: () => props.modelValue,
|
||||
set: (val: Reply) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
const showDialog = ref(false);
|
||||
const fileList = ref([]);
|
||||
const uploadData = reactive({
|
||||
accountId: reply.value.accountId,
|
||||
type: 'voice',
|
||||
title: '',
|
||||
introduction: '',
|
||||
});
|
||||
|
||||
/** 语音上传前校验 */
|
||||
function beforeVoiceUpload(rawFile: UploadRawFile) {
|
||||
return useBeforeUpload(UploadType.Voice, 10)(rawFile);
|
||||
}
|
||||
|
||||
/** 上传成功 */
|
||||
function onUploadSuccess(res: any) {
|
||||
if (res.code !== 0) {
|
||||
message.error(`上传出错:${res.msg}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清空上传时的各种数据
|
||||
fileList.value = [];
|
||||
uploadData.title = '';
|
||||
uploadData.introduction = '';
|
||||
|
||||
// 上传好的文件,本质是个素材,所以可以进行选中
|
||||
selectMaterial(res.data);
|
||||
}
|
||||
|
||||
/** 删除语音 */
|
||||
function onDelete() {
|
||||
reply.value.mediaId = null;
|
||||
reply.value.url = null;
|
||||
reply.value.name = null;
|
||||
}
|
||||
|
||||
/** 选择素材 */
|
||||
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="mx-auto mb-[10px] border border-[#eaeaea] p-[10px]"
|
||||
v-if="reply.url"
|
||||
>
|
||||
<p
|
||||
class="overflow-hidden text-ellipsis whitespace-nowrap text-center text-xs"
|
||||
>
|
||||
{{ reply.name }}
|
||||
</p>
|
||||
<ElRow class="w-full pt-[10px] text-center" justify="center">
|
||||
<VoicePlayer :url="reply.url" />
|
||||
</ElRow>
|
||||
<ElRow class="w-full pt-[10px] text-center" justify="center">
|
||||
<ElButton type="danger" circle @click="onDelete">
|
||||
<IconifyIcon icon="ep:delete" />
|
||||
</ElButton>
|
||||
</ElRow>
|
||||
</div>
|
||||
<ElRow v-else class="text-center">
|
||||
<!-- 选择素材 -->
|
||||
<ElCol
|
||||
:span="12"
|
||||
class="h-[160px] w-[49.5%] border border-[rgb(234,234,234)] py-[50px]"
|
||||
>
|
||||
<ElButton type="success" @click="showDialog = true">
|
||||
素材库选择<IconifyIcon icon="ep:circle-check" />
|
||||
</ElButton>
|
||||
<ElDialog
|
||||
title="选择语音"
|
||||
v-model="showDialog"
|
||||
width="90%"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<MaterialSelect
|
||||
type="voice"
|
||||
:account-id="reply.accountId"
|
||||
@select-material="selectMaterial"
|
||||
/>
|
||||
</ElDialog>
|
||||
</ElCol>
|
||||
<!-- 文件上传 -->
|
||||
<ElCol
|
||||
:span="12"
|
||||
class="float-right h-[160px] w-[49.5%] border border-[rgb(234,234,234)] py-[50px]"
|
||||
>
|
||||
<ElUpload
|
||||
:action="UPLOAD_URL"
|
||||
:headers="HEADERS"
|
||||
multiple
|
||||
:limit="1"
|
||||
:file-list="fileList"
|
||||
:data="uploadData"
|
||||
:before-upload="beforeVoiceUpload"
|
||||
:on-success="onUploadSuccess"
|
||||
>
|
||||
<ElButton type="primary">点击上传</ElButton>
|
||||
<template #tip>
|
||||
<div class="text-center leading-[18px]">
|
||||
格式支持 mp3/wma/wav/amr,文件大小不超过 2M,播放长度不超过 60s
|
||||
</div>
|
||||
</template>
|
||||
</ElUpload>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
53
apps/web-ele/src/views/mp/components/wx-reply/types.ts
Normal file
53
apps/web-ele/src/views/mp/components/wx-reply/types.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { unref } from 'vue';
|
||||
|
||||
enum ReplyType {
|
||||
Image = 'image',
|
||||
Music = 'music',
|
||||
News = 'news',
|
||||
Text = 'text',
|
||||
Video = 'video',
|
||||
Voice = 'voice',
|
||||
}
|
||||
|
||||
interface _Reply {
|
||||
accountId: number;
|
||||
type: ReplyType;
|
||||
name?: null | string;
|
||||
content?: null | string;
|
||||
mediaId?: null | string;
|
||||
url?: null | string;
|
||||
title?: null | string;
|
||||
description?: null | string;
|
||||
thumbMediaId?: null | string;
|
||||
thumbMediaUrl?: null | string;
|
||||
musicUrl?: null | string;
|
||||
hqMusicUrl?: null | string;
|
||||
introduction?: null | string;
|
||||
articles?: any[];
|
||||
}
|
||||
|
||||
type Reply = _Reply; // Partial<_Reply>
|
||||
|
||||
/** 利用旧的reply[accountId, type]初始化新的Reply */
|
||||
const createEmptyReply = (old: Ref<Reply> | Reply): Reply => {
|
||||
return {
|
||||
accountId: unref(old).accountId,
|
||||
type: unref(old).type,
|
||||
name: null,
|
||||
content: null,
|
||||
mediaId: null,
|
||||
url: null,
|
||||
title: null,
|
||||
description: null,
|
||||
thumbMediaId: null,
|
||||
thumbMediaUrl: null,
|
||||
musicUrl: null,
|
||||
hqMusicUrl: null,
|
||||
introduction: null,
|
||||
articles: [],
|
||||
};
|
||||
};
|
||||
|
||||
export { createEmptyReply, type Reply, ReplyType };
|
||||
117
apps/web-ele/src/views/mp/components/wx-reply/wx-reply.vue
Normal file
117
apps/web-ele/src/views/mp/components/wx-reply/wx-reply.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
① 移除多余的 rep 为前缀的变量,让 message 消息更简单
|
||||
② 代码优化,补充注释,提升阅读性
|
||||
③ 优化消息的临时缓存策略,发送消息时,只清理被发送消息的 tab,不会强制切回到 text 输入
|
||||
④ 支持发送【视频】消息时,支持新建视频
|
||||
-->
|
||||
<script lang="ts" setup>
|
||||
import type { Reply } from './types';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElRow, ElTabPane, ElTabs } from 'element-plus';
|
||||
|
||||
import { NewsType } from '../wx-material-select/types';
|
||||
import TabImage from './tab-image.vue';
|
||||
import TabMusic from './tab-music.vue';
|
||||
import TabNews from './tab-news.vue';
|
||||
import TabText from './tab-text.vue';
|
||||
import TabVideo from './tab-video.vue';
|
||||
import TabVoice from './tab-voice.vue';
|
||||
import { createEmptyReply, ReplyType } from './types';
|
||||
|
||||
defineOptions({ name: 'ReplySelect' });
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: Reply | undefined;
|
||||
newsType?: NewsType;
|
||||
}>(),
|
||||
{
|
||||
newsType: () => NewsType.Published,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: Reply): void;
|
||||
}>();
|
||||
// 提供一个默认的 Reply 对象,避免 undefined 导致的错误
|
||||
const defaultReply: Reply = {
|
||||
accountId: -1,
|
||||
type: ReplyType.Text,
|
||||
};
|
||||
|
||||
const reply = computed<Reply>({
|
||||
get: () => props.modelValue || defaultReply,
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
/** 清除除了`type`, `accountId`的字段 */
|
||||
function clear() {
|
||||
reply.value = createEmptyReply(reply);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
clear,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElTabs type="border-card" v-model="reply.type" @tab-change="clear">
|
||||
<!-- 类型 1:文本 -->
|
||||
<ElTabPane :name="ReplyType.Text">
|
||||
<template #label>
|
||||
<ElRow align="middle"><IconifyIcon icon="ep:document" /> 文本</ElRow>
|
||||
</template>
|
||||
<TabText v-model="reply.content" />
|
||||
</ElTabPane>
|
||||
|
||||
<!-- 类型 2:图片 -->
|
||||
<ElTabPane :name="ReplyType.Image">
|
||||
<template #label>
|
||||
<ElRow align="middle">
|
||||
<IconifyIcon icon="ep:picture" class="mr-5px" /> 图片
|
||||
</ElRow>
|
||||
</template>
|
||||
<TabImage v-model="reply" />
|
||||
</ElTabPane>
|
||||
|
||||
<!-- 类型 3:语音 -->
|
||||
<ElTabPane :name="ReplyType.Voice">
|
||||
<template #label>
|
||||
<ElRow align="middle"><IconifyIcon icon="ep:phone" /> 语音</ElRow>
|
||||
</template>
|
||||
<TabVoice v-model="reply" />
|
||||
</ElTabPane>
|
||||
|
||||
<!-- 类型 4:视频 -->
|
||||
<ElTabPane :name="ReplyType.Video">
|
||||
<template #label>
|
||||
<ElRow align="middle"><IconifyIcon icon="ep:share" /> 视频</ElRow>
|
||||
</template>
|
||||
<TabVideo v-model="reply" />
|
||||
</ElTabPane>
|
||||
|
||||
<!-- 类型 5:图文 -->
|
||||
<ElTabPane :name="ReplyType.News">
|
||||
<template #label>
|
||||
<ElRow align="middle"><IconifyIcon icon="ep:reading" /> 图文</ElRow>
|
||||
</template>
|
||||
<TabNews v-model="reply" :news-type="newsType" />
|
||||
</ElTabPane>
|
||||
|
||||
<!-- 类型 6:音乐 -->
|
||||
<ElTabPane :name="ReplyType.Music">
|
||||
<template #label>
|
||||
<ElRow align="middle"><IconifyIcon icon="ep:service" />音乐</ElRow>
|
||||
</template>
|
||||
<TabMusic v-model="reply" />
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
Reference in New Issue
Block a user