Procházet zdrojové kódy

feat(activity): 活动属性新增简介字段 intro

- 后端:activities 表加 intro 列(VARCHAR 200),Activity 实体 + ActivityDTO 映射,迁移388
- 管理端:ActivityEdit 表单标题下方加「活动简介」纯文本输入框(maxlength 200)
- 小程序:活动列表卡片优先展示 intro,无简介时降级显示 description

兼容存量活动(intro 为 NULL 不影响展示)
Sisyphus Agent před 6 dny
rodič
revize
188d936597

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -3101,6 +3101,9 @@ insertSysConfigSeed("gift_membership_pay_price", "198000", "送会员商品达
         // 迁移387: 下线独立“服务角色申请审核”菜单(申请按角色分流合并进审核中心)
         // 迁移387: 下线独立“服务角色申请审核”菜单(申请按角色分流合并进审核中心)
         hideServiceRoleApplicationMenu();
         hideServiceRoleApplicationMenu();
 
 
+        // 迁移388: activities表添加intro列(活动简介,纯文本短摘要,用于活动列表卡片展示)
+        ensureColumn("activities", "intro", "VARCHAR(200) DEFAULT NULL COMMENT '活动简介(列表卡片展示)'");
+
         // 迁移382: health_status 添加 signature 列(用户手签名,base64 PNG data URL,用于问卷回答确认)
         // 迁移382: health_status 添加 signature 列(用户手签名,base64 PNG data URL,用于问卷回答确认)
         ensureColumn("health_status", "signature", "MEDIUMTEXT DEFAULT NULL COMMENT '用户手签名(base64 PNG data URL),确认问卷回答准确性'");
         ensureColumn("health_status", "signature", "MEDIUMTEXT DEFAULT NULL COMMENT '用户手签名(base64 PNG data URL),确认问卷回答准确性'");
 
 

+ 2 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ActivityDTO.java

@@ -10,6 +10,7 @@ public class ActivityDTO {
     private Long id;
     private Long id;
     private String title;
     private String title;
     private String description;
     private String description;
+    private String intro;
     private String coverImage;
     private String coverImage;
     private String dimensionCode;
     private String dimensionCode;
     /** JSON: {"body":0,"mind":0,"wisdom":0,"action":0,"wealth":0} */
     /** JSON: {"body":0,"mind":0,"wisdom":0,"action":0,"wealth":0} */
@@ -80,6 +81,7 @@ public class ActivityDTO {
         dto.setId(activity.getId());
         dto.setId(activity.getId());
         dto.setTitle(activity.getTitle());
         dto.setTitle(activity.getTitle());
         dto.setDescription(activity.getDescription());
         dto.setDescription(activity.getDescription());
+        dto.setIntro(activity.getIntro());
         dto.setCoverImage(activity.getCoverImage());
         dto.setCoverImage(activity.getCoverImage());
         dto.setDimensionCode(activity.getDimensionCode());
         dto.setDimensionCode(activity.getDimensionCode());
         dto.setDimensionWeights(activity.getDimensionWeights());
         dto.setDimensionWeights(activity.getDimensionWeights());

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/Activity.java

@@ -19,6 +19,9 @@ public class Activity implements Serializable {
 
 
     private String description;
     private String description;
 
 
+    /** 活动简介(纯文本短摘要,用于活动列表卡片展示) */
+    private String intro;
+
     private String coverImage;
     private String coverImage;
 
 
     /** body/mind/wisdom/action/wealth */
     /** body/mind/wisdom/action/wealth */

+ 1 - 0
cfc-backend/src/main/resources/schema.sql

@@ -2108,6 +2108,7 @@ CREATE TABLE IF NOT EXISTS activities (
     id               BIGINT AUTO_INCREMENT PRIMARY KEY,
     id               BIGINT AUTO_INCREMENT PRIMARY KEY,
     title            VARCHAR(100) NOT NULL COMMENT '活动标题',
     title            VARCHAR(100) NOT NULL COMMENT '活动标题',
     description      TEXT COMMENT '活动描述',
     description      TEXT COMMENT '活动描述',
+    intro            VARCHAR(200) DEFAULT NULL COMMENT '活动简介(列表卡片展示)',
     cover_image      VARCHAR(500) COMMENT '封面图片URL',
     cover_image      VARCHAR(500) COMMENT '封面图片URL',
     dimension_code   VARCHAR(20) COMMENT '维度代码: body/mind/wisdom/action/wealth',
     dimension_code   VARCHAR(20) COMMENT '维度代码: body/mind/wisdom/action/wealth',
     dimension_weights JSON DEFAULT NULL COMMENT '五维权重百分比: {"body":0,"mind":0,"wisdom":0,"action":0,"wealth":0}',
     dimension_weights JSON DEFAULT NULL COMMENT '五维权重百分比: {"body":0,"mind":0,"wisdom":0,"action":0,"wealth":0}',

+ 1 - 1
cfc-frontend/pages/activity/index.vue

@@ -131,7 +131,7 @@
             <view class="card-info">
             <view class="card-info">
               <text class="card-title">{{ act.title }}</text>
               <text class="card-title">{{ act.title }}</text>
               <text class="card-meta">开始 {{ formatDateTime(act.startTime) }}{{ act.endTime ? ' 结束 ' + formatDateTime(act.endTime) : '' }}</text>
               <text class="card-meta">开始 {{ formatDateTime(act.startTime) }}{{ act.endTime ? ' 结束 ' + formatDateTime(act.endTime) : '' }}</text>
-              <text class="card-desc" v-if="act.description">{{ act.description }}</text>
+              <text class="card-desc" v-if="act.intro || act.description">{{ act.intro || act.description }}</text>
               <text class="card-meta" v-if="act.location">📍 {{ act.location }}</text>
               <text class="card-meta" v-if="act.location">📍 {{ act.location }}</text>
               <view class="card-bottom">
               <view class="card-bottom">
                 <text class="card-status" :class="'status-' + (act.displayStatus || 'open')">
                 <text class="card-status" :class="'status-' + (act.displayStatus || 'open')">

+ 9 - 0
cfc-web/src/views/admin/ActivityEdit.vue

@@ -36,6 +36,10 @@
           <el-input v-model="form.title" placeholder="请输入活动标题" :disabled="readonly" />
           <el-input v-model="form.title" placeholder="请输入活动标题" :disabled="readonly" />
         </el-form-item>
         </el-form-item>
 
 
+        <el-form-item label="活动简介">
+          <el-input v-model="form.intro" placeholder="一句话介绍活动,将展示在活动列表卡片上" maxlength="200" show-word-limit :disabled="readonly" />
+        </el-form-item>
+
         <el-form-item label="活动类型" prop="activityType">
         <el-form-item label="活动类型" prop="activityType">
           <el-select v-model="form.activityType" placeholder="请选择活动类型" style="width:100%" :disabled="readonly">
           <el-select v-model="form.activityType" placeholder="请选择活动类型" style="width:100%" :disabled="readonly">
             <el-option label="线下活动" value="offline" />
             <el-option label="线下活动" value="offline" />
@@ -188,6 +192,7 @@ export default {
       createdActivityId: null,
       createdActivityId: null,
       form: {
       form: {
         title: '',
         title: '',
+        intro: '',
         coverImage: '',
         coverImage: '',
         activityType: '',
         activityType: '',
         description: '',
         description: '',
@@ -307,6 +312,7 @@ export default {
           }
           }
           this.form = {
           this.form = {
             title: d.title || '',
             title: d.title || '',
+            intro: d.intro || '',
             coverImage: d.coverImage || '',
             coverImage: d.coverImage || '',
             activityType: d.activityType || '',
             activityType: d.activityType || '',
             description: d.description || '',
             description: d.description || '',
@@ -377,6 +383,7 @@ export default {
       try {
       try {
         const payload = {
         const payload = {
           title: this.form.title,
           title: this.form.title,
+          intro: this.form.intro,
           coverImage: this.form.coverImage,
           coverImage: this.form.coverImage,
           activityType: this.form.activityType,
           activityType: this.form.activityType,
           description: this.form.description,
           description: this.form.description,
@@ -426,6 +433,7 @@ export default {
       this.successDialogVisible = false
       this.successDialogVisible = false
       this.form = {
       this.form = {
         title: '',
         title: '',
+        intro: '',
         coverImage: '',
         coverImage: '',
         activityType: '',
         activityType: '',
         description: '',
         description: '',
@@ -482,6 +490,7 @@ export default {
         // Save activity first
         // Save activity first
         const payload = {
         const payload = {
           title: this.form.title,
           title: this.form.title,
+          intro: this.form.intro,
           coverImage: this.form.coverImage,
           coverImage: this.form.coverImage,
           activityType: this.form.activityType,
           activityType: this.form.activityType,
           description: this.form.description,
           description: this.form.description,