| | |
| | | activityMembersPage, |
| | | auditActivity |
| | | } from "@/api/activity.js" |
| | | import Editor from '@/components/editor/index.vue' |
| | | import { uploadFileByLmk, delByKey } from "@/api/common.js" |
| | | |
| | | import { quillEditor } from 'vue-quill-editor' |
| | | import Delta from 'quill-delta' |
| | | import 'quill/dist/quill.core.css'; |
| | | import 'quill/dist/quill.snow.css'; |
| | | import 'quill/dist/quill.bubble.css'; |
| | |
| | | |
| | | export default { |
| | | name: "ActivityManagement", |
| | | components: {Editor, quillEditor}, |
| | | components: { quillEditor}, |
| | | data() { |
| | | return { |
| | | auditForm: { |
| | |
| | | } |
| | | }, |
| | | { |
| | | title: '审核', |
| | | title: '审核状态', |
| | | key: 'auditStatus', |
| | | width: 100, |
| | | align: 'center', |
| | | render: (h, params) => { |
| | | const status = params.row.auditStatus; |
| | | let tagText, tagColor; |
| | | |
| | | // 根据状态设置文案和颜色 |
| | | switch (status) { |
| | | case 0: |
| | | tagText = '审核中'; |
| | | tagColor = 'orange'; // 橙色表示进行中 |
| | | break; |
| | | case 1: |
| | | tagText = '已审核'; |
| | | tagColor = 'green'; // 绿色表示通过 |
| | | break; |
| | | case 2: |
| | | tagText = '未通过'; |
| | | tagColor = 'red'; // 红色表示拒绝 |
| | | break; |
| | | default: |
| | | tagText = '未知状态'; |
| | | tagColor = 'default'; // 默认灰色 |
| | | } |
| | | |
| | | return h('Tag', { |
| | | props: { |
| | | color: params.row.auditStatus === 0 ? 'default' : 'green' |
| | | } |
| | | }, params.row.auditStatus === 0 ? '未审核' : '已审核') |
| | | } |
| | | color: tagColor, |
| | | }, |
| | | }, tagText); |
| | | }, |
| | | }, |
| | | { |
| | | title: '状态', |
| | |
| | | |
| | | // 失去焦点 |
| | | onEditorBlur(editor) { |
| | | |
| | | }, |
| | | |
| | | // 获得焦点 |
| | |
| | | </Col> |
| | | <Col span="24"> |
| | | <FormItem label="新闻内容:" prop="content"> |
| | | <editor ref="editor" @input="getReason" /> |
| | | <Upload |
| | | :show-upload-list="false" |
| | | ref="upload" |
| | | style="display: none" |
| | | :before-upload="handleUploadEdit" |
| | | :format="['jpg','jpeg','png','gif','mp4','mov']" |
| | | :max-size="20480" |
| | | action="" |
| | | accept="image/*,video/*" |
| | | > |
| | | </Upload> |
| | | <!-- 基于elementUi的上传组件 el-upload end--> |
| | | <quill-editor |
| | | v-model="newsForm.content" |
| | | ref="QuillEditor" |
| | | class="editor" |
| | | :options="editorOption" |
| | | @blur="onEditorBlur($event)" |
| | | @focus="onEditorFocus($event)" |
| | | @ready="onEditorReady($event)" |
| | | > |
| | | </quill-editor> |
| | | </FormItem> |
| | | </Col> |
| | | </Row> |
| | |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import Editor from '@/components/editor/index.vue' |
| | | import { getNews,editNews,addNews,publish,delById } from '@/api/news.js' |
| | | import { uploadFileByLmk, delByKey } from "@/api/common.js" |
| | | import { quillEditor } from 'vue-quill-editor' |
| | | |
| | | import 'quill/dist/quill.core.css'; |
| | | import 'quill/dist/quill.snow.css'; |
| | | import 'quill/dist/quill.bubble.css'; |
| | | |
| | | import * as Quill from 'quill' //引入编辑器 |
| | | import VideoBlot from '../activity/video.js'; |
| | | |
| | | const toolbarOptions = [ |
| | | ['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线 |
| | | ['blockquote', 'code-block'], //引用,代码块 |
| | | [{ 'header': 1 }, { 'header': 2 }], // 几级标题 |
| | | [{ 'list': 'ordered' }, { 'list': 'bullet' }], // 有序列表,无序列表 |
| | | [{ 'script': 'sub' }, { 'script': 'super' }], // 下角标,上角标 |
| | | [{ 'indent': '-1' }, { 'indent': '+1' }], // 缩进 |
| | | [{ 'direction': 'rtl' }], // 文字输入方向 |
| | | [{ 'size': ['small', false, 'large', 'huge'] }], // 字体大小 |
| | | [{ 'header': [1, 2, 3, 4, 5, 6, false] }], // 标题 |
| | | [{ 'color': [] }, { 'background': [] }], // 颜色选择 |
| | | // [{ 'font': [] }], // 字体 |
| | | [{ 'align': [] }], // 居中 |
| | | ['clean'], // 清除样式, |
| | | ['link'], |
| | | ['myUploadBtn'], |
| | | ] |
| | | |
| | | // toolbar标题 |
| | | const titleConfig = [ |
| | | { Choice: '.ql-insertMetric', title: '跳转配置' }, |
| | | { Choice: '.ql-bold', title: '加粗' }, |
| | | { Choice: '.ql-italic', title: '斜体' }, |
| | | { Choice: '.ql-underline', title: '下划线' }, |
| | | { Choice: '.ql-header', title: '段落格式' }, |
| | | { Choice: '.ql-strike', title: '删除线' }, |
| | | { Choice: '.ql-blockquote', title: '块引用' }, |
| | | { Choice: '.ql-code', title: '插入代码' }, |
| | | { Choice: '.ql-code-block', title: '插入代码段' }, |
| | | { Choice: '.ql-font', title: '字体' }, |
| | | { Choice: '.ql-size', title: '字体大小' }, |
| | | { Choice: '.ql-list[value="ordered"]', title: '编号列表' }, |
| | | { Choice: '.ql-list[value="bullet"]', title: '项目列表' }, |
| | | { Choice: '.ql-direction', title: '文本方向' }, |
| | | { Choice: '.ql-header[value="1"]', title: 'h1' }, |
| | | { Choice: '.ql-header[value="2"]', title: 'h2' }, |
| | | { Choice: '.ql-align', title: '对齐方式' }, |
| | | { Choice: '.ql-color', title: '字体颜色' }, |
| | | { Choice: '.ql-background', title: '背景颜色' }, |
| | | { Choice: '.ql-image', title: '图像' }, |
| | | { Choice: '.ql-video', title: '视频' }, |
| | | { Choice: '.ql-link', title: '添加链接' }, |
| | | { Choice: '.ql-formula', title: '插入公式' }, |
| | | { Choice: '.ql-clean', title: '清除字体格式' }, |
| | | { Choice: '.ql-script[value="sub"]', title: '下标' }, |
| | | { Choice: '.ql-script[value="super"]', title: '上标' }, |
| | | { Choice: '.ql-indent[value="-1"]', title: '向左缩进' }, |
| | | { Choice: '.ql-indent[value="+1"]', title: '向右缩进' }, |
| | | { Choice: '.ql-header .ql-picker-label', title: '标题大小' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="1"]', title: '标题一' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="2"]', title: '标题二' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="3"]', title: '标题三' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="4"]', title: '标题四' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="5"]', title: '标题五' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="6"]', title: '标题六' }, |
| | | { Choice: '.ql-header .ql-picker-item:last-child', title: '标准' }, |
| | | { Choice: '.ql-size .ql-picker-item[data-value="small"]', title: '小号' }, |
| | | { Choice: '.ql-size .ql-picker-item[data-value="large"]', title: '大号' }, |
| | | { Choice: '.ql-size .ql-picker-item[data-value="huge"]', title: '超大号' }, |
| | | { Choice: '.ql-size .ql-picker-item:nth-child(2)', title: '标准' }, |
| | | { Choice: '.ql-align .ql-picker-item:first-child', title: '居左对齐' }, |
| | | { Choice: '.ql-align .ql-picker-item[data-value="center"]', title: '居中对齐' }, |
| | | { Choice: '.ql-align .ql-picker-item[data-value="right"]', title: '居右对齐' }, |
| | | { Choice: '.ql-align .ql-picker-item[data-value="justify"]', title: '两端对齐' } |
| | | ] |
| | | |
| | | |
| | | export default { |
| | | name: "newsManagement", |
| | | components: {Editor}, |
| | | components: {quillEditor}, |
| | | data(){ |
| | | return{ |
| | | // 图片预览 |
| | |
| | | value:1 |
| | | } |
| | | |
| | | ] |
| | | ], |
| | | |
| | | file: null, |
| | | Quill:'', |
| | | defaultValue: '', |
| | | editorOption: { |
| | | placeholder: '请在这里输入', |
| | | theme: 'snow', //主题 snow/bubble |
| | | modules: { |
| | | history: { |
| | | delay: 1000, |
| | | maxStack: 50, |
| | | userOnly: false |
| | | }, |
| | | toolbar: { |
| | | container: toolbarOptions, |
| | | handlers: { |
| | | myUploadBtn: this.myMethod, |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | // 在组件创建前注册 |
| | | beforeCreate() { |
| | | Quill.register(VideoBlot, true); |
| | | }, |
| | | mounted() { |
| | | //初始化 |
| | | this.Quill=this.$refs.QuillEditor.quill |
| | | this.getNewsList(); |
| | | this.initTitle(); |
| | | this.initButton(); |
| | | }, |
| | | methods:{ |
| | | onEditorBlur(editor) { |
| | | }, |
| | | // 获得焦点 |
| | | onEditorFocus(editor) { |
| | | }, |
| | | // 开始 |
| | | onEditorReady(editor) { |
| | | }, |
| | | myMethod(){ |
| | | this.$refs.upload.handleClick(); |
| | | }, |
| | | initButton(){ |
| | | const editorButton = document.querySelector('.ql-myUploadBtn') |
| | | editorButton.innerHTML = '<svg t="1751966766109" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1530" fill="currentColor" style="width: 1em; height: 1em; vertical-align: middle;"><path d="M1024 693.248q0 25.6-8.704 48.128t-24.576 40.448-36.864 30.208-45.568 16.384l1.024 1.024-17.408 0-4.096 0-4.096 0-675.84 0q-5.12 1.024-16.384 1.024-39.936 0-74.752-15.36t-60.928-41.472-40.96-60.928-14.848-74.752 14.848-74.752 40.96-60.928 60.928-41.472 74.752-15.36l1.024 0q-1.024-8.192-1.024-15.36l0-16.384q0-72.704 27.648-137.216t75.776-112.128 112.128-75.264 136.704-27.648 137.216 27.648 112.64 75.264 75.776 112.128 27.648 137.216q0 37.888-8.192 74.24t-22.528 69.12q5.12-1.024 10.752-1.536t10.752-0.512q27.648 0 52.736 10.752t43.52 29.696 29.184 44.032 10.752 53.76zM665.6 571.392q20.48 0 26.624-4.608t-8.192-22.016q-14.336-18.432-31.744-48.128t-36.352-60.416-38.4-57.344-37.888-38.912q-18.432-13.312-27.136-14.336t-25.088 12.288q-18.432 15.36-35.84 38.912t-35.328 50.176-35.84 52.224-36.352 45.056q-18.432 18.432-13.312 32.768t25.6 14.336l16.384 0q9.216 0 19.968 0.512t20.992 0.512l17.408 0q14.336 1.024 18.432 9.728t4.096 24.064q0 17.408-0.512 30.72t-0.512 25.6-0.512 25.6-0.512 30.72q0 7.168 1.536 15.36t5.632 15.36 12.288 11.776 21.504 4.608l23.552 0q9.216 0 27.648 1.024 24.576 0 28.16-12.288t3.584-38.912q0-23.552 0.512-42.496t0.512-51.712q0-23.552 4.608-36.352t19.968-12.8q11.264 0 32.256-0.512t32.256-0.512z" p-id="1531"></path></svg>' |
| | | }, |
| | | |
| | | initTitle() { |
| | | document.getElementsByClassName('ql-editor')[0].dataset.placeholder = '' |
| | | for (let item of titleConfig) { |
| | | let tip = document.querySelector('.quill-editor ' + item.Choice) |
| | | if (!tip) continue |
| | | tip.setAttribute('title', item.title) |
| | | } |
| | | }, |
| | | handleUploadEdit(file){ |
| | | const fileType = file.type |
| | | const isImage = fileType.includes('image') |
| | | const isVideo = fileType.includes('video') |
| | | |
| | | if (!isImage && !isVideo) { |
| | | this.$Message.error('请上传图片或视频文件') |
| | | return false |
| | | } |
| | | |
| | | if (file.size > 20 * 1024 * 1024) { |
| | | this.$Message.error('文件大小不能超过20MB') |
| | | return false |
| | | } |
| | | |
| | | this.file = file |
| | | this.uploadFile2() |
| | | return false |
| | | }, |
| | | uploadFile2() { |
| | | |
| | | if (!this.file) return |
| | | |
| | | this.submitLoading = true |
| | | const formData = new FormData() |
| | | formData.append('file', this.file) |
| | | uploadFileByLmk(formData).then(res => { |
| | | this.submitLoading = false |
| | | if (res.code === 200) { |
| | | let url = res.data.url; |
| | | let fileKey = res.data.fileKey; |
| | | let fileType = this.getFileType(this.file); |
| | | |
| | | const range = this.Quill.getSelection(); |
| | | const index = range ? range.index : this.Quill.getLength(); |
| | | |
| | | console.log("--------"); |
| | | if (fileType === 'video') { |
| | | this.Quill.insertEmbed(index, 'video', { |
| | | url:url, |
| | | controls:'controls', |
| | | width:'100%', |
| | | height:'auto' |
| | | }); |
| | | } else if (fileType === 'image') { |
| | | this.Quill.insertEmbed(index, "image", url); |
| | | } else { |
| | | // 如果不是图片或视频,可以处理其他类型或给出提示 |
| | | this.$Message.warning('不支持的文件类型'); |
| | | return; |
| | | } |
| | | console.log("--------"); |
| | | console.log(this.newsForm.content) |
| | | this.Quill.setSelection(index + 1); |
| | | this.$Message.success('上传成功') |
| | | } |
| | | }).catch(() => { |
| | | this.submitLoading = false |
| | | }) |
| | | }, |
| | | getNewsList(){ |
| | | this.loading = true |
| | | getNews(this.searchForm).then(res =>{ |
| | |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | getReason(content) { |
| | | this.newsForm.content = content |
| | | }, |
| | | saveOrUpdate(){ |
| | | this.$refs.form.validate(valid => { |
| | | if (valid) { |
| | | this.submitLoading = true |
| | | const submitData = { |
| | | ...this.newsForm, |
| | | publish: this.newsForm.publish !== 0, // true → 1, false → 0 |
| | | }; |
| | | if (this.newsForm.id){ |
| | | editNews(submitData).then(res => { |
| | | this.submitLoading = false |
| | | if (res.code === 200) { |
| | | this.$Message.success(res.msg) |
| | | this.modelClose() |
| | | this.getNewsList() |
| | | } |
| | | }).catch(() => { |
| | | this.submitLoading = false |
| | | }) |
| | | }else { |
| | | addNews(submitData).then(res => { |
| | | this.submitLoading = false |
| | | if (res.code === 200) { |
| | | this.$Message.success(res.msg) |
| | | this.modelClose() |
| | | this.getNewsList() |
| | | } |
| | | }).catch(() => { |
| | | this.submitLoading = false |
| | | }) |
| | | } |
| | | |
| | | } |
| | | }) |
| | | saveOrUpdate(){ |
| | | console.log(this.newsForm) |
| | | // this.$refs.form.validate(valid => { |
| | | // if (valid) { |
| | | // this.submitLoading = true |
| | | // const submitData = { |
| | | // ...this.newsForm, |
| | | // publish: this.newsForm.publish !== 0, // true → 1, false → 0 |
| | | // }; |
| | | // if (this.newsForm.id){ |
| | | // editNews(submitData).then(res => { |
| | | // this.submitLoading = false |
| | | // if (res.code === 200) { |
| | | // this.$Message.success(res.msg) |
| | | // this.modelClose() |
| | | // this.getNewsList() |
| | | // } |
| | | // }).catch(() => { |
| | | // this.submitLoading = false |
| | | // }) |
| | | // }else { |
| | | // addNews(submitData).then(res => { |
| | | // this.submitLoading = false |
| | | // if (res.code === 200) { |
| | | // this.$Message.success(res.msg) |
| | | // this.modelClose() |
| | | // this.getNewsList() |
| | | // } |
| | | // }).catch(() => { |
| | | // this.submitLoading = false |
| | | // }) |
| | | // } |
| | | // |
| | | // } |
| | | // }) |
| | | }, |
| | | |
| | | infoModelClose(){ |
| | | this.infoModelShow = false; |
| | | }, |
| | | |
| | | modelClose(){ |
| | | this.modelShow = false; |
| | | }, |
| | | |
| | | changePage(page) { |
| | | this.searchForm.pageNumber = page |
| | | this.getNewsList() |
| | | }, |
| | | |
| | | // 改变每页条数 |
| | | changePageSize(pageSize) { |
| | | this.searchForm.pageNumber = 1 |
| | |
| | | |
| | | }) |
| | | }, |
| | | |
| | | openEdit(row){ |
| | | this.modelTitle = '修改新闻'; |
| | | this.modelShow = true; |
| | |
| | | this.newsForm.id = row.id; |
| | | this.$refs.editor.setContent(this.newsForm.content) |
| | | }, |
| | | |
| | | openAdd(){ |
| | | this.modelTitle = '新增新闻'; |
| | | this.modelShow = true; |
| | | this.$refs.form.resetFields() |
| | | this.newsForm.id = ''; |
| | | this.newsForm.content = ''; |
| | | this.newsForm.publish = 0; |
| | | |
| | | }, |
| | | |
| | | detail(row){ |
| | | this.modelTitle = '活动详情' |
| | | this.infoModelShow = true |
| | | this.newsInfo = row |
| | | }, |
| | | |
| | | changeStatus(row,action){ |
| | | row.statusLoading = true; |
| | | |
| | |
| | | row.statusLoading = false |
| | | }) |
| | | }, |
| | | |
| | | // 表格选择变化 |
| | | showSelect(selection) { |
| | | this.selectList = selection.map(item => item.id) |
| | |
| | | this.searchForm.pageNumber = 1 |
| | | this.getNewsList() |
| | | }, |
| | | |
| | | resetSearch(){ |
| | | this.$refs.searchForm.resetFields() |
| | | this.searchForm.pageNumber = 1 |
| | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .ql-editor .ql-video { |
| | | width: 50%; |
| | | height: auto; /* 根据你的需求调整高度 */ |
| | | max-width: 100%; |
| | | } |
| | | .activity-management { |
| | | .search-form { |
| | | padding: 16px; |
| | |
| | | } |
| | | } |
| | | } |
| | | /* |
| | | 文字大小 |
| | | */ |
| | | .ql-snow .ql-picker.ql-size{ |
| | | width: 70px; // 菜单栏占比宽度 |
| | | } |
| | | /* |
| | | 字体 |
| | | */ |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before { |
| | | content: "黑体"; |
| | | font-family: "SimHei"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before { |
| | | content: "微软雅黑"; |
| | | font-family: "Microsoft YaHei"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before { |
| | | content: "楷体"; |
| | | font-family: "KaiTi"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before { |
| | | content: "仿宋"; |
| | | font-family: "FangSong"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before { |
| | | content: "Arial"; |
| | | font-family: "Arial"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before { |
| | | content: "Times New Roman"; |
| | | font-family: "Times New Roman"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before { |
| | | content: "sans-serif"; |
| | | font-family: "sans-serif"; |
| | | } |
| | | |
| | | .ql-font-SimSun { |
| | | font-family: "SimSun"; |
| | | } |
| | | |
| | | .ql-font-SimHei { |
| | | font-family: "SimHei"; |
| | | } |
| | | |
| | | .ql-font-Microsoft-YaHei { |
| | | font-family: "Microsoft YaHei"; |
| | | } |
| | | |
| | | .ql-font-KaiTi { |
| | | font-family: "KaiTi"; |
| | | } |
| | | |
| | | .ql-font-FangSong { |
| | | font-family: "FangSong"; |
| | | } |
| | | |
| | | .ql-font-Arial { |
| | | font-family: "Arial"; |
| | | } |
| | | |
| | | .ql-font-Times-New-Roman { |
| | | font-family: "Times New Roman"; |
| | | } |
| | | |
| | | .ql-font-sans-serif { |
| | | font-family: "sans-serif"; |
| | | } |
| | | </style> |
| | |
| | | "js-cookie": "^2.2.1", |
| | | "node-sass": "^4.14.1", |
| | | "price-color": "^1.0.2", |
| | | "quill": "^1.3.7", |
| | | "quill-image-drop-module": "^1.0.3", |
| | | "quill-image-extend-module": "^1.1.2", |
| | | "quill-image-resize-module": "^3.0.0", |
| | | "s": "^1.0.0", |
| | | "sass-loader": "^8.0.2", |
| | | "sockjs-client": "^1.4.0", |
| | |
| | | "vue-lazyload": "^1.3.3", |
| | | "vue-print-nb": "^1.7.5", |
| | | "vue-qr": "^2.3.0", |
| | | "vue-quill-editor": "^3.0.6", |
| | | "vue-router": "^3.1.3", |
| | | "vuedraggable": "^2.23.2", |
| | | "vuex": "^3.4.0", |
| New file |
| | |
| | | import { commonUrl, getRequest,postRequest,deleteRequest,putRequest, uploadFileRequest, uploadFile, request} from "@/libs/axios"; |
| | | import { postRequest2 } from "@/libs/axios"; |
| | | import {putRequest2} from "../libs/axios"; |
| | | |
| | | |
| | | |
| | | // 活动详情 |
| | | export const activityInfoById = (params) => { |
| | | return getRequest("/"+params); |
| | | } |
| | | |
| | | // 活动分页列表 |
| | | export const getMyApplyActivityPage = (params) => { |
| | | return getRequest("/lmk/activity/applyActivityPage", params); |
| | | } |
| | | |
| | | export const addActivity = (params) =>{ |
| | | return postRequest2("/lmk/activity/add",params); |
| | | } |
| | | |
| | | export const delActivityById = (params) =>{ |
| | | return deleteRequest(); |
| | | } |
| | | |
| | | export const delActivityBatch = (params) =>{ |
| | | return deleteRequest(); |
| | | } |
| | | |
| | | export const editActivity = (params) =>{ |
| | | return putRequest2("/lmk/activity",params); |
| | | } |
| | |
| | | : BASE.API_PROD.common; |
| | | // 文件上传接口 |
| | | export const uploadFile = commonUrl + "/common/common/upload/file"; |
| | | |
| | | export const uploadFile2 = commonUrl + "/common/lmk/file/upload"; |
| | | |
| | | var isRefreshToken = 0; |
| | | const refreshToken = getTokenDebounce(); |
| | | const service = axios.create({ |
| | |
| | | return service(data); |
| | | }; |
| | | |
| | | export const postRequest2 = (url, params, headers) => { |
| | | let accessToken = getStore("accessToken"); |
| | | return service({ |
| | | method: "post", |
| | | url, |
| | | data: params, // 直接传对象,不要用 [params] 包装 |
| | | headers: { |
| | | "Content-Type": "application/json", |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | export const postRequest = (url, params, headers) => { |
| | | let accessToken = getStore("accessToken"); |
| | | return service({ |
| | |
| | | } |
| | | }); |
| | | }; |
| | | export const putRequest2 = (url, params, headers) => { |
| | | let accessToken = getStore("accessToken"); |
| | | return service({ |
| | | method: "put", |
| | | url, |
| | | data: params, // 直接传对象,不要用 [params] 包装 |
| | | headers: { |
| | | "Content-Type": "application/json", |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | export const putRequestWithNoForm = (url, params) => { |
| | | let accessToken = getStore("accessToken"); |
| | |
| | | }); |
| | | }; |
| | | |
| | | export const uploadFileRequest2 = (url, params) => { |
| | | let accessToken = getStore("accessToken"); |
| | | return axios({ // 直接使用 axios,而不是 service(避免 baseUrl 影响) |
| | | method: "post", |
| | | url: `${url}`, // 直接传完整 URL,如 uploadFile |
| | | data: params, |
| | | headers: { |
| | | accessToken: accessToken, |
| | | 'Content-Type': 'multipart/form-data' |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | export const delFileByFileKeyRequest = (url, params) => { |
| | | let accessToken = getStore("accessToken"); |
| | | return axios({ // 直接使用 axios,而不是 service(避免 baseUrl 影响) |
| | | method: "DELETE", |
| | | url: `${url}` + '/'+params, // 直接传完整 URL,如 uploadFile |
| | | headers: { |
| | | accessToken: accessToken, |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | |
| | | /** |
| New file |
| | |
| | | <template> |
| | | <div class="activity-management"> |
| | | <Card> |
| | | <!-- 搜索表单 --> |
| | | <Form |
| | | ref="searchForm" |
| | | @keydown.enter.native="handleSearch" |
| | | :model="searchForm" |
| | | inline |
| | | :label-width="80" |
| | | class="search-form" |
| | | > |
| | | <FormItem label="活动名称" prop="activityName"> |
| | | <Input |
| | | type="text" |
| | | v-model="searchForm.activityName" |
| | | placeholder="请输入活动名称" |
| | | clearable |
| | | @on-clear="handleSearch" |
| | | style="width: 180px" |
| | | /> |
| | | </FormItem> |
| | | <FormItem label="活动类型" prop="activityType"> |
| | | <Select |
| | | v-model="searchForm.activityType" |
| | | placeholder="请选择活动类型" |
| | | style="width: 180px" |
| | | clearable |
| | | @on-clear="handleSearch" |
| | | @on-change="handleSearch" |
| | | > |
| | | <Option |
| | | v-for="item in typeSelect" |
| | | :value="item.value" |
| | | :key="item.id" |
| | | > |
| | | {{ item.label }} |
| | | </Option> |
| | | </Select> |
| | | </FormItem> |
| | | <FormItem label="报名开始时间" prop="reportStartTime"> |
| | | <DatePicker |
| | | :value="searchForm.reportStartTime" |
| | | type="datetime" |
| | | placeholder="选择开始时间" |
| | | style="width: 180px" |
| | | @on-change="handleSearch('reportStart', $event)" |
| | | @on-clear="handleSearch" |
| | | ></DatePicker> |
| | | </FormItem> |
| | | <FormItem label="报名结束时间" prop="reportEndTime"> |
| | | <DatePicker |
| | | :value="searchForm.reportEndTime" |
| | | type="datetime" |
| | | placeholder="选择结束时间" |
| | | style="width: 180px" |
| | | @on-clear="handleSearch" |
| | | @on-change="handleSearch('reportEnd', $event)" |
| | | ></DatePicker> |
| | | </FormItem> |
| | | <Button |
| | | @click="handleSearch" |
| | | type="primary" |
| | | icon="ios-search" |
| | | class="search-btn" |
| | | >搜索</Button> |
| | | <Button |
| | | @click="resetSearch" |
| | | icon="md-refresh" |
| | | style="margin-left: 8px" |
| | | >重置</Button> |
| | | </Form> |
| | | |
| | | <!-- 操作按钮 --> |
| | | <Row class="operation"> |
| | | <Button @click="openAdd" type="primary" icon="md-add">新增活动</Button> |
| | | </Row> |
| | | |
| | | <!-- 活动表格 --> |
| | | <Table |
| | | :loading="loading" |
| | | border |
| | | :columns="columns" |
| | | :data="activityList" |
| | | ref="table" |
| | | class="activity-table" |
| | | > |
| | | <!-- 封面展示插槽 --> |
| | | <template slot-scope="{ row }" slot="url"> |
| | | <div class="media-container"> |
| | | <!-- 图片类型 --> |
| | | <template v-if="row.coverType === 'image'"> |
| | | <img |
| | | :src="row.url" |
| | | alt="活动封面" |
| | | class="thumbnail" |
| | | @click="previewImage(row.url)" |
| | | > |
| | | </template> |
| | | <!-- 视频类型 --> |
| | | <template v-else-if="row.coverType === 'video'"> |
| | | <video |
| | | :src="row.url" |
| | | class="video-player" |
| | | controls |
| | | ></video> |
| | | </template> |
| | | <!-- 文字类型 --> |
| | | <template v-else> |
| | | <div class="text-cover">{{ row.cover || '暂无封面内容' }}</div> |
| | | </template> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- 操作按钮插槽 --> |
| | | <template slot-scope="{ row }" slot="action"> |
| | | <div class="action-btns"> |
| | | <Button |
| | | type="info" |
| | | size="small" |
| | | @click="detail(row)" |
| | | >详情</Button> |
| | | <Button |
| | | type="info" |
| | | size="small" |
| | | @click="openEdit(row)" |
| | | >编辑</Button> |
| | | </div> |
| | | </template> |
| | | </Table> |
| | | |
| | | <!-- 分页 --> |
| | | <Row type="flex" justify="end" class="page-footer"> |
| | | <Page |
| | | :current="searchForm.pageNumber" |
| | | :total="total" |
| | | :page-size="searchForm.pageSize" |
| | | @on-change="changePage" |
| | | @on-page-size-change="changePageSize" |
| | | :page-size-opts="[10, 20, 50]" |
| | | size="small" |
| | | show-total |
| | | show-elevator |
| | | show-sizer |
| | | ></Page> |
| | | </Row> |
| | | |
| | | <!-- 活动编辑/新增模态框 --> |
| | | <Modal |
| | | v-model="modelShow" |
| | | :title="modelTitle" |
| | | @on-cancel="modelClose" |
| | | width="800" |
| | | :mask-closable="false" |
| | | > |
| | | <Form ref="form" :model="activityFrom" :label-width="100" :rules="rules"> |
| | | <Row :gutter="16"> |
| | | <Col span="12"> |
| | | <FormItem label="活动名称" prop="activityName"> |
| | | <Input |
| | | v-model="activityFrom.activityName" |
| | | placeholder="请输入活动名称" |
| | | clearable |
| | | /> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="12"> |
| | | <FormItem label="活动类型" prop="activityType" :label-width="100"> |
| | | <Select |
| | | v-model="activityFrom.activityType" |
| | | placeholder="请选择活动类型" |
| | | clearable |
| | | > |
| | | <Option |
| | | v-for="item in typeSelect" |
| | | :value="item.value" |
| | | :key="item.id" |
| | | > |
| | | {{ item.label }} |
| | | </Option> |
| | | </Select> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="12"> |
| | | <FormItem label="报名时间段" prop="reportTime"> |
| | | <DatePicker |
| | | v-model="activityFrom.reportTime" |
| | | type="datetimerange" |
| | | format="yyyy-MM-dd HH:mm" |
| | | placeholder="请选择报名时间段" |
| | | style="width: 100%" |
| | | ></DatePicker> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="12"> |
| | | <FormItem label="活动时间段" prop="time"> |
| | | <DatePicker |
| | | v-model="activityFrom.time" |
| | | type="datetimerange" |
| | | format="yyyy-MM-dd HH:mm" |
| | | placeholder="请选择活动时间段" |
| | | style="width: 100%" |
| | | ></DatePicker> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="12"> |
| | | <FormItem label="封面类型" prop="coverType" :labelWidth="100"> |
| | | <Select |
| | | v-model="coverType" |
| | | placeholder="请选择封面类型" |
| | | @on-change="handleCoverTypeChange" |
| | | > |
| | | <Option |
| | | v-for="item in coverTypeOptions" |
| | | :value="item.value" |
| | | :key="item.id" |
| | | > |
| | | {{ item.value }} |
| | | </Option> |
| | | </Select> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="24" v-if="coverType === '输入文字封面'"> |
| | | <FormItem label="封面文字" prop="cover"> |
| | | <Input |
| | | v-model="activityFrom.cover" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请输入封面文字" |
| | | style="width: 50%" |
| | | /> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="24" v-if="coverType === '选择文件封面'"> |
| | | <FormItem label="上传封面" prop="cover"> |
| | | <Upload |
| | | :before-upload="handleBeforeUpload" |
| | | :format="['jpg','jpeg','png','gif','mp4','mov']" |
| | | :max-size="20480" |
| | | action="" |
| | | accept="image/*,video/*" |
| | | > |
| | | <Button icon="ios-cloud-upload-outline">上传封面文件</Button> |
| | | <div class="upload-tip">支持图片或视频文件,最大20MB</div> |
| | | </Upload> |
| | | <div v-if="file" class="upload-file-info"> |
| | | 已选文件: {{ file.name }} |
| | | <Button type="text" @click="handleRemove">删除</Button> |
| | | </div> |
| | | </FormItem> |
| | | </Col> |
| | | <!-- 这两个表单项在同一Row内,会显示在同一行 --> |
| | | <Col span="12"> |
| | | <FormItem label="人数限制" prop="limitUserNum"> |
| | | <InputNumber |
| | | v-model="activityFrom.limitUserNum" |
| | | :min="1" |
| | | placeholder="请输入最大人数" |
| | | style="width: 100%" |
| | | /> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="12" v-if="activityFrom.activityType === 'offline'"> |
| | | <FormItem label="活动地点" prop="activityLocation" > |
| | | <Input |
| | | v-model="activityFrom.activityLocation" |
| | | placeholder="请输入活动地点" |
| | | /> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span="24"> |
| | | <FormItem label="活动内容:" prop="activityContent"> |
| | | |
| | | <!-- 基于elementUi的上传组件 el-upload begin--> |
| | | <Upload |
| | | :show-upload-list="false" |
| | | ref="upload" |
| | | style="display: none" |
| | | :before-upload="handleUploadEdit" |
| | | :format="['jpg','jpeg','png','gif','mp4','mov']" |
| | | :max-size="20480" |
| | | action="" |
| | | accept="image/*,video/*" |
| | | > |
| | | </Upload> |
| | | <!-- 基于elementUi的上传组件 el-upload end--> |
| | | <quill-editor |
| | | v-model="activityFrom.activityContent" |
| | | ref="QuillEditor" |
| | | class="editor" |
| | | :options="editorOption" |
| | | @blur="onEditorBlur($event)" |
| | | @focus="onEditorFocus($event)" |
| | | @ready="onEditorReady($event)" |
| | | > |
| | | </quill-editor> |
| | | </FormItem> |
| | | </Col> |
| | | </Row> |
| | | </Form> |
| | | |
| | | <div slot="footer"> |
| | | <Button @click="modelClose">取消</Button> |
| | | <Button type="primary" :loading="submitLoading" @click="saveOrUpdate">提交</Button> |
| | | </div> |
| | | </Modal> |
| | | |
| | | <Modal |
| | | v-model="infoModelShow" |
| | | :title="modelTitle" |
| | | @on-cancel="infoModelClose" |
| | | width="800" |
| | | :mask-closable="false" |
| | | > |
| | | <div class="detail-container"> |
| | | <Row :gutter="16"> |
| | | <Col span="12"> |
| | | <div class="detail-item"> |
| | | <label>活动名称:</label> |
| | | <span>{{ activityInfo.activityName || '-' }}</span> |
| | | </div> |
| | | </Col> |
| | | <Col span="12"> |
| | | <div class="detail-item"> |
| | | <label>活动类型:</label> |
| | | <span>{{activityInfo.activityType === 'online' ? '线上':'线下'}}</span> |
| | | </div> |
| | | </Col> |
| | | <Col span="12"> |
| | | <div class="detail-item"> |
| | | <label>报名时间段:</label> |
| | | <span>{{ activityInfo.reportStartTime }} - {{ activityInfo.reportEndTime }}</span> |
| | | </div> |
| | | </Col> |
| | | <Col span="12"> |
| | | <div class="detail-item"> |
| | | <label>活动时间段:</label> |
| | | <span>{{ activityInfo.startTime }} - {{ activityInfo.endTime }}</span> |
| | | </div> |
| | | </Col> |
| | | |
| | | <Col span="24" v-if="coverType === '输入文字封面'"> |
| | | <div class="detail-item"> |
| | | <label>封面文字:</label> |
| | | <span>{{ activityInfo.cover || '-' }}</span> |
| | | </div> |
| | | </Col> |
| | | <Col span="24" v-if="coverType === '选择文件封面'"> |
| | | <div class="detail-item"> |
| | | <label>上传封面:</label> |
| | | <span>{{ activityInfo.cover }}</span> |
| | | </div> |
| | | </Col> |
| | | <Col span="12"> |
| | | <div class="detail-item"> |
| | | <label>人数限制:</label> |
| | | <span>{{ activityInfo.limitUserNum || '无限制' }}</span> |
| | | </div> |
| | | </Col> |
| | | <Col span="12"> |
| | | <div class="detail-item"> |
| | | <label>活动地点:</label> |
| | | <span>{{ activityInfo.activityLocation || '-' }}</span> |
| | | </div> |
| | | </Col> |
| | | <Col span="24"> |
| | | <div class="detail-item"> |
| | | <label>活动内容:</label> |
| | | <div |
| | | class="activity-content" |
| | | v-html="activityInfo.activityContent || '无内容'" |
| | | ></div> |
| | | </div> |
| | | </Col> |
| | | </Row> |
| | | </div> |
| | | |
| | | <div slot="footer"> |
| | | <Button @click="infoModelClose">关闭</Button> |
| | | </div> |
| | | </Modal> |
| | | |
| | | <!-- 图片预览模态框 --> |
| | | <Modal v-model="previewVisible" title="图片预览" footer-hide> |
| | | <img :src="previewImageUrl" style="width: 100%"> |
| | | </Modal> |
| | | </Card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | getMyApplyActivityPage, |
| | | addActivity, |
| | | editActivity, |
| | | } from "@/api/activity.js" |
| | | import { uploadFile2,uploadFileRequest2,delFileByFileKeyRequest } from "@/libs/axios"; |
| | | |
| | | import { quillEditor } from 'vue-quill-editor' |
| | | import 'quill/dist/quill.core.css'; |
| | | import 'quill/dist/quill.snow.css'; |
| | | import 'quill/dist/quill.bubble.css'; |
| | | |
| | | import * as Quill from 'quill' //引入编辑器 |
| | | import VideoBlot from './video.js'; |
| | | |
| | | const toolbarOptions = [ |
| | | ['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线 |
| | | ['blockquote', 'code-block'], //引用,代码块 |
| | | [{ 'header': 1 }, { 'header': 2 }], // 几级标题 |
| | | [{ 'list': 'ordered' }, { 'list': 'bullet' }], // 有序列表,无序列表 |
| | | [{ 'script': 'sub' }, { 'script': 'super' }], // 下角标,上角标 |
| | | [{ 'indent': '-1' }, { 'indent': '+1' }], // 缩进 |
| | | [{ 'direction': 'rtl' }], // 文字输入方向 |
| | | [{ 'size': ['small', false, 'large', 'huge'] }], // 字体大小 |
| | | [{ 'header': [1, 2, 3, 4, 5, 6, false] }], // 标题 |
| | | [{ 'color': [] }, { 'background': [] }], // 颜色选择 |
| | | // [{ 'font': [] }], // 字体 |
| | | [{ 'align': [] }], // 居中 |
| | | ['clean'], // 清除样式, |
| | | ['link'], |
| | | ['myUploadBtn'], |
| | | ] |
| | | |
| | | // toolbar标题 |
| | | const titleConfig = [ |
| | | { Choice: '.ql-insertMetric', title: '跳转配置' }, |
| | | { Choice: '.ql-bold', title: '加粗' }, |
| | | { Choice: '.ql-italic', title: '斜体' }, |
| | | { Choice: '.ql-underline', title: '下划线' }, |
| | | { Choice: '.ql-header', title: '段落格式' }, |
| | | { Choice: '.ql-strike', title: '删除线' }, |
| | | { Choice: '.ql-blockquote', title: '块引用' }, |
| | | { Choice: '.ql-code', title: '插入代码' }, |
| | | { Choice: '.ql-code-block', title: '插入代码段' }, |
| | | { Choice: '.ql-font', title: '字体' }, |
| | | { Choice: '.ql-size', title: '字体大小' }, |
| | | { Choice: '.ql-list[value="ordered"]', title: '编号列表' }, |
| | | { Choice: '.ql-list[value="bullet"]', title: '项目列表' }, |
| | | { Choice: '.ql-direction', title: '文本方向' }, |
| | | { Choice: '.ql-header[value="1"]', title: 'h1' }, |
| | | { Choice: '.ql-header[value="2"]', title: 'h2' }, |
| | | { Choice: '.ql-align', title: '对齐方式' }, |
| | | { Choice: '.ql-color', title: '字体颜色' }, |
| | | { Choice: '.ql-background', title: '背景颜色' }, |
| | | { Choice: '.ql-image', title: '图像' }, |
| | | { Choice: '.ql-video', title: '视频' }, |
| | | { Choice: '.ql-link', title: '添加链接' }, |
| | | { Choice: '.ql-formula', title: '插入公式' }, |
| | | { Choice: '.ql-clean', title: '清除字体格式' }, |
| | | { Choice: '.ql-script[value="sub"]', title: '下标' }, |
| | | { Choice: '.ql-script[value="super"]', title: '上标' }, |
| | | { Choice: '.ql-indent[value="-1"]', title: '向左缩进' }, |
| | | { Choice: '.ql-indent[value="+1"]', title: '向右缩进' }, |
| | | { Choice: '.ql-header .ql-picker-label', title: '标题大小' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="1"]', title: '标题一' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="2"]', title: '标题二' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="3"]', title: '标题三' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="4"]', title: '标题四' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="5"]', title: '标题五' }, |
| | | { Choice: '.ql-header .ql-picker-item[data-value="6"]', title: '标题六' }, |
| | | { Choice: '.ql-header .ql-picker-item:last-child', title: '标准' }, |
| | | { Choice: '.ql-size .ql-picker-item[data-value="small"]', title: '小号' }, |
| | | { Choice: '.ql-size .ql-picker-item[data-value="large"]', title: '大号' }, |
| | | { Choice: '.ql-size .ql-picker-item[data-value="huge"]', title: '超大号' }, |
| | | { Choice: '.ql-size .ql-picker-item:nth-child(2)', title: '标准' }, |
| | | { Choice: '.ql-align .ql-picker-item:first-child', title: '居左对齐' }, |
| | | { Choice: '.ql-align .ql-picker-item[data-value="center"]', title: '居中对齐' }, |
| | | { Choice: '.ql-align .ql-picker-item[data-value="right"]', title: '居右对齐' }, |
| | | { Choice: '.ql-align .ql-picker-item[data-value="justify"]', title: '两端对齐' } |
| | | ] |
| | | |
| | | export default { |
| | | name: "ActivityList", |
| | | components: { quillEditor}, |
| | | data() { |
| | | return { |
| | | infoModelShow: false, //详情model窗 |
| | | loading: false, //表格loading状态 |
| | | submitLoading: false, //提交按钮loading状态 |
| | | // 搜索表单 |
| | | searchForm: { |
| | | audit: '', |
| | | activityName: '', |
| | | activityType: '', |
| | | reportStartTime: '', |
| | | reportEndTime: '', |
| | | pageNumber: 1, |
| | | pageSize: 10 |
| | | }, |
| | | |
| | | // 活动列表数据 |
| | | activityList: [], |
| | | total: 0, |
| | | |
| | | // 活动类型选项 |
| | | typeSelect: [ |
| | | {id: 1, value: 'online', label: '线上'}, |
| | | {id: 2, value: 'offline', label: '线下'} |
| | | ], |
| | | // 封面类型选项 |
| | | coverTypeOptions: [ |
| | | {id: 1, value: '输入文字封面'}, |
| | | {id: 2, value: '选择文件封面'} |
| | | ], |
| | | coverType: '', |
| | | file: null, |
| | | |
| | | // 活动表单 |
| | | activityFrom: { |
| | | id: '', |
| | | activityName: '', |
| | | activityType: '', |
| | | reportTime: [], |
| | | time: [], |
| | | activityContent: '', |
| | | cover: '', |
| | | coverType: '', |
| | | status: '', |
| | | reportStartTime: '', |
| | | reportEndTime: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | recommend: false, |
| | | limitUserNum: 0, |
| | | activityLocation: '', |
| | | }, |
| | | activityInfo: { |
| | | id: '', |
| | | activityName: '', |
| | | activityType: '', |
| | | reportTime: [], |
| | | time: [], |
| | | activityContent: '', |
| | | cover: '', |
| | | coverType: '', |
| | | status: '', |
| | | reportStartTime: '', |
| | | reportEndTime: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | recommend: false, |
| | | limitUserNum: 0, |
| | | activityLocation: '', |
| | | }, |
| | | |
| | | // 表单验证规则 |
| | | rules: { |
| | | activityName: [ |
| | | {required: true, message: '请输入活动名称', trigger: 'blur'}, |
| | | {max: 50, message: '长度不能超过50个字符', trigger: 'blur'} |
| | | ], |
| | | activityType: [ |
| | | {required: true, message: '请选择活动类型', trigger: 'change'} |
| | | ], |
| | | reportTime: [ |
| | | {type: 'array', required: true, message: '请选择报名时间段', trigger: 'change'}, |
| | | {validator: this.validateReportTime, trigger: 'change'} |
| | | ], |
| | | time: [ |
| | | {type: 'array', required: true, message: '请选择活动时间段', trigger: 'change'}, |
| | | {validator: this.validateActivityTime, trigger: 'change'} |
| | | ], |
| | | cover: [ |
| | | {required: true, message: '请输入封面内容', trigger: 'blur'} |
| | | ], |
| | | coverType: [ |
| | | {required: true, message: '请选择封面类型', trigger: 'blur'} |
| | | ], |
| | | limitUserNum: [ |
| | | {required: true, type: 'number', message: '请输入人数限制', trigger: 'blur'}, |
| | | {type: 'number', min: 1, message: '人数不能少于1人', trigger: 'blur'} |
| | | ], |
| | | activityLocation: [ |
| | | {required: true, message: '请输入活动地点', trigger: 'blur'}, |
| | | {max: 100, message: '长度不能超过100个字符', trigger: 'blur'} |
| | | ], |
| | | activityContent: [ |
| | | {required: false, message: '请输入活动内容', trigger: 'blur'} |
| | | ] |
| | | }, |
| | | |
| | | // 表格列配置 |
| | | columns: [ |
| | | |
| | | { |
| | | title: '活动名称', |
| | | key: 'activityName', |
| | | minWidth: 120, |
| | | tooltip: true |
| | | }, |
| | | { |
| | | title: '活动类型', |
| | | key: 'activityType', |
| | | width: 100, |
| | | align: 'center', |
| | | render: (h, params) => { |
| | | return h('Tag', {}, params.row.activityType === 'online' ? '线上' : '线下') |
| | | } |
| | | }, |
| | | { |
| | | title: '审核状态', |
| | | key: 'auditStatus', |
| | | width: 100, |
| | | align: 'center', |
| | | render: (h, params) => { |
| | | const status = params.row.auditStatus; |
| | | let tagText, tagColor; |
| | | |
| | | // 根据状态设置文案和颜色 |
| | | switch (status) { |
| | | case 0: |
| | | tagText = '审核中'; |
| | | tagColor = 'orange'; // 橙色表示进行中 |
| | | break; |
| | | case 1: |
| | | tagText = '已审核'; |
| | | tagColor = 'green'; // 绿色表示通过 |
| | | break; |
| | | case 2: |
| | | tagText = '未通过'; |
| | | tagColor = 'red'; // 红色表示拒绝 |
| | | break; |
| | | default: |
| | | tagText = '未知状态'; |
| | | tagColor = 'default'; // 默认灰色 |
| | | } |
| | | |
| | | return h('Tag', { |
| | | props: { |
| | | color: tagColor, |
| | | }, |
| | | }, tagText); |
| | | }, |
| | | }, |
| | | { |
| | | title: '状态', |
| | | key: 'status', |
| | | width: 100, |
| | | align: 'center', |
| | | render: (h, params) => { |
| | | const status = params.row.status; |
| | | const statusMap = { |
| | | 'noStart': {text: '未开始', color: 'default'}, |
| | | 'report': {text: '报名中', color: 'green'}, |
| | | 'inProgress': {text: '进行中', color: 'cyan'}, |
| | | 'end': {text: '已结束', color: 'red'} |
| | | }; |
| | | const currentStatus = statusMap[status] || {text: status, color: 'default'}; |
| | | return h('Tag', { |
| | | props: { |
| | | color: currentStatus.color |
| | | } |
| | | }, currentStatus.text); |
| | | } |
| | | }, |
| | | { |
| | | title: '活动报名时间段', |
| | | key: 'activityReportTimeRange', |
| | | width: 300, |
| | | render: (h, params) => { |
| | | return h('div', [ |
| | | h('div', `开始: ${this.formatDate(params.row.reportStartTime)}`), |
| | | h('div', `结束: ${this.formatDate(params.row.reportEndTime)}`) |
| | | ]) |
| | | } |
| | | }, |
| | | { |
| | | title: '活动时间段', |
| | | key: 'activityTimeRange', |
| | | width: 300, |
| | | render: (h, params) => { |
| | | return h('div', [ |
| | | h('div', `开始: ${this.formatDate(params.row.startTime)}`), |
| | | h('div', `结束: ${this.formatDate(params.row.endTime)}`) |
| | | ]) |
| | | } |
| | | }, |
| | | { |
| | | title: '封面', |
| | | key: 'url', |
| | | slot: 'url', |
| | | width: 150, |
| | | align: 'center' |
| | | }, |
| | | { |
| | | title: '封面类型', |
| | | key: 'coverType', |
| | | width: 100, |
| | | align: 'center', |
| | | render: (h, params) => { |
| | | const typeMap = { |
| | | text: '文本', |
| | | video: '视频', |
| | | image: '图片' |
| | | }; |
| | | const text = typeMap[params.row.coverType] || params.row.coverType; |
| | | return h('span', text); |
| | | } |
| | | }, |
| | | { |
| | | title: '人数限制', |
| | | key: 'limitUserNum', |
| | | width: 100, |
| | | align: 'center' |
| | | }, |
| | | { |
| | | title: '活动地点', |
| | | key: 'activityLocation', |
| | | minWidth: 120, |
| | | tooltip: true |
| | | }, |
| | | { |
| | | title: '操作', |
| | | slot: 'action', |
| | | width: 280, |
| | | align: 'center', |
| | | fixed: 'right' |
| | | } |
| | | ], |
| | | |
| | | // 图片预览 |
| | | previewVisible: false, |
| | | previewImageUrl: '', |
| | | |
| | | // 模态框控制 |
| | | modelShow: false, |
| | | modelTitle: '', |
| | | |
| | | //编辑器配置 |
| | | // 富文本编辑器配置 |
| | | Quill:'', |
| | | defaultValue: '', |
| | | editorOption: { |
| | | placeholder: '请在这里输入', |
| | | theme: 'snow', //主题 snow/bubble |
| | | modules: { |
| | | history: { |
| | | delay: 1000, |
| | | maxStack: 50, |
| | | userOnly: false |
| | | }, |
| | | toolbar: { |
| | | container: toolbarOptions, |
| | | handlers: { |
| | | myUploadBtn: this.myMethod, |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | }, |
| | | // 在组件创建前注册 |
| | | beforeCreate() { |
| | | Quill.register(VideoBlot, true); |
| | | }, |
| | | mounted() { |
| | | //初始化 |
| | | this.Quill=this.$refs.QuillEditor.quill |
| | | this.init() |
| | | this.initTitle() |
| | | this.initButton(); |
| | | }, |
| | | methods: { |
| | | myMethod(){ |
| | | this.$refs.upload.handleClick(); |
| | | }, |
| | | initButton(){ |
| | | const editorButton = document.querySelector('.ql-myUploadBtn') |
| | | editorButton.innerHTML = '<svg t="1751966766109" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1530" fill="currentColor" style="width: 1em; height: 1em; vertical-align: middle;"><path d="M1024 693.248q0 25.6-8.704 48.128t-24.576 40.448-36.864 30.208-45.568 16.384l1.024 1.024-17.408 0-4.096 0-4.096 0-675.84 0q-5.12 1.024-16.384 1.024-39.936 0-74.752-15.36t-60.928-41.472-40.96-60.928-14.848-74.752 14.848-74.752 40.96-60.928 60.928-41.472 74.752-15.36l1.024 0q-1.024-8.192-1.024-15.36l0-16.384q0-72.704 27.648-137.216t75.776-112.128 112.128-75.264 136.704-27.648 137.216 27.648 112.64 75.264 75.776 112.128 27.648 137.216q0 37.888-8.192 74.24t-22.528 69.12q5.12-1.024 10.752-1.536t10.752-0.512q27.648 0 52.736 10.752t43.52 29.696 29.184 44.032 10.752 53.76zM665.6 571.392q20.48 0 26.624-4.608t-8.192-22.016q-14.336-18.432-31.744-48.128t-36.352-60.416-38.4-57.344-37.888-38.912q-18.432-13.312-27.136-14.336t-25.088 12.288q-18.432 15.36-35.84 38.912t-35.328 50.176-35.84 52.224-36.352 45.056q-18.432 18.432-13.312 32.768t25.6 14.336l16.384 0q9.216 0 19.968 0.512t20.992 0.512l17.408 0q14.336 1.024 18.432 9.728t4.096 24.064q0 17.408-0.512 30.72t-0.512 25.6-0.512 25.6-0.512 30.72q0 7.168 1.536 15.36t5.632 15.36 12.288 11.776 21.504 4.608l23.552 0q9.216 0 27.648 1.024 24.576 0 28.16-12.288t3.584-38.912q0-23.552 0.512-42.496t0.512-51.712q0-23.552 4.608-36.352t19.968-12.8q11.264 0 32.256-0.512t32.256-0.512z" p-id="1531"></path></svg>' |
| | | }, |
| | | |
| | | initTitle() { |
| | | document.getElementsByClassName('ql-editor')[0].dataset.placeholder = '' |
| | | for (let item of titleConfig) { |
| | | let tip = document.querySelector('.quill-editor ' + item.Choice) |
| | | if (!tip) continue |
| | | tip.setAttribute('title', item.title) |
| | | } |
| | | }, |
| | | |
| | | // 失去焦点 |
| | | onEditorBlur(editor) { |
| | | }, |
| | | |
| | | // 获得焦点 |
| | | onEditorFocus(editor) { |
| | | }, |
| | | |
| | | // 开始 |
| | | onEditorReady(editor) { |
| | | }, |
| | | handleUploadEdit(file){ |
| | | const fileType = file.type |
| | | const isImage = fileType.includes('image') |
| | | const isVideo = fileType.includes('video') |
| | | |
| | | if (!isImage && !isVideo) { |
| | | this.$Message.error('请上传图片或视频文件') |
| | | return false |
| | | } |
| | | |
| | | if (file.size > 20 * 1024 * 1024) { |
| | | this.$Message.error('文件大小不能超过20MB') |
| | | return false |
| | | } |
| | | |
| | | this.file = file |
| | | this.uploadFileEdit() |
| | | return false |
| | | }, |
| | | // 上传文件 |
| | | uploadFileEdit() { |
| | | if (!this.file) return |
| | | |
| | | this.submitLoading = true |
| | | const formData = new FormData() |
| | | formData.append('file', this.file) |
| | | uploadFileRequest2(uploadFile2,formData).then(res => { |
| | | this.submitLoading = false |
| | | if (res.data.code === 200) { |
| | | let url = res.data.data.url; |
| | | let fileKey = res.data.data.fileKey; |
| | | let fileType = this.getFileType(this.file); |
| | | |
| | | const range = this.Quill.getSelection(); |
| | | const index = range ? range.index : this.Quill.getLength(); |
| | | |
| | | console.log(fileType) |
| | | if (fileType === 'video') { |
| | | this.Quill.insertEmbed(index, 'video', { |
| | | url:url, |
| | | controls:'controls', |
| | | width:'100%', |
| | | height:'auto' |
| | | }); |
| | | } else if (fileType === 'image') { |
| | | this.Quill.insertEmbed(index, "image", url); |
| | | } else { |
| | | // 如果不是图片或视频,可以处理其他类型或给出提示 |
| | | this.$Message.warning('不支持的文件类型'); |
| | | return; |
| | | } |
| | | console.log(this.activityFrom.activityContent) |
| | | this.Quill.setSelection(index + 1); |
| | | this.$Message.success('上传成功') |
| | | } |
| | | }).catch(() => { |
| | | this.submitLoading = false |
| | | }) |
| | | }, |
| | | getFileType(file) { |
| | | // 获取文件类型或扩展名 |
| | | let type, extension; |
| | | |
| | | if (file instanceof File) { |
| | | // 如果是File对象 |
| | | type = file.type; |
| | | const name = file.name.toLowerCase(); |
| | | extension = name.substring(name.lastIndexOf('.') + 1); |
| | | } else if (typeof file === 'string') { |
| | | // 如果是字符串(文件名或URL) |
| | | const name = file.toLowerCase(); |
| | | extension = name.substring(name.lastIndexOf('.') + 1); |
| | | |
| | | // 尝试从URL中提取MIME类型(如果有) |
| | | const mimeMatch = file.match(/^data:(.+?);/); |
| | | if (mimeMatch) { |
| | | type = mimeMatch[1]; |
| | | } |
| | | } else { |
| | | return 'unknown'; |
| | | } |
| | | |
| | | // 常见图片和视频的MIME类型 |
| | | const imageTypes = [ |
| | | 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/bmp', 'image/svg+xml' |
| | | ]; |
| | | |
| | | const videoTypes = [ |
| | | 'video/mp4', 'video/webm', 'video/ogg', 'video/quicktime', 'video/x-msvideo', 'video/x-matroska' |
| | | ]; |
| | | |
| | | // 检查MIME类型 |
| | | if (type) { |
| | | if (imageTypes.includes(type)) return 'image'; |
| | | if (videoTypes.includes(type)) return 'video'; |
| | | } |
| | | |
| | | // 常见图片和视频的扩展名 |
| | | const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg']; |
| | | const videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv']; |
| | | |
| | | // 检查文件扩展名 |
| | | if (extension) { |
| | | if (imageExtensions.includes(extension)) return 'image'; |
| | | if (videoExtensions.includes(extension)) return 'video'; |
| | | } |
| | | |
| | | return 'unknown'; |
| | | }, |
| | | |
| | | escapeStringHTML(str) { |
| | | if (!str) return str; |
| | | str = str.replace(/</g, '<'); |
| | | str = str.replace(/>/g, '>'); |
| | | return str; |
| | | }, |
| | | // 提交 |
| | | detail(row) { |
| | | this.modelTitle = '活动详情' |
| | | this.infoModelShow = true |
| | | this.activityInfo = row |
| | | this.activityInfo.activityContent = this.escapeStringHTML(this.activityInfo.activityContent); |
| | | this.$nextTick(() => { |
| | | this.processVideos(); |
| | | }); |
| | | |
| | | }, |
| | | processVideos() { |
| | | const videos = this.$el.querySelectorAll('video'); |
| | | videos.forEach(video => { |
| | | // 确保视频元素有必要的属性 |
| | | video.setAttribute('controls', ''); |
| | | video.setAttribute('playsinline', ''); // 针对移动端 |
| | | video.load(); |
| | | }); |
| | | }, |
| | | // 获取富文本编辑器的内容 |
| | | // 初始化数据 |
| | | init() { |
| | | this.getMyApplyActivityPage() |
| | | }, |
| | | |
| | | // 获取活动列表 |
| | | getMyApplyActivityPage() { |
| | | this.loading = true |
| | | getMyApplyActivityPage(this.searchForm).then(res => { |
| | | this.loading = false |
| | | if (res.code === 200) { |
| | | // 为每一行添加loading状态 |
| | | this.activityList = res.data.map(item => ({ |
| | | ...item, |
| | | recommendLoading: false, |
| | | statusLoading: false |
| | | })) |
| | | this.total = res.total |
| | | } |
| | | }).catch(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | |
| | | // 搜索活动 |
| | | handleSearch(type, value) { |
| | | if (type === 'reportStart') { |
| | | this.searchForm.reportStartTime = value |
| | | } else if (type === 'reportEnd') { |
| | | this.searchForm.reportEndTime = value |
| | | } |
| | | |
| | | this.searchForm.pageNumber = 1 |
| | | this.getMyApplyActivityPage() |
| | | }, |
| | | |
| | | // 重置搜索 |
| | | resetSearch() { |
| | | this.$refs.searchForm.resetFields() |
| | | this.searchForm.pageNumber = 1 |
| | | this.getMyApplyActivityPage() |
| | | }, |
| | | |
| | | // 改变页码 |
| | | changePage(page) { |
| | | this.searchForm.pageNumber = page |
| | | this.getMyApplyActivityPage() |
| | | }, |
| | | |
| | | // 改变每页条数 |
| | | changePageSize(pageSize) { |
| | | this.searchForm.pageNumber = 1 |
| | | this.searchForm.pageSize = pageSize |
| | | this.getMyApplyActivityPage() |
| | | }, |
| | | |
| | | // 打开新增模态框 |
| | | openAdd() { |
| | | this.modelTitle = '新增活动' |
| | | this.modelShow = true |
| | | this.coverType = '输入文字封面' |
| | | this.file = null |
| | | this.$refs.form.resetFields() |
| | | this.activityFrom.id = '' |
| | | }, |
| | | |
| | | // 打开编辑模态框 |
| | | openEdit(row) { |
| | | this.modelTitle = '编辑活动' |
| | | this.modelShow = true |
| | | this.$nextTick(() => { |
| | | this.$refs.form.resetFields() |
| | | console.log(row) |
| | | // 填充表单数据 |
| | | this.activityFrom = { |
| | | id: row.id, |
| | | activityName: row.activityName, |
| | | activityType: row.activityType, |
| | | reportTime: [ |
| | | this.formatDate(row.reportStartTime, 'YYYY-MM-DD HH:mm:ss'), |
| | | this.formatDate(row.reportEndTime, 'YYYY-MM-DD HH:mm:ss') |
| | | ], |
| | | time: [ |
| | | this.formatDate(row.startTime, 'YYYY-MM-DD HH:mm:ss'), |
| | | this.formatDate(row.endTime, 'YYYY-MM-DD HH:mm:ss') |
| | | ], |
| | | activityContent: row.activityContent, |
| | | cover: row.cover, |
| | | coverType: row.coverType, |
| | | status: row.status, |
| | | reportStartTime: row.reportStartTime, |
| | | reportEndTime: row.reportEndTime, |
| | | startTime: row.startTime, |
| | | endTime: row.endTime, |
| | | recommend: row.recommend, |
| | | limitUserNum: row.limitUserNum, |
| | | activityLocation: row.activityLocation |
| | | } |
| | | // 设置封面类型 |
| | | this.coverType = row.coverType === 'text' ? '输入文字封面' : '选择文件封面' |
| | | }) |
| | | }, |
| | | |
| | | infoModelClose() { |
| | | this.infoModelShow = false |
| | | }, |
| | | // 关闭模态框 |
| | | modelClose() { |
| | | this.modelShow = false |
| | | this.file = null |
| | | this.submitLoading = false |
| | | this.handleRemove(); |
| | | this.$refs.form.resetFields() |
| | | }, |
| | | |
| | | // 保存或更新活动 |
| | | saveOrUpdate() { |
| | | // 设置封面类型 |
| | | this.activityFrom.coverType = this.coverType === '输入文字封面' ? 'text' : |
| | | this.file ? this.getFileCategory(this.file.type) : |
| | | this.activityFrom.coverType |
| | | |
| | | this.$refs.form.validate(valid => { |
| | | if (valid) { |
| | | this.submitLoading = true |
| | | |
| | | // 处理时间数据 |
| | | if (this.activityFrom.reportTime && this.activityFrom.reportTime.length === 2) { |
| | | this.activityFrom.reportStartTime = this.formatDate(this.activityFrom.reportTime[0], 'YYYY-MM-DD HH:mm:ss') |
| | | this.activityFrom.reportEndTime = this.formatDate(this.activityFrom.reportTime[1], 'YYYY-MM-DD HH:mm:ss') |
| | | } |
| | | |
| | | if (this.activityFrom.time && this.activityFrom.time.length === 2) { |
| | | this.activityFrom.startTime = this.formatDate(this.activityFrom.time[0], 'YYYY-MM-DD HH:mm:ss') |
| | | this.activityFrom.endTime = this.formatDate(this.activityFrom.time[1], 'YYYY-MM-DD HH:mm:ss') |
| | | } |
| | | |
| | | const api = this.activityFrom.id ? editActivity : addActivity |
| | | api(this.activityFrom).then(res => { |
| | | this.submitLoading = false |
| | | if (res.code === 200) { |
| | | this.$Message.success(res.msg) |
| | | this.modelClose() |
| | | this.getMyApplyActivityPage() |
| | | } |
| | | }).catch(() => { |
| | | this.submitLoading = false |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | // 封面类型变化处理 |
| | | handleCoverTypeChange(type) { |
| | | if (type === '选择文件封面') { |
| | | this.activityFrom.cover = '' |
| | | } else { |
| | | this.file = null |
| | | } |
| | | }, |
| | | // 文件上传前处理 |
| | | handleBeforeUpload(file) { |
| | | const fileType = file.type |
| | | const isImage = fileType.includes('image') |
| | | const isVideo = fileType.includes('video') |
| | | |
| | | if (!isImage && !isVideo) { |
| | | this.$Message.error('请上传图片或视频文件') |
| | | return false |
| | | } |
| | | |
| | | if (file.size > 20 * 1024 * 1024) { |
| | | this.$Message.error('文件大小不能超过20MB') |
| | | return false |
| | | } |
| | | |
| | | this.file = file |
| | | this.uploadFile() |
| | | return false |
| | | }, |
| | | // 上传文件 |
| | | uploadFile() { |
| | | if (!this.file) return |
| | | |
| | | this.submitLoading = true |
| | | const formData = new FormData() |
| | | formData.append('file', this.file) |
| | | |
| | | uploadFileRequest2(uploadFile2,formData).then(res => { |
| | | this.submitLoading = false |
| | | if (res.data.code === 200) { |
| | | this.activityFrom.cover = res.data.data.fileKey |
| | | this.$Message.success('上传成功') |
| | | } |
| | | }).catch(() => { |
| | | this.submitLoading = false |
| | | }) |
| | | }, |
| | | |
| | | // 删除文件 |
| | | handleRemove() { |
| | | //点击关闭窗口时确保文件已被清除 |
| | | if (this.file === null) { |
| | | return; |
| | | } |
| | | if (!this.activityFrom.cover) { |
| | | this.file = null |
| | | return |
| | | } |
| | | delFileByFileKeyRequest(this.activityFrom.cover).then(res => { |
| | | if (res.code === 200) { |
| | | this.file = null |
| | | this.activityFrom.cover = '' |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 预览图片 |
| | | previewImage(url) { |
| | | this.previewImageUrl = url |
| | | this.previewVisible = true |
| | | }, |
| | | |
| | | // 获取文件分类 |
| | | getFileCategory(mimeType) { |
| | | const typeMap = { |
| | | 'image': 'image', |
| | | 'video': 'video', |
| | | 'audio': 'audio', |
| | | 'application': 'application' |
| | | } |
| | | |
| | | const typePrefix = mimeType.split('/')[0] |
| | | return typeMap[typePrefix] || 'unknown' |
| | | }, |
| | | |
| | | // 格式化日期 |
| | | formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { |
| | | if (!date) return ''; |
| | | |
| | | const d = new Date(date); |
| | | if (isNaN(d.getTime())) return ''; |
| | | |
| | | const padZero = (num) => String(num).padStart(2, '0'); // 更可靠的补零方法 |
| | | |
| | | const year = d.getFullYear(); |
| | | const month = padZero(d.getMonth() + 1); // 月份 0-11 → +1 |
| | | const day = padZero(d.getDate()); |
| | | const hours = padZero(d.getHours()); |
| | | const minutes = padZero(d.getMinutes()); |
| | | const seconds = padZero(d.getSeconds()); |
| | | return format |
| | | .replace('YYYY', year) |
| | | .replace('MM', month) |
| | | .replace('DD', day) |
| | | .replace('HH', hours) |
| | | .replace('mm', minutes) |
| | | .replace('ss', seconds); |
| | | }, |
| | | |
| | | // 验证报名时间 |
| | | validateReportTime(rule, value, callback) { |
| | | if (!value || value.length !== 2) { |
| | | callback(new Error('请选择完整的报名时间段')) |
| | | return |
| | | } |
| | | |
| | | const [start, end] = value |
| | | if (new Date(start) >= new Date(end)) { |
| | | callback(new Error('报名结束时间必须晚于开始时间')) |
| | | return |
| | | } |
| | | |
| | | if (this.activityFrom.time && this.activityFrom.time.length === 2) { |
| | | const activityStart = this.activityFrom.time[0] |
| | | if (new Date(end) > new Date(activityStart)) { |
| | | callback(new Error('报名结束时间不能晚于活动开始时间')) |
| | | return |
| | | } |
| | | } |
| | | |
| | | callback() |
| | | }, |
| | | |
| | | // 验证活动时间 |
| | | validateActivityTime(rule, value, callback) { |
| | | if (!value || value.length !== 2) { |
| | | callback(new Error('请选择完整的活动时间段')) |
| | | return |
| | | } |
| | | |
| | | const [start, end] = value |
| | | if (new Date(start) >= new Date(end)) { |
| | | callback(new Error('活动结束时间必须晚于开始时间')) |
| | | return |
| | | } |
| | | |
| | | if (this.activityFrom.reportTime && this.activityFrom.reportTime.length === 2) { |
| | | const reportEnd = this.activityFrom.reportTime[1] |
| | | if (new Date(reportEnd) > new Date(start)) { |
| | | callback(new Error('活动开始时间必须晚于报名结束时间')) |
| | | return |
| | | } |
| | | } |
| | | |
| | | callback() |
| | | } |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .quill-editor { |
| | | |
| | | } |
| | | |
| | | .ql-editor .ql-video { |
| | | width: 50%; |
| | | height: auto; /* 根据你的需求调整高度 */ |
| | | max-width: 100%; |
| | | } |
| | | |
| | | .activity-management { |
| | | .search-form { |
| | | padding: 16px; |
| | | background: #f8f8f9; |
| | | border-radius: 4px; |
| | | margin-bottom: 16px; |
| | | |
| | | .ivu-form-item { |
| | | margin-bottom: 16px; |
| | | margin-right: 16px; |
| | | } |
| | | |
| | | .search-btn { |
| | | margin-left: 8px; |
| | | } |
| | | } |
| | | |
| | | .operation { |
| | | margin-bottom: 16px; |
| | | |
| | | .ivu-btn { |
| | | margin-right: 8px; |
| | | } |
| | | } |
| | | |
| | | .activity-table { |
| | | .media-container { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 100px; |
| | | |
| | | .thumbnail { |
| | | max-width: 100%; |
| | | max-height: 100%; |
| | | object-fit: contain; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | |
| | | &:hover { |
| | | transform: scale(1.05); |
| | | box-shadow: 0 0 8px rgba(0, 0, 0, 0.2); |
| | | } |
| | | } |
| | | |
| | | .video-player { |
| | | max-width: 100%; |
| | | max-height: 100px; |
| | | background: #000; |
| | | } |
| | | |
| | | .text-cover { |
| | | padding: 8px; |
| | | background: #f8f8f9; |
| | | border-radius: 4px; |
| | | max-width: 100%; |
| | | word-break: break-all; |
| | | } |
| | | } |
| | | |
| | | .action-btns { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | justify-content: center; |
| | | |
| | | .ivu-btn { |
| | | margin: 4px; |
| | | font-size: 12px; |
| | | padding: 2px 6px; |
| | | min-width: 60px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .page-footer { |
| | | margin-top: 16px; |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | .members-modal { |
| | | .members-table { |
| | | margin-bottom: 16px; |
| | | } |
| | | } |
| | | |
| | | .upload-file-info { |
| | | margin-top: 8px; |
| | | padding: 8px; |
| | | background: #f8f8f9; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .upload-tip { |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-top: 4px; |
| | | } |
| | | } |
| | | .detail-container { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .detail-item { |
| | | margin-bottom: 18px; |
| | | line-height: 1.5; |
| | | |
| | | label { |
| | | display: inline-block; |
| | | width: 100px; |
| | | color: #666; |
| | | font-weight: bold; |
| | | vertical-align: top; |
| | | } |
| | | |
| | | span { |
| | | display: inline-block; |
| | | width: calc(100% - 110px); |
| | | } |
| | | } |
| | | |
| | | .activity-content { |
| | | border: 1px solid #dcdee2; |
| | | border-radius: 4px; |
| | | padding: 12px; |
| | | min-height: 100px; |
| | | margin-top: 8px; |
| | | } |
| | | /* |
| | | 文字大小 |
| | | */ |
| | | .ql-snow .ql-picker.ql-size{ |
| | | width: 70px; // 菜单栏占比宽度 |
| | | } |
| | | /* |
| | | 字体 |
| | | */ |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before { |
| | | content: "黑体"; |
| | | font-family: "SimHei"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before { |
| | | content: "微软雅黑"; |
| | | font-family: "Microsoft YaHei"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before { |
| | | content: "楷体"; |
| | | font-family: "KaiTi"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before { |
| | | content: "仿宋"; |
| | | font-family: "FangSong"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before { |
| | | content: "Arial"; |
| | | font-family: "Arial"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before { |
| | | content: "Times New Roman"; |
| | | font-family: "Times New Roman"; |
| | | } |
| | | |
| | | .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before, |
| | | .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before { |
| | | content: "sans-serif"; |
| | | font-family: "sans-serif"; |
| | | } |
| | | |
| | | .ql-font-SimSun { |
| | | font-family: "SimSun"; |
| | | } |
| | | |
| | | .ql-font-SimHei { |
| | | font-family: "SimHei"; |
| | | } |
| | | |
| | | .ql-font-Microsoft-YaHei { |
| | | font-family: "Microsoft YaHei"; |
| | | } |
| | | |
| | | .ql-font-KaiTi { |
| | | font-family: "KaiTi"; |
| | | } |
| | | |
| | | .ql-font-FangSong { |
| | | font-family: "FangSong"; |
| | | } |
| | | |
| | | .ql-font-Arial { |
| | | font-family: "Arial"; |
| | | } |
| | | |
| | | .ql-font-Times-New-Roman { |
| | | font-family: "Times New Roman"; |
| | | } |
| | | |
| | | .ql-font-sans-serif { |
| | | font-family: "sans-serif"; |
| | | } |
| | | </style> |
| New file |
| | |
| | | import { Quill } from 'vue-quill-editor'; |
| | | |
| | | const BlockEmbed = Quill.import('blots/block/embed'); |
| | | const Link = Quill.import('formats/link'); |
| | | |
| | | const ATTRIBUTES = ['height', 'width', 'controls', 'src', ]; |
| | | |
| | | class VideoBlot extends BlockEmbed { |
| | | static create(value) { |
| | | const node = super.create(); |
| | | |
| | | if (typeof value === 'object') { |
| | | node.setAttribute('src', this.sanitize(value.url)); |
| | | node.setAttribute('width', value.width || '100%'); |
| | | node.setAttribute('height', value.height || 'auto'); |
| | | node.setAttribute('controls', value.controls || 'controls'); |
| | | } else { |
| | | node.setAttribute('src', this.sanitize(value)); |
| | | } |
| | | |
| | | return node; |
| | | } |
| | | |
| | | static formats(node) { |
| | | return ATTRIBUTES.reduce((formats, attribute) => { |
| | | if (node.hasAttribute(attribute)) { |
| | | formats[attribute] = node.getAttribute(attribute); |
| | | } |
| | | return formats; |
| | | }, {}); |
| | | } |
| | | |
| | | static sanitize(url) { |
| | | return Link.sanitize(url); |
| | | } |
| | | |
| | | static value(node) { |
| | | return { |
| | | url: node.getAttribute('src'), |
| | | width: node.getAttribute('width'), |
| | | height: node.getAttribute('height'), |
| | | controls: node.getAttribute('controls'), |
| | | }; |
| | | } |
| | | |
| | | format(name, value) { |
| | | if (ATTRIBUTES.includes(name)) { |
| | | if (value) { |
| | | this.domNode.setAttribute(name, value); |
| | | } else { |
| | | this.domNode.removeAttribute(name); |
| | | } |
| | | } else { |
| | | super.format(name, value); |
| | | } |
| | | } |
| | | } |
| | | |
| | | VideoBlot.blotName = 'video'; |
| | | VideoBlot.className = 'ql-video'; |
| | | VideoBlot.tagName = 'video'; |
| | | |
| | | // 添加不会被 sanitize 的标记 |
| | | VideoBlot.sanitize = function(url) { |
| | | const sanitized = Link.sanitize(url); |
| | | return sanitized === Link.PROTOCOL_WHITELIST[0] ? 'about:blank' : sanitized; |
| | | }; |
| | | |
| | | export default VideoBlot; |