mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-10-31 02:08:45 +08:00 
			
		
		
		
	refactor: mp/draft拆分组件
This commit is contained in:
		
							
								
								
									
										183
									
								
								src/views/mp/draft/components/CoverSelect.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/views/mp/draft/components/CoverSelect.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | |||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <p>封面:</p> | ||||||
|  |     <div class="thumb-div"> | ||||||
|  |       <el-image | ||||||
|  |         v-if="newsItem.thumbUrl" | ||||||
|  |         style="width: 300px; max-height: 300px" | ||||||
|  |         :src="newsItem.thumbUrl" | ||||||
|  |         fit="contain" | ||||||
|  |       /> | ||||||
|  |       <Icon | ||||||
|  |         v-else | ||||||
|  |         icon="ep:plus" | ||||||
|  |         class="avatar-uploader-icon" | ||||||
|  |         :class="isFirst ? 'avatar' : 'avatar1'" | ||||||
|  |       /> | ||||||
|  |       <div class="thumb-but"> | ||||||
|  |         <el-upload | ||||||
|  |           :action="UPLOAD_URL" | ||||||
|  |           :headers="HEADERS" | ||||||
|  |           multiple | ||||||
|  |           :limit="1" | ||||||
|  |           :file-list="fileList" | ||||||
|  |           :data="uploadData" | ||||||
|  |           :before-upload="onBeforeUpload" | ||||||
|  |           :on-error="onUploadError" | ||||||
|  |           :on-success="onUploadSuccess" | ||||||
|  |         > | ||||||
|  |           <template #trigger> | ||||||
|  |             <el-button size="small" type="primary" :loading="isUploading" disabled="isUploading"> | ||||||
|  |               {{ isUploading ? '正在上传' : '本地上传' }} | ||||||
|  |             </el-button> | ||||||
|  |           </template> | ||||||
|  |           <el-button | ||||||
|  |             size="small" | ||||||
|  |             type="primary" | ||||||
|  |             @click="showImageDialog = true" | ||||||
|  |             style="margin-left: 5px" | ||||||
|  |           > | ||||||
|  |             素材库选择 | ||||||
|  |           </el-button> | ||||||
|  |           <template #tip> | ||||||
|  |             <div class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div> | ||||||
|  |           </template> | ||||||
|  |         </el-upload> | ||||||
|  |       </div> | ||||||
|  |       <el-dialog | ||||||
|  |         title="选择图片" | ||||||
|  |         v-model="showImageDialog" | ||||||
|  |         width="80%" | ||||||
|  |         append-to-body | ||||||
|  |         destroy-on-close | ||||||
|  |       > | ||||||
|  |         <WxMaterialSelect | ||||||
|  |           :objData="{ type: 'image', accountId: accountId }" | ||||||
|  |           @select-material="onMaterialSelected" | ||||||
|  |         /> | ||||||
|  |       </el-dialog> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' | ||||||
|  | import { getAccessToken } from '@/utils/auth' | ||||||
|  | import type { UploadFiles, UploadProps, UploadRawFile } from 'element-plus' | ||||||
|  | import { NewsItem } from './types' | ||||||
|  |  | ||||||
|  | const message = useMessage() | ||||||
|  |  | ||||||
|  | const UPLOAD_URL = 'http://localhost:8000/upload/' //import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传永久素材的地址 | ||||||
|  | const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 设置上传的请求头部 | ||||||
|  |  | ||||||
|  | const props = defineProps<{ | ||||||
|  |   modelValue: NewsItem | ||||||
|  |   isFirst: boolean | ||||||
|  | }>() | ||||||
|  |  | ||||||
|  | const emit = defineEmits<{ | ||||||
|  |   (e: 'update:modelValue', v: NewsItem) | ||||||
|  | }>() | ||||||
|  | const newsItem = computed<NewsItem>({ | ||||||
|  |   get() { | ||||||
|  |     return props.modelValue | ||||||
|  |   }, | ||||||
|  |   set(val) { | ||||||
|  |     emit('update:modelValue', val) | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | const accountId = inject<number>('accountId') | ||||||
|  | const showImageDialog = ref(false) | ||||||
|  |  | ||||||
|  | const fileList = ref<UploadFiles>([]) | ||||||
|  | interface UploadData { | ||||||
|  |   type: 'image' | 'video' | 'audio' | ||||||
|  |   accountId?: number | ||||||
|  | } | ||||||
|  | const uploadData: UploadData = reactive({ | ||||||
|  |   type: 'image', | ||||||
|  |   accountId: accountId | ||||||
|  | }) | ||||||
|  | const isUploading = ref(false) | ||||||
|  |  | ||||||
|  | /** 素材选择完成事件*/ | ||||||
|  | const onMaterialSelected = (item: any) => { | ||||||
|  |   showImageDialog.value = false | ||||||
|  |   newsItem.value.thumbMediaId = item.mediaId | ||||||
|  |   newsItem.value.thumbUrl = item.url | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ======================== 文件上传 ======================== | ||||||
|  | const onBeforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => { | ||||||
|  |   const isType = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/jpg'].includes( | ||||||
|  |     rawFile.type | ||||||
|  |   ) | ||||||
|  |   if (!isType) { | ||||||
|  |     message.error('上传图片格式不对!') | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (rawFile.size / 1024 / 1024 > 2) { | ||||||
|  |     message.error('上传图片大小不能超过 2M!') | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  |   // 校验通过 | ||||||
|  |   return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const onUploadSuccess: UploadProps['onSuccess'] = (res: any) => { | ||||||
|  |   if (res.code !== 0) { | ||||||
|  |     message.error('上传出错:' + res.msg) | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // 重置上传文件的表单 | ||||||
|  |   fileList.value = [] | ||||||
|  |  | ||||||
|  |   // 设置草稿的封面字段 | ||||||
|  |   newsItem.value.thumbMediaId = res.data.mediaId | ||||||
|  |   newsItem.value.thumbUrl = res.data.url | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const onUploadError = (err: Error) => { | ||||||
|  |   message.error('上传失败: ' + err.message) | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .el-upload__tip { | ||||||
|  |   margin-left: 5px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .thumb-div { | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 100%; | ||||||
|  |   text-align: center; | ||||||
|  |  | ||||||
|  |   .avatar-uploader-icon { | ||||||
|  |     width: 120px; | ||||||
|  |     height: 120px; | ||||||
|  |     font-size: 28px; | ||||||
|  |     line-height: 120px; | ||||||
|  |     color: #8c939d; | ||||||
|  |     text-align: center; | ||||||
|  |     border: 1px solid #d9d9d9; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .avatar { | ||||||
|  |     width: 230px; | ||||||
|  |     height: 120px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .avatar1 { | ||||||
|  |     width: 120px; | ||||||
|  |     height: 120px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .thumb-but { | ||||||
|  |     margin: 5px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										87
									
								
								src/views/mp/draft/components/DraftTable.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/views/mp/draft/components/DraftTable.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="waterfall" v-loading="props.loading"> | ||||||
|  |     <template v-for="item in props.list" :key="item.articleId"> | ||||||
|  |       <div class="waterfall-item" v-if="item.content && item.content.newsItem"> | ||||||
|  |         <WxNews :articles="item.content.newsItem" /> | ||||||
|  |         <!-- 操作按钮 --> | ||||||
|  |         <el-row> | ||||||
|  |           <el-button | ||||||
|  |             type="success" | ||||||
|  |             circle | ||||||
|  |             @click="emit('publish', item)" | ||||||
|  |             v-hasPermi="['mp:free-publish:submit']" | ||||||
|  |           > | ||||||
|  |             <Icon icon="fa:upload" /> | ||||||
|  |           </el-button> | ||||||
|  |           <el-button | ||||||
|  |             type="primary" | ||||||
|  |             circle | ||||||
|  |             @click="emit('update', item)" | ||||||
|  |             v-hasPermi="['mp:draft:update']" | ||||||
|  |           > | ||||||
|  |             <Icon icon="ep:edit" /> | ||||||
|  |           </el-button> | ||||||
|  |           <el-button | ||||||
|  |             type="danger" | ||||||
|  |             circle | ||||||
|  |             @click="emit('delete', item)" | ||||||
|  |             v-hasPermi="['mp:draft:delete']" | ||||||
|  |           > | ||||||
|  |             <Icon icon="ep:delete" /> | ||||||
|  |           </el-button> | ||||||
|  |         </el-row> | ||||||
|  |       </div> | ||||||
|  |     </template> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||||
|  |  | ||||||
|  | import { Article } from './types' | ||||||
|  |  | ||||||
|  | const props = defineProps<{ | ||||||
|  |   list: Article[] | ||||||
|  |   loading: boolean | ||||||
|  | }>() | ||||||
|  |  | ||||||
|  | const emit = defineEmits<{ | ||||||
|  |   (e: 'publish', v: Article) | ||||||
|  |   (e: 'update', v: Article) | ||||||
|  |   (e: 'delete', v: Article) | ||||||
|  | }>() | ||||||
|  | </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; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media (min-width: 992px) and (max-width: 1300px) { | ||||||
|  |   .waterfall { | ||||||
|  |     column-count: 3; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media (min-width: 768px) and (max-width: 991px) { | ||||||
|  |   .waterfall { | ||||||
|  |     column-count: 2; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media (max-width: 767px) { | ||||||
|  |   .waterfall { | ||||||
|  |     column-count: 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										302
									
								
								src/views/mp/draft/components/NewsForm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								src/views/mp/draft/components/NewsForm.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,302 @@ | |||||||
|  | <template> | ||||||
|  |   <el-container> | ||||||
|  |     <el-aside width="40%"> | ||||||
|  |       <div class="select-item"> | ||||||
|  |         <div v-for="(news, index) in newsList" :key="index"> | ||||||
|  |           <div | ||||||
|  |             class="news-main father" | ||||||
|  |             v-if="index === 0" | ||||||
|  |             :class="{ activeAddNews: activeNewsIndex === index }" | ||||||
|  |             @click="activeNewsIndex = index" | ||||||
|  |           > | ||||||
|  |             <div class="news-content"> | ||||||
|  |               <img class="material-img" :src="news.thumbUrl" /> | ||||||
|  |               <div class="news-content-title">{{ news.title }}</div> | ||||||
|  |             </div> | ||||||
|  |             <div class="child" v-if="newsList.length > 1"> | ||||||
|  |               <el-button type="info" circle size="small" @click="() => moveDownNews(index)"> | ||||||
|  |                 <Icon icon="ep:arrow-down-bold" /> | ||||||
|  |               </el-button> | ||||||
|  |               <el-button | ||||||
|  |                 v-if="isCreating" | ||||||
|  |                 type="danger" | ||||||
|  |                 circle | ||||||
|  |                 size="small" | ||||||
|  |                 @click="() => removeNews(index)" | ||||||
|  |               > | ||||||
|  |                 <Icon icon="ep:delete" /> | ||||||
|  |               </el-button> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div | ||||||
|  |             class="news-main-item father" | ||||||
|  |             v-if="index > 0" | ||||||
|  |             :class="{ activeAddNews: activeNewsIndex === index }" | ||||||
|  |             @click="activeNewsIndex = index" | ||||||
|  |           > | ||||||
|  |             <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" width="100%" /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="child"> | ||||||
|  |               <el-button | ||||||
|  |                 v-if="newsList.length > index + 1" | ||||||
|  |                 circle | ||||||
|  |                 type="info" | ||||||
|  |                 size="small" | ||||||
|  |                 @click="() => moveDownNews(index)" | ||||||
|  |               > | ||||||
|  |                 <Icon icon="ep:arrow-down-bold" /> | ||||||
|  |               </el-button> | ||||||
|  |               <el-button | ||||||
|  |                 v-if="index > 0" | ||||||
|  |                 type="info" | ||||||
|  |                 circle | ||||||
|  |                 size="small" | ||||||
|  |                 @click="() => moveUpNews(index)" | ||||||
|  |               > | ||||||
|  |                 <Icon icon="ep:arrow-up-bold" /> | ||||||
|  |               </el-button> | ||||||
|  |               <el-button | ||||||
|  |                 v-if="isCreating" | ||||||
|  |                 type="danger" | ||||||
|  |                 size="small" | ||||||
|  |                 circle | ||||||
|  |                 @click="() => removeNews(index)" | ||||||
|  |               > | ||||||
|  |                 <Icon icon="ep:delete" /> | ||||||
|  |               </el-button> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <el-row justify="center" class="ope-row"> | ||||||
|  |           <el-button | ||||||
|  |             type="primary" | ||||||
|  |             circle | ||||||
|  |             @click="plusNews" | ||||||
|  |             v-if="newsList.length < 8 && isCreating" | ||||||
|  |           > | ||||||
|  |             <Icon icon="ep:plus" /> | ||||||
|  |           </el-button> | ||||||
|  |         </el-row> | ||||||
|  |       </div> | ||||||
|  |     </el-aside> | ||||||
|  |     <el-main> | ||||||
|  |       <div v-if="newsList.length > 0"> | ||||||
|  |         <!-- 标题、作者、原文地址 --> | ||||||
|  |         <el-row :gutter="20"> | ||||||
|  |           <el-input v-model="activeNewsItem.title" placeholder="请输入标题(必填)" /> | ||||||
|  |           <el-input | ||||||
|  |             v-model="activeNewsItem.author" | ||||||
|  |             placeholder="请输入作者" | ||||||
|  |             style="margin-top: 5px" | ||||||
|  |           /> | ||||||
|  |           <el-input | ||||||
|  |             v-model="activeNewsItem.contentSourceUrl" | ||||||
|  |             placeholder="请输入原文地址" | ||||||
|  |             style="margin-top: 5px" | ||||||
|  |           /> | ||||||
|  |         </el-row> | ||||||
|  |         <!-- 封面和摘要 --> | ||||||
|  |         <el-row :gutter="20"> | ||||||
|  |           <el-col :span="12"> | ||||||
|  |             <CoverSelect v-model="activeNewsItem" :is-first="activeNewsIndex === 0" /> | ||||||
|  |           </el-col> | ||||||
|  |           <el-col :span="12"> | ||||||
|  |             <p>摘要:</p> | ||||||
|  |             <el-input | ||||||
|  |               :rows="8" | ||||||
|  |               type="textarea" | ||||||
|  |               v-model="activeNewsItem.digest" | ||||||
|  |               placeholder="请输入摘要" | ||||||
|  |               class="digest" | ||||||
|  |               maxlength="120" | ||||||
|  |             /> | ||||||
|  |           </el-col> | ||||||
|  |         </el-row> | ||||||
|  |         <!--富文本编辑器组件--> | ||||||
|  |         <el-row> | ||||||
|  |           <Editor v-model="activeNewsItem.content" :editor-config="editorConfig" /> | ||||||
|  |         </el-row> | ||||||
|  |       </div> | ||||||
|  |     </el-main> | ||||||
|  |   </el-container> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { Editor } from '@/components/Editor' | ||||||
|  | import { createEditorConfig } from '../editor-config' | ||||||
|  | import CoverSelect from './CoverSelect.vue' | ||||||
|  | import { type NewsItem, createEmptyNewsItem } from './types' | ||||||
|  |  | ||||||
|  | const message = useMessage() | ||||||
|  |  | ||||||
|  | const props = defineProps<{ | ||||||
|  |   isCreating: boolean | ||||||
|  |   modelValue: NewsItem[] | null | ||||||
|  | }>() | ||||||
|  |  | ||||||
|  | const accountId = inject<number>('accountId') | ||||||
|  |  | ||||||
|  | // ========== 文件上传 ========== | ||||||
|  | const UPLOAD_URL = import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传永久素材的地址 | ||||||
|  | const editorConfig = createEditorConfig(UPLOAD_URL, accountId) | ||||||
|  |  | ||||||
|  | // v-model=newsList | ||||||
|  | const emit = defineEmits<{ | ||||||
|  |   (e: 'update:modelValue', v: NewsItem[]) | ||||||
|  | }>() | ||||||
|  | const newsList = computed<NewsItem[]>({ | ||||||
|  |   get() { | ||||||
|  |     return props.modelValue === null ? [createEmptyNewsItem()] : props.modelValue | ||||||
|  |   }, | ||||||
|  |   set(val) { | ||||||
|  |     emit('update:modelValue', val) | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | const activeNewsIndex = ref(0) | ||||||
|  | const activeNewsItem = computed<NewsItem>(() => newsList.value[activeNewsIndex.value]) | ||||||
|  |  | ||||||
|  | // 将图文向下移动 | ||||||
|  | const moveDownNews = (index: number) => { | ||||||
|  |   const temp = newsList.value[index] | ||||||
|  |   newsList.value[index] = newsList.value[index + 1] | ||||||
|  |   newsList.value[index + 1] = temp | ||||||
|  |   activeNewsIndex.value = index + 1 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 将图文向上移动 | ||||||
|  | const moveUpNews = (index: number) => { | ||||||
|  |   const temp = newsList.value[index] | ||||||
|  |   newsList.value[index] = newsList.value[index - 1] | ||||||
|  |   newsList.value[index - 1] = temp | ||||||
|  |   activeNewsIndex.value = index - 1 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 删除指定 index 的图文 | ||||||
|  | const removeNews = async (index: number) => { | ||||||
|  |   try { | ||||||
|  |     await message.confirm('确定删除该图文吗?') | ||||||
|  |     newsList.value.splice(index, 1) | ||||||
|  |     if (activeNewsIndex.value === index) { | ||||||
|  |       activeNewsIndex.value = 0 | ||||||
|  |     } | ||||||
|  |   } catch {} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 添加一个图文 | ||||||
|  | const plusNews = () => { | ||||||
|  |   newsList.value.push(createEmptyNewsItem()) | ||||||
|  |   activeNewsIndex.value = newsList.value.length - 1 | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .ope-row { | ||||||
|  |   padding-top: 5px; | ||||||
|  |   margin-top: 5px; | ||||||
|  |   text-align: center; | ||||||
|  |   border-top: 1px solid #eaeaea; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .el-row { | ||||||
|  |   margin-bottom: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .el-row:last-child { | ||||||
|  |   margin-bottom: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .digest { | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 100%; | ||||||
|  |   vertical-align: top; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 新增图文 */ | ||||||
|  | .news-main { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 120px; | ||||||
|  |   margin: auto; | ||||||
|  |   background-color: #fff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .news-content { | ||||||
|  |   position: relative; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 120px; | ||||||
|  |   background-color: #acadae; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .news-content-title { | ||||||
|  |   position: absolute; | ||||||
|  |   bottom: 0; | ||||||
|  |   left: 0; | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 98%; | ||||||
|  |   height: 25px; | ||||||
|  |   padding: 1%; | ||||||
|  |   overflow: hidden; | ||||||
|  |   font-size: 15px; | ||||||
|  |   color: #fff; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  |   white-space: nowrap; | ||||||
|  |   background-color: black; | ||||||
|  |   opacity: 0.65; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .news-main-item { | ||||||
|  |   width: 100%; | ||||||
|  |   padding: 5px 0; | ||||||
|  |   margin: auto; | ||||||
|  |   background-color: #fff; | ||||||
|  |   border-top: 1px solid #eaeaea; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .news-content-item { | ||||||
|  |   position: relative; | ||||||
|  |   margin-left: -3px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .news-content-item-title { | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 70%; | ||||||
|  |   font-size: 12px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .news-content-item-img { | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 25%; | ||||||
|  |   background-color: #acadae; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .select-item { | ||||||
|  |   width: 60%; | ||||||
|  |   padding: 10px; | ||||||
|  |   margin: 0 auto 10px; | ||||||
|  |   border: 1px solid #eaeaea; | ||||||
|  |  | ||||||
|  |   .activeAddNews { | ||||||
|  |     border: 5px solid #2bb673; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .father .child { | ||||||
|  |   position: relative; | ||||||
|  |   bottom: 25px; | ||||||
|  |   display: none; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .father:hover .child { | ||||||
|  |   display: block; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .material-img { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										7
									
								
								src/views/mp/draft/components/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/views/mp/draft/components/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | import type { Article, NewsItem, NewsItemList } from './types' | ||||||
|  | import { createEmptyNewsItem } from './types' | ||||||
|  | import DraftTable from './DraftTable.vue' | ||||||
|  | import NewsForm from './NewsForm.vue' | ||||||
|  |  | ||||||
|  | export { DraftTable, NewsForm, createEmptyNewsItem } | ||||||
|  | export type { Article, NewsItem, NewsItemList } | ||||||
							
								
								
									
										40
									
								
								src/views/mp/draft/components/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/views/mp/draft/components/types.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | interface NewsItem { | ||||||
|  |   title: string | ||||||
|  |   thumbMediaId: string | ||||||
|  |   author: string | ||||||
|  |   digest: string | ||||||
|  |   showCoverPic: string | ||||||
|  |   content: string | ||||||
|  |   contentSourceUrl: string | ||||||
|  |   needOpenComment: string | ||||||
|  |   onlyFansCanComment: string | ||||||
|  |   thumbUrl: string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface NewsItemList { | ||||||
|  |   newsItem: NewsItem[] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface Article { | ||||||
|  |   mediaId: string | ||||||
|  |   content: NewsItemList | ||||||
|  |   updateTime: number | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const createEmptyNewsItem = (): NewsItem => { | ||||||
|  |   return { | ||||||
|  |     title: '', | ||||||
|  |     thumbMediaId: '', | ||||||
|  |     author: '', | ||||||
|  |     digest: '', | ||||||
|  |     showCoverPic: '', | ||||||
|  |     content: '', | ||||||
|  |     contentSourceUrl: '', | ||||||
|  |     needOpenComment: '', | ||||||
|  |     onlyFansCanComment: '', | ||||||
|  |     thumbUrl: '' | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export type { Article, NewsItem, NewsItemList } | ||||||
|  | export { createEmptyNewsItem } | ||||||
| @@ -14,7 +14,13 @@ | |||||||
|         <WxAccountSelect @change="onAccountChanged" /> |         <WxAccountSelect @change="onAccountChanged" /> | ||||||
|       </el-form-item> |       </el-form-item> | ||||||
|       <el-form-item> |       <el-form-item> | ||||||
|         <el-button type="primary" plain @click="handleAdd" v-hasPermi="['mp:draft:create']"> |         <el-button | ||||||
|  |           type="primary" | ||||||
|  |           plain | ||||||
|  |           @click="handleAdd" | ||||||
|  |           v-hasPermi="['mp:draft:create']" | ||||||
|  |           :disabled="accountId === 0" | ||||||
|  |         > | ||||||
|           <Icon icon="ep:plus" />新增 |           <Icon icon="ep:plus" />新增 | ||||||
|         </el-button> |         </el-button> | ||||||
|       </el-form-item> |       </el-form-item> | ||||||
| @@ -23,40 +29,13 @@ | |||||||
|  |  | ||||||
|   <!-- 列表 --> |   <!-- 列表 --> | ||||||
|   <ContentWrap> |   <ContentWrap> | ||||||
|     <div class="waterfall" v-loading="loading"> |     <DraftTable | ||||||
|       <template v-for="item in list" :key="item.articleId"> |       :loading="loading" | ||||||
|         <div class="waterfall-item" v-if="item.content && item.content.newsItem"> |       :list="list" | ||||||
|           <WxNews :articles="item.content.newsItem" /> |       @update="onUpdate" | ||||||
|           <!-- 操作按钮 --> |       @delete="onDelete" | ||||||
|           <el-row class="ope-row"> |       @publish="onPublish" | ||||||
|             <el-button |     /> | ||||||
|               type="success" |  | ||||||
|               circle |  | ||||||
|               @click="handlePublish(item)" |  | ||||||
|               v-hasPermi="['mp:free-publish:submit']" |  | ||||||
|             > |  | ||||||
|               <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> |  | ||||||
|             <el-button |  | ||||||
|               type="danger" |  | ||||||
|               circle |  | ||||||
|               @click="handleDelete(item)" |  | ||||||
|               v-hasPermi="['mp:draft:delete']" |  | ||||||
|             > |  | ||||||
|               <Icon icon="ep:delete" /> |  | ||||||
|             </el-button> |  | ||||||
|           </el-row> |  | ||||||
|         </div> |  | ||||||
|       </template> |  | ||||||
|     </div> |  | ||||||
|     <!-- 分页记录 --> |     <!-- 分页记录 --> | ||||||
|     <Pagination |     <Pagination | ||||||
|       :total="total" |       :total="total" | ||||||
| @@ -66,287 +45,101 @@ | |||||||
|     /> |     /> | ||||||
|   </ContentWrap> |   </ContentWrap> | ||||||
|  |  | ||||||
|   <div class="app-container"> |   <!-- 添加或修改草稿对话框 --> | ||||||
|     <!-- 添加或修改草稿对话框 --> |   <el-dialog | ||||||
|     <el-dialog |     :title="isCreating ? '新建图文' : '修改图文'" | ||||||
|       :title="operateMaterial === 'add' ? '新建图文' : '修改图文'" |     width="80%" | ||||||
|       width="80%" |     v-model="showDialog" | ||||||
|       v-model="dialogNewsVisible" |     :before-close="onBeforeDialogClose" | ||||||
|       :before-close="dialogNewsClose" |     destroy-on-close | ||||||
|       destroy-on-close |   > | ||||||
|     > |     <NewsForm v-model="newsList" v-loading="isSubmitting" :is-creating="isCreating" /> | ||||||
|       <el-container> |     <template #footer> | ||||||
|         <el-aside width="40%"> |       <el-button @click="showDialog = false">取 消</el-button> | ||||||
|           <div class="select-item"> |       <el-button type="primary" @click="onSubmitNewsItem">提 交</el-button> | ||||||
|             <div v-for="(news, index) in articlesAdd" :key="news.id"> |     </template> | ||||||
|               <div |   </el-dialog> | ||||||
|                 class="news-main father" |  | ||||||
|                 v-if="index === 0" |  | ||||||
|                 :class="{ activeAddNews: isActiveAddNews === index }" |  | ||||||
|                 @click="activeNews(index)" |  | ||||||
|               > |  | ||||||
|                 <div class="news-content"> |  | ||||||
|                   <img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl" /> |  | ||||||
|                   <div class="news-content-title">{{ news.title }}</div> |  | ||||||
|                 </div> |  | ||||||
|                 <div class="child" v-if="articlesAdd.length > 1"> |  | ||||||
|                   <el-button size="small" @click="downNews(index)" |  | ||||||
|                     ><Icon icon="ep:sort-down" />下移</el-button |  | ||||||
|                   > |  | ||||||
|                   <el-button v-if="operateMaterial === 'add'" size="small" @click="minusNews(index)" |  | ||||||
|                     ><Icon icon="ep:delete" />删除 |  | ||||||
|                   </el-button> |  | ||||||
|                 </div> |  | ||||||
|               </div> |  | ||||||
|               <div |  | ||||||
|                 class="news-main-item father" |  | ||||||
|                 v-if="index > 0" |  | ||||||
|                 :class="{ activeAddNews: isActiveAddNews === index }" |  | ||||||
|                 @click="activeNews(index)" |  | ||||||
|               > |  | ||||||
|                 <div class="news-content-item"> |  | ||||||
|                   <div class="news-content-item-title">{{ news.title }}</div> |  | ||||||
|                   <div class="news-content-item-img"> |  | ||||||
|                     <img |  | ||||||
|                       class="material-img" |  | ||||||
|                       v-if="news.thumbUrl" |  | ||||||
|                       :src="news.thumbUrl" |  | ||||||
|                       width="100%" |  | ||||||
|                     /> |  | ||||||
|                   </div> |  | ||||||
|                 </div> |  | ||||||
|                 <div class="child"> |  | ||||||
|                   <el-button |  | ||||||
|                     v-if="articlesAdd.length > index + 1" |  | ||||||
|                     size="small" |  | ||||||
|                     @click="downNews(index)" |  | ||||||
|                     ><Icon icon="ep:sort-down" />下移 |  | ||||||
|                   </el-button> |  | ||||||
|                   <el-button size="small" @click="upNews(index)" |  | ||||||
|                     ><Icon icon="ep:sort-up" />上移</el-button |  | ||||||
|                   > |  | ||||||
|                   <el-button |  | ||||||
|                     v-if="operateMaterial === 'add'" |  | ||||||
|                     type="danger" |  | ||||||
|                     size="small" |  | ||||||
|                     @click="minusNews(index)" |  | ||||||
|                     ><Icon icon="ep:delete" />删除 |  | ||||||
|                   </el-button> |  | ||||||
|                 </div> |  | ||||||
|               </div> |  | ||||||
|             </div> |  | ||||||
|             <el-row justify="center" class="ope-row"> |  | ||||||
|               <el-button |  | ||||||
|                 type="primary" |  | ||||||
|                 circle |  | ||||||
|                 @click="plusNews" |  | ||||||
|                 v-if="articlesAdd.length < 8 && operateMaterial === 'add'" |  | ||||||
|               > |  | ||||||
|                 <Icon icon="ep:plus" /> |  | ||||||
|               </el-button> |  | ||||||
|             </el-row> |  | ||||||
|           </div> |  | ||||||
|         </el-aside> |  | ||||||
|         <el-main> |  | ||||||
|           <div class="" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0"> |  | ||||||
|             <!-- 标题、作者、原文地址 --> |  | ||||||
|             <el-row :gutter="20"> |  | ||||||
|               <el-input |  | ||||||
|                 v-model="articlesAdd[isActiveAddNews].title" |  | ||||||
|                 placeholder="请输入标题(必填)" |  | ||||||
|               /> |  | ||||||
|               <el-input |  | ||||||
|                 v-model="articlesAdd[isActiveAddNews].author" |  | ||||||
|                 placeholder="请输入作者" |  | ||||||
|                 style="margin-top: 5px" |  | ||||||
|               /> |  | ||||||
|               <el-input |  | ||||||
|                 v-model="articlesAdd[isActiveAddNews].contentSourceUrl" |  | ||||||
|                 placeholder="请输入原文地址" |  | ||||||
|                 style="margin-top: 5px" |  | ||||||
|               /> |  | ||||||
|             </el-row> |  | ||||||
|             <!-- 封面和摘要 --> |  | ||||||
|             <el-row :gutter="20"> |  | ||||||
|               <el-col :span="12"> |  | ||||||
|                 <p>封面:</p> |  | ||||||
|                 <div class="thumb-div"> |  | ||||||
|                   <el-image |  | ||||||
|                     v-if="articlesAdd[isActiveAddNews].thumbUrl" |  | ||||||
|                     style="width: 300px; max-height: 300px" |  | ||||||
|                     :src="articlesAdd[isActiveAddNews].thumbUrl" |  | ||||||
|                     fit="contain" |  | ||||||
|                   /> |  | ||||||
|                   <Icon |  | ||||||
|                     v-else |  | ||||||
|                     icon="ep:plus" |  | ||||||
|                     class="avatar-uploader-icon" |  | ||||||
|                     :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'" |  | ||||||
|                   /> |  | ||||||
|                   <div class="thumb-but"> |  | ||||||
|                     <el-upload |  | ||||||
|                       :action="uploadUrl" |  | ||||||
|                       :headers="headers" |  | ||||||
|                       multiple |  | ||||||
|                       :limit="1" |  | ||||||
|                       :file-list="fileList" |  | ||||||
|                       :data="uploadData" |  | ||||||
|                       :before-upload="beforeThumbImageUpload" |  | ||||||
|                       :on-success="handleUploadSuccess" |  | ||||||
|                     > |  | ||||||
|                       <template #trigger> |  | ||||||
|                         <el-button size="small" type="primary">本地上传</el-button> |  | ||||||
|                       </template> |  | ||||||
|                       <el-button |  | ||||||
|                         size="small" |  | ||||||
|                         type="primary" |  | ||||||
|                         @click="openMaterial" |  | ||||||
|                         style="margin-left: 5px" |  | ||||||
|                         >素材库选择</el-button |  | ||||||
|                       > |  | ||||||
|                       <template #tip> |  | ||||||
|                         <div class="el-upload__tip" |  | ||||||
|                           >支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div |  | ||||||
|                         > |  | ||||||
|                       </template> |  | ||||||
|                     </el-upload> |  | ||||||
|                   </div> |  | ||||||
|                   <el-dialog |  | ||||||
|                     title="选择图片" |  | ||||||
|                     v-model="dialogImageVisible" |  | ||||||
|                     width="80%" |  | ||||||
|                     append-to-body |  | ||||||
|                   > |  | ||||||
|                     <WxMaterialSelect |  | ||||||
|                       ref="materialSelectRef" |  | ||||||
|                       :objData="{ type: 'image', accountId: queryParams.accountId }" |  | ||||||
|                       @select-material="selectMaterial" |  | ||||||
|                     /> |  | ||||||
|                   </el-dialog> |  | ||||||
|                 </div> |  | ||||||
|               </el-col> |  | ||||||
|               <el-col :span="12"> |  | ||||||
|                 <p>摘要:</p> |  | ||||||
|                 <el-input |  | ||||||
|                   :rows="8" |  | ||||||
|                   type="textarea" |  | ||||||
|                   v-model="articlesAdd[isActiveAddNews].digest" |  | ||||||
|                   placeholder="请输入摘要" |  | ||||||
|                   class="digest" |  | ||||||
|                   maxlength="120" |  | ||||||
|                 /> |  | ||||||
|               </el-col> |  | ||||||
|             </el-row> |  | ||||||
|             <!--富文本编辑器组件--> |  | ||||||
|             <el-row> |  | ||||||
|               <Editor |  | ||||||
|                 v-model="articlesAdd[isActiveAddNews].content" |  | ||||||
|                 :editor-config="editorConfig" |  | ||||||
|               /> |  | ||||||
|             </el-row> |  | ||||||
|           </div> |  | ||||||
|         </el-main> |  | ||||||
|       </el-container> |  | ||||||
|       <template #footer> |  | ||||||
|         <el-button @click="dialogNewsVisible = false">取 消</el-button> |  | ||||||
|         <el-button type="primary" @click="submitForm">提 交</el-button> |  | ||||||
|       </template> |  | ||||||
|     </el-dialog> |  | ||||||
|   </div> |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts" name="MpDraft"> | <script setup lang="ts" name="MpDraft"> | ||||||
| import { Editor } from '@/components/Editor' |  | ||||||
| import WxNews from '@/views/mp/components/wx-news/main.vue' |  | ||||||
| import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' |  | ||||||
| import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' | ||||||
| import { getAccessToken } from '@/utils/auth' |  | ||||||
| import * as MpDraftApi from '@/api/mp/draft' | import * as MpDraftApi from '@/api/mp/draft' | ||||||
| import * as MpFreePublishApi from '@/api/mp/freePublish' | import * as MpFreePublishApi from '@/api/mp/freePublish' | ||||||
| import type { UploadFiles, UploadProps, UploadRawFile } from 'element-plus' | import { | ||||||
| import { createEditorConfig } from './editor-config' |   type Article, | ||||||
|  |   type NewsItem, | ||||||
|  |   NewsForm, | ||||||
|  |   DraftTable, | ||||||
|  |   createEmptyNewsItem | ||||||
|  | } from './components/' | ||||||
| // import drafts from './mock' // 可以用改本地数据模拟,避免API调用超限 | // import drafts from './mock' // 可以用改本地数据模拟,避免API调用超限 | ||||||
| import { IEditorConfig } from '@wangeditor/editor' |  | ||||||
|  |  | ||||||
| const message = useMessage() // 消息 | const message = useMessage() // 消息 | ||||||
|  |  | ||||||
|  | const accountId = ref(0) | ||||||
|  | provide('accountId', accountId) | ||||||
|  |  | ||||||
| const loading = ref(true) // 列表的加载中 | const loading = ref(true) // 列表的加载中 | ||||||
| const list = ref<any[]>([]) // 列表的数据 | const list = ref<any[]>([]) // 列表的数据 | ||||||
| const total = ref(0) // 列表的总页数 | const total = ref(0) // 列表的总页数 | ||||||
| interface QueryParams { | interface QueryParams { | ||||||
|   pageNo: number |   pageNo: number | ||||||
|   pageSize: number |   pageSize: number | ||||||
|   accountId?: number |   accountId: number | ||||||
| } | } | ||||||
| const queryParams: QueryParams = reactive({ | const queryParams: QueryParams = reactive({ | ||||||
|   pageNo: 1, |   pageNo: 1, | ||||||
|   pageSize: 10, |   pageSize: 10, | ||||||
|   accountId: undefined |   accountId: accountId.value | ||||||
| }) | }) | ||||||
|  |  | ||||||
| // ========== 文件上传 ========== |  | ||||||
| const BASE_URL = import.meta.env.VITE_BASE_URL |  | ||||||
| const uploadUrl = BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传永久素材的地址 |  | ||||||
| const headers = { Authorization: 'Bearer ' + getAccessToken() } // 设置上传的请求头部 |  | ||||||
|  |  | ||||||
| const materialSelectRef = ref<InstanceType<typeof WxMaterialSelect> | null>(null) |  | ||||||
| const fileList = ref<UploadFiles>([]) |  | ||||||
| interface UploadData { | interface UploadData { | ||||||
|   type: 'image' | 'video' | 'audio' |   type: 'image' | 'video' | 'audio' | ||||||
|   accountId?: number |   accountId: number | ||||||
| } | } | ||||||
| const uploadData: UploadData = reactive({ | const uploadData: UploadData = reactive({ | ||||||
|   type: 'image', |   type: 'image', | ||||||
|   accountId: 1 |   accountId: accountId.value | ||||||
| }) | }) | ||||||
|  |  | ||||||
| // ========== 草稿新建 or 修改 ========== | // ========== 草稿新建 or 修改 ========== | ||||||
| interface Article { | const showDialog = ref(false) | ||||||
|   title: string | const newsList = ref<NewsItem[]>([]) | ||||||
|   thumbMediaId: string | const mediaId = ref('') | ||||||
|   author: string | const isCreating = ref(true) | ||||||
|   digest: string | const isSubmitting = ref(false) | ||||||
|   showCoverPic: string |  | ||||||
|   content: string |  | ||||||
|   contentSourceUrl: string |  | ||||||
|   needOpenComment: string |  | ||||||
|   onlyFansCanComment: string |  | ||||||
|   thumbUrl: string |  | ||||||
| } |  | ||||||
| const dialogNewsVisible = ref(false) |  | ||||||
| const addMaterialLoading = ref(false) // 添加草稿的 loading 标识 |  | ||||||
| const articlesAdd = ref<Article[]>([]) |  | ||||||
| const isActiveAddNews = ref(0) |  | ||||||
| const dialogImageVisible = ref(false) |  | ||||||
| const operateMaterial = ref<'add' | 'edit'>('add') |  | ||||||
| const articlesMediaId = ref('') |  | ||||||
|  |  | ||||||
| /** 侦听公众号变化 **/ | /** 侦听公众号变化 **/ | ||||||
| const onAccountChanged = (id?: number) => { | const onAccountChanged = (id: number) => { | ||||||
|   setAccountId(id) |   setAccountId(id) | ||||||
|   getList() |   getList() | ||||||
| } | } | ||||||
|  |  | ||||||
| // ======================== 列表查询 ======================== | // 关闭弹窗 | ||||||
| /** 设置账号编号 */ | const onBeforeDialogClose = async (onDone: () => {}) => { | ||||||
| const setAccountId = (id?: number) => { |   try { | ||||||
|   queryParams.accountId = id |     await message.confirm('修改内容可能还未保存,确定关闭吗?') | ||||||
|   uploadData.accountId = id |     onDone() | ||||||
|   editorConfig.value = createEditorConfig(uploadUrl, queryParams.accountId) |   } catch {} | ||||||
| } | } | ||||||
|  |  | ||||||
| const editorConfig = ref<Partial<IEditorConfig>>({}) | // ======================== 列表查询 ======================== | ||||||
|  | /** 设置账号编号 */ | ||||||
|  | const setAccountId = (id: number) => { | ||||||
|  |   queryParams.accountId = id | ||||||
|  |   uploadData.accountId = id | ||||||
|  | } | ||||||
|  |  | ||||||
| /** 查询列表 */ | /** 查询列表 */ | ||||||
| const getList = async () => { | const getList = async () => { | ||||||
|   loading.value = true |   loading.value = true | ||||||
|   try { |   try { | ||||||
|     const drafts = await MpDraftApi.getDraftPage(queryParams) |     const drafts = await MpDraftApi.getDraftPage(queryParams) | ||||||
|     drafts.list.forEach((item) => { |     drafts.list.forEach((draft) => { | ||||||
|       const newsItem = item.content.newsItem |       const newsList = draft.content.newsItem | ||||||
|       // 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面 |       // 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面 | ||||||
|       newsItem.forEach((article) => { |       newsList.forEach((item) => { | ||||||
|         article.picUrl = article.thumbUrl |         item.picUrl = item.thumbUrl | ||||||
|       }) |       }) | ||||||
|     }) |     }) | ||||||
|     list.value = drafts.list |     list.value = drafts.list | ||||||
| @@ -359,163 +152,45 @@ const getList = async () => { | |||||||
| // ======================== 新增/修改草稿 ======================== | // ======================== 新增/修改草稿 ======================== | ||||||
| /** 新增按钮操作 */ | /** 新增按钮操作 */ | ||||||
| const handleAdd = () => { | const handleAdd = () => { | ||||||
|   reset() |   isCreating.value = true | ||||||
|   // 打开表单,并设置初始化 |   newsList.value = [createEmptyNewsItem()] | ||||||
|   operateMaterial.value = 'add' |   showDialog.value = true | ||||||
|   dialogNewsVisible.value = true |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** 更新按钮操作 */ | /** 更新按钮操作 */ | ||||||
| const handleUpdate = (item: any) => { | const onUpdate = (item: Article) => { | ||||||
|   reset() |   mediaId.value = item.mediaId | ||||||
|   articlesMediaId.value = item.mediaId |   newsList.value = JSON.parse(JSON.stringify(item.content.newsItem)) | ||||||
|   articlesAdd.value = JSON.parse(JSON.stringify(item.content.newsItem)) |   isCreating.value = false | ||||||
|   // 打开表单,并设置初始化 |   showDialog.value = true | ||||||
|   operateMaterial.value = 'edit' |  | ||||||
|   dialogNewsVisible.value = true |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** 提交按钮 */ | /** 提交按钮 */ | ||||||
| const submitForm = async () => { | const onSubmitNewsItem = async () => { | ||||||
|   addMaterialLoading.value = true |   isSubmitting.value = true | ||||||
|   try { |   try { | ||||||
|     if (operateMaterial.value === 'add') { |     if (isCreating.value) { | ||||||
|       await MpDraftApi.createDraft(queryParams.accountId, articlesAdd.value) |       await MpDraftApi.createDraft(queryParams.accountId, newsList.value) | ||||||
|       message.notifySuccess('新增成功') |       message.notifySuccess('新增成功') | ||||||
|     } else { |     } else { | ||||||
|       await MpDraftApi.updateDraft(queryParams.accountId, articlesMediaId.value, articlesAdd.value) |       await MpDraftApi.updateDraft(queryParams.accountId, mediaId.value, newsList.value) | ||||||
|       message.notifySuccess('更新成功') |       message.notifySuccess('更新成功') | ||||||
|     } |     } | ||||||
|   } finally { |   } finally { | ||||||
|     dialogNewsVisible.value = false |     showDialog.value = false | ||||||
|     addMaterialLoading.value = false |     isSubmitting.value = false | ||||||
|     await getList() |     await getList() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| // 关闭弹窗 |  | ||||||
| const dialogNewsClose = async (onDone: () => {}) => { |  | ||||||
|   try { |  | ||||||
|     await message.confirm('修改内容可能还未保存,确定关闭吗?') |  | ||||||
|     reset() |  | ||||||
|     onDone() |  | ||||||
|   } catch {} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 表单重置 |  | ||||||
| const reset = () => { |  | ||||||
|   isActiveAddNews.value = 0 |  | ||||||
|   articlesAdd.value = [buildEmptyArticle()] |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 将图文向下移动 |  | ||||||
| const downNews = (index: number) => { |  | ||||||
|   let temp = articlesAdd.value[index] |  | ||||||
|   articlesAdd.value[index] = articlesAdd.value[index + 1] |  | ||||||
|   articlesAdd.value[index + 1] = temp |  | ||||||
|   isActiveAddNews.value = index + 1 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 将图文向上移动 |  | ||||||
| const upNews = (index: number) => { |  | ||||||
|   const temp = articlesAdd.value[index] |  | ||||||
|   articlesAdd.value[index] = articlesAdd.value[index - 1] |  | ||||||
|   articlesAdd.value[index - 1] = temp |  | ||||||
|   isActiveAddNews.value = index - 1 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 选中指定 index 的图文 |  | ||||||
| const activeNews = (index: number) => { |  | ||||||
|   isActiveAddNews.value = index |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 删除指定 index 的图文 |  | ||||||
| const minusNews = async (index: number) => { |  | ||||||
|   try { |  | ||||||
|     await message.confirm('确定删除该图文吗?') |  | ||||||
|     articlesAdd.value.splice(index, 1) |  | ||||||
|     if (isActiveAddNews.value === index) { |  | ||||||
|       isActiveAddNews.value = 0 |  | ||||||
|     } |  | ||||||
|   } catch {} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 添加一个图文 |  | ||||||
| const plusNews = () => { |  | ||||||
|   articlesAdd.value.push(buildEmptyArticle()) |  | ||||||
|   isActiveAddNews.value = articlesAdd.value.length - 1 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 创建空的 article |  | ||||||
| const buildEmptyArticle = (): Article => { |  | ||||||
|   return { |  | ||||||
|     title: '', |  | ||||||
|     thumbMediaId: '', |  | ||||||
|     author: '', |  | ||||||
|     digest: '', |  | ||||||
|     showCoverPic: '', |  | ||||||
|     content: '', |  | ||||||
|     contentSourceUrl: '', |  | ||||||
|     needOpenComment: '', |  | ||||||
|     onlyFansCanComment: '', |  | ||||||
|     thumbUrl: '' |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ======================== 文件上传 ======================== |  | ||||||
| const beforeThumbImageUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => { |  | ||||||
|   addMaterialLoading.value = true |  | ||||||
|   const isType = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/jpg'].includes( |  | ||||||
|     rawFile.type |  | ||||||
|   ) |  | ||||||
|   if (!isType) { |  | ||||||
|     message.error('上传图片格式不对!') |  | ||||||
|     addMaterialLoading.value = false |  | ||||||
|     return false |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (rawFile.size / 1024 / 1024 > 2) { |  | ||||||
|     message.error('上传图片大小不能超过 2M!') |  | ||||||
|     addMaterialLoading.value = false |  | ||||||
|     return false |  | ||||||
|   } |  | ||||||
|   // 校验通过 |  | ||||||
|   return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const handleUploadSuccess: UploadProps['onSuccess'] = (res: any) => { |  | ||||||
|   addMaterialLoading.value = false |  | ||||||
|   if (res.code !== 0) { |  | ||||||
|     message.error('上传出错:' + res.msg) |  | ||||||
|     return false |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // 重置上传文件的表单 |  | ||||||
|   fileList.value = [] |  | ||||||
|  |  | ||||||
|   // 设置草稿的封面字段 |  | ||||||
|   articlesAdd.value[isActiveAddNews.value].thumbMediaId = res.data.mediaId |  | ||||||
|   articlesAdd.value[isActiveAddNews.value].thumbUrl = res.data.url |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 选择 or 上传完素材,设置回草稿 |  | ||||||
| const selectMaterial = (item: any) => { |  | ||||||
|   dialogImageVisible.value = false |  | ||||||
|   articlesAdd.value[isActiveAddNews.value].thumbMediaId = item.mediaId |  | ||||||
|   articlesAdd.value[isActiveAddNews.value].thumbUrl = item.url |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 打开素材选择 |  | ||||||
| const openMaterial = () => { |  | ||||||
|   dialogImageVisible.value = true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ======================== 草稿箱发布 ======================== | // ======================== 草稿箱发布 ======================== | ||||||
| const handlePublish = async (item: any) => { | const onPublish = async (item: Article) => { | ||||||
|   const accountId = queryParams.accountId |   const accountId = queryParams.accountId | ||||||
|   const mediaId = item.mediaId |   const mediaId = item.mediaId | ||||||
|   const content = |   const content = | ||||||
|     '你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。' |     '你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。' + | ||||||
|  |     '已发布内容不会推送给用户,也不会展示在公众号主页中。 ' + | ||||||
|  |     '发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。' | ||||||
|   try { |   try { | ||||||
|     await message.confirm(content) |     await message.confirm(content) | ||||||
|     await MpFreePublishApi.submitFreePublish(accountId, mediaId) |     await MpFreePublishApi.submitFreePublish(accountId, mediaId) | ||||||
| @@ -525,7 +200,7 @@ const handlePublish = async (item: any) => { | |||||||
| } | } | ||||||
|  |  | ||||||
| /** 删除按钮操作 */ | /** 删除按钮操作 */ | ||||||
| const handleDelete = async (item: any) => { | const onDelete = async (item: Article) => { | ||||||
|   const accountId = queryParams.accountId |   const accountId = queryParams.accountId | ||||||
|   const mediaId = item.mediaId |   const mediaId = item.mediaId | ||||||
|   try { |   try { | ||||||
| @@ -536,234 +211,10 @@ const handleDelete = async (item: any) => { | |||||||
|   } catch {} |   } catch {} | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| .pagination { | .pagination { | ||||||
|   float: right; |   float: right; | ||||||
|   margin-right: 25px; |   margin-right: 25px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .add_but { |  | ||||||
|   padding: 10px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .ope-row { |  | ||||||
|   margin-top: 5px; |  | ||||||
|   text-align: center; |  | ||||||
|   border-top: 1px solid #eaeaea; |  | ||||||
|   padding-top: 5px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .el-row { |  | ||||||
|   margin-bottom: 20px; |  | ||||||
| } |  | ||||||
| .el-row:last-child { |  | ||||||
|   margin-bottom: 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .item-name { |  | ||||||
|   font-size: 12px; |  | ||||||
|   overflow: hidden; |  | ||||||
|   text-overflow: ellipsis; |  | ||||||
|   white-space: nowrap; |  | ||||||
|   text-align: center; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .el-upload__tip { |  | ||||||
|   margin-left: 5px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*新增图文*/ |  | ||||||
| // .left { |  | ||||||
| //   display: inline-block; |  | ||||||
| //   vertical-align: top; |  | ||||||
| //   margin-top: 200px; |  | ||||||
| // } |  | ||||||
|  |  | ||||||
| // .right { |  | ||||||
| //   display: inline-block; |  | ||||||
| //   width: 100%; |  | ||||||
| // } |  | ||||||
|  |  | ||||||
| // .avatar-uploader { |  | ||||||
| //   width: 20%; |  | ||||||
| //   display: inline-block; |  | ||||||
| // } |  | ||||||
|  |  | ||||||
| // .avatar-uploader .el-upload { |  | ||||||
| //   border-radius: 6px; |  | ||||||
| //   cursor: pointer; |  | ||||||
| //   position: relative; |  | ||||||
| //   overflow: hidden; |  | ||||||
| //   text-align: unset !important; |  | ||||||
| // } |  | ||||||
|  |  | ||||||
| // .avatar-uploader .el-upload:hover { |  | ||||||
| //   border-color: #165dff; |  | ||||||
| // } |  | ||||||
|  |  | ||||||
| .avatar-uploader-icon { |  | ||||||
|   border: 1px solid #d9d9d9; |  | ||||||
|   font-size: 28px; |  | ||||||
|   color: #8c939d; |  | ||||||
|   width: 120px; |  | ||||||
|   height: 120px; |  | ||||||
|   line-height: 120px; |  | ||||||
|   text-align: center; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .avatar { |  | ||||||
|   width: 230px; |  | ||||||
|   height: 120px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .avatar1 { |  | ||||||
|   width: 120px; |  | ||||||
|   height: 120px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .digest { |  | ||||||
|   width: 100%; |  | ||||||
|   display: inline-block; |  | ||||||
|   vertical-align: top; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*新增图文*/ |  | ||||||
| /*瀑布流样式*/ |  | ||||||
| .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; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @media (min-width: 992px) and (max-width: 1300px) { |  | ||||||
|   .waterfall { |  | ||||||
|     column-count: 3; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @media (min-width: 768px) and (max-width: 991px) { |  | ||||||
|   .waterfall { |  | ||||||
|     column-count: 2; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @media (max-width: 767px) { |  | ||||||
|   .waterfall { |  | ||||||
|     column-count: 1; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*瀑布流样式*/ |  | ||||||
| .news-main { |  | ||||||
|   background-color: #ffffff; |  | ||||||
|   width: 100%; |  | ||||||
|   margin: auto; |  | ||||||
|   height: 120px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .news-content { |  | ||||||
|   background-color: #acadae; |  | ||||||
|   width: 100%; |  | ||||||
|   height: 120px; |  | ||||||
|   position: relative; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .news-content-title { |  | ||||||
|   display: inline-block; |  | ||||||
|   font-size: 15px; |  | ||||||
|   color: #ffffff; |  | ||||||
|   position: absolute; |  | ||||||
|   left: 0px; |  | ||||||
|   bottom: 0px; |  | ||||||
|   background-color: black; |  | ||||||
|   width: 98%; |  | ||||||
|   padding: 1%; |  | ||||||
|   opacity: 0.65; |  | ||||||
|   overflow: hidden; |  | ||||||
|   text-overflow: ellipsis; |  | ||||||
|   white-space: nowrap; |  | ||||||
|   height: 25px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .news-main-item { |  | ||||||
|   background-color: #ffffff; |  | ||||||
|   padding: 5px 0px; |  | ||||||
|   border-top: 1px solid #eaeaea; |  | ||||||
|   width: 100%; |  | ||||||
|   margin: auto; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .news-content-item { |  | ||||||
|   position: relative; |  | ||||||
|   margin-left: -3px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .news-content-item-title { |  | ||||||
|   display: inline-block; |  | ||||||
|   font-size: 12px; |  | ||||||
|   width: 70%; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .news-content-item-img { |  | ||||||
|   display: inline-block; |  | ||||||
|   width: 25%; |  | ||||||
|   background-color: #acadae; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .activeAddNews { |  | ||||||
|   border: 5px solid #2bb673; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .news-main-plus { |  | ||||||
|   width: 280px; |  | ||||||
|   text-align: center; |  | ||||||
|   margin: auto; |  | ||||||
|   height: 50px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .icon-plus { |  | ||||||
|   margin: 10px; |  | ||||||
|   font-size: 25px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .select-item { |  | ||||||
|   width: 60%; |  | ||||||
|   padding: 10px; |  | ||||||
|   margin: 0 auto 10px auto; |  | ||||||
|   border: 1px solid #eaeaea; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .father .child { |  | ||||||
|   display: none; |  | ||||||
|   text-align: center; |  | ||||||
|   position: relative; |  | ||||||
|   bottom: 25px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .father:hover .child { |  | ||||||
|   display: block; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .thumb-div { |  | ||||||
|   display: inline-block; |  | ||||||
|   width: 100%; |  | ||||||
|   text-align: center; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .thumb-but { |  | ||||||
|   margin: 5px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .material-img { |  | ||||||
|   width: 100%; |  | ||||||
|   height: 100%; |  | ||||||
| } |  | ||||||
| </style> | </style> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 dhb52
					dhb52