Merge branch 'dev' of gitee.com:yudaocode/yudao-ui-admin-vue3 into dev

Signed-off-by: dhb52 <dhb52@126.com>
This commit is contained in:
dhb52
2023-04-09 05:16:23 +00:00
committed by Gitee
100 changed files with 1669 additions and 1105 deletions

View File

@@ -13,15 +13,14 @@
<img class="material-img" :src="item.url" />
<p class="item-name">{{ item.name }}</p>
<el-row class="ope-row">
<el-button type="success" @click="selectMaterialFun(item)"
>选择 <Icon icon="ep:circle-check" />
<el-button type="success" @click="selectMaterialFun(item)">
选择 <Icon icon="ep:circle-check" />
</el-button>
</el-row>
</div>
</div>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@@ -39,18 +38,16 @@
<WxVoicePlayer :url="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ formatDate(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
label="上传时间"
align="center"
fixed="right"
class-name="small-padding fixed-width"
>
prop="createTime"
width="180"
:formatter="dateFormatter"
/>
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" link @click="selectMaterialFun(scope.row)"
>选择<Icon icon="ep:plus" />
</el-button>
@@ -58,8 +55,7 @@
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@@ -79,11 +75,13 @@
<WxVideoPlayer :url="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ formatDate(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="上传时间"
align="center"
prop="createTime"
width="180"
:formatter="dateFormatter"
/>
<el-table-column
label="操作"
align="center"
@@ -98,8 +96,7 @@
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@@ -121,8 +118,7 @@
</div>
</div>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@@ -139,7 +135,7 @@ import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
import { getMaterialPage } from '@/api/mp/material'
import { getFreePublishPage } from '@/api/mp/freePublish'
import { getDraftPage } from '@/api/mp/draft'
import { dateFormatter, formatDate } from '@/utils/formatTime'
import { dateFormatter } from '@/utils/formatTime'
import { defineComponent, PropType } from 'vue'
export default defineComponent({
@@ -244,7 +240,6 @@ export default defineComponent({
getMaterialPageFun,
getPage,
formatDate,
newsTypeRef,
queryParams,
objDataRef,
list,
@@ -254,7 +249,6 @@ export default defineComponent({
}
})
</script>
<style lang="scss" scoped>
/*瀑布流样式*/
.waterfall {

View File

@@ -139,7 +139,7 @@ import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'
import WxNews from '@/views/mp/components/wx-news/main.vue'
import WxLocation from '@/views/mp/components/wx-location/main.vue'
import WxMusic from '@/views/mp/components/wx-music/main.vue'
import { getUser } from '@/api/mp/mpuser'
import { getUser } from '@/api/mp/user'
import { defineComponent } from 'vue'
const message = useMessage() // 消息弹窗

View File

@@ -12,10 +12,7 @@
<!-- 类型 1文本 -->
<el-tab-pane name="text">
<template #label>
<el-row align="middle">
<icon icon="ep:document" />
文本
</el-row>
<el-row align="middle"><Icon icon="ep:document" /> 文本</el-row>
</template>
<el-input
type="textarea"
@@ -28,10 +25,7 @@
<!-- 类型 2图片 -->
<el-tab-pane name="image">
<template #label>
<el-row align="middle">
<icon icon="ep:picture" class="mr-5px" />
图片
</el-row>
<el-row align="middle"><Icon icon="ep:picture" class="mr-5px" /> 图片</el-row>
</template>
<!-- 情况一已经选择好素材或者上传好图片 -->
<div class="select-item" v-if="objDataRef.url">
@@ -39,7 +33,7 @@
<p class="item-name" v-if="objDataRef.name">{{ objDataRef.name }}</p>
<el-row class="ope-row" justify="center">
<el-button type="danger" circle @click="deleteObj">
<icon icon="ep:delete" />
<Icon icon="ep:delete" />
</el-button>
</el-row>
</div>
@@ -48,8 +42,7 @@
<!-- 选择素材 -->
<el-col :span="12" class="col-select">
<el-button type="success" @click="openMaterial">
素材库选择
<icon icon="ep:circle-check" />
素材库选择 <Icon icon="ep:circle-check" />
</el-button>
<el-dialog title="选择图片" v-model="dialogImageVisible" width="90%" append-to-body>
<WxMaterialSelect :obj-data="objDataRef" @select-material="selectMaterial" />
@@ -70,10 +63,8 @@
<el-button type="primary">上传图片</el-button>
<template #tip>
<span>
<div class="el-upload__tip"
>支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M</div
></span
>
<div class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M</div>
</span>
</template>
</el-upload>
</el-col>
@@ -82,12 +73,8 @@
<!-- 类型 3语音 -->
<el-tab-pane name="voice">
<template #label>
<el-row align="middle">
<icon icon="ep:phone" />
语音
</el-row>
<el-row align="middle"><Icon icon="ep:phone" /> 语音</el-row>
</template>
<div class="select-item2" v-if="objDataRef.url">
<p class="item-name">{{ objDataRef.name }}</p>
<div class="item-infos">
@@ -121,8 +108,8 @@
>
<el-button type="primary">点击上传</el-button>
<template #tip>
<div class="el-upload__tip"
>格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s
<div class="el-upload__tip">
格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s
</div>
</template>
</el-upload>
@@ -132,10 +119,7 @@
<!-- 类型 4视频 -->
<el-tab-pane name="video">
<template #label>
<el-row align="middle">
<icon icon="ep:share" />
视频
</el-row>
<el-row align="middle"><Icon icon="ep:share" /> 视频</el-row>
</template>
<el-row>
<el-input
@@ -158,8 +142,7 @@
<!-- 选择素材 -->
<el-col :span="12">
<el-button type="success" @click="openMaterial">
素材库选择
<icon icon="ep:circle-check" />
素材库选择 <Icon icon="ep:circle-check" />
</el-button>
<el-dialog title="选择视频" v-model="dialogVideoVisible" width="90%" append-to-body>
<WxMaterialSelect :objData="objDataRef" @select-material="selectMaterial" />
@@ -177,10 +160,7 @@
:before-upload="beforeVideoUpload"
:on-success="handleUploadSuccess"
>
<el-button type="primary"
>新建视频
<icon icon="ep:upload" />
</el-button>
<el-button type="primary">新建视频 <Icon icon="ep:upload" /></el-button>
</el-upload>
</el-col>
</el-row>
@@ -190,17 +170,14 @@
<!-- 类型 5图文 -->
<el-tab-pane name="news">
<template #label>
<el-row align="middle">
<icon icon="ep:reading" />
图文
</el-row>
<el-row align="middle"><Icon icon="ep:reading" /> 图文</el-row>
</template>
<el-row>
<div class="select-item" v-if="objDataRef.articles?.length > 0">
<WxNews :articles="objDataRef.articles" />
<el-col class="ope-row">
<el-button type="danger" circle @click="deleteObj">
<icon icon="ep:delete" />
<Icon icon="ep:delete" />
</el-button>
</el-col>
</div>
@@ -208,8 +185,8 @@
<el-col :span="24" v-if="!objDataRef.content">
<el-row style="text-align: center" align="middle">
<el-col :span="24">
<el-button type="success" @click="openMaterial"
>{{ newsType === '1' ? '选择已发布图文' : '选择草稿箱图文' }}
<el-button type="success" @click="openMaterial">
{{ newsType === '1' ? '选择已发布图文' : '选择草稿箱图文' }}
<icon icon="ep:circle-check" />
</el-button>
</el-col>
@@ -227,10 +204,7 @@
<!-- 类型 6音乐 -->
<el-tab-pane name="music">
<template #label>
<el-row align="middle">
<icon icon="ep:service" />
音乐
</el-row>
<el-row align="middle"><Icon icon="ep:service" />音乐</el-row>
</template>
<el-row align="middle" justify="center">
<el-col :span="6">
@@ -295,7 +269,6 @@
</el-tab-pane>
</el-tabs>
</template>
<script lang="ts" name="WxReplySelect">
import WxNews from '@/views/mp/components/wx-news/main.vue'
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'

View File

@@ -1,40 +1,37 @@
<template>
<div class="app-container">
<doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryFormRef" size="small" :inline="true" label-width="68px">
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option
v-for="item in accountList"
:key="parseInt(item.id)"
:key="item.id"
:label="item.name"
:value="parseInt(item.id)"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
<el-button type="primary" plain @click="handleAdd" v-hasPermi="['mp:draft:create']">
<Icon icon="ep:plus" />新增
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['mp:draft:create']"
><Icon icon="ep:plus" />新增
</el-button>
</el-col>
</el-row>
<!-- 列表 -->
<!-- 列表 -->
<ContentWrap>
<div class="waterfall" v-loading="loading">
<template v-for="item in list" :key="item.articleId">
<div class="waterfall-item" v-if="item.content && item.content.newsItem">
@@ -46,35 +43,40 @@
circle
@click="handlePublish(item)"
v-hasPermi="['mp:free-publish:submit']"
><Icon icon="fa:upload"
/></el-button>
>
<Icon icon="fa:upload" />
</el-button>
<el-button
type="primary"
circle
@click="handleUpdate(item)"
v-hasPermi="['mp:draft:update']"
><Icon icon="ep:edit"
/></el-button>
>
<Icon icon="ep:edit" />
</el-button>
<el-button
type="danger"
circle
@click="handleDelete(item)"
v-hasPermi="['mp:draft:delete']"
><Icon icon="ep:delete"
/></el-button>
>
<Icon icon="ep:delete" />
</el-button>
</el-row>
</div>
</template>
</div>
<!-- 分页记录 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- TODO @Dhb52迁移成独立路由 -->
<div class="app-container">
<!-- 添加或修改草稿对话框 -->
<Teleport to="body">
<el-dialog
@@ -245,49 +247,39 @@
</el-row>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogNewsVisible = false"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</div>
<el-button @click="dialogNewsVisible = false"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</template>
</el-dialog>
</Teleport>
</div>
</template>
<script setup name="MpDraft">
import { ref, onMounted, reactive, nextTick } from 'vue'
import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue'
import WxNews from '@/views/mp/components/wx-news/main.vue'
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
import { getAccessToken } from '@/utils/auth'
import { createDraft, deleteDraft, getDraftPage, updateDraft } from '@/api/mp/draft'
import { getSimpleAccountList } from '@/api/mp/account'
import { submitFreePublish } from '@/api/mp/freePublish'
import * as MpAccountApi from '@/api/mp/account'
import * as MpDraftApi from '@/api/mp/draft'
import * as MpFreePublishApi from '@/api/mp/freePublish'
const message = useMessage() // 消息
// 可以用改本地数据模拟避免API调用超限
// import drafts from './mock'
const BASE_URL = import.meta.env.VITE_BASE_URL
const message = useMessage()
const materialSelectRef = ref()
const queryFormRef = ref()
// 遮罩层
const loading = ref(false)
// 显示搜索条件
// 总条数
const total = ref(0)
// 数据列表
const list = ref([])
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
accountId: undefined
})
const queryFormRef = ref() // 搜索的表单
const accountList = ref([]) // 公众号账号列表
// ========== 文件上传 ==========
const materialSelectRef = ref()
const BASE_URL = import.meta.env.VITE_BASE_URL
const actionUrl = ref(BASE_URL + '/admin-api/mp/material/upload-permanent') // 上传永久素材的地址
const headers = ref({ Authorization: 'Bearer ' + getAccessToken() }) // 设置上传的请求头部
const fileList = ref([])
@@ -305,11 +297,10 @@ const dialogImageVisible = ref(false)
const operateMaterial = ref('add')
const articlesMediaId = ref('')
const hackResetEditor = ref(false)
// 公众号账号列表
const accountList = ref([])
/** 初始化 **/
onMounted(async () => {
accountList.value = await getSimpleAccountList()
accountList.value = await MpAccountApi.getSimpleAccountList()
// 选中第一个
if (accountList.value.length > 0) {
// @ts-ignore
@@ -335,7 +326,7 @@ const getList = async () => {
loading.value = true
try {
const drafts = await getDraftPage(queryParams)
const drafts = await MpDraftApi.getDraftPage(queryParams)
drafts.list.forEach((item) => {
const newsItem = item.content.newsItem
// 将 thumbUrl 转成 picUrl保证 wx-news 组件可以预览封面
@@ -393,9 +384,10 @@ const handleUpdate = (item) => {
/** 提交按钮 */
const submitForm = () => {
// TODO @Dhb52: 参考别的模块写法,改成 await 方式
addMaterialLoading.value = true
if (operateMaterial.value === 'add') {
createDraft(queryParams.accountId, articlesAdd.value)
MpDraftApi.createDraft(queryParams.accountId, articlesAdd.value)
.then(() => {
message.notifySuccess('新增成功')
dialogNewsVisible.value = false
@@ -405,7 +397,7 @@ const submitForm = () => {
addMaterialLoading.value = false
})
} else {
updateDraft(queryParams.accountId, articlesMediaId.value, articlesAdd.value)
MpDraftApi.updateDraft(queryParams.accountId, articlesMediaId.value, articlesAdd.value)
.then(() => {
message.notifySuccess('更新成功')
dialogNewsVisible.value = false
@@ -559,24 +551,24 @@ const handlePublish = async (item) => {
'你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。'
try {
await message.confirm(content)
await submitFreePublish(accountId, mediaId)
getList()
await MpFreePublishApi.submitFreePublish(accountId, mediaId)
message.notifySuccess('发布成功')
await getList()
} catch {}
}
/** 删除按钮操作 */
const handleDelete = async (item) => {
const accountId = queryParams.accountId
const mediaId = item.mediaId
try {
await message.confirm('此操作将永久删除该草稿, 是否继续?')
await deleteDraft(accountId, mediaId)
getList()
await MpDraftApi.deleteDraft(accountId, mediaId)
message.notifySuccess('删除成功')
await getList()
} catch {}
}
</script>
<style lang="scss" scoped>
.pagination {
float: right;

View File

@@ -59,7 +59,7 @@
</ContentWrap>
</template>
<script setup lang="ts" name="freePublish">
<script setup lang="ts" name="MpFreePublish">
import * as FreePublishApi from '@/api/mp/freePublish'
import * as MpAccountApi from '@/api/mp/account'
import WxNews from '@/views/mp/components/wx-news/main.vue'

View File

@@ -1,32 +1,32 @@
<template>
<div class="app-container">
<doc-alert title="公众号素材" url="https://doc.iocoder.cn/mp/material/" />
<!-- 搜索工作栏 -->
<doc-alert title="公众号素材" url="https://doc.iocoder.cn/mp/material/" />
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
size="small"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号" class="!w-240px">
<el-option
v-for="item in accounts"
:key="parseInt(item.id)"
v-for="item in accountList"
:key="item.id"
:label="item.name"
:value="parseInt(item.id)"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-tabs v-model="type" @tab-change="handleTabChange">
<!-- tab 1图片 -->
<el-tab-pane name="image">
@@ -44,11 +44,11 @@
:before-upload="beforeImageUpload"
:on-success="handleUploadSuccess"
>
<el-button size="small" type="primary">点击上传</el-button>
<el-button type="primary" plain>点击上传</el-button>
<template #tip>
<span class="el-upload__tip" style="margin-left: 5px"
>支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M</span
>
<span class="el-upload__tip" style="margin-left: 5px">
支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M
</span>
</template>
</el-upload>
</div>
@@ -64,14 +64,14 @@
circle
@click="handleDelete(item)"
v-hasPermi="['mp:material:delete']"
><Icon icon="ep:delete"
/></el-button>
>
<Icon icon="ep:delete" />
</el-button>
</el-row>
</div>
</div>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@@ -95,11 +95,11 @@
:on-success="handleUploadSuccess"
:before-upload="beforeVoiceUpload"
>
<el-button size="small" type="primary">点击上传</el-button>
<el-button type="primary" plain>点击上传</el-button>
<template #tip>
<span class="el-upload__tip" style="margin-left: 5px"
>格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s</span
>
<span class="el-upload__tip" style="margin-left: 5px">
格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s
</span>
</template>
</el-upload>
</div>
@@ -118,24 +118,23 @@
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button type="primary" link size="small" plain @click="handleDownload(scope.row)"
><Icon icon="ep:download" />下载</el-button
>
<el-button type="primary" link plain @click="handleDownload(scope.row)">
<Icon icon="ep:download" />下载
</el-button>
<el-button
type="primary"
link
size="small"
plain
@click="handleDelete(scope.row)"
v-hasPermi="['mp:material:delete']"
><Icon icon="ep:delete" />删除</el-button
>
<Icon icon="ep:delete" />删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@@ -149,7 +148,7 @@
<span><Icon icon="ep:video-play" /> 视频</span>
</template>
<div class="add_but" v-hasPermi="['mp:material:upload-permanent']">
<el-button size="small" type="primary" @click="handleAddVideo">新建视频</el-button>
<el-button type="primary" plain @click="handleAddVideo">新建视频</el-button>
</div>
<!-- 新建视频的弹窗 -->
<el-dialog
@@ -220,14 +219,9 @@
<span>{{ formatDate(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
fixed="right"
class-name="small-padding fixed-width"
>
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" link size="small" plain @click="handleDownload(scope.row)"
<el-button type="primary" link plain @click="handleDownload(scope.row)"
><Icon icon="ep:download" />下载</el-button
>
<el-button
@@ -237,14 +231,14 @@
plain
@click="handleDelete(scope.row)"
v-hasPermi="['mp:material:delete']"
><Icon icon="ep:delete" />删除</el-button
>
<Icon icon="ep:delete" />删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@@ -252,11 +246,9 @@
/>
</el-tab-pane>
</el-tabs>
</div>
</ContentWrap>
</template>
<script setup>
import { ref } from 'vue'
<script setup name="MpMaterial">
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
import { getSimpleAccountList } from '@/api/mp/account'
@@ -275,8 +267,6 @@ const uploadVideoRef = ref()
const type = ref('image')
// 遮罩层
const loading = ref(false)
// 显示搜索条件
const showSearch = ref(true)
// 总条数
const total = ref(0)
// 数据列表
@@ -308,14 +298,14 @@ const uploadRules = reactive({
})
// 公众号账号列表
const accounts = ref([])
const accountList = ref([])
onMounted(() => {
getSimpleAccountList().then((data) => {
accounts.value = data
accountList.value = data
// 默认选中第一个
if (accounts.value.length > 0) {
setAccountId(accounts.value[0].id)
if (accountList.value.length > 0) {
setAccountId(accountList.value[0].id)
}
// 加载数据
getList()
@@ -365,8 +355,8 @@ const handleQuery = () => {
const resetQuery = () => {
queryFormRef.value?.resetFields()
// 默认选中第一个
if (accounts.value.length > 0) {
setAccountId(accounts.value[0].id)
if (accountList.value.length > 0) {
setAccountId(accountList.value[0].id)
}
handleQuery()
}

View File

@@ -1,25 +1,32 @@
<template>
<div class="app-container">
<doc-alert title="公众号菜单" url="https://doc.iocoder.cn/mp/menu/" />
<!-- 搜索工作栏 -->
<el-form ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<doc-alert title="公众号菜单" url="https://doc.iocoder.cn/mp/menu/" />
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form
class="-mb-15px"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="公众号" prop="accountId">
<el-select v-model="accountId" placeholder="请选择公众号">
<el-select v-model="accountId" placeholder="请选择公众号" class="!w-240px">
<el-option
v-for="item in accountList"
:key="parseInt(item.id)"
:key="item.id"
:label="item.name"
:value="parseInt(item.id)"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<div class="public-account-management clearfix" v-loading="loading">
<!--左边配置菜单-->
<div class="left">
@@ -63,7 +70,6 @@
<el-button
class="save_btn"
type="success"
size="small"
@click="handleSave"
v-hasPermi="['mp:menu:save']"
>保存并发布菜单</el-button
@@ -71,7 +77,6 @@
<el-button
class="save_btn"
type="danger"
size="small"
@click="handleDelete"
v-hasPermi="['mp:menu:delete']"
>清空菜单</el-button
@@ -82,9 +87,9 @@
<div v-if="showRightFlag" class="right">
<div class="configure_page">
<div class="delete_btn">
<el-button size="small" type="danger" @click="handleDeleteMenu(tempObj)"
>删除当前菜单<Icon icon="ep:delete"
/></el-button>
<el-button size="small" type="danger" @click="handleDeleteMenu(tempObj)">
删除当前菜单<Icon icon="ep:delete" />
</el-button>
</div>
<div>
<span>菜单名称</span>
@@ -161,9 +166,9 @@
<div class="select-item" v-if="tempObj && tempObj.replyArticles">
<WxNews :articles="tempObj.replyArticles" />
<el-row class="ope-row" justify="center" align="middle">
<el-button type="danger" circle @click="deleteMaterial"
><icon icon="ep:delete"
/></el-button>
<el-button type="danger" circle @click="deleteMaterial">
<icon icon="ep:delete" />
</el-button>
</el-row>
</div>
<div v-else>
@@ -197,33 +202,25 @@
<p>请选择菜单配置</p>
</div>
</div>
</div>
</ContentWrap>
</template>
<script setup>
import { ref, nextTick } from 'vue'
<script setup name="MpMenu">
import { handleTree } from '@/utils/tree'
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
import WxNews from '@/views/mp/components/wx-news/main.vue'
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
import { deleteMenu, getMenuList, saveMenu } from '@/api/mp/menu'
import { getSimpleAccountList } from '@/api/mp/account'
import { handleTree } from '@/utils/tree'
import * as MpAccountApi from '@/api/mp/account'
import menuOptions from './menuOptions'
const message = useMessage()
const message = useMessage() // 消息
// ======================== 列表查询 ========================
// 遮罩层
const loading = ref(true)
// 显示搜索条件
const showSearch = ref(true)
// 公众号Id
const accountId = ref(undefined)
// 公众号名
const name = ref('')
const loading = ref(true) // 遮罩层
const accountId = ref(undefined) // 公众号Id
const name = ref('') // 公众号名
const menuList = ref({ children: [] })
const accountList = ref([]) // 公众号账号列表
// const menuList = ref(menuListData)
// ======================== 菜单操作 ========================
const isActive = ref(-1) // 一级菜单点中样式
const isSubMenuActive = ref(-1) // 一级菜单点中样式
@@ -241,11 +238,8 @@ const tempSelfObj = ref({
})
const dialogNewsVisible = ref(false) // 跳转图文时的素材选择弹窗
// 公众号账号列表
const accountList = ref([])
onMounted(async () => {
accountList.value = await getSimpleAccountList()
accountList.value = await MpAccountApi.getSimpleAccountList()
// 选中第一个
if (accountList.value.length > 0) {
// @ts-ignore

View File

@@ -1,293 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="公众号粉丝" url="https://doc.iocoder.cn/mp/user/" />
<!-- 搜索工作栏 -->
<el-form
:model="queryParams"
ref="queryFormRef"
size="small"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option
v-for="item in accounts"
:key="parseInt(item.id)"
:label="item.name"
:value="parseInt(item.id)"
/>
</el-select>
</el-form-item>
<el-form-item label="用户标识" prop="openid">
<el-input
v-model="queryParams.openid"
placeholder="请输入用户标识"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input
v-model="queryParams.nickname"
placeholder="请输入昵称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-refresh"
size="small"
@click="handleSync"
v-hasPermi="['mp:user:sync']"
>同步
</el-button>
</el-col>
<!-- <right-toolbar :showSearch="showSearch" @query-table="getList" /> -->
</el-row>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="用户标识" align="center" prop="openid" width="260" />
<el-table-column label="昵称" align="center" prop="nickname" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="标签" align="center" prop="tagIds" width="200">
<template #default="scope">
<span v-for="(tagId, index) in scope.row.tagIds" :key="index">
<el-tag>{{ tags.find((tag) => tag.tagId === tagId)?.name }} </el-tag>&nbsp;
</span>
</template>
</el-table-column>
<el-table-column label="订阅状态" align="center" prop="subscribeStatus">
<template #default="scope">
<el-tag v-if="scope.row.subscribeStatus === 0" type="success">已订阅</el-tag>
<el-tag v-else type="danger">未订阅</el-tag>
</template>
</el-table-column>
<el-table-column label="订阅时间" align="center" prop="subscribeTime" width="180">
<template #default="scope">
<span>{{ formatDate(scope.row.subscribeTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<div class="flex justify-center items-center">
<el-button
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['mp:user:update']"
>
<Icon icon="ep:edit" />修改
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</ContentWrap>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="昵称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入昵称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="标签" prop="tagIds">
<el-select v-model="form.tagIds" multiple clearable placeholder="请选择标签">
<el-option
v-for="item in tags"
:key="parseInt(item.tagId)"
:label="item.name"
:value="parseInt(item.tagId)"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="MpUser">
import { ref, reactive } from 'vue'
import { updateUser, getUser, getUserPage, syncUser } from '@/api/mp/mpuser'
import { getSimpleAccountList } from '@/api/mp/account'
import { getSimpleTagList } from '@/api/mp/tag'
import { formatDate } from '@/utils/formatTime'
const message = useMessage()
const formRef = ref()
const queryFormRef = ref()
// 遮罩层
const loading = ref(true)
// 显示搜索条件
const showSearch = ref(true)
// 总条数
const total = ref(0)
// 微信公众号粉丝列表
const list = ref([])
// 弹出层标题
const title = ref('')
// 是否显示弹出层
const open = ref(false)
// 查询参数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
accountId: null,
openid: null,
nickname: null
})
// 表单参数
const form = ref({})
// 表单校验
const rules = ref({})
// 公众号账号列表
const accounts = ref([])
// 公众号标签列表
const tags = ref([])
onMounted(() => {
getSimpleAccountList().then((data) => {
accounts.value = data
// 默认选中第一个
if (accounts.value.length > 0) {
queryParams.accountId = accounts.value[0].id
}
// 加载数据
getList()
})
// 加载标签
getSimpleTagList().then((data) => {
tags.value = data
})
})
/** 查询列表 */
const getList = () => {
// 如果没有选中公众号账号,则进行提示。
if (!queryParams.accountId) {
message.error('未选中公众号,无法查询用户')
return false
}
loading.value = true
// 处理查询参数
let params = { ...queryParams }
// 执行查询
getUserPage(params)
.then((data) => {
list.value = data.list
total.value = data.total
})
.finally(() => {
loading.value = false
})
}
/** 取消按钮 */
const cancel = () => {
open.value = false
reset()
}
/** 表单重置 */
const reset = () => {
form.value = {
id: undefined,
nickname: undefined,
remark: undefined,
tagIds: []
}
formRef.value?.resetFields()
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
// 默认选中第一个
if (accounts.value.length > 0) {
queryParams.accountId = accounts.value[0].id
}
handleQuery()
}
/** 修改按钮操作 */
const handleUpdate = (row) => {
reset()
getUser(row.id).then((data) => {
form.value = data
open.value = true
title.value = '修改公众号粉丝'
})
}
/** 提交按钮 */
const submitForm = () => {
formRef.value.validate((valid) => {
if (!valid) {
return
}
// 修改的提交
if (form.value.id != null) {
updateUser(form.value).then(() => {
message.success('修改成功')
open.value = false
getList()
})
}
})
}
/** 同步标签 */
const handleSync = async () => {
const accountId = queryParams.accountId
try {
await message.confirm('是否确认同步粉丝?')
await syncUser(accountId)
message.success('开始从微信公众号同步粉丝信息,同步需要一段时间,建议稍后再查询')
} catch {}
}
</script>

View File

@@ -0,0 +1,99 @@
<template>
<Dialog title="修改" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="80px"
v-loading="formLoading"
>
<el-form-item label="昵称" prop="nickname">
<el-input v-model="formData.nickname" placeholder="请输入昵称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="标签" prop="tagIds">
<el-select v-model="formData.tagIds" multiple clearable placeholder="请选择标签">
<el-option
v-for="item in tagList"
:key="item.tagId"
:label="item.name"
:value="item.tagId"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import * as MpTagApi from '@/api/mp/tag'
import * as MpUserApi from '@/api/mp/user'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const formData = ref({
id: undefined,
nickname: undefined,
remark: undefined,
tagIds: []
})
const formRules = reactive({}) // 表单的校验
const formRef = ref() // 表单 Ref
const tagList = ref([]) // 公众号标签列表
/** 打开弹窗 */
const open = async (id: number) => {
dialogVisible.value = true
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await MpUserApi.getUser(id)
} finally {
formLoading.value = false
}
}
// 加载标签
tagList.value = await MpTagApi.getSimpleTagList()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await MpUserApi.updateUser(formData.value)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
nickname: undefined,
remark: undefined,
tagIds: []
}
formRef.value?.resetFields()
}
</script>

187
src/views/mp/user/index.vue Normal file
View File

@@ -0,0 +1,187 @@
<template>
<doc-alert title="公众号粉丝" url="https://doc.iocoder.cn/mp/user/" />
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号" class="!w-240px">
<el-option
v-for="item in accountList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="用户标识" prop="openid">
<el-input
v-model="queryParams.openid"
placeholder="请输入用户标识"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input
v-model="queryParams.nickname"
placeholder="请输入昵称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
<el-button type="success" plain @click="handleSync" v-hasPermi="['mp:user:sync']">
<Icon icon="ep:refresh" class="mr-5px" /> 同步
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="用户标识" align="center" prop="openid" width="260" />
<el-table-column label="昵称" align="center" prop="nickname" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="标签" align="center" prop="tagIds" width="200">
<template #default="scope">
<span v-for="(tagId, index) in scope.row.tagIds" :key="index">
<el-tag>{{ tagList.find((tag) => tag.tagId === tagId)?.name }} </el-tag>&nbsp;
</span>
</template>
</el-table-column>
<el-table-column label="订阅状态" align="center" prop="subscribeStatus">
<template #default="scope">
<el-tag v-if="scope.row.subscribeStatus === 0" type="success">已订阅</el-tag>
<el-tag v-else type="danger">未订阅</el-tag>
</template>
</el-table-column>
<el-table-column
label="订阅时间"
align="center"
prop="subscribeTime"
width="180"
:formatter="dateFormatter"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
type="primary"
link
@click="openForm(scope.row.id)"
v-hasPermi="['mp:user:update']"
>
修改
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗修改 -->
<UserForm ref="formRef" @success="getList" />
</template>
<script lang="ts" setup name="MpUser">
import { dateFormatter } from '@/utils/formatTime'
import * as MpAccountApi from '@/api/mp/account'
import * as MpUserApi from '@/api/mp/user'
import * as MpTagApi from '@/api/mp/tag'
import UserForm from './UserForm.vue'
const message = useMessage() // 消息
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
accountId: null,
openid: null,
nickname: null
})
const queryFormRef = ref() // 搜索的表单
const accountList = ref([]) // 公众号账号列表
const tagList = ref([]) // 公众号标签列表
/** 查询列表 */
const getList = async () => {
// 如果没有选中公众号账号,则进行提示。
if (!queryParams.accountId) {
message.error('未选中公众号,无法查询用户')
return false
}
try {
loading.value = true
const data = await MpUserApi.getUserPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
// 默认选中第一个
if (accountList.value.length > 0) {
queryParams.accountId = accountList.value[0].id
}
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (id: number) => {
formRef.value.open(id)
}
/** 同步标签 */
const handleSync = async () => {
const accountId = queryParams.accountId
try {
await message.confirm('是否确认同步粉丝?')
await MpUserApi.syncUser(accountId)
message.success('开始从微信公众号同步粉丝信息,同步需要一段时间,建议稍后再查询')
await getList()
} catch {}
}
/** 初始化 */
onMounted(async () => {
// 加载标签
tagList.value = await MpTagApi.getSimpleTagList()
// 加载账号
accountList.value = await MpAccountApi.getSimpleAccountList()
if (accountList.value.length > 0) {
queryParams.accountId = accountList.value[0].id
}
await getList()
})
</script>