feat(project): wire quick business status toggle in store list
This commit is contained in:
@@ -76,6 +76,14 @@ export interface SaveStoreDto {
|
|||||||
serviceTypes?: ServiceType[];
|
serviceTypes?: ServiceType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 快速切换门店营业状态参数 */
|
||||||
|
export interface ToggleStoreBusinessStatusDto {
|
||||||
|
storeId: string;
|
||||||
|
businessStatus: StoreBusinessStatus;
|
||||||
|
closureReason?: number;
|
||||||
|
closureReasonText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取门店列表 */
|
/** 获取门店列表 */
|
||||||
export async function getStoreListApi(params: StoreListQuery) {
|
export async function getStoreListApi(params: StoreListQuery) {
|
||||||
return requestClient.get<PaginatedResult<StoreListItemDto>>('/store/list', {
|
return requestClient.get<PaginatedResult<StoreListItemDto>>('/store/list', {
|
||||||
@@ -102,3 +110,10 @@ export async function updateStoreApi(data: SaveStoreDto) {
|
|||||||
export async function deleteStoreApi(id: string) {
|
export async function deleteStoreApi(id: string) {
|
||||||
return requestClient.post('/store/delete', { id });
|
return requestClient.post('/store/delete', { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 快速切换门店营业状态 */
|
||||||
|
export async function toggleStoreBusinessStatusApi(
|
||||||
|
data: ToggleStoreBusinessStatusDto,
|
||||||
|
) {
|
||||||
|
return requestClient.post('/store/toggle-business-status', data);
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ import type { StoreListItemDto } from '#/api/store';
|
|||||||
|
|
||||||
import { Button, Card, Popconfirm, Table, Tag } from 'ant-design-vue';
|
import { Button, Card, Popconfirm, Table, Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
StoreAuditStatus as StoreAuditStatusEnum,
|
||||||
|
StoreBusinessStatus as StoreBusinessStatusEnum,
|
||||||
|
} from '#/api/store';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
auditStatusMap: Record<number, StatusTagMeta>;
|
auditStatusMap: Record<number, StatusTagMeta>;
|
||||||
businessStatusMap: Record<number, StatusTagMeta>;
|
businessStatusMap: Record<number, StatusTagMeta>;
|
||||||
@@ -30,6 +35,7 @@ const props = defineProps<Props>();
|
|||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: 'delete', record: StoreListItemDto): void;
|
(event: 'delete', record: StoreListItemDto): void;
|
||||||
(event: 'edit', record: StoreListItemDto): void;
|
(event: 'edit', record: StoreListItemDto): void;
|
||||||
|
(event: 'toggleBusinessStatus', record: StoreListItemDto): void;
|
||||||
(event: 'tableChange', pagination: TablePagination): void;
|
(event: 'tableChange', pagination: TablePagination): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@@ -56,6 +62,51 @@ function emitDelete(record: unknown) {
|
|||||||
if (!isStoreRecord(record)) return;
|
if (!isStoreRecord(record)) return;
|
||||||
emit('delete', record);
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -129,11 +180,23 @@ function emitDelete(record: unknown) {
|
|||||||
</Tag>
|
</Tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="column.key === 'createdAt'">
|
||||||
|
<span>{{ formatCreatedDate(record.createdAt) }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
<div class="store-action-row">
|
<div class="store-action-row">
|
||||||
<Button type="link" size="small" @click="emitEdit(record)">
|
<Button type="link" size="small" @click="emitEdit(record)">
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
:disabled="!canQuickToggleBusinessStatus(record)"
|
||||||
|
@click="emitToggleBusinessStatus(record)"
|
||||||
|
>
|
||||||
|
{{ getToggleBusinessStatusText(record) }}
|
||||||
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定要删除该门店吗?"
|
title="确定要删除该门店吗?"
|
||||||
ok-text="确定"
|
ok-text="确定"
|
||||||
|
|||||||
@@ -8,11 +8,17 @@ import type { Ref } from 'vue';
|
|||||||
|
|
||||||
import type { DrawerMode, StoreFormState } from '../../types';
|
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 { 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';
|
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 {
|
return {
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
handleToggleBusinessStatus,
|
||||||
openDrawer,
|
openDrawer,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,14 +108,15 @@ export function useStoreListPage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 6. 组装抽屉与删除动作。
|
// 6. 组装抽屉与删除动作。
|
||||||
const { handleDelete, handleSubmit, openDrawer } = createDrawerActions({
|
const { handleDelete, handleSubmit, handleToggleBusinessStatus, openDrawer } =
|
||||||
drawerMode,
|
createDrawerActions({
|
||||||
formState,
|
drawerMode,
|
||||||
isDrawerVisible,
|
formState,
|
||||||
isSubmitting,
|
isDrawerVisible,
|
||||||
loadList,
|
isSubmitting,
|
||||||
loadStats,
|
loadList,
|
||||||
});
|
loadStats,
|
||||||
|
});
|
||||||
|
|
||||||
// 7. 筛选字段更新方法。
|
// 7. 筛选字段更新方法。
|
||||||
function setKeyword(value: string) {
|
function setKeyword(value: string) {
|
||||||
@@ -185,6 +186,7 @@ export function useStoreListPage() {
|
|||||||
formState,
|
formState,
|
||||||
getAvatarColor,
|
getAvatarColor,
|
||||||
handleDeleteStore: handleDelete,
|
handleDeleteStore: handleDelete,
|
||||||
|
handleToggleStoreBusinessStatus: handleToggleBusinessStatus,
|
||||||
handleReset,
|
handleReset,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleSubmitStore: handleSubmit,
|
handleSubmitStore: handleSubmit,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const {
|
|||||||
formState,
|
formState,
|
||||||
getAvatarColor,
|
getAvatarColor,
|
||||||
handleDeleteStore,
|
handleDeleteStore,
|
||||||
|
handleToggleStoreBusinessStatus,
|
||||||
handleReset,
|
handleReset,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleSubmitStore,
|
handleSubmitStore,
|
||||||
@@ -96,6 +97,7 @@ function handleExport() {
|
|||||||
@table-change="handleTableChange"
|
@table-change="handleTableChange"
|
||||||
@edit="openEditDrawer"
|
@edit="openEditDrawer"
|
||||||
@delete="handleDeleteStore"
|
@delete="handleDeleteStore"
|
||||||
|
@toggle-business-status="handleToggleStoreBusinessStatus"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<StoreEditorDrawer
|
<StoreEditorDrawer
|
||||||
|
|||||||
Reference in New Issue
Block a user