StoreList.vue 8.18 KB
<template>
  <div>
    <a-card title="店铺管理" :bordered="false" class="list-table-card">
      <div class="list-toolbar">
        <div class="list-toolbar-left">
          <a-select v-model:value="filterCityId" placeholder="选择租户" allowClear class="list-filter" @change="loadList">
            <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
          </a-select>
          <a-input-search v-model:value="keyword" placeholder="搜索店铺名" @search="loadList" class="list-search" />
        </div>
        <div class="list-toolbar-right">
          <a-button type="primary" @click="openAdd">新增店铺</a-button>
        </div>
      </div>
      <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'operatingState'">
            <a-tag :color="record.operatingState === 1 ? 'green' : 'default'">
              {{ record.operatingState === 1 ? '营业中' : '打烊' }}
            </a-tag>
          </template>
          <template v-if="column.key === 'cityId'">
            {{ getCityName(record.cityId) }}
          </template>
          <template v-if="column.key === 'shippingType'">
            {{ record.shippingType === 1 ? '外卖配送' : '到店自提' }}
          </template>
          <template v-if="column.key === 'action'">
            <a-space>
              <a @click="openEdit(record)">编辑</a>
              <a @click="openFeeConfig(record)">费用配置</a>
              <a-popconfirm
                :title="record.operatingState === 1 ? '确认打烊?' : '确认开始营业?'"
                @confirm="toggleState(record)">
                <a>{{ record.operatingState === 1 ? '打烊' : '营业' }}</a>
              </a-popconfirm>
              <a-popconfirm title="确认删除?" @confirm="handleDel(record.id)">
                <a style="color:red">删除</a>
              </a-popconfirm>
            </a-space>
          </template>
        </template>
      </a-table>
    </a-card>

    <a-modal v-model:open="modalVisible" :title="editingId ? '编辑店铺' : '新增店铺'" @ok="handleSave" :confirmLoading="saving" width="600px">
      <div class="soft-page-stack">
        <div class="soft-note-card">
          <strong>店铺资料说明</strong>
          <p>当前页面只维护外卖相关店铺信息。新增时可选填商家账号手机号,系统会同步创建登录账号。</p>
        </div>
        <a-form :model="form" layout="vertical">
          <a-form-item label="店铺名称"><a-input v-model:value="form.name" /></a-form-item>
          <a-form-item label="所属租户">
            <a-select v-model:value="form.cityId" placeholder="选择租户">
              <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
            </a-select>
          </a-form-item>
          <a-form-item label="外部门店编号(选填,接入方对账用)">
            <a-input v-model:value="form.outStoreId" placeholder="接入方自己系统的门店ID" />
          </a-form-item>
          <a-form-item label="店铺地址"><a-input v-model:value="form.address" /></a-form-item>
          <a-row :gutter="16">
            <a-col :span="12">
              <a-form-item label="经度"><a-input v-model:value="form.lng" /></a-form-item>
            </a-col>
            <a-col :span="12">
              <a-form-item label="纬度"><a-input v-model:value="form.lat" /></a-form-item>
            </a-col>
          </a-row>
          <a-form-item label="配送类型">
            <a-radio-group v-model:value="form.shippingType">
              <a-radio :value="1">外卖配送</a-radio>
              <a-radio :value="2">到店自提</a-radio>
            </a-radio-group>
          </a-form-item>
          <a-form-item label="自动接单">
            <a-switch v-model:checked="form.automaticOrder" :checked-value="1" :un-checked-value="0" />
          </a-form-item>
          <a-form-item label="商家账号手机号(新增时创建登录账号)" v-if="!editingId">
            <a-input v-model:value="form.accountMobile" placeholder="选填" />
          </a-form-item>
          <a-form-item label="店铺简介"><a-textarea v-model:value="form.about" :rows="3" /></a-form-item>
        </a-form>
      </div>
    </a-modal>

    <a-modal v-model:open="feeVisible" title="费用配置" @ok="handleFeeSave" :confirmLoading="saving">
      <div class="soft-page-stack">
        <div class="soft-note-card">
          <strong>店铺费用配置</strong>
          <p>这里只配置店铺侧起送和免运费门槛,不替代租户计价方案;最终配送费仍按租户默认计价方案计算。</p>
        </div>
        <a-form layout="vertical">
          <a-form-item label="免运费门槛(元,0=不免运费)">
            <a-input-number v-model:value="feeForm.freeShipping" :min="0" style="width:100%" />
          </a-form-item>
          <a-form-item label="起送金额(元,0=不限)">
            <a-input-number v-model:value="feeForm.upToSend" :min="0" style="width:100%" />
          </a-form-item>
        </a-form>
      </div>
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { merchantApi } from '@/api'
import { useRoleCityList } from '@/composables/useRoleCityList'

