|
|
@@ -17,7 +17,7 @@
|
|
|
<el-option label="已发布" value="published" />
|
|
|
<el-option label="已结束" value="ended" />
|
|
|
</el-select>
|
|
|
- <el-button type="primary" size="mini" @click="openCreate">创建活动</el-button>
|
|
|
+ <el-button type="primary" size="mini" @click="$router.push('/activity-edit')">创建活动</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<el-table :data="activities" v-loading="loading" border stripe>
|
|
|
@@ -57,8 +57,8 @@
|
|
|
<el-table-column prop="endTime" label="结束时间" width="170" />
|
|
|
<el-table-column label="操作" width="280">
|
|
|
<template slot-scope="{ row }">
|
|
|
- <el-button size="mini" @click="viewActivity(row)" v-if="row.status !== 'draft'">查看</el-button>
|
|
|
- <el-button size="mini" @click="editActivity(row)" :disabled="row.status !== 'draft'" v-else>编辑</el-button>
|
|
|
+ <el-button size="mini" @click="$router.push('/activity-edit?id=' + row.id + '&readonly=1')" v-if="row.status !== 'draft'">查看</el-button>
|
|
|
+ <el-button size="mini" @click="$router.push('/activity-edit?id=' + row.id)" v-else>编辑</el-button>
|
|
|
<el-button
|
|
|
size="mini"
|
|
|
type="success"
|
|
|
@@ -87,117 +87,6 @@
|
|
|
/>
|
|
|
</el-card>
|
|
|
|
|
|
- <el-dialog
|
|
|
- :title="viewMode ? '查看活动' : (editId ? '编辑活动' : '创建活动')"
|
|
|
- :visible.sync="dialogVisible"
|
|
|
- width="700px"
|
|
|
- @closed="resetForm"
|
|
|
- >
|
|
|
- <el-form label-width="120px" ref="activityForm">
|
|
|
- <el-form-item label="活动标题">
|
|
|
- <el-input v-model="form.title" placeholder="请输入活动标题" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="封面图片">
|
|
|
- <el-upload
|
|
|
- :action="uploadActionUrl"
|
|
|
- :headers="uploadHeaders"
|
|
|
- :on-success="handleUploadSuccess"
|
|
|
- :on-error="handleUploadError"
|
|
|
- :before-upload="beforeUpload"
|
|
|
- :show-file-list="false"
|
|
|
- accept="image/jpeg,image/png,image/gif,image/webp"
|
|
|
- >
|
|
|
- <img v-if="form.coverImage" :src="form.coverImage" class="cover-preview" />
|
|
|
- <el-button v-else type="primary" plain>+ 上传封面</el-button>
|
|
|
- </el-upload>
|
|
|
- <el-button v-if="form.coverImage" size="mini" type="text" style="margin-left:10px;" @click="form.coverImage = ''">清除</el-button>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="活动类型">
|
|
|
- <el-select v-model="form.activityType" placeholder="请选择活动类型" style="width:100%">
|
|
|
- <el-option label="线下活动" value="offline" />
|
|
|
- <el-option label="线上活动" value="online" />
|
|
|
- <el-option label="主题活动" value="campaign" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="活动描述">
|
|
|
- <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入活动描述" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="五维权重">
|
|
|
- <dimension-weight-picker v-model="form.dimensionWeights" :show-presets="true" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="活动地点">
|
|
|
- <el-input v-model="form.location" placeholder="请输入活动地点" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-row :gutter="16">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="最大人数">
|
|
|
- <el-input-number v-model="form.maxParticipants" :min="0" :max="99999" placeholder="0表示不限" style="width:100%" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="价格(元)">
|
|
|
- <el-input-number v-model="form.priceYuan" :min="0" :precision="2" :step="0.01" placeholder="0表示免费" style="width:100%" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-row :gutter="16">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="开始时间">
|
|
|
- <el-date-picker v-model="form.startTime" type="datetime" placeholder="选择开始时间" style="width:100%" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="结束时间">
|
|
|
- <el-date-picker v-model="form.endTime" type="datetime" placeholder="选择结束时间" style="width:100%" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-row :gutter="16">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="可见范围">
|
|
|
- <el-select v-model="form.visibility" placeholder="请选择可见范围" style="width:100%">
|
|
|
- <el-option label="公开" value="public" />
|
|
|
- <el-option label="受限" value="restricted" />
|
|
|
- <el-option label="全体" value="all" />
|
|
|
- <el-option label="会员" value="member" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="可见对象">
|
|
|
- <el-select v-model="form.visibleScope" placeholder="请选择可见对象类型" style="width:100%">
|
|
|
- <el-option label="不限" value="" />
|
|
|
- <el-option label="家庭" value="family" />
|
|
|
- <el-option label="角色" value="role" />
|
|
|
- <el-option label="用户" value="user" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-form-item label="可见对象ID" v-if="form.visibleScope">
|
|
|
- <el-input v-model="form.visibleTo" placeholder="多个用逗号分隔,如: family_1,family_2" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="签到积分">
|
|
|
- <el-input-number v-model="form.checkinPoints" :min="0" :max="9999" placeholder="0使用系统默认" style="width:200px" />
|
|
|
- <span style="margin-left:8px;color:#999;font-size:12px;">0表示使用系统默认积分</span>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- <div slot="footer">
|
|
|
- <el-button @click="dialogVisible = false">{{ viewMode ? '关闭' : '取消' }}</el-button>
|
|
|
- <el-button v-if="!viewMode" type="primary" @click="saveActivity" :loading="saving">保存</el-button>
|
|
|
- </div>
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
<!-- 报名列表对话框 -->
|
|
|
<el-dialog
|
|
|
:title="'报名列表 - ' + (currentActivity ? currentActivity.title : '')"
|
|
|
@@ -235,9 +124,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import DimensionWeightPicker from '@/components/DimensionWeightPicker.vue'
|
|
|
-import { getActivityList, createActivity, updateActivity, publishActivity, endActivity, getRegistrationList, approveRegistration, rejectRegistration } from '@/api/activity'
|
|
|
-import { saveDimensionWeights, getDimensionWeights } from '@/api/dimension-weight.js'
|
|
|
+import { getActivityList, publishActivity, endActivity, getRegistrationList, approveRegistration, rejectRegistration } from '@/api/activity'
|
|
|
|
|
|
const STATUS_MAP = {
|
|
|
draft: { label: '草稿', type: 'info' },
|
|
|
@@ -259,38 +146,16 @@ const DIMENSION_MAP = {
|
|
|
|
|
|
export default {
|
|
|
name: 'Activities',
|
|
|
- components: { DimensionWeightPicker },
|
|
|
data() {
|
|
|
return {
|
|
|
loading: false,
|
|
|
- saving: false,
|
|
|
activities: [],
|
|
|
total: 0,
|
|
|
- editId: null,
|
|
|
- viewMode: false,
|
|
|
- dialogVisible: false,
|
|
|
filter: {
|
|
|
status: '',
|
|
|
page: 1,
|
|
|
size: 10
|
|
|
},
|
|
|
- form: {
|
|
|
- title: '',
|
|
|
- coverImage: '',
|
|
|
- activityType: '',
|
|
|
- description: '',
|
|
|
- dimensionCode: '',
|
|
|
- dimensionWeights: [],
|
|
|
- location: '',
|
|
|
- maxParticipants: null,
|
|
|
- priceYuan: null,
|
|
|
- startTime: null,
|
|
|
- endTime: null,
|
|
|
- visibility: 'public',
|
|
|
- visibleScope: '',
|
|
|
- visibleTo: '',
|
|
|
- checkinPoints: 0
|
|
|
- },
|
|
|
// 报名列表对话框
|
|
|
regDialogVisible: false,
|
|
|
regLoading: false,
|
|
|
@@ -298,16 +163,6 @@ export default {
|
|
|
registrations: []
|
|
|
}
|
|
|
},
|
|
|
- computed: {
|
|
|
- uploadActionUrl() {
|
|
|
- const baseURL = process.env.VUE_APP_BASE_API || ''
|
|
|
- return baseURL + '/api/admin/articles/upload/image'
|
|
|
- },
|
|
|
- uploadHeaders() {
|
|
|
- const token = localStorage.getItem('token')
|
|
|
- return token ? { Authorization: 'Bearer ' + token } : {}
|
|
|
- }
|
|
|
- },
|
|
|
created() {
|
|
|
this.loadActivities()
|
|
|
},
|
|
|
@@ -324,31 +179,6 @@ export default {
|
|
|
dimensionLabel(code) {
|
|
|
return DIMENSION_MAP[code] || code || '—'
|
|
|
},
|
|
|
- beforeUpload(file) {
|
|
|
- const isImage = file.type === 'image/jpeg' || file.type === 'image/png' ||
|
|
|
- file.type === 'image/gif' || file.type === 'image/webp'
|
|
|
- if (!isImage) {
|
|
|
- this.$message.error('只能上传 JPG/PNG/GIF/WebP 格式图片')
|
|
|
- return false
|
|
|
- }
|
|
|
- const isLt5M = file.size / 1024 / 1024 < 5
|
|
|
- if (!isLt5M) {
|
|
|
- this.$message.error('图片大小不能超过 5MB')
|
|
|
- return false
|
|
|
- }
|
|
|
- return true
|
|
|
- },
|
|
|
- handleUploadSuccess(resp) {
|
|
|
- if (resp.code === 0 || resp.code === 200) {
|
|
|
- this.form.coverImage = resp.data || resp
|
|
|
- this.$message.success('封面上传成功')
|
|
|
- } else {
|
|
|
- this.$message.error((resp && resp.message) || '封面上传失败')
|
|
|
- }
|
|
|
- },
|
|
|
- handleUploadError() {
|
|
|
- this.$message.error('封面上传失败,请重试')
|
|
|
- },
|
|
|
async loadActivities() {
|
|
|
this.loading = true
|
|
|
try {
|
|
|
@@ -377,118 +207,6 @@ export default {
|
|
|
this.filter.page = page
|
|
|
this.loadActivities()
|
|
|
},
|
|
|
- openCreate() {
|
|
|
- this.editId = null
|
|
|
- this.viewMode = false
|
|
|
- this.resetForm()
|
|
|
- this.dialogVisible = true
|
|
|
- },
|
|
|
- resetForm() {
|
|
|
- this.viewMode = false
|
|
|
- this.form = {
|
|
|
- title: '',
|
|
|
- coverImage: '',
|
|
|
- activityType: '',
|
|
|
- description: '',
|
|
|
- dimensionCode: '',
|
|
|
- dimensionWeights: [],
|
|
|
- location: '',
|
|
|
- maxParticipants: null,
|
|
|
- priceYuan: null,
|
|
|
- startTime: null,
|
|
|
- endTime: null,
|
|
|
- visibility: 'public',
|
|
|
- visibleScope: '',
|
|
|
- visibleTo: '',
|
|
|
- checkinPoints: 0
|
|
|
- }
|
|
|
- },
|
|
|
- editActivity(row) {
|
|
|
- this.editId = row.id
|
|
|
- this.viewMode = false
|
|
|
- this.form = {
|
|
|
- title: row.title || '',
|
|
|
- coverImage: row.coverImage || '',
|
|
|
- activityType: row.activityType || '',
|
|
|
- description: row.description || '',
|
|
|
- dimensionCode: row.dimensionCode || '',
|
|
|
- dimensionWeights: [],
|
|
|
- location: row.location || '',
|
|
|
- maxParticipants: row.maxParticipants || null,
|
|
|
- priceYuan: row.price ? row.price / 100 : null,
|
|
|
- startTime: row.startTime || null,
|
|
|
- endTime: row.endTime || null,
|
|
|
- visibility: row.visibility || 'public',
|
|
|
- visibleScope: row.visibleScope || '',
|
|
|
- visibleTo: row.visibleTo || '',
|
|
|
- checkinPoints: row.checkinPoints || 0
|
|
|
- }
|
|
|
- this.dialogVisible = true
|
|
|
- this.loadDimensionWeights(row.id)
|
|
|
- },
|
|
|
- async loadDimensionWeights(activityId) {
|
|
|
- try {
|
|
|
- const res = await getDimensionWeights('activity', activityId)
|
|
|
- if (res.data) {
|
|
|
- this.form.dimensionWeights = res.data
|
|
|
- }
|
|
|
- } catch (e) {
|
|
|
- }
|
|
|
- },
|
|
|
- async saveActivity() {
|
|
|
- if (!this.form.title) {
|
|
|
- this.$message.warning('请填写活动标题')
|
|
|
- return
|
|
|
- }
|
|
|
- if (!this.form.startTime || !this.form.endTime) {
|
|
|
- this.$message.warning('请填写开始时间和结束时间')
|
|
|
- return
|
|
|
- }
|
|
|
- this.saving = true
|
|
|
- try {
|
|
|
- const payload = {
|
|
|
- title: this.form.title,
|
|
|
- coverImage: this.form.coverImage,
|
|
|
- activityType: this.form.activityType,
|
|
|
- description: this.form.description,
|
|
|
- dimensionCode: this.form.dimensionCode,
|
|
|
- location: this.form.location,
|
|
|
- maxParticipants: this.form.maxParticipants,
|
|
|
- price: this.form.priceYuan != null ? Math.round(this.form.priceYuan * 100) : 0,
|
|
|
- startTime: this.form.startTime,
|
|
|
- endTime: this.form.endTime,
|
|
|
- visibility: this.form.visibility,
|
|
|
- visibleScope: this.form.visibleScope,
|
|
|
- visibleTo: this.form.visibleTo,
|
|
|
- checkinPoints: this.form.checkinPoints || 0
|
|
|
- }
|
|
|
- let activityId = this.editId
|
|
|
- if (this.editId) {
|
|
|
- payload.id = this.editId
|
|
|
- await updateActivity(payload)
|
|
|
- this.$message.success('更新成功')
|
|
|
- } else {
|
|
|
- const res = await createActivity(payload)
|
|
|
- if (res.data && res.data.id) {
|
|
|
- activityId = res.data.id
|
|
|
- }
|
|
|
- this.$message.success('创建成功')
|
|
|
- }
|
|
|
- if (activityId && this.form.dimensionWeights && this.form.dimensionWeights.length) {
|
|
|
- try {
|
|
|
- await saveDimensionWeights('activity', activityId, this.form.dimensionWeights)
|
|
|
- } catch (e) {
|
|
|
- console.error('保存维度权重失败', e)
|
|
|
- }
|
|
|
- }
|
|
|
- this.dialogVisible = false
|
|
|
- this.loadActivities()
|
|
|
- } catch (e) {
|
|
|
- this.$message.error('保存失败')
|
|
|
- } finally {
|
|
|
- this.saving = false
|
|
|
- }
|
|
|
- },
|
|
|
async handlePublish(row) {
|
|
|
try {
|
|
|
await publishActivity(row.id)
|
|
|
@@ -507,40 +225,6 @@ export default {
|
|
|
this.$message.error('操作失败')
|
|
|
}
|
|
|
},
|
|
|
- // ===== 查看活动(只读,使用同一对话框) =====
|
|
|
- viewActivity(row) {
|
|
|
- this.viewMode = true
|
|
|
- this.editId = row.id
|
|
|
- this.form = {
|
|
|
- title: row.title || '',
|
|
|
- coverImage: row.coverImage || '',
|
|
|
- activityType: row.activityType || '',
|
|
|
- description: row.description || '',
|
|
|
- dimensionCode: row.dimensionCode || '',
|
|
|
- dimensionWeights: [],
|
|
|
- location: row.location || '',
|
|
|
- maxParticipants: row.maxParticipants || null,
|
|
|
- priceYuan: row.price ? row.price / 100 : null,
|
|
|
- startTime: row.startTime || null,
|
|
|
- endTime: row.endTime || null,
|
|
|
- visibility: row.visibility || 'public',
|
|
|
- visibleScope: row.visibleScope || '',
|
|
|
- visibleTo: row.visibleTo || '',
|
|
|
- checkinPoints: row.checkinPoints || 0
|
|
|
- }
|
|
|
- this.dialogVisible = true
|
|
|
- this.loadDimensionWeights(row.id)
|
|
|
- this.$nextTick(() => {
|
|
|
- if (this.$refs.activityForm) {
|
|
|
- // 禁用所有 el-form-item 内的输入控件
|
|
|
- this.$refs.activityForm.$el.querySelectorAll('input, textarea, .el-select, .el-input-number, .el-switch, .el-date-editor, .el-upload').forEach(el => {
|
|
|
- el.classList.add('is-disabled')
|
|
|
- const inp = el.querySelector('input') || el.querySelector('textarea')
|
|
|
- if (inp) inp.disabled = true
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
// ===== 报名列表 =====
|
|
|
showRegistrations(row) {
|
|
|
this.currentActivity = row
|
|
|
@@ -596,11 +280,4 @@ export default {
|
|
|
|
|
|
<style scoped>
|
|
|
.activities { padding: 20px; }
|
|
|
-.cover-preview {
|
|
|
- width: 200px;
|
|
|
- height: 120px;
|
|
|
- object-fit: cover;
|
|
|
- border-radius: 4px;
|
|
|
- border: 1px solid #ddd;
|
|
|
-}
|
|
|
</style>
|