<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>
|