feat: 新增门店列表页面并修复全局类型错误
1. 新增门店列表页(筛选/统计/表格/抽屉编辑),使用 mockjs 提供接口数据 2. 新增门店相关枚举、API 定义、路由配置 3. 修复 auth.ts loginApi 参数类型不匹配 4. 修复 merchant-center stores 属性路径错误及 merchant prop 类型不兼容 5. 修复 merchant-setting showSubmitButton 不存在于 VbenFormProps
This commit is contained in:
4
apps/web-antd/src/mock/index.ts
Normal file
4
apps/web-antd/src/mock/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// Mock 数据入口,仅在开发环境下使用
|
||||
import './store';
|
||||
|
||||
console.warn('[Mock] Mock 数据已启用');
|
||||
253
apps/web-antd/src/mock/store.ts
Normal file
253
apps/web-antd/src/mock/store.ts
Normal file
@@ -0,0 +1,253 @@
|
||||
import Mock from 'mockjs';
|
||||
|
||||
const Random = Mock.Random;
|
||||
|
||||
/** mockjs 请求回调参数 */
|
||||
interface MockRequestOptions {
|
||||
url: string;
|
||||
type: string;
|
||||
body: null | string;
|
||||
}
|
||||
|
||||
/** 门店筛选参数 */
|
||||
interface StoreFilterParams {
|
||||
keyword?: string;
|
||||
businessStatus?: string;
|
||||
auditStatus?: string;
|
||||
serviceType?: string;
|
||||
page?: string;
|
||||
pageSize?: string;
|
||||
}
|
||||
|
||||
// 预定义门店数据,保证每次请求返回一致的数据
|
||||
const storePool = generateStores(23);
|
||||
|
||||
function generateStores(count: number) {
|
||||
const districts = [
|
||||
'朝阳区建国路88号',
|
||||
'海淀区中关村大街66号',
|
||||
'朝阳区望京西路50号',
|
||||
'通州区新华大街120号',
|
||||
'丰台区丰台路18号',
|
||||
'西城区西单北大街100号',
|
||||
'东城区王府井大街200号',
|
||||
'大兴区黄村镇兴华路30号',
|
||||
'昌平区回龙观东大街15号',
|
||||
'顺义区府前街8号',
|
||||
'石景山区石景山路22号',
|
||||
'房山区良乡拱辰大街55号',
|
||||
'密云区鼓楼东大街10号',
|
||||
'怀柔区青春路6号',
|
||||
'平谷区府前街12号',
|
||||
'门头沟区新桥大街3号',
|
||||
'延庆区妫水北街9号',
|
||||
'亦庄经济开发区荣华南路1号',
|
||||
'望京SOHO T1-2层',
|
||||
'三里屯太古里南区B1',
|
||||
'国贸商城3层',
|
||||
'五道口华联商厦1层',
|
||||
'中关村食宝街B1层',
|
||||
];
|
||||
|
||||
const managerNames = [
|
||||
'张伟',
|
||||
'李娜',
|
||||
'王磊',
|
||||
'赵敏',
|
||||
'刘洋',
|
||||
'陈静',
|
||||
'杨帆',
|
||||
'周杰',
|
||||
'吴芳',
|
||||
'孙涛',
|
||||
'马丽',
|
||||
'朱军',
|
||||
'胡明',
|
||||
'郭强',
|
||||
'何欢',
|
||||
'林峰',
|
||||
'徐婷',
|
||||
'高远',
|
||||
'罗斌',
|
||||
'梁宇',
|
||||
'宋佳',
|
||||
'唐亮',
|
||||
'韩雪',
|
||||
];
|
||||
|
||||
const storeNames = [
|
||||
'老三家外卖(朝阳店)',
|
||||
'老三家外卖(海淀店)',
|
||||
'老三家外卖(望京店)',
|
||||
'老三家外卖(通州店)',
|
||||
'老三家外卖(丰台店)',
|
||||
'老三家外卖(西单店)',
|
||||
'老三家外卖(王府井店)',
|
||||
'老三家外卖(大兴店)',
|
||||
'老三家外卖(回龙观店)',
|
||||
'老三家外卖(顺义店)',
|
||||
'老三家外卖(石景山店)',
|
||||
'老三家外卖(良乡店)',
|
||||
'老三家外卖(密云店)',
|
||||
'老三家外卖(怀柔店)',
|
||||
'老三家外卖(平谷店)',
|
||||
'老三家外卖(门头沟店)',
|
||||
'老三家外卖(延庆店)',
|
||||
'老三家外卖(亦庄店)',
|
||||
'老三家外卖(望京SOHO店)',
|
||||
'老三家外卖(三里屯店)',
|
||||
'老三家外卖(国贸店)',
|
||||
'老三家外卖(五道口店)',
|
||||
'老三家外卖(中关村店)',
|
||||
];
|
||||
|
||||
const avatarColors = [
|
||||
'#3b82f6',
|
||||
'#f59e0b',
|
||||
'#8b5cf6',
|
||||
'#ef4444',
|
||||
'#22c55e',
|
||||
'#06b6d4',
|
||||
'#ec4899',
|
||||
'#f97316',
|
||||
'#14b8a6',
|
||||
'#6366f1',
|
||||
];
|
||||
|
||||
const stores = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
// 1. 按索引分配营业状态,模拟真实分布
|
||||
let businessStatus = 1;
|
||||
if (i >= 21) {
|
||||
businessStatus = Random.pick([1, 2, 3]);
|
||||
} else if (i >= 18) {
|
||||
businessStatus = 3;
|
||||
} else if (i >= 14) {
|
||||
businessStatus = 2;
|
||||
}
|
||||
|
||||
// 2. 按索引分配审核状态
|
||||
let auditStatus = 2;
|
||||
if (i < 20) {
|
||||
auditStatus = 1;
|
||||
} else if (i < 22) {
|
||||
auditStatus = 0;
|
||||
}
|
||||
|
||||
// 3. 循环分配服务方式组合
|
||||
const serviceTypeCombos = [[1], [1, 2], [1, 2, 3], [1, 3], [2, 3]];
|
||||
|
||||
stores.push({
|
||||
id: Random.guid(),
|
||||
name: storeNames[i] || `老三家外卖(分店${i + 1})`,
|
||||
code: `ST2025${String(i + 1).padStart(4, '0')}`,
|
||||
contactPhone: `138****${String(8001 + i).slice(-4)}`,
|
||||
managerName: managerNames[i] || Random.cname(),
|
||||
address: `北京市${districts[i] || `朝阳区某路${i + 1}号`}`,
|
||||
coverImage: '',
|
||||
businessStatus,
|
||||
auditStatus,
|
||||
serviceTypes: serviceTypeCombos[i % serviceTypeCombos.length],
|
||||
createdAt: Random.datetime('yyyy-MM-dd'),
|
||||
_avatarColor: avatarColors[i % avatarColors.length],
|
||||
});
|
||||
}
|
||||
return stores;
|
||||
}
|
||||
|
||||
function filterStores(params: StoreFilterParams) {
|
||||
let list = [...storePool];
|
||||
|
||||
// 1. 关键词模糊匹配(名称/编码/电话)
|
||||
if (params.keyword) {
|
||||
const kw = params.keyword.toLowerCase();
|
||||
list = list.filter(
|
||||
(s) =>
|
||||
s.name.toLowerCase().includes(kw) ||
|
||||
s.code.toLowerCase().includes(kw) ||
|
||||
s.contactPhone.includes(kw),
|
||||
);
|
||||
}
|
||||
|
||||
// 2. 营业状态筛选
|
||||
if (params.businessStatus) {
|
||||
const status = Number(params.businessStatus);
|
||||
list = list.filter((s) => s.businessStatus === status);
|
||||
}
|
||||
|
||||
// 3. 审核状态筛选
|
||||
if (params.auditStatus !== undefined && params.auditStatus !== '') {
|
||||
const status = Number(params.auditStatus);
|
||||
list = list.filter((s) => s.auditStatus === status);
|
||||
}
|
||||
|
||||
// 4. 服务方式筛选
|
||||
if (params.serviceType) {
|
||||
const type = Number(params.serviceType);
|
||||
list = list.filter((s) => (s.serviceTypes ?? []).includes(type));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/** 从 URL 中解析查询参数 */
|
||||
function parseUrlParams(url: string): StoreFilterParams {
|
||||
const parsed = new URL(url, 'http://localhost');
|
||||
const params: Record<string, string> = {};
|
||||
parsed.searchParams.forEach((value, key) => {
|
||||
params[key] = value;
|
||||
});
|
||||
return params;
|
||||
}
|
||||
|
||||
// 门店列表
|
||||
Mock.mock(/\/store\/list/, 'get', (options: MockRequestOptions) => {
|
||||
const params = parseUrlParams(options.url);
|
||||
|
||||
const page = Number(params.page) || 1;
|
||||
const pageSize = Number(params.pageSize) || 10;
|
||||
const filtered = filterStores(params);
|
||||
const start = (page - 1) * pageSize;
|
||||
const items = filtered.slice(start, start + pageSize);
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
items,
|
||||
total: filtered.length,
|
||||
page,
|
||||
pageSize,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// 门店统计
|
||||
Mock.mock(/\/store\/stats/, 'get', () => {
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
total: storePool.length,
|
||||
operating: storePool.filter((s) => s.businessStatus === 1).length,
|
||||
resting: storePool.filter((s) => s.businessStatus === 2).length,
|
||||
pendingAudit: storePool.filter((s) => s.auditStatus === 0).length,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// 创建门店
|
||||
Mock.mock(/\/store\/create/, 'post', () => {
|
||||
return { code: 200, data: null };
|
||||
});
|
||||
|
||||
// 更新门店
|
||||
Mock.mock(/\/store\/update/, 'post', () => {
|
||||
return { code: 200, data: null };
|
||||
});
|
||||
|
||||
// 删除门店
|
||||
Mock.mock(/\/store\/delete/, 'post', () => {
|
||||
return { code: 200, data: null };
|
||||
});
|
||||
|
||||
// 设置 mock 响应延迟
|
||||
Mock.setup({ timeout: '200-400' });
|
||||
Reference in New Issue
Block a user