浏览代码

迁移代码、字典页面优化

yjwang 6 小时之前
父节点
当前提交
f742a2731c
共有 4 个文件被更改,包括 677 次插入301 次删除
  1. 45 0
      src/api/his/promotionalActive.js
  2. 131 111
      src/views/his/adv/index.vue
  3. 89 1
      src/views/system/dict/data.vue
  4. 412 189
      src/views/system/pointsRule/index.vue

+ 45 - 0
src/api/his/promotionalActive.js

@@ -0,0 +1,45 @@
+import request from '@/utils/request'
+
+export function list(query) {
+  return request({
+    url: '/his/promotionActive/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function get(id) {
+  return request({
+    url: '/his/promotionActive/' + id,
+    method: 'get'
+  })
+}
+
+export function add(data) {
+  return request({
+    url: '/his/promotionActive',
+    method: 'post',
+    data: data
+  })
+}
+
+export function update(data) {
+  return request({
+    url: '/his/promotionActive',
+    method: 'put',
+    data: data
+  })
+}
+export function del(id) {
+  return request({
+    url: '/his/promotionActive/' + id,
+    method: 'delete'
+  })
+}
+
+export function getPromotionalActiveOption() {
+  return request({
+    url: '/his/promotionActive/getPromotionalActiveOption',
+    method: 'get',
+  })
+}

+ 131 - 111
src/views/his/adv/index.vue

@@ -11,34 +11,34 @@
         />
       </el-form-item>
       <el-form-item label="状态" prop="status">
-          <el-select v-model="queryParams.status" placeholder="状态" clearable size="small">
-               <el-option
-                 v-for="dict in statusOptions"
-                 :key="dict.dictValue"
-                 :label="dict.dictLabel"
-                 :value="dict.dictValue"
-               />
-             </el-select>
+        <el-select v-model="queryParams.status" placeholder="状态" clearable size="small">
+          <el-option
+            v-for="dict in statusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
       </el-form-item>
       <el-form-item label="显示类型" prop="showType">
         <el-select v-model="queryParams.showType" placeholder="展示类型" clearable size="small">
-            <el-option
-              v-for="dict in showOptions"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="dict.dictValue"
-            />
-          </el-select>
-        </el-form-item>
+          <el-option
+            v-for="dict in showOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="广告类型" prop="advType">
-      <el-select v-model="queryParams.advType" placeholder="广告类型" clearable size="small">
-           <el-option
-             v-for="dict in devOptions"
-             :key="dict.dictValue"
-             :label="dict.dictLabel"
-             :value="dict.dictValue"
-           />
-         </el-select>
+        <el-select v-model="queryParams.advType" placeholder="广告类型" clearable size="small">
+          <el-option
+            v-for="dict in devOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
       </el-form-item>
 
       <el-form-item>
@@ -113,21 +113,21 @@
       <el-table-column label="排序" align="center" prop="sort" />
       <el-table-column label="类型" align="center" prop="advType" width="130px">
         <template slot-scope="scope">
-              <dict-tag :options="devOptions" :value="scope.row.advType"/>
-            </template>
+          <dict-tag :options="devOptions" :value="scope.row.advType"/>
+        </template>
       </el-table-column>
       <el-table-column label="显示类型" align="center" prop="showType" width="130px">
         <template slot-scope="scope">
-              <dict-tag :options="showOptions" :value="scope.row.showType"/>
-            </template>
+          <dict-tag :options="showOptions" :value="scope.row.showType"/>
+        </template>
       </el-table-column>
       <el-table-column label="状态" align="center" prop="status" >
-           <template slot-scope="scope">
-              <dict-tag :options="statusOptions" :value="scope.row.status"/>
-            </template>
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
       </el-table-column>
-        <el-table-column label="创建时间" align="center" prop="createTime" width="150px" />
-        <el-table-column label="修改时间" align="center" prop="updateTime" width="150px" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="150px" />
+      <el-table-column label="修改时间" align="center" prop="updateTime" width="150px" />
 
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="150px">
         <template slot-scope="scope">
@@ -158,21 +158,21 @@
     />
 
     <!-- 添加或修改广告对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="700px" append-to-body>
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="90px">
         <el-form-item label="标题名称" prop="advTitle">
           <el-input v-model="form.advTitle" placeholder="请输入广告标题" />
         </el-form-item>
         <el-form-item label="图片" prop="imageUrl" >
           <el-upload
-                      v-model="form.imageUrl"
-                      class="avatar-uploader"
-                      :action="uploadUrl"
-                      :show-file-list="false"
-                      :on-success="handleAvatarSuccess"
-                      :before-upload="beforeAvatarUpload">
-                      <img v-if="form.imageUrl" :src="form.imageUrl" class="avatar" width="300px">
-                      <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+            v-model="form.imageUrl"
+            class="avatar-uploader"
+            :action="uploadUrl"
+            :show-file-list="false"
+            :on-success="handleAvatarSuccess"
+            :before-upload="beforeAvatarUpload">
+            <img v-if="form.imageUrl" :src="form.imageUrl" class="avatar" width="300px">
+            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
           </el-upload>
         </el-form-item>
         <el-form-item label="类型" prop="advType">
@@ -186,25 +186,35 @@
           </el-select>
         </el-form-item>
         <el-form-item label="显示类型" prop="showType">
-         <el-radio-group v-model="form.showType">
+          <el-radio-group v-model="form.showType">
             <el-radio :label="item.dictValue" v-for="item in showOptions" >{{item.dictLabel}}</el-radio>
-         </el-radio-group>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="公众号链接" prop="advUrl" v-if="form.showType==1">
+          <el-input v-model="form.advUrl" placeholder="请输入公众号链接" />
+        </el-form-item>
+        <el-form-item label="小程序地址" prop="advUrl" v-if="form.showType==2">
+          <el-input v-model="form.advUrl" placeholder="请输入小程序地址" />
+        </el-form-item>
+        <el-form-item label="APP地址" prop="advUrl" v-if="form.showType==2||form.showType==4 ">
+          <el-input v-model="form.appAdvUrl" placeholder="请输入APP地址" />
+        </el-form-item>
+        <el-form-item label="文章内容" v-show="form.showType==3">
+          <editor ref="myeditor" @on-text-change="updateText" />
+        </el-form-item>
+        <el-form-item label="活动" prop="activeId" v-show="form.showType === '5'">
+          <el-select v-model="form.activeId"  placeholder="请选择活动" >
+            <el-option
+              v-for="item in activeOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+            ></el-option>
+          </el-select>
         </el-form-item>
-       <el-form-item label="公众号链接" prop="advUrl" v-if="form.showType==1">
-         <el-input v-model="form.advUrl" placeholder="请输入公众号链接" />
-       </el-form-item>
-       <el-form-item label="小程序地址" prop="advUrl" v-if="form.showType==2">
-         <el-input v-model="form.advUrl" placeholder="请输入小程序地址" />
-       </el-form-item>
-       <el-form-item label="APP地址" prop="advUrl" v-if="form.showType==2||form.showType==4 ">
-         <el-input v-model="form.appAdvUrl" placeholder="请输入APP地址" />
-       </el-form-item>
-       <el-form-item label="文章内容" v-show="form.showType==3">
-         <editor ref="myeditor" @on-text-change="updateText" />
-       </el-form-item>
         <el-form-item label="状态">
           <el-radio-group v-model="form.status">
-             <el-radio :label="item.dictValue" v-for="item in statusOptions" >{{item.dictLabel}}</el-radio>
+            <el-radio :label="item.dictValue" v-for="item in statusOptions" >{{item.dictLabel}}</el-radio>
           </el-radio-group>
         </el-form-item>
 
@@ -220,6 +230,7 @@
 <script>
 import { listAdv, getAdv, delAdv, addAdv, updateAdv, exportAdv } from "@/api/his/adv";
 import Editor from '@/components/Editor/wang';
+import { getPromotionalActiveOption } from '@/api/his/promotionalActive'
 export default {
   name: "Adv",
   components: { Editor },
@@ -230,6 +241,7 @@ export default {
       showOptions: [],
       devOptions: [],
       statusOptions: [],
+      activeOptions: [],
       // 遮罩层
       loading: true,
       // 导出遮罩层
@@ -296,26 +308,33 @@ export default {
     this.getDicts("sys_adv_type").then(response => {
       this.devOptions = response.data;
     });
+    this.getActiveOption()
   },
   methods: {
+    getActiveOption() {
+      getPromotionalActiveOption().then(response => {
+        console.log(response)
+        this.activeOptions = response.list;
+      });
+    },
     updateText(text){
-        this.form.content=text
-      },
+      this.form.content=text
+    },
     handleAvatarSuccess(res, file) {
-            if(res.code==200){
-              this.form.imageUrl=res.url;
-              self.$forceUpdate()
-            }
-            else{
-              this.msgError(res.msg);
-            }
-        },
+      if(res.code==200){
+        this.form.imageUrl=res.url;
+        self.$forceUpdate()
+      }
+      else{
+        this.msgError(res.msg);
+      }
+    },
     beforeAvatarUpload(file) {
-          const isLt1M = file.size / 1024 / 1024 < 1;
-          if (!isLt1M) {
-            this.$message.error('上传图片大小不能超过 1MB!');
-          }
-          return   isLt1M;
+      const isLt1M = file.size / 1024 / 1024 < 1;
+      if (!isLt1M) {
+        this.$message.error('上传图片大小不能超过 1MB!');
+      }
+      return   isLt1M;
     },
     /** 查询广告列表 */
     getList() {
@@ -345,7 +364,8 @@ export default {
         status: "0",
         sort: null,
         advType: null,
-        showType: null
+        showType: null,
+        activeId: null
       };
       this.resetForm("form");
     },
@@ -384,7 +404,7 @@ export default {
         this.title = "修改广告";
         this.form.advType = String(this.form.advType)
         this.form.showType = String(this.form.showType)
-      setTimeout(() => {
+        setTimeout(() => {
           if(this.form.content==null){
             this.$refs.myeditor.setText("");
           }
@@ -418,53 +438,53 @@ export default {
     handleDelete(row) {
       const advIds = row.advId || this.ids;
       this.$confirm('是否确认删除广告编号为"' + advIds + '"的数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return delAdv(advIds);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("删除成功");
-        }).catch(() => {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delAdv(advIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {});
     },
     /** 导出按钮操作 */
     handleExport() {
       const queryParams = this.queryParams;
       this.$confirm('是否确认导出所有广告数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(() => {
-          this.exportLoading = true;
-          return exportAdv(queryParams);
-        }).then(response => {
-          this.download(response.msg);
-          this.exportLoading = false;
-        }).catch(() => {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportAdv(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {});
     }
   }
 };
 </script>
 <style>
-   .avatar-uploader .el-upload {
-       border: 1px dashed #d9d9d9;
-       border-radius: 6px;
-       cursor: pointer;
-       position: relative;
-       overflow: hidden;
-     }
-     .avatar-uploader .el-upload:hover {
-       border-color: #409EFF;
-     }
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409EFF;
+}
 
-     .avatar-uploader-icon {
-       font-size: 28px;
-       color: #8c939d;
-       width: 150px;
-       height: 150px;
-       line-height: 150px;
-       text-align: center;
-     }
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 150px;
+  height: 150px;
+  line-height: 150px;
+  text-align: center;
+}
 
 </style>

+ 89 - 1
src/views/system/dict/data.vue

@@ -93,6 +93,18 @@
         </template>
       </el-table-column>
       <el-table-column label="字典键值" align="center" prop="dictValue" />
+      <el-table-column label="图片" align="center" prop="dictImage" width="80">
+        <template slot-scope="scope">
+          <el-image
+            v-if="scope.row.dictImage"
+            :src="scope.row.dictImage"
+            :preview-src-list="[scope.row.dictImage]"
+            fit="cover"
+            style="width: 40px; height: 40px; border-radius: 4px;"
+          />
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
       <el-table-column label="字典排序" align="center" prop="dictSort" />
       <el-table-column label="状态" align="center" prop="status">
         <template slot-scope="scope">
@@ -148,6 +160,22 @@
         <el-form-item label="样式属性" prop="cssClass">
           <el-input v-model="form.cssClass" placeholder="请输入样式属性" />
         </el-form-item>
+        <el-form-item label="图片上传">
+          <el-upload
+            class="dict-image-upload"
+            :action="uploadUrl"
+            :headers="uploadHeaders"
+            :show-file-list="false"
+            :on-success="handleUploadSuccess"
+            :before-upload="beforeImageUpload"
+            accept="image/*"
+          >
+            <img v-if="form.dictImage" :src="form.dictImage" class="dict-image-preview" />
+            <i v-else class="el-icon-plus dict-image-uploader-icon"></i>
+          </el-upload>
+          <span class="form-tip" style="margin-left: 12px; font-size: 12px; color: #999;">{{ form.dictImage ? '点击更换图片' : '点击上传图片(可选)' }}</span>
+          <el-button v-if="form.dictImage" type="text" style="margin-left: 8px; color: #f56c6c;" @click="handleRemoveImage">删除图片</el-button>
+        </el-form-item>
         <el-form-item label="显示排序" prop="dictSort">
           <el-input-number v-model="form.dictSort" controls-position="right" :min="0" />
         </el-form-item>
@@ -185,6 +213,7 @@
 <script>
 import { listData, getData, delData, addData, updateData, exportData } from "@/api/system/dict/data";
 import { listType, getType } from "@/api/system/dict/type";
+import { getToken } from "@/utils/auth";
 
 export default {
   name: "Data",
@@ -194,6 +223,10 @@ export default {
       loading: true,
       // 导出遮罩层
       exportLoading: false,
+      // 上传地址
+      uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadImage",
+      // 上传请求头
+      uploadHeaders: { Authorization: "Bearer " + getToken() },
       // 选中数组
       ids: [],
       // 非单个禁用
@@ -311,6 +344,7 @@ export default {
         dictLabel: undefined,
         dictValue: undefined,
         cssClass: undefined,
+        dictImage: undefined,
         listClass: 'default',
         dictSort: 0,
         status: "0",
@@ -386,6 +420,33 @@ export default {
           this.msgSuccess("删除成功");
         }).catch(() => {});
     },
+    /** 上传图片前置校验 */
+    beforeImageUpload(file) {
+      const isImage = file.type.startsWith('image/');
+      const isLt2M = file.size / 1024 / 1024 < 2;
+      if (!isImage) {
+        this.$message.error('只能上传图片文件');
+        return false;
+      }
+      if (!isLt2M) {
+        this.$message.error('图片大小不能超过 2MB');
+        return false;
+      }
+      return true;
+    },
+    /** 上传成功回调 */
+    handleUploadSuccess(response) {
+      if (response.code === 200) {
+        this.form.dictImage = response.url || response.data;
+        this.$message.success('图片上传成功');
+      } else {
+        this.$message.error(response.msg || '上传失败');
+      }
+    },
+    /** 删除图片 */
+    handleRemoveImage() {
+      this.form.dictImage = undefined;
+    },
     /** 导出按钮操作 */
     handleExport() {
       const queryParams = this.queryParams;
@@ -403,4 +464,31 @@ export default {
     }
   }
 };
-</script>
+</script>
+<style scoped>
+.dict-image-upload >>> .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  width: 80px;
+  height: 80px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.dict-image-upload >>> .el-upload:hover {
+  border-color: #409eff;
+}
+.dict-image-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+}
+.dict-image-preview {
+  width: 80px;
+  height: 80px;
+  object-fit: cover;
+  border-radius: 6px;
+}
+</style>

+ 412 - 189
src/views/system/pointsRule/index.vue

@@ -1,161 +1,242 @@
 <template>
-  <div class="app-container">
-    <el-card shadow="never">
-      <div slot="header" class="clearfix">
-        <span class="card-title">
-          <i class="el-icon-s-cooperation"></i>
-          发放积分规则配置
-        </span>
-        <span style="font-size: 13px; color: #909399; margin-left: 12px;">配置消费送积分体系规则</span>
+  <div class="app-container points-rule">
+    <el-card shadow="never" class="main-card">
+      <!-- 头部 -->
+      <div slot="header" class="card-header">
+        <div class="header-left">
+          <span class="header-icon-bg">
+            <i class="el-icon-s-cooperation"></i>
+          </span>
+          <div class="header-text">
+            <span class="header-title">发放积分规则配置</span>
+            <span class="header-sub">消费送积分体系规则管理</span>
+          </div>
+        </div>
+        <el-tag :type="form.enabled === 1 ? 'success' : 'info'" size="small" effect="plain">
+          {{ form.enabled === 1 ? '已启用' : '已禁用' }}
+        </el-tag>
       </div>
 
-      <el-form ref="form" :model="form" :rules="rules" label-width="160px" size="small">
-        <!-- 1. 积分比例 -->
-        <el-divider content-position="left">
-          <i class="el-icon-coin"></i> 积分比例配置
-        </el-divider>
-        <el-form-item label="积分发放比例" prop="rate">
-          <el-col :span="6">
-            <el-input-number
-              v-model="form.rate"
-              :precision="2"
-              :min="0.01"
-              :max="9999"
-              :step="0.1"
-              style="width: 100%;"
-            />
-          </el-col>
-          <span class="form-tip">1元 = {{ form.rate || 0 }} 积分</span>
-        </el-form-item>
+      <el-form ref="form" :model="form" :rules="rules" label-width="150px" size="small">
+        <!-- 1. 积分发放比例 -->
+        <div class="section-block section-earn">
+          <div class="section-header">
+            <span class="section-icon earn"><i class="el-icon-coin"></i></span>
+            <span class="section-label">积分发放比例配置</span>
+            <span class="section-desc">设置消费可获得多少积分</span>
+          </div>
+          <div class="section-body">
+            <el-form-item label="积分发放比例" prop="rate">
+              <el-input-number
+                v-model="form.rate"
+                :precision="2"
+                :min="0.01"
+                :max="9999"
+                :step="0.1"
+                class="input-number-wide"
+              />
+              <span class="tip-box tip-success">
+                <i class="el-icon-info"></i>
+                消费 <b>1 元</b> = <b class="highlight">{{ form.rate || 0 }}</b> 积分
+              </span>
+            </el-form-item>
+          </div>
+        </div>
 
         <!-- 2. 参与范围 -->
-        <el-divider content-position="left">
-          <i class="el-icon-s-grid"></i> 参与范围限定
-        </el-divider>
-        <el-form-item label="参与范围" prop="scopeType">
-          <el-radio-group v-model="form.scopeType">
-            <el-radio :label="1">全场商品参与送积分</el-radio>
-            <el-radio :label="2">指定商品分类不送积分</el-radio>
-            <el-radio :label="3">指定单个商品不送积分</el-radio>
-          </el-radio-group>
-        </el-form-item>
-
-        <!-- 指定不送积分的商品分类 -->
-        <el-form-item v-if="form.scopeType === 2" label="不送积分分类" prop="excludeCategoryIds">
-          <el-select
-            v-model="form.excludeCategoryIds"
-            multiple
-            filterable
-            placeholder="请选择商品分类"
-            style="width: 480px;"
-          >
-            <el-option
-              v-for="item in categoryOptions"
-              :key="item.dictValue"
-              :label="item.dictLabel"
-              :value="Number(item.dictValue)"
-            />
-          </el-select>
-          <div style="margin-top: 4px; color: #909399; font-size: 12px;">
-            已选 {{ (form.excludeCategoryIds || []).length }} 个分类,这些分类下的商品不参与送积分
+        <div class="section-block section-scope">
+          <div class="section-header">
+            <span class="section-icon scope"><i class="el-icon-s-grid"></i></span>
+            <span class="section-label">参与范围限定</span>
+            <span class="section-desc">控制哪些商品可以送积分</span>
+          </div>
+          <div class="section-body">
+            <el-form-item label="参与范围" prop="scopeType">
+              <el-radio-group v-model="form.scopeType">
+                <el-radio :label="1">全场商品参与送积分</el-radio>
+                <el-radio :label="2">指定商品分类不送积分</el-radio>
+                <el-radio :label="3">指定单个商品不送积分</el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+            <el-form-item v-if="form.scopeType === 2" label="不送积分分类" prop="excludeCategoryIds">
+              <el-select
+                v-model="form.excludeCategoryIds"
+                multiple
+                filterable
+                placeholder="请选择商品分类"
+                class="select-wide"
+              >
+                <el-option
+                  v-for="item in categoryOptions"
+                  :key="item.dictValue"
+                  :label="item.dictLabel"
+                  :value="Number(item.dictValue)"
+                />
+              </el-select>
+              <span class="tip-box tip-warning">
+                <i class="el-icon-warning-outline"></i>
+                已选 <b>{{ (form.excludeCategoryIds || []).length }}</b> 个分类,这些分类下的商品不参与送积分
+              </span>
+            </el-form-item>
+
+            <el-form-item v-if="form.scopeType === 3" label="不送积分商品" prop="excludeProductIds">
+              <el-select
+                v-model="form.excludeProductIds"
+                multiple
+                filterable
+                placeholder="请选择商品(默认展示10条,可搜索)"
+                :loading="productLoading"
+                class="select-wide-x"
+                @visible-change="onProductSelectVisible"
+              >
+                <el-option
+                  v-for="item in productOptions"
+                  :key="item.productId"
+                  :label="item.displayName"
+                  :value="item.productId"
+                >
+                  <div class="product-option">
+                    <img v-if="item.image" :src="item.image" class="product-option-img" />
+                    <span v-else class="product-option-img product-option-img-placeholder"></span>
+                    <span class="product-option-name">{{ item.displayName }}</span>
+                    <span v-if="item.storeName" class="product-option-store">{{ item.storeName }}</span>
+                    <span class="product-option-price">¥{{ item.price }}</span>
+                  </div>
+                </el-option>
+              </el-select>
+              <span class="tip-box tip-warning">
+                <i class="el-icon-warning-outline"></i>
+                已选 <b>{{ (form.excludeProductIds || []).length }}</b> 个商品,这些商品不参与送积分
+              </span>
+            </el-form-item>
           </div>
-        </el-form-item>
-
-        <!-- 指定不送积分的单个商品 -->
-        <el-form-item v-if="form.scopeType === 3" label="不送积分商品" prop="excludeProductIds">
-          <el-select
-            v-model="form.excludeProductIds"
-            multiple
-            filterable
-            placeholder="请选择商品(默认展示10条,可搜索)"
-            :loading="productLoading"
-            style="width: 560px;"
-            @visible-change="onProductSelectVisible"
-          >
-            <el-option
-              v-for="item in productOptions"
-              :key="item.productId"
-              :label="item.displayName"
-              :value="item.productId"
-            >
-              <div class="product-option">
-                <img v-if="item.image" :src="item.image" class="product-option-img" />
-                <span v-else class="product-option-img product-option-img-placeholder"></span>
-                <span class="product-option-name">{{ item.displayName }}</span>
-                <span v-if="item.storeName" class="product-option-store">{{ item.storeName }}</span>
-                <span class="product-option-price">¥{{ item.price }}</span>
+        </div>
+
+        <!-- 3. 积分抵扣配置 -->
+        <div class="section-block section-deduct">
+          <div class="section-header">
+            <span class="section-icon deduct"><i class="el-icon-s-finance"></i></span>
+            <span class="section-label">积分抵扣配置</span>
+            <span class="section-desc">设置积分抵现规则</span>
+          </div>
+          <div class="section-body">
+            <el-form-item label="积分支付比例" prop="payRate">
+              <el-input-number
+                v-model="form.payRate"
+                :precision="0"
+                :min="1"
+                :max="99999"
+                :step="10"
+                class="input-number-sm"
+              />
+              <span class="tip-box tip-primary">
+                <i class="el-icon-info"></i>
+                <b class="highlight">{{ form.payRate || 0 }}</b> 积分 = <b>1 元</b>(每 <b>{{ form.payRate || 0 }}</b> 积分可抵现 1 元)
+              </span>
+            </el-form-item>
+
+            <el-form-item label="最高抵扣积分" prop="maxDeduct">
+              <el-input-number
+                v-model="form.maxDeduct"
+                :min="0"
+                :max="999999"
+                :step="10"
+                class="input-number-sm"
+              />
+              <span class="tip-box tip-default">
+                <i class="el-icon-info"></i>
+                每笔订单最多可用 <b>{{ form.maxDeduct || '不限' }}</b> 积分抵扣{{ form.maxDeduct > 0 ? ',超出部分不可使用' : '' }}
+              </span>
+            </el-form-item>
+
+            <el-form-item label="满减门槛" prop="minDeductAmount">
+              <el-input-number
+                v-model="form.minDeductAmount"
+                :precision="2"
+                :min="0"
+                :max="999999"
+                :step="10"
+                class="input-number-sm"
+              />
+              <span class="tip-box tip-default">
+                <i class="el-icon-info"></i>
+                {{ form.minDeductAmount > 0 ? '订单满 ' + form.minDeductAmount + ' 元才可使用积分抵扣' : '不限制,任意金额均可使用积分抵扣' }}
+              </span>
+            </el-form-item>
+          </div>
+        </div>
+
+        <!-- 4. 特殊规则 -->
+        <div class="section-block section-rules">
+          <div class="section-header">
+            <span class="section-icon rules"><i class="el-icon-setting"></i></span>
+            <span class="section-label">特殊规则设置</span>
+            <span class="section-desc">退款、限额等高级配置</span>
+          </div>
+          <div class="section-body">
+            <el-form-item label="退款扣回积分" prop="refundDeduct">
+              <el-radio-group v-model="form.refundDeduct">
+                <el-radio :label="1">是,退款时自动扣回</el-radio>
+                <el-radio :label="0">否,退款不扣回</el-radio>
+              </el-radio-group>
+              <div v-if="form.refundDeduct === 1" class="tip-box tip-success" style="display: inline-flex; vertical-align: middle;">
+                <i class="el-icon-check"></i> 退款时将自动按比例扣回已发放的积分
               </div>
-            </el-option>
-          </el-select>
-          <div style="margin-top: 4px; color: #909399; font-size: 12px;">
-            已选 {{ (form.excludeProductIds || []).length }} 个商品,这些商品不参与送积分
+            </el-form-item>
+
+            <el-form-item label="每日积分上限" prop="dailyLimit">
+              <el-input-number
+                v-model="form.dailyLimit"
+                :min="0"
+                :max="999999"
+                :step="100"
+                class="input-number-sm"
+              />
+              <span class="tip-box tip-default">
+                <i class="el-icon-info"></i>
+                {{ form.dailyLimit > 0 ? '每日最多发放 ' + form.dailyLimit + ' 积分' : '不限制每日积分获取' }}
+              </span>
+            </el-form-item>
+
+            <el-form-item label="每月积分上限" prop="monthlyLimit">
+              <el-input-number
+                v-model="form.monthlyLimit"
+                :min="0"
+                :max="999999"
+                :step="100"
+                class="input-number-sm"
+              />
+              <span class="tip-box tip-default">
+                <i class="el-icon-info"></i>
+                {{ form.monthlyLimit > 0 ? '每月最多发放 ' + form.monthlyLimit + ' 积分' : '不限制每月积分获取' }}
+              </span>
+            </el-form-item>
+
+            <el-form-item label="启用状态" prop="enabled">
+              <el-switch
+                v-model="form.enabled"
+                :active-value="1"
+                :inactive-value="0"
+                active-color="#13ce66"
+                inactive-color="#c0ccda"
+                active-text="启用"
+                inactive-text="禁用"
+              />
+              <span class="tip-box tip-default" style="margin-left: 12px;">
+                <i class="el-icon-info"></i> 关闭后所有积分发放将立即停止
+              </span>
+            </el-form-item>
           </div>
-        </el-form-item>
-
-        <!-- 3. 特殊规则 -->
-        <el-divider content-position="left">
-          <i class="el-icon-setting"></i> 特殊规则设置
-        </el-divider>
-        <el-form-item label="退款扣回积分" prop="refundDeduct">
-          <el-radio-group v-model="form.refundDeduct">
-            <el-radio :label="1">是,退款时自动扣回</el-radio>
-            <el-radio :label="0">否,退款不扣回</el-radio>
-          </el-radio-group>
-          <div class="form-tip">开启后,发生退款时将按实退金额比例自动扣回已发放积分</div>
-        </el-form-item>
-
-        <el-form-item label="每日积分上限" prop="dailyLimit">
-          <el-col :span="4">
-            <el-input-number
-              v-model="form.dailyLimit"
-              :min="0"
-              :max="999999"
-              :step="100"
-              style="width: 100%;"
-            />
-          </el-col>
-          <span class="form-tip">0 = 不限制,超出上限自动不再发放</span>
-        </el-form-item>
-
-        <el-form-item label="每月积分上限" prop="monthlyLimit">
-          <el-col :span="4">
-            <el-input-number
-              v-model="form.monthlyLimit"
-              :min="0"
-              :max="999999"
-              :step="100"
-              style="width: 100%;"
-            />
-          </el-col>
-          <span class="form-tip">0 = 不限制,超出上限自动不再发放</span>
-        </el-form-item>
-
-        <el-form-item label="最高抵扣积分" prop="maxDeduct">
-          <el-col :span="4">
-            <el-input-number
-              v-model="form.maxDeduct"
-              :min="0"
-              :max="999999"
-              :step="10"
-              style="width: 100%;"
-            />
-          </el-col>
-          <span class="form-tip">0 = 不限制,用户每笔订单最多可用积分抵扣的数量</span>
-        </el-form-item>
-
-        <el-form-item label="启用状态" prop="enabled">
-          <el-radio-group v-model="form.enabled">
-            <el-radio :label="1">启用</el-radio>
-            <el-radio :label="0">禁用</el-radio>
-          </el-radio-group>
-        </el-form-item>
+        </div>
       </el-form>
 
-      <div style="text-align: center; margin-top: 24px; padding-top: 20px; border-top: 1px solid #EBEEF5;">
-        <el-button type="primary" :loading="submitLoading" @click="handleSubmit" icon="el-icon-check">
+      <!-- 底部操作 -->
+      <div class="form-footer">
+        <el-button size="medium" @click="handleReset" icon="el-icon-refresh">重 置</el-button>
+        <el-button type="primary" size="medium" :loading="submitLoading" @click="handleSubmit" icon="el-icon-check">
           保 存 配 置
         </el-button>
-        <el-button @click="handleReset" icon="el-icon-refresh">重 置</el-button>
       </div>
     </el-card>
   </div>
@@ -197,6 +278,7 @@ export default {
       productOptions: [],
       form: {
         rate: 1.00,
+        payRate: 100,
         scopeType: 1,
         excludeCategoryIds: [],
         excludeProductIds: [],
@@ -204,6 +286,7 @@ export default {
         dailyLimit: 0,
         monthlyLimit: 0,
         maxDeduct: 0,
+        minDeductAmount: 0,
         enabled: 1
       },
       rules: {
@@ -211,6 +294,9 @@ export default {
           { required: true, message: '请输入积分发放比例', trigger: 'blur' },
           { validator: validateRate, trigger: 'blur' }
         ],
+        payRate: [
+          { required: true, message: '请输入积分支付比例', trigger: 'blur' }
+        ],
         scopeType: [
           { required: true, message: '请选择参与范围', trigger: 'change' }
         ],
@@ -232,6 +318,9 @@ export default {
         maxDeduct: [
           { required: true, message: '请输入最高抵扣积分', trigger: 'blur' }
         ],
+        minDeductAmount: [
+          { required: true, message: '请输入满减门槛', trigger: 'blur' }
+        ],
         enabled: [
           { required: true, message: '请选择启用状态', trigger: 'change' }
         ]
@@ -243,22 +332,20 @@ export default {
     this.loadConfig()
   },
   methods: {
-    /** 加载商品分类字典 */
     loadCategoryDict() {
       this.getDicts('store_product_type').then(response => {
         this.categoryOptions = response.data || []
       })
     },
-    /** 加载配置 */
     loadConfig() {
       getConfigByKey('points.grantRule').then(response => {
         if (response.data && response.data.configValue) {
           const configValue = response.data.configValue
           let config = typeof configValue === 'string' ? JSON.parse(configValue) : configValue
           this.configId = response.data.configId
-          // 合并已保存的值,保留默认值兜底
           this.form = {
             rate: config.rate ?? 1.00,
+            payRate: config.payRate ?? 100,
             scopeType: config.scopeType ?? 1,
             excludeCategoryIds: config.excludeCategoryIds || [],
             excludeProductIds: config.excludeProductIds || [],
@@ -266,9 +353,9 @@ export default {
             dailyLimit: config.dailyLimit ?? 0,
             monthlyLimit: config.monthlyLimit ?? 0,
             maxDeduct: config.maxDeduct ?? 0,
+            minDeductAmount: config.minDeductAmount ?? 0,
             enabled: config.enabled ?? 1
           }
-          // 回填已选商品 options
           if (config.excludeProductIds && config.excludeProductIds.length > 0) {
             this.loadSelectedProducts(config.excludeProductIds)
           }
@@ -280,7 +367,6 @@ export default {
       })
     },
 
-    /** 回填已选商品名称 */
     loadSelectedProducts(ids) {
       listStoreProduct({ pageSize: 999 }).then(response => {
         const rows = response.data || response.rows || []
@@ -288,7 +374,6 @@ export default {
       }).catch(() => {})
     },
 
-    /** 构建商品选项对象 */
     buildProductOption(item) {
       const name = item.productName && item.productName !== '-' ? item.productName : (item.commonName || '')
       return {
@@ -301,22 +386,6 @@ export default {
       }
     },
 
-    /** 加载商品分类选项(默认10条) */
-    loadCategoryOptions() {
-      this.categoryLoading = true
-      listCategory({ pageSize: 10 }).then(response => {
-        const rows = response.data || response.rows || []
-        this.categoryOptions = rows.map(item => ({
-          categoryId: item.categoryId,
-          categoryName: item.categoryName || item.name
-        }))
-        this.categoryLoading = false
-      }).catch(() => {
-        this.categoryLoading = false
-      })
-    },
-
-    /** 加载商品选项(默认10条) */
     loadProductOptions() {
       this.productLoading = true
       listStoreProduct({ pageSize: 10 }).then(response => {
@@ -328,27 +397,17 @@ export default {
       })
     },
 
-    /** 分类下拉展开时加载数据 */
-    onCategorySelectVisible(visible) {
-      if (visible && this.categoryOptions.length === 0) {
-        this.loadCategoryOptions()
-      }
-    },
-
-    /** 商品下拉展开时加载数据 */
     onProductSelectVisible(visible) {
       if (visible && this.productOptions.length === 0) {
         this.loadProductOptions()
       }
     },
 
-    /** 提交保存 */
     handleSubmit() {
       this.$refs.form.validate(valid => {
         if (!valid) return
         this.submitLoading = true
         const configValue = JSON.stringify(this.form)
-        // configId 不存在则新增,存在则更新
         let request
         if (this.configId) {
           request = updateConfigByKey({
@@ -378,7 +437,6 @@ export default {
       })
     },
 
-    /** 重置为默认值 */
     handleReset() {
       this.$confirm('确认重置为默认值?已保存的配置将丢失。', '提示', {
         confirmButtonText: '确定',
@@ -387,6 +445,7 @@ export default {
       }).then(() => {
         this.form = {
           rate: 1.00,
+          payRate: 100,
           scopeType: 1,
           excludeCategoryIds: [],
           excludeProductIds: [],
@@ -394,6 +453,7 @@ export default {
           dailyLimit: 0,
           monthlyLimit: 0,
           maxDeduct: 0,
+          minDeductAmount: 0,
           enabled: 1
         }
         this.categoryOptions = []
@@ -406,25 +466,188 @@ export default {
 </script>
 
 <style scoped>
-.card-title {
-  font-size: 16px;
+/* ===== 主卡片 ===== */
+.main-card {
+  border-radius: 8px;
+  overflow: hidden;
+}
+.main-card >>> .el-card__header {
+  padding: 0;
+  border-bottom: none;
+}
+
+/* ===== 卡片头部 ===== */
+.card-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 20px 24px;
+  background: linear-gradient(135deg, #f5f7fa 0%, #e8edf3 100%);
+}
+.header-left {
+  display: flex;
+  align-items: center;
+  gap: 14px;
+}
+.header-icon-bg {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 44px;
+  height: 44px;
+  border-radius: 10px;
+  background: linear-gradient(135deg, #409EFF 0%, #337ECC 100%);
+  color: #fff;
+  font-size: 22px;
+  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
+}
+.header-text {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+.header-title {
+  font-size: 17px;
   font-weight: 600;
   color: #303133;
 }
-.card-title i {
-  margin-right: 6px;
-  color: #409EFF;
+.header-sub {
+  font-size: 13px;
+  color: #909399;
+}
+
+/* ===== 分区块 ===== */
+.section-block {
+  margin: 0 24px 16px;
+  border-radius: 8px;
+  border: 1px solid #EBEEF5;
+  overflow: hidden;
+  transition: box-shadow 0.2s;
+}
+.section-block:hover {
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+}
+
+.section-header {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 12px 20px;
+  background: #fafbfc;
+  border-bottom: 1px solid #EBEEF5;
+}
+.section-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 28px;
+  height: 28px;
+  border-radius: 6px;
+  color: #fff;
+  font-size: 14px;
+  flex-shrink: 0;
 }
-.form-tip {
+.section-icon.earn   { background: linear-gradient(135deg, #67C23A, #529b2e); }
+.section-icon.scope  { background: linear-gradient(135deg, #409EFF, #337ECC); }
+.section-icon.deduct { background: linear-gradient(135deg, #E6A23C, #cf9236); }
+.section-icon.rules  { background: linear-gradient(135deg, #9B59B6, #7d3c98); }
+
+.section-label {
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
+}
+.section-desc {
   font-size: 12px;
   color: #909399;
-  margin-left: 12px;
+  margin-left: 4px;
+}
+
+.section-body {
+  padding: 16px 20px 4px;
+}
+
+/* ===== 表单优化 ===== */
+.section-body >>> .el-form-item {
+  margin-bottom: 18px;
+}
+.section-body >>> .el-form-item__label {
+  font-weight: 500;
+  color: #606266;
+}
+
+/* ===== 输入框 ===== */
+.input-number-wide {
+  width: 200px;
+}
+.input-number-sm {
+  width: 160px;
+}
+.select-wide {
+  width: 480px;
+}
+.select-wide-x {
+  width: 580px;
+}
+
+/* ===== 提示标签 ===== */
+.tip-box {
+  display: inline-flex;
+  align-items: center;
+  gap: 5px;
+  margin-left: 14px;
+  padding: 4px 12px;
+  border-radius: 4px;
+  font-size: 12px;
+  line-height: 1.8;
+  vertical-align: middle;
+}
+.tip-box i { font-size: 13px; }
+.tip-box b { font-weight: 600; }
+.tip-box b.highlight {
+  color: inherit;
+  font-size: 15px;
+}
+
+.tip-success {
+  background: #f0f9eb;
+  color: #67C23A;
+  border: 1px solid #e1f3d8;
+}
+.tip-primary {
+  background: #ecf5ff;
+  color: #409EFF;
+  border: 1px solid #d9ecff;
+}
+.tip-warning {
+  display: block;
+  margin: 6px 0 0 0;
+  background: #fdf6ec;
+  color: #E6A23C;
+  border: 1px solid #faecd8;
+}
+.tip-default {
+  background: #f5f7fa;
+  color: #909399;
+  border: 1px solid #e4e7ed;
+}
+
+/* ===== 底部操作栏 ===== */
+.form-footer {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 16px;
+  margin: 0 24px 8px;
+  padding: 20px 0;
+  border-top: 1px solid #EBEEF5;
 }
-.el-divider i {
-  margin-right: 6px;
+.form-footer .el-button {
+  min-width: 120px;
+  border-radius: 6px;
 }
 
-/* 商品选项样式 */
+/* ===== 商品下拉选项 ===== */
 .product-option {
   display: flex;
   align-items: center;