zxl
2025-07-11 9069768170942a641fa57f269686c6ed3faf09da
富文本编辑
4个文件已修改
2个文件已添加
680 ■■■■■ 已修改文件
manager/package.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/api/activity.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/assets/upload.png 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/components/editor/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/activity/index.vue 595 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/activity/video.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/package.json
@@ -20,6 +20,10 @@
    "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",
    "sass-loader": "^8.0.2",
    "sockjs-client": "^1.4.0",
    "swiper": "^6.3.5",
@@ -31,6 +35,7 @@
    "vue-json-excel": "^0.3.0",
    "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",
manager/src/api/activity.js
@@ -1,5 +1,15 @@
import service from "../libs/axios";
//审核活动
export const auditActivity = (params) =>{
  return service({
    url: "/activity/audit",
    method: "POST",
    data: params
  })
}
//获取活动列表
export const getActivityList = (params) =>{
  return service({
manager/src/assets/upload.png
manager/src/components/editor/index.vue
@@ -11,6 +11,7 @@
<script>
import { initEditor } from "@/components/editor/config";
import uploadImage from "@/components/editor/upload-image.vue";
export default {
  components:{uploadImage},
  name: "Tinymce",
manager/src/views/activity/index.vue
@@ -38,6 +38,24 @@
            </Option>
          </Select>
        </FormItem>
        <FormItem label="审核情况" prop="audit">
          <Select
            v-model="searchForm.audit"
            placeholder="请选择审核情况"
            style="width: 180px"
            clearable
            @on-clear="handleSearch"
            @on-change="handleSearch"
          >
            <Option
              v-for="item in auditSelect"
              :value="item.value"
              :key="item.id"
            >
              {{ item.label }}
            </Option>
          </Select>
        </FormItem>
        <FormItem label="报名开始时间" prop="reportStartTime">
          <DatePicker
            :value="searchForm.reportStartTime"
@@ -153,6 +171,12 @@
              size="small"
              @click="openMembersModal(row)"
            >报名人员</Button>
            <Button
              type="success"
              size="small"
              :disabled="row.auditStatus !== 0"
              @click="openAuditModal(row)"
            >审核活动</Button>
          </div>
        </template>
      </Table>
@@ -288,7 +312,7 @@
                />
              </FormItem>
            </Col>
            <Col span="12">
            <Col span="12" v-if="activityFrom.activityType === 'offline'">
              <FormItem label="活动地点" prop="activityLocation">
                <Input
                  v-model="activityFrom.activityLocation"
@@ -298,7 +322,30 @@
            </Col>
            <Col span="24">
              <FormItem label="活动内容:" prop="activityContent">
                <editor ref="editor" @input="getReason" />
                  <!-- 基于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>
@@ -417,6 +464,109 @@
      </Modal>
      <Modal
        v-model="auditModelShow"
        :title="modelTitle"
        @on-cancel="closeAuditModel"
        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>
            <Col span="24">
              <Form :model="auditForm" :rules="auditRules" ref="auditForm" class="audit-form">
                <FormItem label="审核结果:" prop="audit">
                  <RadioGroup v-model="auditForm.audit">
                    <Radio label="1">通过</Radio>
                    <Radio label="2">未通过</Radio>
                  </RadioGroup>
                </FormItem>
                <FormItem label="审核备注:" prop="remarks">
                  <Input
                    v-model="auditForm.remarks"
                    type="textarea"
                    :rows="4"
                    placeholder="请输入审核备注(选填)"
                    :disabled="!auditForm.audit"
                  />
                </FormItem>
              </Form>
            </Col>
          </Row>
        </div>
        <!-- 审核表单 -->
        <div slot="footer">
          <Button @click="closeAuditModel">关闭</Button>
          <Button
            type="primary"
            @click="handleAuditSubmit(activityInfo.id)"
            :disabled="!auditForm.audit"
          >提交审核</Button>
        </div>
      </Modal>
      <!-- 图片预览模态框 -->
      <Modal v-model="previewVisible" title="图片预览" footer-hide>
        <img :src="previewImageUrl" style="width: 100%">
@@ -433,16 +583,104 @@
  delActivityById,
  activityChangeStatus,
  activityChangeRecommend,
  activityMembersPage
  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';
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: "ActivityManagement",
  components: {Editor},
  components: {Editor, quillEditor},
  data() {
    return {
      auditForm: {
        activityId: '',
        audit: null,
        remarks: ''
      },
      auditRules: {
        audit: [
          {required: true, message: '请选择审核结果', trigger: 'change'}
        ]
      },
      infoModelShow:false,
      auditModelShow: false,
      loading: false,
      membersLoading: false,
@@ -450,6 +688,7 @@
      // 搜索表单
      searchForm: {
        audit: '',
        activityName: '',
        activityType: '',
        reportStartTime: '',
@@ -468,6 +707,10 @@
      typeSelect: [
        { id: 1, value: 'online',label:'线上' },
        { id: 2, value: 'offline',label:'线下' }
      ],
      auditSelect: [
        {id: 1, value: '1', label: '已审核'},
        {id: 2, value: '0', label: '未审核'}
      ],
      // 封面类型选项
@@ -548,7 +791,7 @@
          { max: 100, message: '长度不能超过100个字符', trigger: 'blur' }
        ],
        activityContent: [
          { required: true, message: '请输入活动内容', trigger: 'blur' }
          {required: false, message: '请输入活动内容', trigger: 'blur'}
        ]
      },
@@ -571,8 +814,7 @@
          width: 100,
          align: 'center',
          render: (h, params) => {
            return h('Tag', {
            }, params.row.activityType === 'online' ? '线上' : '线下')
            return h('Tag', {}, params.row.activityType === 'online' ? '线上' : '线下')
          }
        },
        {
@@ -599,6 +841,19 @@
                color: params.row.publish ? 'green' : 'default'
              }
            }, params.row.publish ? '已发布' : '未发布')
          }
        },
        {
          title: '审核',
          key: 'auditStatus',
          width: 100,
          align: 'center',
          render: (h, params) => {
            return h('Tag', {
              props: {
                color: params.row.auditStatus === 0 ? 'default' : 'green'
              }
            }, params.row.auditStatus === 0 ? '未审核' : '已审核')
          }
        },
        {
@@ -739,22 +994,232 @@
      // 模态框控制
      modelShow: false,
      modelTitle: ''
      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.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();
        if (fileType === 'video') {
          console.log("插入")
          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;
        }
        this.Quill.setSelection(index + 1);
        console.log(this.content)
        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';
    },
  handleAuditSubmit(activityId) {
    console.log(this.auditForm)
    this.$refs.auditForm.validate(valid => {
      if (valid) {
        const form = {
          activityId: activityId,
          audit: this.auditForm.audit,
          remarks: this.auditForm.remarks,
        }
        // 提交时 auditForm.audit 已经是数字类型(0 或 1)
        auditActivity(form).then(res => {
          if (res.code === 200) {
            // 为每一行添加loading状态
            this.$Message.success(res.msg);
            this.getActivityList();
            this.closeAuditModel();
          }
        })
      }
    });
  },
  openAuditModal(row) {
    this.modelTitle = '活动审核'
    this.auditModelShow = true
    this.activityInfo = row
  },
  closeAuditModel() {
    this.$refs.auditForm.resetFields();
    this.auditModelShow = false
  },
    escapeStringHTML(str) {
      if (!str) return str;
      str = str.replace(/&lt;/g, '<');
      str = str.replace(/&gt;/g, '>');
      return str;
    },
    // 提交
    detail(row){
      this.modelTitle = '活动详情'
      this.infoModelShow = true
      this.activityInfo = row
    this.activityInfo.activityContent = this.escapeStringHTML(this.activityInfo.activityContent);
    },
    // 获取富文本编辑器的内容
    getReason(content) {
      this.activityFrom.activityContent = content
    },
    // 初始化数据
    init() {
      this.getActivityList()
@@ -833,6 +1298,7 @@
      this.modelShow = true
      this.$nextTick(() => {
        this.$refs.form.resetFields()
      console.log(row)
        // 填充表单数据
        this.activityFrom = {
          id: row.id,
@@ -858,11 +1324,11 @@
          limitUserNum: row.limitUserNum,
          activityLocation: row.activityLocation
        }
        this.$refs.editor.setContent(this.activityFrom.activityLocation)
        // 设置封面类型
        this.coverType = row.coverType === 'text' ? '输入文字封面' : '选择文件封面'
      })
    },
    infoModelClose(){
      this.infoModelShow = false
    },