const loading = ref(false)
const saving = ref(false)
const list = ref<any[]>([])
const keyword = ref('')
const filterCityId = ref<number | undefined>()
const modalVisible = ref(false)
const feeVisible = ref(false)
const editingId = ref<number | null>(null)
const currentStoreId = ref(0)
const form = reactive<any>({ name: '', cityId: undefined, address: '', lng: '', lat: '', shippingType: 1, automaticOrder: 0, accountMobile: '', about: '', outStoreId: '' })
const feeForm = reactive({ freeShipping: 0, upToSend: 0 })
const { cityList, loadCities, getCityName } = useRoleCityList()

const columns = [
  { title: 'ID', dataIndex: 'id', width: 80 },
  { title: '店铺名', dataIndex: 'name' },
  { title: '租户', key: 'cityId' },
  { title: '外部编号', dataIndex: 'outStoreId', ellipsis: true },
  { title: '接入方', dataIndex: 'appKey', ellipsis: true },
  { title: '地址', dataIndex: 'address', ellipsis: true },
  { title: '配送', key: 'shippingType' },
  { title: '状态', key: 'operatingState' },
  { title: '操作', key: 'action' },
]

async function loadList() {
  loading.value = true
  try {
    const res: any = await merchantApi.storeList({ cityId: filterCityId.value, keyword: keyword.value, page: 1 })
    list.value = res.data
  } finally { loading.value = false }
}

function openAdd() {
  editingId.value = null
  Object.assign(form, { name: '', cityId: undefined, address: '', lng: '', lat: '', shippingType: 1, automaticOrder: 0, accountMobile: '', about: '', outStoreId: '' })
  modalVisible.value = true
}

function openEdit(record: any) {
  editingId.value = record.id
  Object.assign(form, record)
  modalVisible.value = true
}

async function handleSave() {
  saving.value = true
  try {
    if (editingId.value) {
      await merchantApi.editStore({ ...form, id: editingId.value })
    } else {
      await merchantApi.addStore(form)
    }
    message.success('保存成功')
    modalVisible.value = false
    loadList()
  } finally { saving.value = false }
}

async function toggleState(record: any) {
  await merchantApi.setOperatingState(record.id, record.operatingState === 1 ? 0 : 1)
  message.success('操作成功')
  loadList()
}

async function handleDel(id: number) {
  await merchantApi.delStore(id)
  message.success('删除成功')
  loadList()
}

function openFeeConfig(record: any) {
  currentStoreId.value = record.id
  feeForm.freeShipping = record.freeShipping || 0
  feeForm.upToSend = record.upToSend || 0
  feeVisible.value = true
}

async function handleFeeSave() {
  saving.value = true
  try {
    await merchantApi.updateFeeConfig(currentStoreId.value, feeForm.freeShipping, feeForm.upToSend)
    message.success('保存成功')
    feeVisible.value = false
    loadList()
  } finally { saving.value = false }
}

onMounted(() => { loadList(); loadCities() })
</script>