first commit
This commit is contained in:
95
src/components/Wechat/fileUpload/index.vue
Normal file
95
src/components/Wechat/fileUpload/index.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<el-upload
|
||||
ref="fileUpload"
|
||||
:action="actionUrl"
|
||||
:headers="headers"
|
||||
multiple
|
||||
:limit="1"
|
||||
:on-success="handleUploadSuccess"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeThumbImageUpload"
|
||||
:auto-upload="autoUpload"
|
||||
:data="uploadData"
|
||||
>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip" v-if="props.type.length > 0">支持{{ props.type.join('/') }}格式,大小不超过2M</div>
|
||||
</template>
|
||||
<el-button type="primary">本地上传</el-button>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-file-upload">
|
||||
import { Session } from '/@/utils/storage';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
|
||||
const actionUrl = ref('/api/mp/wx-material/materialFileUpload');
|
||||
|
||||
const fileUpload = ref();
|
||||
|
||||
const headers = computed(() => {
|
||||
const tenantId = Session.getTenant();
|
||||
return {
|
||||
Authorization: 'Bearer ' + Session.getToken(),
|
||||
'TENANT-ID': tenantId,
|
||||
};
|
||||
});
|
||||
// 定义刷新表格emit
|
||||
const emit = defineEmits(['success']);
|
||||
const fileList = ref([]);
|
||||
|
||||
const props = defineProps({
|
||||
uploadData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
appId: '',
|
||||
mediaType: 'image',
|
||||
title: '',
|
||||
introduction: '',
|
||||
};
|
||||
},
|
||||
},
|
||||
autoUpload: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
type: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const beforeThumbImageUpload = (file: any) => {
|
||||
let isType = true;
|
||||
if (props.type?.length > 0) {
|
||||
isType = props.type?.includes(file.type);
|
||||
}
|
||||
const isLt = file.size / 1024 / 1024 < 2;
|
||||
if (!isType) {
|
||||
useMessage().error('上传文件格式不对!');
|
||||
}
|
||||
if (!isLt) {
|
||||
useMessage().error('上传文件大小不能超过2M!!');
|
||||
}
|
||||
return isType && isLt;
|
||||
};
|
||||
|
||||
const handleUploadSuccess = (response, file, fileList) => {
|
||||
fileList.value = [];
|
||||
emit('success', response, file, fileList);
|
||||
};
|
||||
const submit = () => {
|
||||
return new Promise((resolve) => {
|
||||
fileUpload.value.submit();
|
||||
resolve('');
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
submit,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
166
src/components/Wechat/wx-material-select/main.vue
Normal file
166
src/components/Wechat/wx-material-select/main.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<el-dialog title="选择图文" v-model="visible" :close-on-click-modal="false" draggable width="80%">
|
||||
<div v-if="objData.type === 'image'">
|
||||
<div class="waterfall" v-loading="state.loading">
|
||||
<div class="waterfall-item" v-for="item in state.dataList" :key="item.mediaId">
|
||||
<img class="material-img" :src="item.url" />
|
||||
<p class="item-name">{{ item.name }}</p>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="success" @click="selectMaterial(item)"
|
||||
>选择
|
||||
<el-icon class="el-icon--right"></el-icon>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle" />
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'voice'">
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="state.loading" :data="state.dataList">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="语音" align="center" prop="url"> </el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button icon="el-icon-circle-plus" @click="selectMaterial(scope.row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle" />
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'video'">
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="state.loading" :data="state.dataList">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="标题" align="center" prop="title" />
|
||||
<el-table-column label="介绍" align="center" prop="introduction" />
|
||||
<el-table-column label="视频" align="center" prop="url"> </el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button @click="selectMaterial(scope.row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle" />
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'news'">
|
||||
<div class="waterfall" v-loading="state.loading">
|
||||
<template v-for="item in state.dataList">
|
||||
<div v-if="item.content && item.content.newsItem" class="waterfall-item" :key="item.id">
|
||||
<wx-news :obj-data="item.content.newsItem"></wx-news>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="success" @click="selectMaterial(item)"> 选择<el-icon class="el-icon--right" /> </el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<pagination v-bind="state.pagination" @size-change="sizeChangeHandle" @current-change="currentChangeHandle" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-material-select">
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { getPage } from '/@/api/mp/wx-material';
|
||||
const WxNews = defineAsyncComponent(() => import('../wx-news/index.vue'));
|
||||
|
||||
const emit = defineEmits(['selectMaterial']);
|
||||
|
||||
const objData = reactive({
|
||||
repType: '',
|
||||
accountId: '',
|
||||
type: '',
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {
|
||||
type: '',
|
||||
appId: '',
|
||||
},
|
||||
pageList: getPage,
|
||||
createdIsNeed: false,
|
||||
props: {
|
||||
item: 'items',
|
||||
totalCount: 'totalCount',
|
||||
},
|
||||
});
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle } = useTable(state);
|
||||
|
||||
const selectMaterial = (item: any) => {
|
||||
visible.value = false;
|
||||
emit('selectMaterial', item, objData.accountId);
|
||||
};
|
||||
|
||||
const openDialog = (data: any) => {
|
||||
state.queryForm.type = data.type;
|
||||
state.queryForm.appId = data.accountId;
|
||||
objData.type = data.type;
|
||||
objData.accountId = data.accountId;
|
||||
visible.value = true;
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap: 10px;
|
||||
column-count: 5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
line-height: 30px;
|
||||
}
|
||||
@media (min-width: 992px) and (max-width: 1300px) {
|
||||
.waterfall {
|
||||
column-count: 3;
|
||||
}
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
.waterfall {
|
||||
column-count: 2;
|
||||
}
|
||||
p {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
/*瀑布流样式*/
|
||||
</style>
|
||||
101
src/components/Wechat/wx-msg/card.scss
Normal file
101
src/components/Wechat/wx-msg/card.scss
Normal file
@@ -0,0 +1,101 @@
|
||||
.avue-card {
|
||||
&__item {
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #e8e8e8;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 14px;
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5;
|
||||
list-style: none;
|
||||
font-feature-settings: 'tnum';
|
||||
cursor: pointer;
|
||||
height: 200px;
|
||||
&:hover {
|
||||
border-color: rgba(0, 0, 0, 0.09);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
|
||||
}
|
||||
&--add {
|
||||
border: 1px dashed #000;
|
||||
width: 100%;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
background-color: #fff;
|
||||
border-color: #d9d9d9;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
&:hover {
|
||||
color: #40a9ff;
|
||||
background-color: #fff;
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__body {
|
||||
display: flex;
|
||||
padding: 24px;
|
||||
}
|
||||
&__detail {
|
||||
flex: 1;
|
||||
}
|
||||
&__avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 48px;
|
||||
overflow: hidden;
|
||||
margin-right: 12px;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
&__title {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
&__info {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
height: 64px;
|
||||
}
|
||||
&__menu {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
height: 50px;
|
||||
background: #f7f9fa;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** joolun 额外加的 */
|
||||
.avue-comment__main {
|
||||
flex: unset !important;
|
||||
border-radius: 5px !important;
|
||||
margin: 0 8px !important;
|
||||
}
|
||||
.avue-comment__header {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
.avue-comment__body {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
92
src/components/Wechat/wx-msg/comment.scss
Normal file
92
src/components/Wechat/wx-msg/comment.scss
Normal file
@@ -0,0 +1,92 @@
|
||||
/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */
|
||||
.avue-comment {
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
&--reverse {
|
||||
flex-direction: row-reverse;
|
||||
.avue-comment__main {
|
||||
&:before,
|
||||
&:after {
|
||||
left: auto;
|
||||
right: -8px;
|
||||
border-width: 8px 0 8px 8px;
|
||||
}
|
||||
&:before {
|
||||
border-left-color: #dedede;
|
||||
}
|
||||
&:after {
|
||||
border-left-color: #f8f8f8;
|
||||
margin-right: 1px;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&__header {
|
||||
padding: 5px 15px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
&__author {
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
&__main {
|
||||
flex: 1;
|
||||
margin: 0 20px;
|
||||
position: relative;
|
||||
border: 1px solid #dedede;
|
||||
border-radius: 2px;
|
||||
&:before,
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
right: 100%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
display: block;
|
||||
content: ' ';
|
||||
border-color: transparent;
|
||||
border-style: solid solid outset;
|
||||
border-width: 8px 8px 8px 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
&:before {
|
||||
border-right-color: #dedede;
|
||||
z-index: 1;
|
||||
}
|
||||
&:after {
|
||||
border-right-color: #f8f8f8;
|
||||
margin-left: 1px;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
&__body {
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
font-family: Segoe UI, Lucida Grande, Helvetica, Arial, Microsoft YaHei, FreeSans, Arimo, Droid Sans, wenquanyi micro hei, Hiragino Sans GB,
|
||||
Hiragino Sans GB W3, FontAwesome, sans-serif;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
blockquote {
|
||||
margin: 0;
|
||||
font-family: Georgia, Times New Roman, Times, Kai, Kaiti SC, KaiTi, BiauKai, FontAwesome, serif;
|
||||
padding: 1px 0 1px 15px;
|
||||
border-left: 4px solid #ddd;
|
||||
}
|
||||
}
|
||||
258
src/components/Wechat/wx-msg/index.vue
Normal file
258
src/components/Wechat/wx-msg/index.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<el-dialog title="用户消息" v-model="visible" :close-on-click-modal="false" draggable>
|
||||
<div v-loading="mainLoading" class="msg-main">
|
||||
<div id="msg-div" class="msg-div">
|
||||
<div v-if="!tableLoading">
|
||||
<div v-if="loadMore" class="el-table__empty-block" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span></div>
|
||||
<div v-if="!loadMore" class="el-table__empty-block"><span class="el-table__empty-text">没有更多了</span></div>
|
||||
</div>
|
||||
<div v-for="item in tableData" :key="item.id" class="execution" id="msgTable">
|
||||
<div class="avue-comment" :class="item.type === '2' ? 'avue-comment--reverse' : ''">
|
||||
<div class="avatar-div">
|
||||
<name-avatar v-if="item.type === '1'" scale="2" :name="item.nickName" />
|
||||
<name-avatar v-if="item.type !== '1'" scale="2" :face-url="item.appLogo" />
|
||||
</div>
|
||||
<div class="avue-comment__main">
|
||||
<div class="avue-comment__header">
|
||||
<div class="avue-comment__create_time">{{ item.createTime }}</div>
|
||||
</div>
|
||||
<div class="avue-comment__body" :style="item.type === '2' ? 'background: #6BED72;' : ''">
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'subscribe'">
|
||||
<el-tag type="success" size="mini">关注</el-tag>
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'unsubscribe'">
|
||||
<el-tag type="danger" size="mini">取消关注</el-tag>
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'CLICK'">
|
||||
<el-tag size="mini">点击菜单</el-tag>
|
||||
:【{{ item.repName }}】
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'VIEW'">
|
||||
<el-tag size="mini">点击菜单链接</el-tag>
|
||||
:【{{ item.repUrl }}】
|
||||
</div>
|
||||
<div v-if="item.repType === 'event' && item.repEvent === 'scancode_waitmsg'">
|
||||
<el-tag size="mini">扫码结果:</el-tag>
|
||||
:【{{ item.repContent }}】
|
||||
</div>
|
||||
<div v-if="item.repType === 'text'">{{ item.repContent }}</div>
|
||||
<div v-if="item.repType === 'image'">
|
||||
<a target="_blank" :href="item.repUrl"><img :src="item.repUrl" style="width: 100px" /></a>
|
||||
</div>
|
||||
<div v-if="item.repType === 'voice'">
|
||||
<SvgIcon name="local-wx-voice" :size="80" @click="loadVideo(item)"></SvgIcon>
|
||||
</div>
|
||||
<div v-if="item.repType === 'video'" style="text-align: center">
|
||||
<SvgIcon name="local-wx-video" :size="80" @click="loadVideo(item)"></SvgIcon>
|
||||
</div>
|
||||
<div v-if="item.repType === 'shortvideo'" style="text-align: center">
|
||||
<svg-icon name="local-wx-video" :size="80" @click="loadVideo(item)"></svg-icon>
|
||||
</div>
|
||||
<div v-if="item.repType === 'location'">
|
||||
<el-link
|
||||
type="primary"
|
||||
target="_blank"
|
||||
:href="
|
||||
'https://map.qq.com/?type=marker&isopeninfowin=1&markertype=1&pointx=' +
|
||||
item.repLocationY +
|
||||
'&pointy=' +
|
||||
item.repLocationX +
|
||||
'&name=' +
|
||||
item.repContent +
|
||||
'&ref=joolun'
|
||||
"
|
||||
>
|
||||
<img
|
||||
:src="
|
||||
'https://apis.map.qq.com/ws/staticmap/v2/?zoom=10&markers=color:blue|label:A|' +
|
||||
item.repLocationX +
|
||||
',' +
|
||||
item.repLocationY +
|
||||
'&key=PFFBZ-RBM3V-IEEPP-UH6KE-6QUQE-C4BVJ&size=250*180'
|
||||
"
|
||||
/>
|
||||
<p />
|
||||
<i class="el-icon-map-location"></i>{{ item.repContent }}
|
||||
</el-link>
|
||||
</div>
|
||||
<div v-if="item.repType === 'link'" class="avue-card__detail">
|
||||
<el-link type="success" :underline="false" target="_blank" :href="item.repUrl">
|
||||
<div class="avue-card__title"><i class="el-icon-link"></i>{{ item.repName }}</div>
|
||||
</el-link>
|
||||
<div class="avue-card__info" style="height: unset">{{ item.repDesc }}</div>
|
||||
</div>
|
||||
<div v-if="item.repType === 'news'" style="width: 300px">
|
||||
<wx-news :obj-data="JSON.parse(item.content)"></wx-news>
|
||||
</div>
|
||||
<div v-if="item.repType === 'music'">
|
||||
<el-link type="success" :underline="false" target="_blank" :href="item.repUrl">
|
||||
<div class="avue-card__body" style="padding: 10px; background-color: #fff; border-radius: 5px">
|
||||
<div class="avue-card__avatar"><img :src="item.repThumbUrl" alt="" /></div>
|
||||
<div class="avue-card__detail">
|
||||
<div class="avue-card__title" style="margin-bottom: unset">{{ item.repName }}</div>
|
||||
<div class="avue-card__info" style="height: unset">{{ item.repDesc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-loading="sendLoading" class="msg-send" @keyup.enter="sendMsg">
|
||||
<wx-reply :objData="objData"></wx-reply>
|
||||
<el-button type="success" class="send-but" @click="sendMsg">发送(S)</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-msg">
|
||||
import { fetchList, addObj } from '/@/api/mp/wx-fans-msg';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { getMaterialVideo } from '/@/api/mp/wx-material';
|
||||
|
||||
const WxReply = defineAsyncComponent(() => import('../wx-reply/index.vue'));
|
||||
const WxNews = defineAsyncComponent(() => import('../wx-news/index.vue'));
|
||||
|
||||
const NameAvatar = defineAsyncComponent(() => import('/@/components/NameAvatar/index.vue'));
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
// 各种loading
|
||||
const mainLoading = ref(false);
|
||||
const sendLoading = ref(false);
|
||||
|
||||
const loadMore = ref(false);
|
||||
|
||||
const objData = ref({
|
||||
repType: 'text',
|
||||
appId: '',
|
||||
}) as any;
|
||||
|
||||
const page = reactive({
|
||||
total: 0, // 总页数
|
||||
currentPage: 1, // 当前页数
|
||||
pageSize: 10, // 每页显示多少条
|
||||
ascs: [], //升序字段
|
||||
descs: 'create_time', //降序字段
|
||||
});
|
||||
|
||||
const wxData = reactive({
|
||||
appId: '',
|
||||
wxUserId: '',
|
||||
});
|
||||
|
||||
const sendMsg = () => {
|
||||
if (objData.value) {
|
||||
if (objData.value.repType === 'news') {
|
||||
if (JSON.parse(objData.value.content).length > 1) {
|
||||
useMessage().error('图文消息条数限制在1条以内,已默认发送第一条');
|
||||
objData.value.content = JSON.parse(objData.value.content)[0];
|
||||
}
|
||||
}
|
||||
sendLoading.value = true;
|
||||
addObj(
|
||||
Object.assign(
|
||||
{
|
||||
wxUserId: wxData.wxUserId,
|
||||
appId: wxData.appId,
|
||||
},
|
||||
objData.value
|
||||
)
|
||||
)
|
||||
.then(() => {
|
||||
tableData.value = [];
|
||||
getData().then(() => {
|
||||
//box-container是添加overflow的父div,也就是出现滚动条的div
|
||||
var scrollTarget = document.getElementById('msg-div');
|
||||
//scrollTarget.scrollHeight是获取dom元素的高度,然后设置scrollTop
|
||||
scrollTarget.scrollTop = scrollTarget.scrollHeight;
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
sendLoading.value = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const tableData = ref([] as any);
|
||||
const tableLoading = ref(false);
|
||||
|
||||
const openDialog = (data: any) => {
|
||||
wxData.wxUserId = data.wxUserId;
|
||||
wxData.appId = data.appId;
|
||||
objData.value.appId = data.appId;
|
||||
getData();
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
tableLoading.value = true;
|
||||
return fetchList({
|
||||
...page,
|
||||
...wxData,
|
||||
}).then((res) => {
|
||||
const data = res.data.records.reverse();
|
||||
tableData.value = [...data, ...tableData.value];
|
||||
page.total = res.data.total;
|
||||
tableLoading.value = false;
|
||||
if (data.length < page.pageSize || data.length === 0) {
|
||||
loadMore.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const loadVideo = (item) => {
|
||||
getMaterialVideo({
|
||||
mediaId: item.repMediaId,
|
||||
appId: item.appId,
|
||||
}).then((response) => {
|
||||
const data = response.data;
|
||||
window.open(data.downUrl, 'target', '');
|
||||
});
|
||||
};
|
||||
|
||||
const loadingMore = () => {
|
||||
page.currentPage = page.currentPage + 1;
|
||||
getData();
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './comment.scss';
|
||||
@import './card.scss';
|
||||
|
||||
.msg-main {
|
||||
margin-top: -30px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.msg-div {
|
||||
height: 50vh;
|
||||
overflow: auto;
|
||||
background-color: #eaeaea;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.msg-send {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.avatar-div {
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.send-but {
|
||||
float: right;
|
||||
margin-top: 8px !important;
|
||||
}
|
||||
</style>
|
||||
90
src/components/Wechat/wx-news/index.vue
Normal file
90
src/components/Wechat/wx-news/index.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="news-home">
|
||||
<div v-for="(news, index) in props.objData" :key="index" class="news-div">
|
||||
<a v-if="index === 0" target="_blank" :href="news.url">
|
||||
<div class="news-main">
|
||||
<div class="news-content">
|
||||
<img class="material-img" :src="news.thumbUrl" style="width: 280; height: 120" />
|
||||
<div class="news-content-title">
|
||||
<span>{{ news.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a v-if="index > 0" target="_blank" :href="news.url">
|
||||
<div class="news-main-item">
|
||||
<div class="news-content-item">
|
||||
<div class="news-content-item-title">{{ news.title }}</div>
|
||||
<div class="news-content-item-img">
|
||||
<img class="material-img" :src="news.thumbUrl" height="100%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-news">
|
||||
const props = defineProps({
|
||||
objData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-home {
|
||||
background-color: #ffffff;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.news-main {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.news-content {
|
||||
background-color: #acadae;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.news-content-title {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: #ffffff;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
background-color: black;
|
||||
width: 98%;
|
||||
padding: 1%;
|
||||
opacity: 0.65;
|
||||
white-space: normal;
|
||||
box-sizing: unset !important;
|
||||
}
|
||||
.news-main-item {
|
||||
background-color: #ffffff;
|
||||
padding: 5px 0px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
.news-content-item {
|
||||
position: relative;
|
||||
}
|
||||
.news-content-item-title {
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
width: 70%;
|
||||
margin-left: 1%;
|
||||
white-space: normal;
|
||||
}
|
||||
.news-content-item-img {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
background-color: #acadae;
|
||||
margin-right: 1%;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
309
src/components/Wechat/wx-reply/index.vue
Normal file
309
src/components/Wechat/wx-reply/index.vue
Normal file
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<el-tabs v-model="objData.repType" type="border-card" @tab-click="handleClick" style="width: 100%">
|
||||
<el-tab-pane name="text" label="text">
|
||||
<template #label><i class="el-icon-document"></i> 文本</template>
|
||||
<el-input v-model="objData.repContent" type="textarea" :rows="5" placeholder="请输入内容"> </el-input>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="image" label="image">
|
||||
<template #label><i class="el-icon-picture"></i> 图片</template>
|
||||
<el-row>
|
||||
<div v-if="objData.repUrl" class="select-item">
|
||||
<img class="material-img" :src="objData.repUrl" />
|
||||
<p v-if="objData.repName" class="item-name">{{ objData.repName }}</p>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="!objData.repUrl" style="width: 100%">
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="12" class="col-select">
|
||||
<el-button type="success" @click="openMaterial({ type: 'image', accountId: props.objData.appId })"
|
||||
>素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12" class="col-add">
|
||||
<wx-file-upload :data="uploadData" @success="handelUpload"></wx-file-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="voice" label="voice">
|
||||
<template #label><i class="el-icon-phone"></i> 语音</template>
|
||||
<el-row>
|
||||
<div v-if="objData.repName" class="select-item">
|
||||
<p class="item-name">{{ objData.repName }}</p>
|
||||
<div class="item-infos">
|
||||
<img :src="WxVoice" style="width: 100px" @click="loadVideo(item)" />
|
||||
</div>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="!objData.repName" style="width: 100%">
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="12" class="col-select">
|
||||
<el-button type="success" @click="openMaterial({ type: 'voice', accountId: props.objData.appId })"
|
||||
>素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12" class="col-add">
|
||||
<wx-file-upload :data="uploadData" @success="handelUpload"></wx-file-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="video" label="video">
|
||||
<template #label><i class="el-icon-share"></i> 视频</template>
|
||||
<el-row style="text-align: center; flex: 1">
|
||||
<el-input v-if="objData.repUrl" v-model="objData.repName" placeholder="请输入标题" style="margin: 10px"></el-input>
|
||||
<el-input v-if="objData.repUrl" v-model="objData.repDesc" placeholder="请输入描述" style="margin: 10px"></el-input>
|
||||
</el-row>
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="12" class="col-select">
|
||||
<a v-if="objData.repUrl" target="_blank" :href="objData.repUrl"> <SvgIcon name="local-wx-video" :size="45" /> </a
|
||||
></el-col>
|
||||
<el-col :span="12" class="col-add">
|
||||
<el-button type="success" @click="openMaterial({ type: 'video', accountId: props.objData.appId })">素材库选择 </el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="news" label="news">
|
||||
<template #label><i class="el-icon-news"></i> 图文</template>
|
||||
<el-row>
|
||||
<div v-if="objData.content" class="select-item">
|
||||
<wx-news :obj-data="JSON.parse(objData.content)"></wx-news>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="!objData.content" style="width: 100%">
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="24" class="col-select2">
|
||||
<el-button type="success" @click="openMaterial({ type: 'news', accountId: props.objData.appId })"
|
||||
>素材库选择<i class="fansel-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<wx-material-select ref="dialogNewsRef" @selectMaterial="selectMaterial"></wx-material-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="wx-reply">
|
||||
import { getMaterialVideo } from '/@/api/mp/wx-material';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
const WxMaterialSelect = defineAsyncComponent(() => import('/@/components/Wechat/wx-material-select/main.vue'));
|
||||
|
||||
const WxFileUpload = defineAsyncComponent(() => import('/@/components/Wechat/fileUpload/index.vue'));
|
||||
|
||||
const WxNews = defineAsyncComponent(() => import('/@/components/Wechat/wx-news/index.vue'));
|
||||
|
||||
const props = defineProps({
|
||||
objData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
repType: '',
|
||||
repContent: '',
|
||||
repName: '',
|
||||
repDesc: '',
|
||||
repUrl: '',
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const uploadData = reactive({
|
||||
mediaType: props.objData.repType,
|
||||
title: '',
|
||||
introduction: '',
|
||||
appId: props.objData.appId,
|
||||
});
|
||||
|
||||
const tempObj = ref({}) as any;
|
||||
|
||||
const handleClick = (tab) => {
|
||||
uploadData.mediaType = tab.paneName;
|
||||
uploadData.appId = props.objData.appId;
|
||||
|
||||
const tempObjItem = tempObj.value[tab.paneName];
|
||||
if (tempObjItem) {
|
||||
props.objData.repName = tempObjItem.repName ? tempObjItem.repName : null;
|
||||
props.objData.repMediaId = tempObjItem.repMediaId ? tempObjItem.repMediaId : null;
|
||||
props.objData.media_id = tempObjItem.media_id ? tempObjItem.media_id : null;
|
||||
props.objData.repUrl = tempObjItem.repUrl ? tempObjItem.repUrl : null;
|
||||
props.objData.content = tempObjItem.content ? tempObjItem.content : null;
|
||||
props.objData.repDesc = tempObjItem.repDesc ? tempObjItem.repDesc : null;
|
||||
} else {
|
||||
props.objData.repName = '';
|
||||
props.objData.repMediaId = '';
|
||||
props.objData.media_id = '';
|
||||
props.objData.repUrl = '';
|
||||
props.objData.content = '';
|
||||
props.objData.repDesc = '';
|
||||
}
|
||||
};
|
||||
|
||||
const deleteObj = () => {
|
||||
props.objData.repName = '';
|
||||
props.objData.repUrl = '';
|
||||
props.objData.content = '';
|
||||
};
|
||||
|
||||
const openMaterial = (data: any) => {
|
||||
dialogNewsRef.value.openDialog(data);
|
||||
};
|
||||
|
||||
const dialogNewsRef = ref();
|
||||
|
||||
const selectMaterial = (item, appId) => {
|
||||
let tempObjItem = {
|
||||
repType: '',
|
||||
repMediaId: '',
|
||||
media_id: '',
|
||||
content: '',
|
||||
} as any;
|
||||
|
||||
tempObjItem.repType = props.objData.repType;
|
||||
tempObjItem.repMediaId = item.mediaId;
|
||||
tempObjItem.media_id = item.mediaId;
|
||||
tempObjItem.content = item.content;
|
||||
|
||||
props.objData.repMediaId = item.mediaId;
|
||||
props.objData.media_id = item.mediaId;
|
||||
props.objData.content = item.content;
|
||||
if (props.objData.repType === 'music') {
|
||||
tempObjItem.repThumbMediaId = item.mediaId;
|
||||
tempObjItem.repThumbUrl = item.url;
|
||||
props.objData.repThumbMediaId = item.mediaId;
|
||||
props.objData.repThumbUrl = item.url;
|
||||
} else {
|
||||
tempObjItem.repName = item.name;
|
||||
tempObjItem.repUrl = item.url;
|
||||
props.objData.repName = item.name;
|
||||
props.objData.repUrl = item.url;
|
||||
}
|
||||
if (props.objData.repType === 'video') {
|
||||
getMaterialVideo({
|
||||
mediaId: item.mediaId,
|
||||
appId: appId,
|
||||
}).then((response) => {
|
||||
const data = response.data;
|
||||
tempObjItem.repDesc = data.description || '';
|
||||
tempObjItem.repUrl = data.downUrl;
|
||||
props.objData.repName = data.title;
|
||||
props.objData.repDesc = data.description || '';
|
||||
props.objData.repUrl = data.downUrl;
|
||||
});
|
||||
}
|
||||
if (props.objData.repType === 'news') {
|
||||
props.objData.content = JSON.stringify(item.content.newsItem);
|
||||
}
|
||||
tempObj.value[props.objData.repType] = tempObjItem;
|
||||
};
|
||||
|
||||
const handelUpload = (response) => {
|
||||
if (response.code === 0) {
|
||||
const item = response.data;
|
||||
selectMaterial(item, props.objData.appId);
|
||||
} else {
|
||||
useMessage().error('上传错误' + response.msg);
|
||||
}
|
||||
};
|
||||
|
||||
const loadVideo = (item) => {
|
||||
getMaterialVideo({
|
||||
mediaId: item.repMediaId,
|
||||
appId: item.appId,
|
||||
}).then((response) => {
|
||||
const data = response.data;
|
||||
window.open(data.downUrl, 'target', '');
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.select-item {
|
||||
width: 280px;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.select-item2 {
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.ope-row {
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
line-height: unset !important;
|
||||
}
|
||||
|
||||
.col-select {
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
width: 49.5%;
|
||||
}
|
||||
|
||||
.col-select2 {
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
.col-add {
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
width: 49.5%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100px !important;
|
||||
height: 100px !important;
|
||||
line-height: 100px !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.thumb-div {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item-infos {
|
||||
width: 30%;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user