feat(project): wire quick business status toggle in store list

This commit is contained in:
2026-02-18 10:04:59 +08:00
parent 882663c983
commit f7854bb925
5 changed files with 129 additions and 10 deletions

View File

@@ -76,6 +76,14 @@ export interface SaveStoreDto {
serviceTypes?: ServiceType[];
}
/** 快速切换门店营业状态参数 */
export interface ToggleStoreBusinessStatusDto {
storeId: string;
businessStatus: StoreBusinessStatus;
closureReason?: number;
closureReasonText?: string;
}
/** 获取门店列表 */
export async function getStoreListApi(params: StoreListQuery) {
return requestClient.get<PaginatedResult<StoreListItemDto>>('/store/list', {
@@ -102,3 +110,10 @@ export async function updateStoreApi(data: SaveStoreDto) {
export async function deleteStoreApi(id: string) {
return requestClient.post('/store/delete', { id });
}
/** 快速切换门店营业状态 */
export async function toggleStoreBusinessStatusApi(
data: ToggleStoreBusinessStatusDto,
) {
return requestClient.post('/store/toggle-business-status', data);
}

View File

@@ -14,6 +14,11 @@ import type { StoreListItemDto } from '#/api/store';
import { Button, Card, Popconfirm, Table, Tag } from 'ant-design-vue';
import {
StoreAuditStatus as StoreAuditStatusEnum,
StoreBusinessStatus as StoreBusinessStatusEnum,
} from '#/api/store';
interface Props {
auditStatusMap: Record<number, StatusTagMeta>;
businessStatusMap: Record<number, StatusTagMeta>;
@@ -30,6 +35,7 @@ const props = defineProps<Props>();
const emit = defineEmits<{
(event: 'delete', record: StoreListItemDto): void;
(event: 'edit', record: StoreListItemDto): void;
(event: 'toggleBusinessStatus', record: StoreListItemDto): void;
(event: 'tableChange', pagination: TablePagination): void;
}>();
@@ -56,6 +62,51 @@ function emitDelete(record: unknown) {
if (!isStoreRecord(record)) return;
emit('delete', record);
}
/** 当前门店是否允许快速切换营业状态。 */
function canQuickToggleBusinessStatus(record: unknown) {
if (!isStoreRecord(record)) return false;
return (
record.auditStatus === StoreAuditStatusEnum.Approved &&
record.businessStatus !== StoreBusinessStatusEnum.ForceClosed
);
}
/** 获取快速切换按钮文案。 */
function getToggleBusinessStatusText(record: unknown) {
if (!isStoreRecord(record)) return '不可切换';
const status = record.businessStatus;
if (status === StoreBusinessStatusEnum.Operating) {
return '设为休息';
}
if (status === StoreBusinessStatusEnum.Resting) {
return '恢复营业';
}
return '不可切换';
}
/** 安全触发营业状态切换事件。 */
function emitToggleBusinessStatus(record: unknown) {
if (!isStoreRecord(record)) return;
emit('toggleBusinessStatus', record);
}
/** 创建时间格式化为 yyyy-MM-dd。 */
function formatCreatedDate(value: string) {
if (!value) return '--';
const isoDateMatch = /^(\d{4}-\d{2}-\d{2})/.exec(value.trim());
if (isoDateMatch) {
return isoDateMatch[1] ?? '--';
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return value.slice(0, 10) || '--';
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
</script>
<template>
@@ -129,11 +180,23 @@ function emitDelete(record: unknown) {
</Tag>
</template>
<template v-if="column.key === 'createdAt'">
<span>{{ formatCreatedDate(record.createdAt) }}</span>
</template>
<template v-if="column.key === 'action'">
<div class="store-action-row">
<Button type="link" size="small" @click="emitEdit(record)">
编辑
</Button>
<Button
type="link"
size="small"
:disabled="!canQuickToggleBusinessStatus(record)"
@click="emitToggleBusinessStatus(record)"
>
{{ getToggleBusinessStatusText(record) }}
</Button>
<Popconfirm
title="确定要删除该门店吗?"
ok-text="确定"

View File

@@ -8,11 +8,17 @@ import type { Ref } from 'vue';
import type { DrawerMode, StoreFormState } from '../../types';
import type { StoreListItemDto } from '#/api/store';
import type { StoreBusinessStatus, StoreListItemDto } from '#/api/store';
import { message } from 'ant-design-vue';
import { createStoreApi, deleteStoreApi, updateStoreApi } from '#/api/store';
import {
createStoreApi,
deleteStoreApi,
StoreBusinessStatus as StoreBusinessStatusEnum,
toggleStoreBusinessStatusApi,
updateStoreApi,
} from '#/api/store';
import { applyRecordToForm, resetStoreForm, toSavePayload } from './helpers';
@@ -81,9 +87,40 @@ export function createDrawerActions(options: CreateDrawerActionsOptions) {
}
}
/** 快速切换门店营业状态。 */
async function handleToggleBusinessStatus(record: StoreListItemDto) {
try {
const nextStatus: StoreBusinessStatus =
record.businessStatus === StoreBusinessStatusEnum.Operating
? StoreBusinessStatusEnum.Resting
: StoreBusinessStatusEnum.Operating;
await toggleStoreBusinessStatusApi({
storeId: record.id,
businessStatus: nextStatus,
...(nextStatus === StoreBusinessStatusEnum.Resting
? {
closureReason: 99,
closureReasonText: '门店手动切换为休息中',
}
: {}),
});
message.success(
nextStatus === StoreBusinessStatusEnum.Operating
? '已恢复营业'
: '已切换为休息中',
);
await Promise.all([options.loadList(), options.loadStats()]);
} catch (error) {
console.error(error);
}
}
return {
handleDelete,
handleSubmit,
handleToggleBusinessStatus,
openDrawer,
};
}

View File

@@ -108,14 +108,15 @@ export function useStoreListPage() {
});
// 6. 组装抽屉与删除动作。
const { handleDelete, handleSubmit, openDrawer } = createDrawerActions({
drawerMode,
formState,
isDrawerVisible,
isSubmitting,
loadList,
loadStats,
});
const { handleDelete, handleSubmit, handleToggleBusinessStatus, openDrawer } =
createDrawerActions({
drawerMode,
formState,
isDrawerVisible,
isSubmitting,
loadList,
loadStats,
});
// 7. 筛选字段更新方法。
function setKeyword(value: string) {
@@ -185,6 +186,7 @@ export function useStoreListPage() {
formState,
getAvatarColor,
handleDeleteStore: handleDelete,
handleToggleStoreBusinessStatus: handleToggleBusinessStatus,
handleReset,
handleSearch,
handleSubmitStore: handleSubmit,

View File

@@ -25,6 +25,7 @@ const {
formState,
getAvatarColor,
handleDeleteStore,
handleToggleStoreBusinessStatus,
handleReset,
handleSearch,
handleSubmitStore,
@@ -96,6 +97,7 @@ function handleExport() {
@table-change="handleTableChange"
@edit="openEditDrawer"
@delete="handleDeleteStore"
@toggle-business-status="handleToggleStoreBusinessStatus"
/>
<StoreEditorDrawer