skyline 2 месяцев назад
Родитель
Сommit
805b7c1533

+ 290 - 110
haha-admin-web/src/views/marketing/activity/utils/hook.tsx

@@ -272,49 +272,210 @@ export function useActivity() {
   }
 
   const formStyles = `
-    .activity-form { padding: 0; }
-    .form-section { margin-bottom: 16px; }
-    .section-title { 
-      font-size: 14px; 
-      font-weight: 600; 
-      color: #303133; 
-      margin-bottom: 12px; 
-      padding-bottom: 8px; 
-      border-bottom: 1px solid #ebeef5; 
-    }
-    .type-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 16px; }
-    .type-card { 
-      padding: 16px; 
-      border: 2px solid #e4e7ed; 
-      border-radius: 8px; 
-      cursor: pointer; 
-      transition: all 0.2s; 
-      background: #fff; 
+    .activity-form {
+      padding: 0;
+      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
+    }
+    
+    /* 活动类型选择器 */
+    .type-selector {
+      display: flex;
+      gap: 12px;
+      margin-bottom: 18px;
+    }
+    
+    .type-btn {
+      flex: 1;
+      padding: 16px 18px;
+      border: 1px solid #e5e7eb;
+      border-radius: 6px;
+      background: #fff;
+      cursor: pointer;
+      transition: all 0.15s ease;
+      text-align: center;
+    }
+    
+    .type-btn:hover {
+      border-color: #3b82f6;
+    }
+    
+    .type-btn.active {
+      border-color: #3b82f6;
+      background: #eff6ff;
+    }
+    
+    .type-btn .name {
+      font-size: 15px;
+      font-weight: 600;
+      color: #374151;
+      margin-bottom: 4px;
+    }
+    
+    .type-btn .hint {
+      font-size: 13px;
+      color: #9ca3af;
+    }
+    
+    .type-btn.active .name {
+      color: #3b82f6;
+    }
+    
+    /* 表单区块 */
+    .form-block {
+      margin-bottom: 18px;
+    }
+    
+    .block-title {
+      font-size: 15px;
+      font-weight: 600;
+      color: #374151;
+      margin-bottom: 12px;
+      padding-bottom: 8px;
+      border-bottom: 1px solid #f3f4f6;
+    }
+    
+    /* 表单网格 */
+    .form-grid-2 {
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 16px;
+    }
+    
+    /* 适用范围 - 网格布局 */
+    .scope-grid {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 20px;
+    }
+    
+    .scope-card {
+      background: #f9fafb;
+      border-radius: 6px;
+      padding: 14px;
+    }
+    
+    .scope-card .card-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 10px;
+    }
+    
+    .scope-card .card-label {
+      font-size: 14px;
+      font-weight: 500;
+      color: #374151;
+    }
+    
+    .scope-card .card-body {
+      min-height: 40px;
+    }
+    
+    .scope-card .selected-items {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 6px;
+      margin-top: 10px;
+      max-height: 120px;
+      overflow-y: auto;
+    }
+    
+    .scope-card .selected-tag {
+      display: inline-flex;
+      align-items: center;
+      gap: 4px;
+      padding: 4px 8px;
+      background: #fff;
+      border: 1px solid #e5e7eb;
+      border-radius: 4px;
+      font-size: 12px;
+      color: #374151;
+    }
+    
+    .scope-card .selected-tag .close {
+      cursor: pointer;
+      color: #9ca3af;
+      font-size: 14px;
+    }
+    
+    .scope-card .selected-tag .close:hover {
+      color: #ef4444;
+    }
+    
+    /* 优惠预览 */
+    .discount-preview {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 8px;
+      padding: 12px 16px;
+      background: #f9fafb;
+      border-radius: 4px;
+      margin-top: 12px;
+      font-size: 15px;
+      color: #374151;
+    }
+    
+    .discount-preview .value {
+      font-weight: 600;
+    }
+    
+    /* 提示信息 */
+    .empty-tip {
       text-align: center;
+      padding: 24px;
+      color: #9ca3af;
+      font-size: 14px;
+      background: #f9fafb;
+      border-radius: 4px;
     }
-    .type-card:hover { border-color: var(--card-color); background: var(--card-bg); }
-    .type-card.active { border-color: var(--card-color); background: var(--card-bg); }
-    .type-card .title { font-size: 15px; font-weight: 600; color: #303133; margin-bottom: 4px; }
-    .type-card .desc { font-size: 12px; color: #909399; }
-    .scope-row { display: flex; gap: 24px; flex-wrap: wrap; }
-    .scope-item { display: flex; align-items: center; gap: 8px; }
-    .scope-item .label { font-size: 13px; color: #606266; min-width: 60px; }
-    .discount-preview { 
-      display: flex; 
-      align-items: center; 
-      justify-content: center; 
-      padding: 12px; 
-      background: #f5f7fa; 
-      border-radius: 6px; 
-      font-size: 15px; 
-      font-weight: 600; 
-      margin-top: 12px; 
-    }
-    .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
+    
+    /* 表单项样式 */
+    .activity-form .el-form-item {
+      margin-bottom: 14px;
+    }
+    
+    .activity-form .el-form-item__label {
+      font-size: 14px;
+      color: #6b7280;
+    }
+    
+    .activity-form .el-input__wrapper,
+    .activity-form .el-textarea__inner {
+      font-size: 14px;
+    }
+    
+    .activity-form .el-input-number {
+      font-size: 14px;
+    }
+    
+    /* 时间范围 */
+    .time-range {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+    }
+    
+    .time-range .el-date-editor {
+      flex: 1;
+    }
+    
+    .time-sep {
+      color: #9ca3af;
+      font-size: 14px;
+    }
+    
+    /* 响应式 */
     @media (max-width: 768px) {
-      .type-cards { grid-template-columns: 1fr; }
-      .form-row { grid-template-columns: 1fr; }
-      .scope-row { flex-direction: column; }
+      .type-selector {
+        flex-direction: column;
+      }
+      .form-grid-2 {
+        grid-template-columns: 1fr;
+      }
+      .scope-grid {
+        grid-template-columns: 1fr;
+      }
     }
   `;
 
