Files
rui-docs/superpowers/plans/2026-06-08-store-new-fields.md
T

17 KiB
Raw Blame History

门店管理新增字段 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers-subagent-driven-development (recommended) or superpowers-executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在 admin-ui 门店管理模块中适配后端新增的 9 个字段,修改表单弹窗和列表页,所有变更在现有 useApiForm + ApiFormDialog + RuiTable 框架内完成。

Architecture: 修改 2 个现有文件。StoreFormDialog.vue 新增 7 个可编辑字段(useApiForm fields 数组)、2 个只读字段(custom-fields 插槽)、数据双向转换逻辑(amenities JSON 序列化、serviceFeeRate 百分比转换)。Index.vue 新增 3 列(门店类型 Tag、包间信息、设施标签)、1 个筛选条件(门店类型下拉)、parseAmenities 工具函数。

Tech Stack: Vue 3 (Composition API / <script setup>) + TypeScript + Element Plus + Vite + pnpm


File Structure

# 文件 操作 变更说明
1 admin-ui/src/views/cashier/store/StoreFormDialog.vue 修改 扩展 useApiForminitialfields+7 字段);修改 onSubmit 添加 amenities/serviceFeeRate 数据转换;修改 watch 添加编辑回填数据转换;模板添加 width="720px"#custom-fields 插槽
2 admin-ui/src/views/cashier/store/Index.vue 修改 queryParams 增加 storeTypehandleReset 补充 storeType 重置;新增 parseAmenities 工具函数;columns 增加 3 列;模板增加门店类型筛选和 3 个列 slot

Task 1: StoreFormDialog — 扩展 initial 默认值

Files:

  • Modify: admin-ui/src/views/cashier/store/StoreFormDialog.vue (lines 3240)

  • Step 1: 在 useApiForminitial 对象中追加新字段默认值

Old string:

  initial: {
    storeName: '',
    storeCode: '',
    address: '',
    contactPhone: '',
    contactName: '',
    businessHours: '',
    status: 1,
  },

New string:

  initial: {
    storeName: '',
    storeCode: '',
    address: '',
    contactPhone: '',
    contactName: '',
    businessHours: '',
    status: 1,
    storeType: 'STANDARD',
    amenities: [],
    longitude: undefined,
    latitude: undefined,
    serviceFeeRate: undefined,
    openingDate: '',
    legalPerson: '',
  },
  • Step 2: Commit
git add admin-ui/src/views/cashier/store/StoreFormDialog.vue
git commit -m "feat(store): 扩展 useApiForm initial 默认值,新增 7 个字段默认值"

Task 2: StoreFormDialog — 在 fields 数组中追加 7 个新字段

Files:

  • Modify: admin-ui/src/views/cashier/store/StoreFormDialog.vue (lines 7686, status 字段之后、fields 数组结束之前)

  • Step 1: 在 status 字段配置之后、], 之前,插入 7 个新字段配置

