|
|
@@ -22,8 +22,26 @@
|
|
|
</div>
|
|
|
<el-table :data="activities" v-loading="loading" border stripe>
|
|
|
<el-table-column prop="id" label="ID" width="80" />
|
|
|
- <el-table-column prop="title" label="活动标题" min-width="160" />
|
|
|
- <el-table-column prop="dimensionCode" label="维度" width="100" />
|
|
|
+ <el-table-column label="封面" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <img v-if="row.coverImage" :src="row.coverImage" style="width:40px;height:40px;object-fit:cover;border-radius:4px;" />
|
|
|
+ <span v-else style="color:#999">无</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="title" label="活动标题" min-width="160" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="dimensionCode" label="维度" width="100">
|
|
|
+ <template slot-scope="{ row }">{{ dimensionLabel(row.dimensionCode) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="类型" width="90">
|
|
|
+ <template slot-scope="{ row }">{{ activityTypeLabel(row.activityType) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="location" label="地点" width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="费用(元)" width="90">
|
|
|
+ <template slot-scope="{ row }">{{ row.price ? (row.price / 100).toFixed(2) : '免费' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="人数" width="90">
|
|
|
+ <template slot-scope="{ row }">{{ row.currentParticipants || 0 }}/{{ row.maxParticipants || '不限' }}</template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column label="状态" width="100">
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-tag :type="statusType(row.status)" size="mini">
|
|
|
@@ -67,30 +85,117 @@
|
|
|
<el-dialog
|
|
|
:title="editId ? '编辑活动' : '创建活动'"
|
|
|
:visible.sync="dialogVisible"
|
|
|
- width="600px"
|
|
|
+ width="700px"
|
|
|
@closed="resetForm"
|
|
|
>
|
|
|
<el-form label-width="120px">
|
|
|
<el-form-item label="活动标题">
|
|
|
<el-input v-model="form.title" placeholder="请输入活动标题" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="活动描述">
|
|
|
- <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入活动描述" />
|
|
|
+
|
|
|
+ <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.dimensionCode" placeholder="请选择维度" style="width:100%">
|
|
|
- <el-option label="身 (健康)" value="body" />
|
|
|
- <el-option label="智 (智慧)" value="mind" />
|
|
|
- <el-option label="行 (行动)" value="action" />
|
|
|
- <el-option label="富 (财富)" value="wealth" />
|
|
|
- <el-option label="心 (心灵)" value="heart" />
|
|
|
+
|
|
|
+ <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-date-picker v-model="form.startTime" type="datetime" placeholder="选择开始时间" style="width:100%" />
|
|
|
+
|
|
|
+ <el-form-item label="活动描述">
|
|
|
+ <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入活动描述" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="维度">
|
|
|
+ <el-select v-model="form.dimensionCode" placeholder="请选择维度" style="width:100%">
|
|
|
+ <el-option label="身 (健康)" value="body" />
|
|
|
+ <el-option label="智 (智慧)" value="mind" />
|
|
|
+ <el-option label="行 (行动)" value="action" />
|
|
|
+ <el-option label="富 (财富)" value="wealth" />
|
|
|
+ <el-option label="心 (心灵)" value="heart" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="活动地点">
|
|
|
+ <el-input v-model="form.location" placeholder="请输入活动地点" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <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%" value-format="yyyy-MM-dd HH:mm:ss" />
|
|
|
+ </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%" value-format="yyyy-MM-dd HH:mm:ss" />
|
|
|
+ </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-date-picker v-model="form.endTime" type="datetime" placeholder="选择结束时间" style="width:100%" />
|
|
|
+
|
|
|
+ <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">
|
|
|
@@ -109,6 +214,18 @@ const STATUS_MAP = {
|
|
|
published: { label: '已发布', type: 'success' },
|
|
|
ended: { label: '已结束', type: 'danger' }
|
|
|
}
|
|
|
+const ACTIVITY_TYPE_MAP = {
|
|
|
+ offline: '线下活动',
|
|
|
+ online: '线上活动',
|
|
|
+ campaign: '主题活动'
|
|
|
+}
|
|
|
+const DIMENSION_MAP = {
|
|
|
+ body: '身',
|
|
|
+ mind: '智',
|
|
|
+ action: '行',
|
|
|
+ wealth: '富',
|
|
|
+ heart: '心'
|
|
|
+}
|
|
|
|
|
|
export default {
|
|
|
name: 'Activities',
|
|
|
@@ -127,13 +244,32 @@ export default {
|
|
|
},
|
|
|
form: {
|
|
|
title: '',
|
|
|
+ coverImage: '',
|
|
|
+ activityType: '',
|
|
|
description: '',
|
|
|
dimensionCode: '',
|
|
|
+ location: '',
|
|
|
+ maxParticipants: null,
|
|
|
+ priceYuan: null,
|
|
|
startTime: null,
|
|
|
- endTime: null
|
|
|
+ endTime: null,
|
|
|
+ visibility: 'public',
|
|
|
+ visibleScope: '',
|
|
|
+ visibleTo: '',
|
|
|
+ checkinPoints: 0
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
+ 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()
|
|
|
},
|
|
|
@@ -144,6 +280,37 @@ export default {
|
|
|
statusLabel(status) {
|
|
|
return (STATUS_MAP[status] && STATUS_MAP[status].label) || status
|
|
|
},
|
|
|
+ activityTypeLabel(type) {
|
|
|
+ return ACTIVITY_TYPE_MAP[type] || type || '—'
|
|
|
+ },
|
|
|
+ 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 {
|
|
|
@@ -180,37 +347,80 @@ export default {
|
|
|
resetForm() {
|
|
|
this.form = {
|
|
|
title: '',
|
|
|
+ coverImage: '',
|
|
|
+ activityType: '',
|
|
|
description: '',
|
|
|
dimensionCode: '',
|
|
|
+ location: '',
|
|
|
+ maxParticipants: null,
|
|
|
+ priceYuan: null,
|
|
|
startTime: null,
|
|
|
- endTime: null
|
|
|
+ endTime: null,
|
|
|
+ visibility: 'public',
|
|
|
+ visibleScope: '',
|
|
|
+ visibleTo: '',
|
|
|
+ checkinPoints: 0
|
|
|
}
|
|
|
},
|
|
|
editActivity(row) {
|
|
|
this.editId = row.id
|
|
|
this.form = {
|
|
|
title: row.title || '',
|
|
|
+ coverImage: row.coverImage || '',
|
|
|
+ activityType: row.activityType || '',
|
|
|
description: row.description || '',
|
|
|
dimensionCode: row.dimensionCode || '',
|
|
|
+ location: row.location || '',
|
|
|
+ maxParticipants: row.maxParticipants || null,
|
|
|
+ priceYuan: row.price ? row.price / 100 : null,
|
|
|
startTime: row.startTime || null,
|
|
|
- endTime: row.endTime || null
|
|
|
+ endTime: row.endTime || null,
|
|
|
+ visibility: row.visibility || 'public',
|
|
|
+ visibleScope: row.visibleScope || '',
|
|
|
+ visibleTo: row.visibleTo || '',
|
|
|
+ checkinPoints: row.checkinPoints || 0
|
|
|
}
|
|
|
this.dialogVisible = true
|
|
|
},
|
|
|
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
|
|
|
+ }
|
|
|
if (this.editId) {
|
|
|
- await updateActivity({ ...this.form, id: this.editId })
|
|
|
- this.$message && this.$message.success && this.$message.success('更新成功')
|
|
|
+ payload.id = this.editId
|
|
|
+ await updateActivity(payload)
|
|
|
+ this.$message.success('更新成功')
|
|
|
} else {
|
|
|
- await createActivity(this.form)
|
|
|
- this.$message && this.$message.success && this.$message.success('创建成功')
|
|
|
+ await createActivity(payload)
|
|
|
+ this.$message.success('创建成功')
|
|
|
}
|
|
|
this.dialogVisible = false
|
|
|
this.loadActivities()
|
|
|
} catch (e) {
|
|
|
- this.$message && this.$message.error && this.$message.error('保存失败')
|
|
|
+ this.$message.error('保存失败')
|
|
|
} finally {
|
|
|
this.saving = false
|
|
|
}
|
|
|
@@ -218,21 +428,32 @@ export default {
|
|
|
async handlePublish(row) {
|
|
|
try {
|
|
|
await publishActivity(row.id)
|
|
|
- this.$message && this.$message.success && this.$message.success('发布成功')
|
|
|
+ this.$message.success('发布成功')
|
|
|
this.loadActivities()
|
|
|
} catch (e) {
|
|
|
- this.$message && this.$message.error && this.$message.error('发布失败')
|
|
|
+ this.$message.error('发布失败')
|
|
|
}
|
|
|
},
|
|
|
async handleEnd(row) {
|
|
|
try {
|
|
|
await endActivity(row.id)
|
|
|
- this.$message && this.$message.success && this.$message.success('活动已结束')
|
|
|
+ this.$message.success('活动已结束')
|
|
|
this.loadActivities()
|
|
|
} catch (e) {
|
|
|
- this.$message && this.$message.error && this.$message.error('操作失败')
|
|
|
+ this.$message.error('操作失败')
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.activities { padding: 20px; }
|
|
|
+.cover-preview {
|
|
|
+ width: 200px;
|
|
|
+ height: 120px;
|
|
|
+ object-fit: cover;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #ddd;
|
|
|
+}
|
|
|
+</style>
|