@@ -323,34 +484,31 @@ export function useActivity() {
       <div class="activity-form">
         <style>{formStyles}</style>
         
-        <div class="form-section">
-          <div class="section-title">选择活动类型</div>
-          <div class="type-cards">
-            {[1, 2, 3].map(type => {
-              const info = typeMap[type];
-              const bgColors = { 1: "#f0f9ff", 2: "#f0f9eb", 3: "#fdf6ec" };
-              return (
-                <div 
-                  class={`type-card ${formData.activityType === type ? 'active' : ''}`}
-                  style={`--card-color: ${info.color}; --card-bg: ${bgColors[type]};`}
-                  onClick={() => { formData.activityType = type; }}
-                >
-                  <div class="title">{info.text}</div>
-                  <div class="desc">
-                    {type === 1 && "新用户首单优惠"}
-                    {type === 2 && "全场折扣优惠"}
-                    {type === 3 && "满额减免优惠"}
-                  </div>
+        {/* 活动类型选择 */}
+        <div class="form-block">
+          <div class="block-title">活动类型</div>
+          <div class="type-selector">
+            {[1, 2, 3].map(type => (
+              <div 
+                class={`type-btn ${formData.activityType === type ? 'active' : ''}`}
+                onClick={() => { formData.activityType = type; }}
+              >
+                <div class="name">{typeMap[type].text}</div>
+                <div class="hint">
+                  {type === 1 && "新用户首单优惠"}
+                  {type === 2 && "全场折扣优惠"}
+                  {type === 3 && "满额减免优惠"}
                 </div>
-              );
-            })}
+              </div>
+            ))}
           </div>
         </div>
 
-        <div class="form-section">
-          <div class="section-title">基本信息</div>
-          <ElForm label-width="80px" size="small">
-            <div class="form-row">
+        {/* 基本信息 */}
+        <div class="form-block">
+          <div class="block-title">基本信息</div>
+          <ElForm label-width="80px" size="default">
+            <div class="form-grid-2">
               <ElFormItem label="活动名称" required>
                 <ElInput v-model={formData.activityName} placeholder="请输入活动名称" clearable maxlength={50} showWordLimit />
               </ElFormItem>
@@ -359,34 +517,39 @@ export function useActivity() {
               </ElFormItem>
             </div>
             <ElFormItem label="活动时间" required>
-              <div class="flex gap-2 w-full">
-                <ElDatePicker v-model={formData.startTime} type="datetime" placeholder="开始时间" class="flex-1" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" />
-                <span class="leading-8 text-gray-400">至</span>
-                <ElDatePicker v-model={formData.endTime} type="datetime" placeholder="结束时间" class="flex-1" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" />
+              <div class="time-range">
+                <ElDatePicker v-model={formData.startTime} type="datetime" placeholder="开始时间" format="YYYY-MM-DD HH:mm" value-format="YYYY-MM-DD HH:mm:ss" />
+                <span class="time-sep">至</span>
+                <ElDatePicker v-model={formData.endTime} type="datetime" placeholder="结束时间" format="YYYY-MM-DD HH:mm" value-format="YYYY-MM-DD HH:mm:ss" />
               </div>
             </ElFormItem>
           </ElForm>
         </div>
 
-        <div class="form-section">
-          <div class="section-title">优惠规则</div>
-          {!formData.activityType && <div style="text-align: center; color: #909399; padding: 20px;">请先选择活动类型</div>}
+        {/* 优惠规则 */}
+        <div class="form-block">
+          <div class="block-title">优惠规则</div>
+          
+          {!formData.activityType && <div class="empty-tip">请先选择活动类型</div>}
           
           {formData.activityType === 1 && (
             <div>
-              <ElForm label-width="80px" size="small">
+              <ElForm label-width="80px" size="default">
                 <ElFormItem label="立减金额" required>
                   <ElInputNumber v-model={formData.discountValue} min={0.01} precision={2} class="w-full" placeholder="首单立减金额" prefix="¥" />
                 </ElFormItem>
               </ElForm>
-              <div class="discount-preview" style="color: #409EFF;">新用户首单立减 ¥{formData.discountValue || '0.00'}</div>
+              <div class="discount-preview">
+                <span>预览:</span>
+                <span class="value">新用户首单立减 ¥{formData.discountValue || '0.00'}</span>
+              </div>
             </div>
           )}
 
           {formData.activityType === 2 && (
             <div>
-              <ElForm label-width="80px" size="small">
-                <div class="form-row">
+              <ElForm label-width="80px" size="default">
+                <div class="form-grid-2">
                   <ElFormItem label="折扣比例" required>
                     <ElInputNumber v-model={formData.discountValue} min={0.1} max={1} step={0.1} precision={1} class="w-full" placeholder="如 0.8 表示 8 折" />
                   </ElFormItem>
@@ -395,17 +558,17 @@ export function useActivity() {
                   </ElFormItem>
                 </div>
               </ElForm>
-              <div class="discount-preview" style="color: #67C23A;">
-                全场 {formData.discountValue ? (formData.discountValue * 10).toFixed(1) : '0'} 折优惠
-                {formData.maxDiscount && ` (最高减¥${formData.maxDiscount})`}
+              <div class="discount-preview">
+                <span>预览:</span>
+                <span class="value">全场 {formData.discountValue ? (formData.discountValue * 10).toFixed(1) : '0'} 折优惠{formData.maxDiscount && ` (最高减¥${formData.maxDiscount})`}</span>
               </div>
             </div>
           )}
 
           {formData.activityType === 3 && (
             <div>
-              <ElForm label-width="80px" size="small">
-                <div class="form-row">
+              <ElForm label-width="80px" size="default">
+                <div class="form-grid-2">
                   <ElFormItem label="满减门槛" required>
                     <ElInputNumber v-model={formData.minAmount} min={0} precision={2} class="w-full" placeholder="订单满多少" prefix="¥" />
                   </ElFormItem>
@@ -414,44 +577,61 @@ export function useActivity() {
                   </ElFormItem>
                 </div>
               </ElForm>
-              <div class="discount-preview" style="color: #E6A23C;">满 ¥{formData.minAmount || '0'} 减 ¥{formData.discountValue || '0'}</div>
+              <div class="discount-preview">
+                <span>预览:</span>
+                <span class="value">满 ¥{formData.minAmount || '0'} 减 ¥{formData.discountValue || '0'}</span>
+              </div>
             </div>
           )}
         </div>
 
-        <div class="form-section">
-          <div class="section-title">适用范围</div>
-          <div class="scope-row">
-            <div class="scope-item">
-              <span class="label">门店范围</span>
-              <ElRadioGroup v-model={formData.applyScope} size="small">
-                <ElRadioButton value={1}>全部</ElRadioButton>
-                <ElRadioButton value={2}>指定</ElRadioButton>
-              </ElRadioGroup>
-              {formData.applyScope === 2 && <ShopSelector v-model={formData.shopIds} />}
+        {/* 适用范围 */}
+        <div class="form-block">
+          <div class="block-title">适用范围</div>
+          <div class="scope-grid">
+            <div class="scope-card">
+              <div class="card-header">
+                <span class="card-label">门店范围</span>
+                <ElRadioGroup v-model={formData.applyScope} size="small">
+                  <ElRadioButton value={1}>全部</ElRadioButton>
+                  <ElRadioButton value={2}>指定</ElRadioButton>
+                </ElRadioGroup>
+              </div>
+              <div class="card-body">
+                {formData.applyScope === 2 && <ShopSelector v-model={formData.shopIds} />}
+              </div>
             </div>
-            <div class="scope-item">
-              <span class="label">设备范围</span>
-              <ElRadioGroup v-model={formData.deviceScope} size="small">
-                <ElRadioButton value={1}>全部</ElRadioButton>
-                <ElRadioButton value={2}>指定</ElRadioButton>
-              </ElRadioGroup>
-              {formData.deviceScope === 2 && <DeviceSelector v-model={formData.deviceIds} />}
+            <div class="scope-card">
+              <div class="card-header">
+                <span class="card-label">设备范围</span>
+                <ElRadioGroup v-model={formData.deviceScope} size="small">
+                  <ElRadioButton value={1}>全部</ElRadioButton>
+                  <ElRadioButton value={2}>指定</ElRadioButton>
+                </ElRadioGroup>
+              </div>
+              <div class="card-body">
+                {formData.deviceScope === 2 && <DeviceSelector v-model={formData.deviceIds} />}
+              </div>
             </div>
-            <div class="scope-item">
-              <span class="label">商品范围</span>
-              <ElRadioGroup v-model={formData.productScope} size="small">
-                <ElRadioButton value={1}>全部</ElRadioButton>
-                <ElRadioButton value={2}>指定</ElRadioButton>
-              </ElRadioGroup>
-              {formData.productScope === 2 && <ProductSelector v-model={formData.productIds} />}
+            <div class="scope-card">
+              <div class="card-header">
+                <span class="card-label">商品范围</span>
+                <ElRadioGroup v-model={formData.productScope} size="small">
+                  <ElRadioButton value={1}>全部</ElRadioButton>
+                  <ElRadioButton value={2}>指定</ElRadioButton>
+                </ElRadioGroup>
+              </div>
+              <div class="card-body">
+                {formData.productScope === 2 && <ProductSelector v-model={formData.productIds} />}
+              </div>
             </div>
           </div>
         </div>
 
-        <div class="form-section">
-          <div class="section-title">活动说明</div>
-          <ElForm size="small">
+        {/* 活动说明 */}
+        <div class="form-block">
+          <div class="block-title">活动说明</div>
+          <ElForm size="default">
             <ElFormItem label="">
               <ElInput v-model={formData.activityDesc} type="textarea" rows={2} placeholder="请输入活动说明(可选)" maxlength={500} showWordLimit />
             </ElFormItem>
@@ -524,7 +704,7 @@ export function useActivity() {
 
     addDialog({
       title: "编辑活动",
-      width: "800px",
+      width: "1100px",
       draggable: true,
       fullscreen: deviceDetection(),
       contentRenderer: () => renderForm(formData),
@@ -566,7 +746,7 @@ export function useActivity() {
 
     addDialog({
       title: "新增活动",
-      width: "800px",
+      width: "1100px",
       draggable: true,
       fullscreen: deviceDetection(),
       contentRenderer: () => renderForm(formData),

+ 1 - 1
haha-admin-web/src/views/marketing/activity/utils/types.ts

@@ -1,7 +1,7 @@
 interface FormItemProps {
   id?: number;
   activityName: string;
-  activityType: number;
+  activityType: number | undefined;
   activityDesc?: string;
   startTime: string;
   endTime: string;