Old string:

    {
      key: 'status',
      label: '状态',
      type: 'radio',
      required: true,
      options: [
        { label: '启用', value: 1 },
        { label: '禁用', value: 0 },
      ],
    },
  ],
  onSubmit: async (data) => {

New string:

    {
      key: 'status',
      label: '状态',
      type: 'radio',
      required: true,
      options: [
        { label: '启用', value: 1 },
        { label: '禁用', value: 0 },
      ],
    },
    {
      key: 'storeType',
      label: '门店类型',
      type: 'select',
      required: true,
      options: [
        { label: '旗舰店', value: 'FLAGSHIP' },
        { label: '标准店', value: 'STANDARD' },
        { label: '社区店', value: 'COMMUNITY' },
      ],
    },
    {
      key: 'amenities',
      label: '设施标签',
      type: 'checkbox',
      options: [
        { label: '免费停车', value: '免费停车' },
        { label: '免费WiFi', value: '免费WiFi' },
        { label: '充电桩', value: '充电桩' },
        { label: '24小时营业', value: '24小时营业' },
        { label: '包厢', value: '包厢' },
        { label: '吸烟区', value: '吸烟区' },
      ],
    },
    {
      key: 'longitude',
      label: '经度',
      type: 'number',
      props: { min: -180, max: 180, precision: 6, step: 0.000001 },
    },
    {
      key: 'latitude',
      label: '纬度',
      type: 'number',
      props: { min: -90, max: 90, precision: 6, step: 0.000001 },
    },
    {
      key: 'serviceFeeRate',
      label: '平台服务费率(%)',
      type: 'number',
      props: { min: 0, max: 100, precision: 2, step: 0.01 },
    },
    {
      key: 'openingDate',
      label: '开业日期',
      type: 'date',
      props: { valueFormat: 'YYYY-MM-DD' },
    },
    {
      key: 'legalPerson',
      label: '法人姓名',
      type: 'input',
    },
  ],
  onSubmit: async (data) => {
  • Step 2: Commit
git add admin-ui/src/views/cashier/store/StoreFormDialog.vue
git commit -m "feat(store): 在 useApiForm fields 中新增 7 个可编辑字段配置"

Task 3: StoreFormDialog — 修改 onSubmit 添加数据转换

Files:

  • Modify: admin-ui/src/views/cashier/store/StoreFormDialog.vue (lines 86100, onSubmit 回调)

  • Step 1: 替换 onSubmit 回调,添加 amenities JSON 序列化和 serviceFeeRate 百分比转小数

Old string:

  onSubmit: async (data) => {
    const isEdit = !!(data as any).id

    if (isEdit) {
      await storeService.update(data)
      ElMessage.success('修改成功')
    }
    else {
      await storeService.add(data)
      ElMessage.success('新增成功')
    }

    emit('success')
    emit('update:visible', false)
  },

New string:

  onSubmit: async (rawData) => {
    const data = { ...rawData } as any

    // amenities: string[] → JSON 字符串
    if (Array.isArray(data.amenities)) {
      data.amenities = JSON.stringify(data.amenities)
    }

    // serviceFeeRate: 百分比 → 小数
    if (data.serviceFeeRate != null && data.serviceFeeRate !== '') {
      data.serviceFeeRate = Number(data.serviceFeeRate) / 100
    }

    const isEdit = !!data.id

    if (isEdit) {
      await storeService.update(data)
      ElMessage.success('修改成功')
    }
    else {
      await storeService.add(data)
      ElMessage.success('新增成功')
    }

    emit('success')
    emit('update:visible', false)
  },
  • Step 2: Commit
git add admin-ui/src/views/cashier/store/StoreFormDialog.vue
git commit -m "feat(store): onSubmit 中添加 amenities JSON 序列化和 serviceFeeRate 百分比转小数"

Task 4: StoreFormDialog — 修改 watch 添加编辑回填数据转换

Files:

  • Modify: admin-ui/src/views/cashier/store/StoreFormDialog.vue (lines 104116, watch 回调)

  • Step 1: 替换 watch 回调,添加 amenities JSON 解析和 serviceFeeRate 小数转百分比

Old string:

// 监听编辑数据
watch(() => props.visible, (val) => {
  if (val) {
    if (props.row) {
      // 编辑时设置表单数据
      form.value = {
        ...props.row,
      }
    }
    else {
      resetForm()
    }
  }
})

New string:

// 监听编辑数据
watch(() => props.visible, (val) => {
  if (val) {
    if (props.row) {
      const rowData = { ...props.row }

      // amenities: JSON 字符串 → string[]
      if (typeof rowData.amenities === 'string' && rowData.amenities) {
        try {
          rowData.amenities = JSON.parse(rowData.amenities)
        }
        catch {
          rowData.amenities = []
        }
      }
      else if (!Array.isArray(rowData.amenities)) {
        rowData.amenities = []
      }

      // serviceFeeRate: 小数 → 百分比
      if (rowData.serviceFeeRate != null) {
        rowData.serviceFeeRate = Number(rowData.serviceFeeRate) * 100
      }

      // 编辑时设置表单数据
      form.value = rowData
    }
    else {
      resetForm()
    }
  }
})
  • Step 2: Commit
git add admin-ui/src/views/cashier/store/StoreFormDialog.vue
git commit -m "feat(store): watch 中添加 amenities JSON 解析和 serviceFeeRate 小数转百分比回填"

Task 5: StoreFormDialog — 模板添加 width 属性和 custom-fields 插槽

Files:

  • Modify: admin-ui/src/views/cashier/store/StoreFormDialog.vue (lines 120128, template 中的 ApiFormDialog 标签)

  • Step 1: 替换 ApiFormDialog 自闭合标签为开闭标签,添加 width 和 custom-fields 插槽

Old string:

  <ApiFormDialog
    v-model:visible="localVisible"
    v-model:form="form"
    :title="(form as any).id ? '编辑门店' : '新增门店'"
    :fields="fields"
    :rules="rules"
    :loading="loading"
    @submit="handleSubmit"
  />

New string:

  <ApiFormDialog
    v-model:visible="localVisible"
    v-model:form="form"
    :title="(form as any).id ? '编辑门店' : '新增门店'"
    :width="'720px'"
    :fields="fields"
    :rules="rules"
    :loading="loading"
    @submit="handleSubmit"
  >
    <template #custom-fields="{ form: formData }">
      <template v-if="formData.id">
        <el-form-item label="包间总数">
          <span>{{ formData.roomCount ?? '-' }}</span>
        </el-form-item>
        <el-form-item label="空闲包间数">
          <span>{{ formData.freeRoomCount ?? '-' }}</span>
        </el-form-item>
      </template>
    </template>
  </ApiFormDialog>
  • Step 2: Commit
git add admin-ui/src/views/cashier/store/StoreFormDialog.vue
git commit -m "feat(store): ApiFormDialog 增加 width=720px 和 custom-fields 插槽展示只读包间字段"

Task 6: Index.vue — 扩展 queryParams、handleReset、新增 parseAmenities 工具函数

Files:

  • Modify: admin-ui/src/views/cashier/store/Index.vue

  • Step 1: 在 queryParams 中增加 storeType 字段

Old string:

const queryParams = ref({
  storeName: '',
  status: undefined as number | undefined,
})

New string:

const queryParams = ref({
  storeName: '',
  status: undefined as number | undefined,
  storeType: undefined as string | undefined,
})
  • Step 2: 在 handleReset 中补充 storeType 重置

Old string:

function handleReset() {
  queryParams.value = {
    storeName: '',
    status: undefined,
  }
  tableRef.value?.reset()
}

New string:

function handleReset() {
  queryParams.value = {
    storeName: '',
    status: undefined,
    storeType: undefined,
  }
  tableRef.value?.reset()
}
  • Step 3: 在 handleStatusChange 函数之后(</script> 标签之前),添加 parseAmenities 工具函数

Old string:

async function handleStatusChange(row: any, status: number) {
  if (!row?.id) return
  try {
    await storeService.changeStatus(row.id, status)
    ElMessage.success(status === 1 ? '启用成功' : '禁用成功')
  } catch {
    row.status = status === 1 ? 0 : 1
  }
}
</script>

New string:

async function handleStatusChange(row: any, status: number) {
  if (!row?.id) return
  try {
    await storeService.changeStatus(row.id, status)
    ElMessage.success(status === 1 ? '启用成功' : '禁用成功')
  } catch {
    row.status = status === 1 ? 0 : 1
  }
}

/**
 * 解析设施标签(兼容 JSON 字符串和数组)
 */
function parseAmenities(val: any): string[] {
  if (Array.isArray(val)) return val
  if (typeof val === 'string' && val) {
    try { return JSON.parse(val) } catch { return [] }
  }
  return []
}
</script>
  • Step 4: Commit
git add admin-ui/src/views/cashier/store/Index.vue
git commit -m "feat(store): queryParams 增加 storeType 筛选、handleReset 补充重置、新增 parseAmenities 工具函数"

Task 7: Index.vue — 在 columns 数组中追加 3 列配置

Files:

  • Modify: admin-ui/src/views/cashier/store/Index.vue (lines 1228, columns 数组)

  • Step 1: 在 address 列之后追加 storeType、roomInfo、amenities 3 列

Old string:

  { prop: 'address', label: '地址', minWidth: 200, tooltip: true },
  { prop: 'businessHours', label: '营业时间', width: 120 },

New string:

  { prop: 'address', label: '地址', minWidth: 200, tooltip: true },
  { prop: 'storeType', label: '门店类型', width: 100, align: 'center', slot: true },
  { prop: 'roomInfo', label: '包间', width: 120, align: 'center', slot: true },
  { prop: 'amenities', label: '设施标签', minWidth: 200, slot: true },
  { prop: 'businessHours', label: '营业时间', width: 120 },
  • Step 2: Commit
git add admin-ui/src/views/cashier/store/Index.vue
git commit -m "feat(store): columns 增加 storeType/roomInfo/amenities 3 列配置"

Task 8: Index.vue — 模板添加门店类型筛选和 3 个列 slot

Files:

  • Modify: admin-ui/src/views/cashier/store/Index.vue (template 部分)

  • Step 1: 在状态筛选的 </el-form-item> 之后、查询按钮的 <el-form-item> 之前,插入门店类型筛选

Old string:

        <el-form-item label="状态">
          <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
            <el-option label="启用" :value="1" />
            <el-option label="禁用" :value="0" />
          </el-select>
        </el-form-item>
        <el-form-item>

New string:

        <el-form-item label="状态">
          <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
            <el-option label="启用" :value="1" />
            <el-option label="禁用" :value="0" />
          </el-select>
        </el-form-item>
        <el-form-item label="门店类型">
          <el-select v-model="queryParams.storeType" placeholder="请选择门店类型" clearable>
            <el-option label="旗舰店" value="FLAGSHIP" />
            <el-option label="标准店" value="STANDARD" />
            <el-option label="社区店" value="COMMUNITY" />
          </el-select>
        </el-form-item>
        <el-form-item>
  • Step 2: 在状态列 slot#column-status)的 </template> 之后、操作列 slot 之前,插入 3 个列 slot 模板

Old string:

      <!-- 状态列 -->
      <template #column-status="{ row }">
        <el-switch
          v-model="row.status"
          :active-value="1"
          :inactive-value="0"
          @change="(val: any) => handleStatusChange(row, val as number)"
        />
      </template>

      <!-- 操作列 -->

New string:

      <!-- 状态列 -->
      <template #column-status="{ row }">
        <el-switch
          v-model="row.status"
          :active-value="1"
          :inactive-value="0"
          @change="(val: any) => handleStatusChange(row, val as number)"
        />
      </template>

      <!-- 门店类型列 -->
      <template #column-storeType="{ row }">
        <el-tag
          :type="row.storeType === 'FLAGSHIP' ? 'danger' : row.storeType === 'STANDARD' ? '' : 'info'"
          size="small"
        >
          {{ { FLAGSHIP: '旗舰店', STANDARD: '标准店', COMMUNITY: '社区店' }[row.storeType] || '-' }}
        </el-tag>
      </template>

      <!-- 包间信息列 -->
      <template #column-roomInfo="{ row }">
        <span>{{ row.freeRoomCount ?? '-' }}/{{ row.roomCount ?? '-' }}</span>
      </template>

      <!-- 设施标签列 -->
      <template #column-amenities="{ row }">
        <template v-if="parseAmenities(row.amenities).length">
          <el-tag
            v-for="tag in parseAmenities(row.amenities)"
            :key="tag"
            size="small"
            type="info"
            class="mr-1 mb-1"
          >
            {{ tag }}
          </el-tag>
        </template>
        <span v-else>-</span>
      </template>

      <!-- 操作列 -->
  • Step 3: Commit
git add admin-ui/src/views/cashier/store/Index.vue
git commit -m "feat(store): 模板新增门店类型筛选条件和 storeType/roomInfo/amenities 列 slot"

Task 9: 构建验证 + 最终提交

  • Step 1: 运行 TypeScript 类型检查

Run: pnpm --filter admin-ui type-check Expected: 0 errors, 命令退出码 0

  • Step 2: 运行 ESLint 检查

Run: pnpm --filter admin-ui lint Expected: 0 errors, 0 warnings(或仅有与本次修改无关的已存在 warnings)

  • Step 3: 运行 Vite 构建

Run: pnpm --filter admin-ui build Expected: 构建成功,无编译错误,输出 dist 目录

  • Step 4: 启动开发服务器进行手动验证

Run: pnpm --filter admin-ui dev Expected: 开发服务器正常启动,浏览器打开后:

  1. 门店管理列表页正确展示新增 3 列
  2. 门店类型下拉筛选功能正常
  3. 点击新增门店,弹窗宽度 720px,7 个新字段正确渲染
  4. 新增模式下不显示包间总数/空闲包间数
  5. 点击编辑门店,所有新字段正确回填,包间字段只读展示
  6. 提交表单无报错

验证完毕后按 Ctrl+C 停止开发服务器。


Verification Checklist

  • pnpm --filter admin-ui type-check 通过
  • pnpm --filter admin-ui lint 通过
  • pnpm --filter admin-ui build 成功
  • 列表新增 3 列正确展示(门店类型 Tag、包间 X/Y、设施多 Tag)
  • 门店类型筛选功能正常(筛选 + 重置)
  • 新增门店:7 个新字段可正常填写和提交
  • 编辑门店:所有新字段正确回填,只读字段不可编辑
  • amenities 数据双向转换正确(JSON 字符串 ↔ 数组)
  • serviceFeeRate 数据双向转换正确(小数 ↔ 百分比)
  • 新增模式下 roomCount/freeRoomCount 区域不显示
  • 无数据时各列正确降级显示 -