@@ -878,9 +1344,10 @@
    // 保存或更新活动
    saveOrUpdate() {
      // 设置封面类型
      this.activityFrom.coverType = this.coverType === '输入文字封面' ? '文字' :
    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
@@ -973,6 +1440,12 @@
    // 改变活动状态
    changeStatus(row, action) {
    //判断是否存在审核记录
    if (row.audit === null) {
      this.$Message.error("发布前请先审核!")
      return
    }
      row.statusLoading = true
      const publish = action === '发布'
@@ -1039,7 +1512,6 @@
        this.file = null
      }
    },
    // 文件上传前处理
    handleBeforeUpload(file) {
      const fileType = file.type
@@ -1060,7 +1532,6 @@
      this.uploadFile()
      return false
    },
    // 上传文件
    uploadFile() {
      if (!this.file) return
@@ -1188,11 +1659,21 @@
      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;
@@ -1320,4 +1801,86 @@
  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>
manager/src/views/activity/video.js
New file
@@ -0,0 +1,69 @@
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', 'poster'];
class VideoBlot extends BlockEmbed {
  static create(value) {
    const node = super.create();
    // 设置基本属性
    node.setAttribute('controls', 'controls');
    node.setAttribute('controlsList', 'nodownload');
    node.setAttribute('preload', 'metadata');
    // 支持对象形式的value
    if (typeof value === 'object') {
      node.setAttribute('src', this.sanitize(value.url));
      node.setAttribute('poster', value.poster || '');
      node.setAttribute('width', value.width || '100%');
      node.setAttribute('height', value.height || 'auto');
    } 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'),
      poster: node.getAttribute('poster'),
      width: node.getAttribute('width'),
      height: node.getAttribute('height')
    };
  }
  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';
export default VideoBlot;