skyline 2 сар өмнө
parent
commit
0bd3f29fd1

+ 9 - 0
haha-admin-web/src/utils/http/index.ts

@@ -132,6 +132,15 @@ class PureHttp {
       (error: PureHttpError) => {
         const $error = error;
         $error.isCancelRequest = Axios.isCancel($error);
+        
+        // 处理 401 未登录异常
+        if ($error.response?.status === 401) {
+          const errorMessage = $error.response?.data?.message || "未登录或登录已过期";
+          console.warn("认证失败:", errorMessage);
+          // 清除 token 并跳转到登录页
+          useUserStoreHook().logOut();
+        }
+        
         // 所有的响应异常 区分来源为取消请求/非取消请求
         return Promise.reject($error);
       }

+ 388 - 110
haha-admin-web/src/views/marketing/coupon/utils/hook.tsx

@@ -12,8 +12,28 @@ import {
 import { distributeCoupons, getCouponDistributeRecords } from "@/api/marketing";
 import ShopSelector from "../../components/ShopSelector.vue";
 import ProductSelector from "../../components/ProductSelector.vue";
-import type { PaginationProps } from "@pureadmin/table";
+import type { PaginationProps, TableColumnList } from "@pureadmin/table";
+import { deviceDetection } from "@pureadmin/utils";
 import { onMounted, reactive, ref, toRaw } from "vue";
+import {
+  ElForm,
+  ElInput,
+  ElFormItem,
+  ElSelect,
+  ElOption,
+  ElDatePicker,
+  ElInputNumber,
+  ElTag,
+  ElRadioGroup,
+  ElRadioButton
+} from "element-plus";
+import { 
+  initPagination, 
+  handlePageSizeChange, 
+  handleCurrentPageChange, 
+  resetPagination,
+  updatePaginationData 
+} from "@/utils/paginationHelper";
 
 interface SearchFormProps {
   name: string;
@@ -21,6 +41,22 @@ interface SearchFormProps {
   status: number | undefined;
 }
 
+interface CouponFormData {
+  id?: number;
+  name: string;
+  activityId: string;
+  type: number | undefined;
+  value: number | null;
+  totalCount: number | null;
+  shopIds: number[];
+  productIds: number[];
+  validPeriod: string[];
+  description: string;
+  applyScope: number;
+  deviceScope: number;
+  productScope: number;
+}
+
 export function useCoupon() {
   const form = reactive<SearchFormProps>({
     name: "",
@@ -29,21 +65,16 @@ export function useCoupon() {
   });
   const loading = ref(true);
   const dataList = ref([]);
-  const pagination = reactive<PaginationProps>({
-    total: 0,
-    pageSize: 10,
-    currentPage: 1,
-    background: true
-  });
+  const pagination = reactive<PaginationProps>(initPagination());
 
-  const typeMap = {
-    1: "满减券",
-    2: "折扣券",
-    3: "现金券",
-    4: "兑换券"
+  const typeMap: Record<number, { text: string; type: string }> = {
+    1: { text: "满减券", type: "primary" },
+    2: { text: "折扣券", type: "success" },
+    3: { text: "现金券", type: "warning" },
+    4: { text: "兑换券", type: "info" }
   };
 
-  const statusMap = {
+  const statusMap: Record<number, { text: string; type: string }> = {
     0: { text: "未启用", type: "info" },
     1: { text: "已启用", type: "success" },
     2: { text: "已过期", type: "danger" }
@@ -70,7 +101,10 @@ export function useCoupon() {
       label: "类型",
       prop: "type",
       minWidth: 100,
-      formatter: ({ type }) => typeMap[type] || "未知"
+      cellRenderer: ({ row }) => {
+        const item = typeMap[row.type] || { text: "未知", type: "info" };
+        return <ElTag type={item.type}>{item.text}</ElTag>;
+      }
     },
     {
       label: "优惠值",
@@ -113,7 +147,7 @@ export function useCoupon() {
       minWidth: 100,
       cellRenderer: ({ row }) => {
         const item = statusMap[row.status] || { text: "未知", type: "info" };
-        return <el-tag type={item.type}>{item.text}</el-tag>;
+        return <ElTag type={item.type}>{item.text}</ElTag>;
       }
     },
     {
@@ -132,7 +166,6 @@ export function useCoupon() {
         pageSize: pagination.pageSize
       };
       
-      // 只添加非空的搜索条件
       if (form.name) searchParams.name = form.name;
       if (form.type !== undefined) searchParams.type = form.type;
       if (form.status !== undefined) searchParams.status = form.status;
@@ -140,7 +173,7 @@ export function useCoupon() {
       const { data } = await getCouponList(searchParams);
       if (data) {
         dataList.value = data.list || [];
-        pagination.total = data.total || 0;
+        updatePaginationData(pagination, data);
       }
     } finally {
       loading.value = false;
@@ -150,8 +183,7 @@ export function useCoupon() {
   function resetForm(formEl) {
     if (!formEl) return;
     formEl.resetFields();
-    pagination.currentPage = 1;
-    onSearch();
+    resetPagination(pagination, onSearch);
   }
 
   async function handleDelete(row) {
@@ -175,26 +207,28 @@ export function useCoupon() {
     addDialog({
       title: "定向发放优惠券",
       width: "600px",
+      draggable: true,
+      fullscreen: deviceDetection(),
       contentRenderer: () => (
-        <el-form label-width="120px">
-          <el-form-item label="发放类型">
-            <el-radio-group>
-              <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 label="目标用户">
-            <el-select placeholder="请选择目标用户" class="w-full" multiple>
-              <el-option label="全部用户" value="all" />
-              <el-option label="新用户" value="new" />
-              <el-option label="VIP用户" value="vip" />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="发放数量">
-            <el-input-number placeholder="请输入发放数量" class="w-full" min={1} max={10000} />
-          </el-form-item>
-        </el-form>
+        <ElForm label-width="100px" size="default">
+          <ElFormItem label="发放类型">
+            <ElRadioGroup>
+              <ElRadioButton value="1">主动领取</ElRadioButton>
+              <ElRadioButton value="2">系统发放</ElRadioButton>
+              <ElRadioButton value="3">定向发放</ElRadioButton>
+            </ElRadioGroup>
+          </ElFormItem>
+          <ElFormItem label="目标用户">
+            <ElSelect placeholder="请选择目标用户" class="w-full" multiple>
+              <ElOption label="全部用户" value="all" />
+              <ElOption label="新用户" value="new" />
+              <ElOption label="VIP用户" value="vip" />
+            </ElSelect>
+          </ElFormItem>
+          <ElFormItem label="发放数量">
+            <ElInputNumber placeholder="请输入发放数量" class="w-full" min={1} max={10000} />
+          </ElFormItem>
+        </ElForm>
       ),
       beforeSure: done => {
         message("发放成功", { type: "success" });
@@ -207,104 +241,348 @@ export function useCoupon() {
     addDialog({
       title: "发放记录",
       width: "800px",
+      draggable: true,
+      fullscreen: deviceDetection(),
       contentRenderer: () => (
         <div>
-          <el-table data={[]} style="width: 100%">
-            <el-table-column prop="userName" label="用户名" width="120" />
-            <el-table-column prop="phone" label="手机号" width="120" />
-            <el-table-column prop="distributeTime" label="发放时间" width="160" />
-            <el-table-column prop="status" label="状态" width="100" />
-          </el-table>
+          <ElTable data={[]} style="width: 100%">
+            <ElTable.Column prop="userName" label="用户名" width="120" />
+            <ElTable.Column prop="phone" label="手机号" width="120" />
+            <ElTable.Column prop="distributeTime" label="发放时间" width="160" />
+            <ElTable.Column prop="status" label="状态" width="100" />
+          </ElTable>
         </div>
       )
     });
   }
 
+  const formStyles = `
+    .coupon-form {
+      padding: 0;
+      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
+    }
+    
+    .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;
+    }
+    
+    .type-selector {
+      display: flex;
+      gap: 12px;
+      margin-bottom: 18px;
+    }
+    
+    .type-btn {
+      flex: 1;
+      padding: 14px 16px;
+      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;
+    }
+    
+    .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;
+    }
+    
+    .coupon-form .el-form-item {
+      margin-bottom: 14px;
+    }
+    
+    .coupon-form .el-form-item__label {
+      font-size: 14px;
+      color: #6b7280;
+    }
+    
+    .coupon-form .el-input__wrapper,
+    .coupon-form .el-textarea__inner {
+      font-size: 14px;
+    }
+    
+    .preview-box {
+      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;
+    }
+    
+    .preview-box .value {
+      font-weight: 600;
+    }
+    
+    @media (max-width: 768px) {
+      .form-grid-2,
+      .scope-grid {
+        grid-template-columns: 1fr;
+      }
+      .type-selector {
+        flex-direction: column;
+      }
+    }
+  `;
+
+  function renderForm(formData: CouponFormData) {
+    return (
+      <div class="coupon-form">
+        <style>{formStyles}</style>
+        
+        <div class="form-block">
+          <div class="block-title">基本信息</div>
+          <ElForm label-width="80px" size="default">
+            <ElFormItem label="优惠券名称" required>
+              <ElInput v-model={formData.name} placeholder="请输入优惠券名称" clearable maxlength={50} showWordLimit />
+            </ElFormItem>
+            <ElFormItem label="关联活动">
+              <ElSelect v-model={formData.activityId} placeholder="请选择关联的营销活动(可选)" class="w-full" clearable>
+                <ElOption label="春节大促活动" value="1" />
+                <ElOption label="新品上市活动" value="2" />
+                <ElOption label="会员专享活动" value="3" />
+                <ElOption label="不关联活动" value="" />
+              </ElSelect>
+            </ElFormItem>
+          </ElForm>
+        </div>
+
+        <div class="form-block">
+          <div class="block-title">优惠券类型</div>
+          <div class="type-selector">
+            {[1, 2, 3, 4].map(type => (
+              <div 
+                class={`type-btn ${formData.type === type ? 'active' : ''}`}
+                onClick={() => { formData.type = type; }}
+              >
+                <div class="name">{typeMap[type].text}</div>
+                <div class="hint">
+                  {type === 1 && "满额减免"}
+                  {type === 2 && "折扣优惠"}
+                  {type === 3 && "直接抵扣"}
+                  {type === 4 && "免费兑换"}
+                </div>
+              </div>
+            ))}
+          </div>
+          
+          {formData.type && (
+            <ElForm label-width="80px" size="default">
+              <div class="form-grid-2">
+                <ElFormItem label="优惠值" required>
+                  <ElInputNumber v-model={formData.value} min={0} precision={2} class="w-full" placeholder={formData.type === 2 ? "折扣值如8表示8折" : "优惠金额"} prefix={formData.type !== 2 ? "¥" : ""} />
+                </ElFormItem>
+                <ElFormItem label="发放总量" required>
+                  <ElInputNumber v-model={formData.totalCount} min={1} class="w-full" placeholder="发放总数量" />
+                </ElFormItem>
+              </div>
+            </ElForm>
+          )}
+        </div>
+
+        <div class="form-block">
+          <div class="block-title">有效期</div>
+          <ElForm label-width="80px" size="default">
+            <ElFormItem label="有效期" required>
+              <ElDatePicker
+                v-model={formData.validPeriod}
+                type="daterange"
+                range-separator="至"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                class="w-full"
+              />
+            </ElFormItem>
+          </ElForm>
+        </div>
+
+        <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-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 && <span style="color: #9ca3af; font-size: 13px;">选择设备</span>}
+              </div>
+            </div>
+            <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-block">
+          <div class="block-title">使用说明</div>
+          <ElForm size="default">
+            <ElFormItem label="">
+              <ElInput v-model={formData.description} type="textarea" rows={2} placeholder="请输入使用说明(可选)" maxlength={500} showWordLimit />
+            </ElFormItem>
+          </ElForm>
+        </div>
+      </div>
+    );
+  }
+
+  function validateForm(formData: CouponFormData): boolean {
+    if (!formData.name) {
+      message("请输入优惠券名称", { type: "warning" });
+      return false;
+    }
+    if (!formData.type) {
+      message("请选择优惠券类型", { type: "warning" });
+      return false;
+    }
+    if (!formData.value || formData.value <= 0) {
+      message("请输入有效的优惠值", { type: "warning" });
+      return false;
+    }
+    if (!formData.totalCount || formData.totalCount <= 0) {
+      message("请输入发放总量", { type: "warning" });
+      return false;
+    }
+    return true;
+  }
+
   function handleAdd() {
-    const formData = reactive({
+    const formData = reactive<CouponFormData>({
       name: "",
       activityId: "",
       type: undefined,
-      value: undefined,
-      totalCount: undefined,
+      value: null,
+      totalCount: null,
       shopIds: [],
       productIds: [],
       validPeriod: [],
-      description: ""
+      description: "",
+      applyScope: 1,
+      deviceScope: 1,
+      productScope: 1
     });
 
     addDialog({
       title: "新增优惠券",
-      width: "700px",
-      contentRenderer: () => (
-        <el-form label-width="120px">
-          <el-form-item label="优惠券名称">
-            <el-input v-model={formData.name} placeholder="请输入优惠券名称" />
-          </el-form-item>
-          <el-form-item label="关联活动">
-            <el-select v-model={formData.activityId} placeholder="请选择关联的营销活动(可选)" class="w-full" clearable>
-              <el-option label="春节大促活动" value="1" />
-              <el-option label="新品上市活动" value="2" />
-              <el-option label="会员专享活动" value="3" />
-              <el-option label="不关联活动" value="" />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="类型">
-            <el-select v-model={formData.type} placeholder="请选择类型" class="w-full">
-              <el-option label="满减券" value={1} />
-              <el-option label="折扣券" value={2} />
-              <el-option label="现金券" value={3} />
-              <el-option label="兑换券" value={4} />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="优惠值">
-            <el-input-number v-model={formData.value} placeholder="请输入优惠值" class="w-full" min={0} />
-          </el-form-item>
-          <el-form-item label="发放总量">
-            <el-input-number v-model={formData.totalCount} placeholder="请输入发放总量" class="w-full" min={1} />
-          </el-form-item>
-          <el-form-item label="适用门店">
-            <ShopSelector v-model={formData.shopIds} multiple placeholder="请选择适用门店(可选)" />
-          </el-form-item>
-          <el-form-item label="适用商品">
-            <ProductSelector v-model={formData.productIds} multiple placeholder="请选择适用商品(可选)" />
-          </el-form-item>
-          <el-form-item label="有效期">
-            <el-date-picker
-              v-model={formData.validPeriod}
-              type="daterange"
-              range-separator="至"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
-              class="w-full"
-            />
-          </el-form-item>
-          <el-form-item label="使用说明">
-            <el-input
-              v-model={formData.description}
-              type="textarea"
-              rows={3}
-              placeholder="请输入使用说明"
-            />
-          </el-form-item>
-        </el-form>
-      ),
-      beforeSure: done => {
-        message("新增成功", { type: "success" });
-        done();
-        onSearch();
+      width: "1100px",
+      draggable: true,
+      fullscreen: deviceDetection(),
+      contentRenderer: () => renderForm(formData),
+      beforeSure: async (done) => {
+        if (!validateForm(formData)) return;
+        try {
+          const { code } = await createCoupon(toRaw(formData));
+          if (code === 200) {
+            message("新增成功", { type: "success" });
+            done();
+            onSearch();
+          }
+        } catch (error) {
+          message("新增失败", { type: "error" });
+        }
       }
     });
   }
 
   function handleSizeChange(val: number) {
-    pagination.pageSize = val;
-    onSearch();
+    handlePageSizeChange(val, pagination, onSearch);
   }
 
   function handleCurrentChange(val: number) {
-    pagination.currentPage = val;
-    onSearch();
+    handleCurrentPageChange(val, pagination, onSearch);
   }
 
   onMounted(() => {

+ 37 - 20
haha-admin-web/src/views/shop/utils/hook.tsx

@@ -241,10 +241,15 @@ export function useShop(tableRef: Ref) {
   });
 
   // 省市区联动选择器
-  const selectedProvince = ref("");
-  const selectedCity = ref("");
+  const selectedProvince = ref("440000"); // 广东省
+  const selectedCity = ref("440300"); // 深圳市
   const selectedDistrict = ref("");
 
+  // 完整的省市区信息(用于只读框显示)
+  const fullRegionText = computed(() => {
+    return `${shopForm.province || ''}${shopForm.city || ''}${shopForm.district || ''}`;
+  });
+
   // 省份列表
   const provinceOptions = computed(() => regionData);
 
@@ -302,31 +307,28 @@ export function useShop(tableRef: Ref) {
     updateAddressPrefix();
   }
 
+  // 初始化省市区默认值
+  function initDefaultRegion() {
+    selectedProvince.value = "440000"; // 广东省
+    selectedCity.value = "440300"; // 深圳市
+    shopForm.province = CodeToText["440000"] || "广东省";
+    shopForm.city = CodeToText["440300"] || "深圳市";
+    shopForm.district = "";
+    updateAddressPrefix();
+  }
+
   // 打开新增/编辑弹窗
   function openDialog(title = "新增", row?: ShopItem) {
-    Object.assign(shopForm, {
-      name: row?.name ?? "",
-      province: row?.province ?? "",
-      city: row?.city ?? "",
-      district: row?.district ?? "",
-      address: row?.address ?? "",
-      latitude: row?.latitude ?? "",
-      longitude: row?.longitude ?? "",
-      contactPhone: row?.contactPhone ?? "",
-      contactName: row?.contactName ?? "",
-      businessHours: row?.businessHours ?? "",
-      status: row?.status ?? 1
-    });
-
-    
     // 初始化省市区选择器
     if (row?.province) {
       selectedProvince.value = TextToCode[row.province]?.code || "";
     } else {
-      selectedProvince.value = "";
+      selectedProvince.value = "440000"; // 默认广东省
     }
     if (row?.city && selectedProvince.value) {
       selectedCity.value = TextToCode[row.province]?.[row.city]?.code || "";
+    } else if (!row?.province) {
+      selectedCity.value = "440300"; // 默认深圳市
     } else {
       selectedCity.value = "";
     }
@@ -335,6 +337,21 @@ export function useShop(tableRef: Ref) {
     } else {
       selectedDistrict.value = "";
     }
+    
+    // 设置表单数据,新增时使用默认省市区
+    Object.assign(shopForm, {
+      name: row?.name ?? "",
+      province: row?.province ?? CodeToText["440000"] ?? "广东省",
+      city: row?.city ?? CodeToText["440300"] ?? "深圳市",
+      district: row?.district ?? "",
+      address: row?.address ?? "",
+      latitude: row?.latitude ?? "",
+      longitude: row?.longitude ?? "",
+      contactPhone: row?.contactPhone ?? "",
+      contactName: row?.contactName ?? "",
+      businessHours: row?.businessHours ?? "",
+      status: row?.status ?? 1
+    });
 
     // 从地址中提取详细部分(去除省市区前缀)
     if (row?.address) {
@@ -423,7 +440,7 @@ export function useShop(tableRef: Ref) {
             >
               <div class="flex items-center gap-2 w-full">
                 <ElInput 
-                  value={shopForm.province + shopForm.city + shopForm.district} 
+                  value={fullRegionText.value} 
                   placeholder="省市区" 
                   readonly 
                   class="w-[180px]!" 
@@ -431,7 +448,7 @@ export function useShop(tableRef: Ref) {
                 />
                 <ElInput 
                   v-model={addressDetail.value} 
-                  placeholder="请输入详细地址(如:xx路xx号)" 
+                  placeholder="请输入详细地址(如:xx  xx 号)" 
                   clearable 
                   class="flex-1"
                   onInput={() => updateAddressPrefix()}

+ 256 - 123
haha-admin-web/src/views/timed-discount/utils/hook.tsx

@@ -292,7 +292,7 @@ export function useTimedDiscount() {
 
     addDialog({
       title: "编辑活动",
-      width: "700px",
+      width: "1100px",
       draggable: true,
       fullscreen: deviceDetection(),
       contentRenderer: () => renderForm(formData),
@@ -337,7 +337,7 @@ export function useTimedDiscount() {
 
     addDialog({
       title: "新增活动",
-      width: "700px",
+      width: "1100px",
       draggable: true,
       fullscreen: deviceDetection(),
       contentRenderer: () => renderForm(formData),
@@ -357,130 +357,263 @@ export function useTimedDiscount() {
     });
   }
 
+  const formStyles = `
+    .form-container {
+      padding: 8px 0;
+    }
+    .form-block {
+      margin-bottom: 24px;
+      padding-bottom: 20px;
+      border-bottom: 1px solid #f0f0f0;
+    }
+    .form-block:last-child {
+      border-bottom: none;
+      margin-bottom: 0;
+      padding-bottom: 0;
+    }
+    .form-block-title {
+      font-size: 15px;
+      font-weight: 600;
+      color: #1f2937;
+      margin-bottom: 16px;
+      padding-left: 10px;
+      border-left: 3px solid #3b82f6;
+    }
+    .form-row {
+      display: flex;
+      gap: 20px;
+      margin-bottom: 16px;
+    }
+    .form-row .el-form-item {
+      flex: 1;
+      margin-bottom: 0;
+    }
+    .scope-grid {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 20px;
+    }
+    .scope-card {
+      background: #f9fafb;
+      border-radius: 6px;
+      padding: 14px;
+    }
+    .scope-card-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 10px;
+    }
+    .scope-card-title {
+      font-size: 14px;
+      font-weight: 500;
+      color: #374151;
+    }
+    .scope-card-body {
+      min-height: 36px;
+    }
+    .inline-switch {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+    }
+    .inline-switch-label {
+      font-size: 14px;
+      color: #374151;
+    }
+    @media (max-width: 900px) {
+      .scope-grid {
+        grid-template-columns: 1fr;
+      }
+      .form-row {
+        flex-direction: column;
+        gap: 16px;
+      }
+    }
+  `;
+
   function renderForm(formData: FormDataProps) {
     return (
-      <ElForm label-width="120px">
-        <ElFormItem label="活动名称" required>
-          <ElInput v-model={formData.activityName} placeholder="请输入活动名称" clearable />
-        </ElFormItem>
-        <ElFormItem label="活动说明">
-          <ElInput v-model={formData.activityDesc} type="textarea" rows={2} placeholder="请输入活动说明" />
-        </ElFormItem>
-        <ElFormItem label="开始时间" required>
-          <ElTimePicker
-            v-model={formData.startTime}
-            placeholder="选择开始时间"
-            format="HH:mm:ss"
-            value-format="HH:mm:ss"
-            class="w-full"
-          />
-        </ElFormItem>
-        <ElFormItem label="结束时间">
-          <ElTimePicker
-            v-model={formData.endTime}
-            placeholder="选择结束时间(空表示次日营业)"
-            format="HH:mm:ss"
-            value-format="HH:mm:ss"
-            class="w-full"
-            clearable
-          />
-        </ElFormItem>
-        <ElFormItem label="折扣类型" required>
-          <ElSelect v-model={formData.discountType} placeholder="请选择折扣类型" class="w-full">
-            <ElOption label="固定折扣" value={1} />
-            <ElOption label="阶梯折扣" value={2} />
-            <ElOption label="固定价格" value={3} />
-          </ElSelect>
-        </ElFormItem>
-        <ElFormItem label="折扣值" required>
-          <ElInputNumber
-            v-model={formData.discountValue}
-            min={0}
-            max={formData.discountType === 1 ? 1 : 9999}
-            step={formData.discountType === 1 ? 0.1 : 1}
-            precision={formData.discountType === 1 ? 2 : 0}
-            class="w-full"
-            placeholder={formData.discountType === 1 ? "折扣比例(如0.5表示5折)" : "固定价格"}
-          />
-        </ElFormItem>
-        <ElFormItem label="最低折扣价">
-          <ElInputNumber
-            v-model={formData.minDiscountPrice}
-            min={0}
-            precision={2}
-            class="w-full"
-            placeholder="最低折扣价格(防止价格过低)"
-          />
-        </ElFormItem>
-        <ElFormItem label="适用范围">
-          <ElSelect v-model={formData.applyScope} placeholder="请选择适用范围" class="w-full">
-            <ElOption label="全部门店" value={1} />
-            <ElOption label="指定门店" value={2} />
-          </ElSelect>
-        </ElFormItem>
-        {formData.applyScope === 2 && (
-          <ElFormItem label="选择门店">
-            <ShopSelector v-model={formData.shopIds} />
-          </ElFormItem>
-        )}
-        <ElFormItem label="设备范围">
-          <ElSelect v-model={formData.deviceScope} placeholder="请选择设备范围" class="w-full">
-            <ElOption label="全部设备" value={1} />
-            <ElOption label="指定设备" value={2} />
-          </ElSelect>
-        </ElFormItem>
-        {formData.deviceScope === 2 && (
-          <ElFormItem label="选择设备">
-            <DeviceSelector v-model={formData.deviceIds} />
-          </ElFormItem>
-        )}
-        <ElFormItem label="商品范围">
-          <ElSelect v-model={formData.productScope} placeholder="请选择商品范围" class="w-full">
-            <ElOption label="全部商品" value={1} />
-            <ElOption label="指定分类" value={2} />
-            <ElOption label="指定商品" value={3} />
-          </ElSelect>
-        </ElFormItem>
-        {formData.productScope === 2 && (
-          <ElFormItem label="适用商品分类">
-            <ElSelect
-              v-model={formData.productTypes}
-              multiple
-              filterable
-              allow-create
-              placeholder="请选择商品分类"
-              class="w-full"
-            >
-              {productTypeOptions.map(item => (
-                <ElOption key={item.value} label={item.label} value={item.value} />
-              ))}
-            </ElSelect>
+      <div class="form-container">
+        <style>{formStyles}</style>
+        
+        <div class="form-block">
+          <div class="form-block-title">基本信息</div>
+          <div class="form-row">
+            <ElFormItem label="活动名称" required>
+              <ElInput v-model={formData.activityName} placeholder="请输入活动名称" clearable />
+            </ElFormItem>
+            <ElFormItem label="自动执行">
+              <div class="inline-switch">
+                <ElSwitch v-model={formData.autoExecute} active-value={1} inactive-value={0} />
+                <span class="inline-switch-label">{formData.autoExecute === 1 ? "开启" : "关闭"}</span>
+              </div>
+            </ElFormItem>
+          </div>
+          <ElFormItem label="活动说明">
+            <ElInput v-model={formData.activityDesc} type="textarea" rows={2} placeholder="请输入活动说明" />
           </ElFormItem>
-        )}
-        <ElFormItem label="优先商品分类">
-          <ElSelect
-            v-model={formData.priorityProductTypes}
-            multiple
-            filterable
-            allow-create
-            placeholder="请选择优先商品分类(自制餐品等)"
-            class="w-full"
-          >
-            {productTypeOptions.map(item => (
-              <ElOption key={item.value} label={item.label} value={item.value} />
-            ))}
-          </ElSelect>
-        </ElFormItem>
-        <ElFormItem label="最低库存阈值">
-          <ElInputNumber v-model={formData.minStock} min={1} class="w-full" placeholder="低于此数量不参与优惠" />
-        </ElFormItem>
-        <ElFormItem label="最高库存阈值">
-          <ElInputNumber v-model={formData.maxStock} min={1} class="w-full" placeholder="高于此数量不参与优惠(空不限制)" clearable />
-        </ElFormItem>
-        <ElFormItem label="自动执行">
-          <ElSwitch v-model={formData.autoExecute} active-value={1} inactive-value={0} />
-        </ElFormItem>
-      </ElForm>
+        </div>
+
+        <div class="form-block">
+          <div class="form-block-title">时间设置</div>
+          <div class="form-row">
+            <ElFormItem label="开始时间" required>
+              <ElTimePicker
+                v-model={formData.startTime}
+                placeholder="选择开始时间"
+                format="HH:mm:ss"
+                value-format="HH:mm:ss"
+                class="w-full"
+              />
+            </ElFormItem>
+            <ElFormItem label="结束时间">
+              <ElTimePicker
+                v-model={formData.endTime}
+                placeholder="选择结束时间(空表示次日营业)"
+                format="HH:mm:ss"
+                value-format="HH:mm:ss"
+                class="w-full"
+                clearable
+              />
+            </ElFormItem>
+          </div>
+        </div>
+
+        <div class="form-block">
+          <div class="form-block-title">折扣设置</div>
+          <div class="form-row">
+            <ElFormItem label="折扣类型" required>
+              <ElSelect v-model={formData.discountType} placeholder="请选择折扣类型" class="w-full">
+                <ElOption label="固定折扣" value={1} />
+                <ElOption label="阶梯折扣" value={2} />
+                <ElOption label="固定价格" value={3} />
+              </ElSelect>
+            </ElFormItem>
+            <ElFormItem label="折扣值" required>
+              <ElInputNumber
+                v-model={formData.discountValue}
+                min={0}
+                max={formData.discountType === 1 ? 1 : 9999}
+                step={formData.discountType === 1 ? 0.1 : 1}
+                precision={formData.discountType === 1 ? 2 : 0}
+                class="w-full"
+                placeholder={formData.discountType === 1 ? "折扣比例(如0.5表示5折)" : "固定价格"}
+              />
+            </ElFormItem>
+          </div>
+          <div class="form-row">
+            <ElFormItem label="最低折扣价">
+              <ElInputNumber
+                v-model={formData.minDiscountPrice}
+                min={0}
+                precision={2}
+                class="w-full"
+                placeholder="最低折扣价格(防止价格过低)"
+              />
+            </ElFormItem>
+            <div></div>
+          </div>
+        </div>
+
+        <div class="form-block">
+          <div class="form-block-title">适用范围</div>
+          <div class="scope-grid">
+            <div class="scope-card">
+              <div class="scope-card-header">
+                <span class="scope-card-title">门店范围</span>
+              </div>
+              <div class="scope-card-body">
+                <ElSelect v-model={formData.applyScope} placeholder="请选择适用范围" class="w-full">
+                  <ElOption label="全部门店" value={1} />
+                  <ElOption label="指定门店" value={2} />
+                </ElSelect>
+                {formData.applyScope === 2 && (
+                  <div style="margin-top: 10px;">
+                    <ShopSelector v-model={formData.shopIds} />
+                  </div>
+                )}
+              </div>
+            </div>
+            <div class="scope-card">
+              <div class="scope-card-header">
+                <span class="scope-card-title">设备范围</span>
+              </div>
+              <div class="scope-card-body">
+                <ElSelect v-model={formData.deviceScope} placeholder="请选择设备范围" class="w-full">
+                  <ElOption label="全部设备" value={1} />
+                  <ElOption label="指定设备" value={2} />
+                </ElSelect>
+                {formData.deviceScope === 2 && (
+                  <div style="margin-top: 10px;">
+                    <DeviceSelector v-model={formData.deviceIds} />
+                  </div>
+                )}
+              </div>
+            </div>
+            <div class="scope-card">
+              <div class="scope-card-header">
+                <span class="scope-card-title">商品范围</span>
+              </div>
+              <div class="scope-card-body">
+                <ElSelect v-model={formData.productScope} placeholder="请选择商品范围" class="w-full">
+                  <ElOption label="全部商品" value={1} />
+                  <ElOption label="指定分类" value={2} />
+                  <ElOption label="指定商品" value={3} />
+                </ElSelect>
+                {formData.productScope === 2 && (
+                  <div style="margin-top: 10px;">
+                    <ElSelect
+                      v-model={formData.productTypes}
+                      multiple
+                      filterable
+                      allow-create
+                      placeholder="请选择商品分类"
+                      class="w-full"
+                    >
+                      {productTypeOptions.map(item => (
+                        <ElOption key={item.value} label={item.label} value={item.value} />
+                      ))}
+                    </ElSelect>
+                  </div>
+                )}
+                {formData.productScope === 3 && (
+                  <div style="margin-top: 10px;">
+                    <ProductSelector v-model={formData.productIds} />
+                  </div>
+                )}
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="form-block">
+          <div class="form-block-title">库存与优先级</div>
+          <div class="form-row">
+            <ElFormItem label="优先商品分类">
+              <ElSelect
+                v-model={formData.priorityProductTypes}
+                multiple
+                filterable
+                allow-create
+                placeholder="请选择优先商品分类"
+                class="w-full"
+              >
+                {productTypeOptions.map(item => (
+                  <ElOption key={item.value} label={item.label} value={item.value} />
+                ))}
+              </ElSelect>
+            </ElFormItem>
+          </div>
+          <div class="form-row">
+            <ElFormItem label="最低库存阈值">
+              <ElInputNumber v-model={formData.minStock} min={1} class="w-full" placeholder="低于此数量不参与优惠" />
+            </ElFormItem>
+            <ElFormItem label="最高库存阈值">
+              <ElInputNumber v-model={formData.maxStock} min={1} class="w-full" placeholder="高于此数量不参与优惠(空不限制)" clearable />
+            </ElFormItem>
+          </div>
+        </div>
+      </div>
     );
   }