<template> <div class="createPost-container"> <el-form ref="postForm" :model="postForm" :rules="rules" class="form-container"> <sticky :class-name="'sub-navbar '+postForm.status"> <el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">保存 </el-button> </sticky> <div class="createPost-main-container"> <el-row :gutter="20"> <el-card class="box-card"> <div slot="header" class="clearfix"> <div style="float: left; text-align: center;"> 帖子相关 </div> </div> <el-row> <el-col :span="24"> <div class="postInfo-container"> <el-row> <el-col :span="12"> <el-form-item label-width="75px" label="帖子ID:" class="postInfo-container-item" v-if="isEdit"> <el-input :rows="1" v-model="postForm.id" type="number" class="article-textarea" style="width: 230px" disabled/> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label-width="75px" label="举报时间:" class="postInfo-container-item" v-if="isEdit"> <el-input :rows="1" v-model="postForm.reported_time" type="text" class="article-textarea" style="width: 230px" disabled/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label-width="65px" label="发帖人:" class="postInfo-container-item"> <el-select v-model="user" :remote-method="getRemoteUserList" filterable remote clearable placeholder="搜索用户" style="width: 230px;margin-left: 10px"> <el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item"/> </el-select> <el-tag type="danger" v-if="postForm.is_puppet">马甲</el-tag> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label-width="75px" label="发帖时间:" class="postInfo-container-item" prop="posting_time"> <el-date-picker v-model="postForm.posting_time " type="datetime" value-format="yyyy-MM-dd HH:mm:ss" :picker-options="expireTimeOption" placeholder="选择日期时间" style="width: 230px" :disabled="this.isEdit" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label-width="75px" label="明星名称:" class="postInfo-container-item"> <el-select v-model="star" :remote-method="getRemoteStarList" filterable remote clearable placeholder="搜索明星" style="width: 230px"> <el-option v-for="(item,index) in starListOptions" :key="item+index" :label="item" :value="item"/> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label-width="75px" label="小组名称:" class="postInfo-container-item"> <el-select v-model="group" :remote-method="getRemoteGroupList" filterable remote clearable placeholder="搜索小组" style="width: 230px"> <el-option v-for="(item,index) in groupListOptions" :key="item+index" :label="item" :value="item"/> </el-select> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label-width="75px" label="帖子星级:" class="postInfo-container-item"> <el-select v-model="postForm.content_level" :placeholder="'级别:'" clearable class="postInfo-container-item" style="width:230px"> <el-option v-for="item in TopicLevelOptions" :key="item.key" :label="item.display_name" :value="item.key"/> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label-width="75px" label="标签:" class="postInfo-container-item"> <el-select v-model="tags" :remote-method="getRemoteTagList" filterable remote multiple value-key="id" placeholder="搜索标签" style="width: 230px"> <el-option v-for="(item,index) in tagListOptions" :key="item+index" :label="item" :value="item"/> </el-select> </el-form-item> </el-col> </el-row> </div> </el-col> </el-row> <el-form-item label-width="75px" label="人工减分:" class="postInfo-container-item" style="margin-bottom: 20px"> <el-input v-model="postForm.drop_score" type="number" style="width: 80%"/> </el-form-item> <el-form-item style="margin-bottom: 20px" label-width="75px" label="是否在线:"> <el-radio-group v-model="postForm.is_online"> <el-radio :label="1">是</el-radio> <el-radio :label="0">否</el-radio> </el-radio-group> </el-form-item> <el-form-item style="margin-bottom: 40px;" label-width="75px" label="帖子内容:"> <el-input :rows="1" v-model="postForm.content" type="textarea" class="article-textarea" autosize placeholder="请输入内容"/> <span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}字</span> </el-form-item> <div class="item real"> <div class="name"> <span style="color: #606266;text-align:center;font-size:14px;padding:0 12px 0 0;">帖子图片</span> </div> <div class="value" style="margin-left: 50px"> <edit-upload :value="edit.hospital_pics" @input="uploadPics" multiple :limit="9" :uploadType="'26'" @limit="uploadRealLimit"></edit-upload> <div class="error_icon" v-show="showHospitalPicTip"> <span class="icon-warn"> <span class="path1"></span> <span class="path2"></span> <span class="path3"></span> </span> </div> </div> </div> <div v-if="hide"> <div class="item video"> <div class="name"> <span style="color: #606266;text-align:center;font-size:14px;padding:0 12px 0 0;">帖子视频</span> </div> <div class="value" style="margin-left: 50px"> <div class="up-video"> <ul class="video-items clearfix" v-if="edit.video_url"> <li class="video-item" @click.stop="playVideo"> <video ref="previewVideo" :src="videoUrl"></video> <span class="close" @click.stop="removeVideo"></span> </li> </ul> <form v-else :class="{ uploading: uploading }"> <input name="token" type="hidden" v-model="video_token"> <input name="file" type="file" class="file" @change="uploadVideo" accept="video/mp4, video/x-m4v, video/quicktime, video/*"> <div class="loader" v-show="uploading"> <span class="preloader"></span> <p>上传中...</p> </div> </form> </div> <modal :show="videoVisible" @click="videoVisible = false"> <div class="preview-video" @click.stop=""> <video-player :options="opts" :playsinline="true" ref="videoPlayer"> </video-player> </div> </modal> </div> </div> </div> </el-card> </el-row> <el-row :gutter="20" style="margin-top:50px;" v-if="isEdit"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>评论相关</span> </div> <div style="margin-bottom:50px;"> <div class="filter-container"> <el-select v-model="listQuery.filter.type" :placeholder="'评论搜索'" clearable class="filter-item" style="width: 120px"> <el-option v-for="item in ReplyTypeOptions" :key="item.key" :label="item.display_name" :value="item.key"/> </el-select> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">搜索 </el-button> <el-button class="filter-item" type="primary" icon="el-icon-edit" @click="reply('')">回复话题</el-button> <el-button v-waves class="filter-item" type="primary" icon="el-icon-delete" @click="delreply">下线 </el-button> </div> <el-table :data="list" border fit highlight-current-row style="width: 100%" ref="multipleTable" @selection-change="handleSelectionChange"> <el-table-column type="selection" align="center"></el-table-column> <el-table-column align="center" label="回复ID "> <template slot-scope="scope"> <span>{{ scope.row.id }}</span> </template> </el-table-column> <el-table-column align="center" label="评论用户"> <template slot-scope="scope"> <span>{{ scope.row.reply_user.name }}</span> </template> </el-table-column> <el-table-column align="center" label="被评论用户"> <template slot-scope="scope"> <span>{{ scope.row.be_reply_user.name }}</span> </template> </el-table-column> <el-table-column align="center" label="评论时间"> <template slot-scope="scope"> <span>{{ scope.row.create_time }}</span> </template> </el-table-column> <el-table-column align="center" label="贴主评论"> <template slot-scope="scope"> <span>{{ scope.row.reply_type }}</span> </template> </el-table-column> <el-table-column align="center" label="是否在线"> <template slot-scope="scope"> <el-tag :type="scope.row.is_online | isOnlineFilter">{{ scope.row.is_online=== 1 ? '是' : '否' }}</el-tag> </template> </el-table-column> <el-table-column label="评论内容"> <template slot-scope="scope"> <span >{{ scope.row.content }}</span> </template> </el-table-column> <el-table-column align="center" label="操作" > <template slot-scope="scope"> <el-button class="filter-item" type="primary" icon="el-icon-edit" @click="reply(scope.row)">回复</el-button> </template> </el-table-column> </el-table> <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" style="margin-left: 150px;" @pagination="getList"/> </div> </el-card> </el-row> </div> </el-form> <el-dialog :visible.sync="dialogFormVisible"> <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;"> <el-form-item :label="'评论用户'" prop="user_name"> <el-select v-model="temp.user_id" :remote-method="getRemoteUserList" filterable remote value-key="id" placeholder="搜索用户" style="width: 100%"> <el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item"/> </el-select> </el-form-item> <el-form-item :label="'评论内容'"> <el-input :autosize="{ minRows: 2, maxRows: 4}" v-model="temp.content" type="textarea" placeholder="Please input"/> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取消</el-button> <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()" :disabled="this.is_click">确认</el-button> </div> </el-dialog> </div> </template> <script> import axios from 'axios' import MDinput from '@/components/MDinput' import Sticky from '@/components/Sticky' // 粘性header组件 import Dropzone from '@/components/Dropzone' import Pagination from '@/components/Pagination' import EditUpload from '@/components/Upload/EditUpload' import Modal from '@/components/Modal' import VideoPlayer from '@/components/Video' import waves from '@/directive/waves' import {createRemark, fetchTopic, createTopic, fetchReply, DelReply, ModifyReply} from '@/api/topic' import {fetchList} from '@/api/user' import {getToken} from '@/api/qiniu' import {postVideo} from '@/api/upload' import {userSearch, groupSearch, starSearch, tagSearch} from '@/api/remoteSearch' import {VIDEO_PREFIX} from '@/components/Upload/utils/default' const VIDEO_MAX_SIZE = 100 * 1024 * 1024 const defaultForm = { status: 'draft', id: '', posting_time: '', content: '', content_level: 0, reported_time: '', group: '', user: '', star: '', is_puppet: '', is_online: 0, drop_score: 0, } export default { name: 'GroupDetail', components: {MDinput, Sticky, Dropzone, Pagination, EditUpload, VideoPlayer, Modal}, directives: {waves}, props: { isEdit: { type: Boolean, default: false } }, filters: { isOnlineFilter(status) { const statusMap = { 1: 'success', 0: 'info', } return statusMap[status] }, }, data() { const validateRequire = (rule, value, callback) => { if (value === '') { this.$message({ message: rule.field + '为必传项', type: 'error' }) callback(new Error(rule.field + '为必传项')) } else { callback() } } return { postForm: Object.assign({}, defaultForm), loading: false, is_click: false, user: '', star: '', group: '', tags: [], // 搜索相关 userListOptions: [], groupListOptions: [], starListOptions: [], // 表单验证相关 rules: { posting_time: [{validator: validateRequire, trigger: 'blur'}], }, ReplyTypeOptions: [ {'key': '1', 'display_name': '帖主的评论'}, {'key': '2', 'display_name': '评论帖子'}, {'key': '3', 'display_name': '评论的回复'}, {'key': '4', 'display_name': '马甲的评论'}, {'key': '5', 'display_name': '马甲评论的回复'}, ], edit: { video_url: '', hospital_pics: [] }, expireTimeOption: { disabledDate(date) { return date.getTime() <= Date.now(); } }, tempRoute: {}, TopicLevelOptions: [ {'key': 0, 'display_name': '未审核'}, {'key': 1, 'display_name': '1星'}, {'key': 2, 'display_name': '2星'}, {'key': 3, 'display_name': '3星'}, {'key': 4, 'display_name': '4星'}, {'key': 5, 'display_name': '5星'}, ], list: null, total: 1, listLoading: true, showHospitalPicTip: false, areaType: 1, multipleSelection: [], tagListOptions: [], is_puppet: 0, listQuery: { id: '', page: 0, limit: 10, filter: { type: '' }, }, // 视频上传 video_token: '', uploading: false, poster: '', progress: 0, complete: false, // 视频弹窗 videoVisible: false, editInstrument: false, opts: { muted: true, language: 'zh-CN', playbackRates: [0.7, 1.0, 1.5, 2.0], sources: [], poster: '', height: 400 }, temp: { user_id: '', content: '', be_replied_id: '', }, dialogFormVisible: false, dialogStatus: '', query: {} } }, computed: { contentShortLength() { return this.postForm.content.length }, lang() { return this.$store.getters.language }, player() { return this.$ref.videoPlayer }, }, created() { if (this.isEdit) { const id = this.$route.params && this.$route.params.id this.fetchData(id) this.getList() getToken().then(response => { this.video_token = response.data.data.token; }) } else { this.postForm = Object.assign({}, defaultForm) getToken().then(response => { this.video_token = response.data.data.token; }) } this.tempRoute = Object.assign({}, this.$route) }, methods: { fetchData(id) { fetchTopic(id).then(response => { console.log(response.data.data.data) let star = response.data.data.data.star let group = response.data.data.data.group let user = response.data.data.data.user let tags = response.data.data.data.tags this.postForm = response.data.data.data for (let i = 0; i < tags.length; i++) { this.tags.push(tags[i].id + ':' + tags[i].name); } if (star.id && star.name){ this.star = star.id + ':' + star.name }else{ this.star = '' } if (group.id && group.name){ this.group = group.id + ':' + group.name }else{ this.group = '' } this.user = user.id + ':' + user.name this.postForm.is_puppet = user.is_puppet this.edit.hospital_pics = response.data.data.data.topic_images this.edit.video_url = response.data.data.data.video_url }).catch(err => { console.log(err) }) }, submitForm() { this.$refs.postForm.validate(valid => { if (valid) { this.loading = true if (!this.tags.length){ this.$message.error('标签必填') this.loading = false return false } if (!this.user){ this.$message.error('发帖人必填') this.loading = false return false } this.postForm.tags = JSON.stringify(this.tags) this.postForm.user = this.user; this.postForm.star = this.star; this.postForm.group = this.group; // 序列化图片 this.postForm.topic_images = JSON.stringify(this.edit.hospital_pics); this.postForm.video_url = this.edit.video_url; createTopic(this.postForm).then(response => { this.$notify({ title: '成功', message: response.data.data.message, type: 'success', duration: 2000 }) setTimeout(() => { this.$router.push('/topic/list') }, 1000) }).catch(err => { this.$notify({ title: '失败', message: '操作失败', type: 'danger', duration: 2000 }) }); this.postForm.status = 'published' this.loading = false } else { console.log('error submit!!') return false } }) }, // 远程数据搜索 getRemoteUserList(query) { userSearch(query).then(response => { if (!response.data.data.data) return this.userListOptions = response.data.data.data }) }, getRemoteGroupList(query) { groupSearch(query).then(response => { if (!response.data.data.data) return this.groupListOptions = response.data.data.data }) }, getRemoteStarList(query) { starSearch(query).then(response => { if (!response.data.data.data) return this.starListOptions = response.data.data.data }) }, getRemoteTagList(query) { this.query['name'] = query tagSearch(this.query).then(response => { if (!response.data.data.data) return this.tagListOptions = response.data.data.data }) }, delreply() { const length = this.multipleSelection.length let del_list = [] for (let i = 0; i < length; i++) { del_list.push(this.multipleSelection[i].id) } DelReply({'reply_ids': JSON.stringify(del_list)}).then(response => { this.$message({ message: response.data.data.message, type: 'success' }) }) setTimeout(() => { this.$router.go(0) }, 1000) }, getList() { this.listLoading = true this.listQuery.id = this.$route.params && this.$route.params.id fetchReply(this.listQuery).then(response => { const items = response.data.data.data this.list = items.map(v => { this.$set(v, 'edit', false) v.originalcontent = v.content return v }) this.total = response.data.data.total this.listLoading = false }) }, // 图片上传 dropzoneS(file) { this.$message({message: 'Upload success', type: 'success'}) }, dropzoneR(file) { this.$message({message: 'Delete success', type: 'success'}) }, uploadPics(images) { this.edit.hospital_pics = images }, uploadRealLimit() { this.$message({ message: '最多上传9张', type: 'warning' }) }, hide() { return Number(this.areaType) === 1 }, // 分页相关 handleSelectionChange(val) { this.multipleSelection = val; }, // 上传视频 uploadVideo(e) { let file = e.target.files[0] let token = this.video_token // 校验video类型 if (!/video\/\w+/.test(file.type)) { this.$message({ message: '请上传视频格式', type: 'warning' }) return false } if (file.size > VIDEO_MAX_SIZE) { this.$message({ message: '视频大小不能超过100M, 请压缩后上传', type: 'warning' }) return false } const uploadQiniu = (file, token) => { if (this.uploading) return this.uploading = true let formdata = new window.FormData() formdata.append('file', file) formdata.append('token', token) axios.post('http://upload.qiniu.com/', formdata, { headers: {'Content-Type': 'multipart/form-data'} }).then(res => { let key = res.data.key axios.get(VIDEO_PREFIX + `${key}?avinfo`).then(res => { let data = res.data this.uploading = false let duration = data.format.duration if (Math.round(duration) > 1200) { this.$message({ message: '视频时长不能大于20分钟', type: 'warning' }) this.edit.video_url = '' } else { this.edit.video_url = `${key}` } }).catch(() => { this.uploading = false }) }).catch(() => { this.uploading = false }) } if (window.FileReader) { let reader = new window.FileReader() reader.readAsDataURL(file) reader.onprogress = (e) => { this.progress = Math.round(e.loaded / e.total * 100 * 2) } reader.onload = () => { console.log('support FileReader') this.complete = true uploadQiniu(file, token) } } else { console.log('not support FileReader') uploadQiniu(file, token) } }, removeVideo() { this.edit.video_url = '' this.uploading = false }, playVideo() { this.videoVisible = true this.opts.sources = [{ type: 'video/mp4', src: this.videoUrl() }] }, videoUrl() { return VIDEO_PREFIX + this.edit.video_url }, resetTemp() { this.temp = { user_id: '', content: '', be_replied_id: '', replied_user_id: '' } }, reply(row) { this.resetTemp() if (row){ // this.temp.be_replied_id = row.be_reply_user.id this.temp.replied_user_id = row.reply_user.id this.temp.replied_id = row.id } this.dialogStatus = 'create' this.dialogFormVisible = true this.$nextTick(() => { this.$refs['dataForm'].clearValidate() }) }, handleSizeChange(val) { this.listQuery.limit = val this.getList() }, handleCurrentChange(val) { this.listQuery.page = val this.getList() }, createData() { this.$refs['dataForm'].validate((valid) => { if (valid) { this.is_click = true this.temp.topic_id = this.postForm.id createRemark(this.temp).then((response) => { this.list.push(response.data.data.data) this.dialogFormVisible = false this.$notify({ message: '创建成功', type: 'success', duration: 2000 }) this.is_click = false }) } }) }, handleFilter() { this.listQuery.offset = 1 this.getList(); }, } } </script> <style rel="stylesheet/scss" lang="scss" scoped> @import "src/styles/mixin.scss"; .edit-input { padding-right: 100px; } .cancel-btn { position: absolute; right: 15px; top: 10px; } .up-video { .video-items { float: left; margin-right: 16px; } .video-item { float: left; position: relative; width: 110px; height: 110px; border-radius: 4px; background-color: #F5FBFF; cursor: pointer; overflow: hidden; vertical-align: top; &::before { content: ''; position: absolute; top: 0px; right: 0; width: 30px; height: 30px; background: url('../../../assets/image/svg/uploadSuccess.svg') 50% 50% no-repeat; border-top-right-radius: 4px; } &::after { position: absolute; content: ''; width: 100%; height: 100%; top: 50%; left: 50%; transform: translate(-50%, -50%); background: url('../../../assets/image/svg/video.svg') center center no-repeat; background-size: 38px 38px; } .close { display: none; position: absolute; width: 30px; height: 30px; top: 0px; right: 0px; border-top-right-radius: 4px; cursor: pointer; background: url('../../../assets/image/svg/video-close.svg') center center no-repeat; z-index: 1; } video { display: block; width: 110px; height: 110px; } &:hover { &::before { content: ''; position: absolute; top: 0px; right: 0; width: 30px; height: 30px; background-image: none; border-top-right-radius: 4px; } .close { display: block; } } } form { position: relative; display: inline-block; width: 110px; height: 110px; border-radius: 4px; border: 1px dashed #E5E5E5; background: #F5FBFF url('../../../assets/image/svg/video-upload.svg') center center no-repeat; .file { display: inline-block; width: 100%; height: 100%; padding: 0; opacity: 0; border: none; cursor: pointer; -webkit-appearance: none; appearance: none; } &.uploading { background: none; } } &:hover { form { border: 1px dashed #5CAEDC; } } } .createPost-container { position: relative; .createPost-main-container { padding: 40px 45px 20px 50px; .postInfo-container { position: relative; @include clearfix; margin-bottom: 10px; .postInfo-container-item { float: left; } } .editor-container { min-height: 500px; margin: 0 0 30px; .editor-upload-btn-container { text-align: right; margin-right: 10px; .editor-upload-btn { display: inline-block; } } } } .word-counter { width: 40px; position: absolute; right: -10px; top: 0px; } } .real .error_icon { margin-top: 90px; } </style>