hikvision-web 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.html +12 -0
- package/package.json +27 -0
- package/src/App.css +48 -0
- package/src/App.css.new +48 -0
- package/src/App.tsx +157 -0
- package/src/App.tsx.new +132 -0
- package/src/index.css +39 -0
- package/src/main.tsx +14 -0
- package/src/pages/BindingPage.tsx +351 -0
- package/src/pages/CardForm.tsx +173 -0
- package/src/pages/CardList.tsx +193 -0
- package/src/pages/DashboardPage.tsx +300 -0
- package/src/pages/GroupPage.tsx +336 -0
- package/src/pages/OrgPage.tsx +198 -0
- package/src/pages/OrgTreePage.tsx +203 -0
- package/src/pages/PersonDetail.tsx +146 -0
- package/src/pages/PersonForm.tsx +199 -0
- package/src/pages/PersonList.tsx +174 -0
- package/src/pages/PersonTreePage.tsx +563 -0
- package/src/pages/SyncPage.tsx +29 -0
- package/src/pages/SystemPage.tsx +758 -0
- package/src/pages/VehicleForm.tsx +231 -0
- package/src/pages/VehicleList.tsx +199 -0
- package/src/services/api.ts +128 -0
- package/src/store/appStore.ts +159 -0
- package/src/utils/constants.ts +105 -0
- package/tsconfig.json +21 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +16 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { personApi, cardApi, vehicleApi, orgApi } from '../services/api';
|
|
3
|
+
|
|
4
|
+
interface Person {
|
|
5
|
+
personId: string;
|
|
6
|
+
personName: string;
|
|
7
|
+
gender?: number;
|
|
8
|
+
orgPath?: string;
|
|
9
|
+
orgIndexCode?: string;
|
|
10
|
+
orgName?: string;
|
|
11
|
+
certificateType?: number;
|
|
12
|
+
certificateNo?: string;
|
|
13
|
+
phoneNo?: string;
|
|
14
|
+
jobNo?: string;
|
|
15
|
+
email?: string;
|
|
16
|
+
birthday?: string;
|
|
17
|
+
address?: string;
|
|
18
|
+
personPhoto?: { picUri: string };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Organization {
|
|
22
|
+
orgIndexCode: string;
|
|
23
|
+
orgName: string;
|
|
24
|
+
parentOrgIndexCode?: string;
|
|
25
|
+
orgPath?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface Card {
|
|
29
|
+
cardNo: string;
|
|
30
|
+
personId?: string;
|
|
31
|
+
personName?: string;
|
|
32
|
+
cardType?: number;
|
|
33
|
+
status?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface Vehicle {
|
|
37
|
+
vehicleId: string;
|
|
38
|
+
plateNo: string;
|
|
39
|
+
vehicleType?: string;
|
|
40
|
+
vehicleColor?: string;
|
|
41
|
+
vehicleBrand?: string;
|
|
42
|
+
vehicleModel?: string;
|
|
43
|
+
personId?: string;
|
|
44
|
+
personName?: string;
|
|
45
|
+
groupId?: string;
|
|
46
|
+
groupName?: string;
|
|
47
|
+
vehicleGroup?: string;
|
|
48
|
+
vehicleGroupName?: string;
|
|
49
|
+
categoryCode?: string;
|
|
50
|
+
categoryName?: string;
|
|
51
|
+
status?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface AppState {
|
|
55
|
+
organizations: Organization[];
|
|
56
|
+
isOrgLoading: boolean;
|
|
57
|
+
orgError: string | null;
|
|
58
|
+
persons: Person[];
|
|
59
|
+
personsTotal: number;
|
|
60
|
+
isPersonLoading: boolean;
|
|
61
|
+
personError: string | null;
|
|
62
|
+
cards: Card[];
|
|
63
|
+
cardsTotal: number;
|
|
64
|
+
isCardLoading: boolean;
|
|
65
|
+
cardError: string | null;
|
|
66
|
+
vehicles: Vehicle[];
|
|
67
|
+
vehiclesTotal: number;
|
|
68
|
+
isVehicleLoading: boolean;
|
|
69
|
+
vehicleError: string | null;
|
|
70
|
+
loadOrganizations: () => Promise<void>;
|
|
71
|
+
loadPersons: (params?: { pageNo?: number; pageSize?: number; personName?: string }) => Promise<void>;
|
|
72
|
+
loadCards: (params?: { pageNo?: number; pageSize?: number }) => Promise<void>;
|
|
73
|
+
loadVehicles: (params?: { pageNo?: number; pageSize?: number; plateNo?: string; personName?: string }) => Promise<void>;
|
|
74
|
+
refreshPerson: (personId: string) => Promise<void>;
|
|
75
|
+
refreshCard: (cardNo: string) => Promise<void>;
|
|
76
|
+
refreshVehicle: (vehicleId: string) => Promise<void>;
|
|
77
|
+
clearData: () => void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const useAppStore = create<AppState>((set) => ({
|
|
81
|
+
organizations: [],
|
|
82
|
+
isOrgLoading: false,
|
|
83
|
+
orgError: null,
|
|
84
|
+
persons: [],
|
|
85
|
+
personsTotal: 0,
|
|
86
|
+
isPersonLoading: false,
|
|
87
|
+
personError: null,
|
|
88
|
+
cards: [],
|
|
89
|
+
cardsTotal: 0,
|
|
90
|
+
isCardLoading: false,
|
|
91
|
+
cardError: null,
|
|
92
|
+
vehicles: [],
|
|
93
|
+
vehiclesTotal: 0,
|
|
94
|
+
isVehicleLoading: false,
|
|
95
|
+
vehicleError: null,
|
|
96
|
+
|
|
97
|
+
loadOrganizations: async () => {
|
|
98
|
+
set({ isOrgLoading: true, orgError: null });
|
|
99
|
+
try {
|
|
100
|
+
const res = await orgApi.list();
|
|
101
|
+
set({ organizations: Array.isArray(res.data) ? res.data : res.data?.list || [], isOrgLoading: false });
|
|
102
|
+
} catch (error: any) {
|
|
103
|
+
set({ isOrgLoading: false, orgError: error.message || '加载组织失败' });
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
loadPersons: async (params) => {
|
|
108
|
+
set({ isPersonLoading: true, personError: null });
|
|
109
|
+
try {
|
|
110
|
+
const res = await (params?.personName ? personApi.search({ pageNo: params?.pageNo || 1, pageSize: params?.pageSize || 20, personName: params?.personName }) : personApi.list({ pageNo: params?.pageNo || 1, pageSize: params?.pageSize || 20 }));
|
|
111
|
+
set({ persons: res.data?.list || [], personsTotal: res.data?.total || 0, isPersonLoading: false });
|
|
112
|
+
} catch (error: any) {
|
|
113
|
+
set({ isPersonLoading: false, personError: error.message || '加载人员失败' });
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
loadCards: async (params) => {
|
|
118
|
+
set({ isCardLoading: true, cardError: null });
|
|
119
|
+
try {
|
|
120
|
+
const res = await cardApi.list({ pageNo: params?.pageNo || 1, pageSize: params?.pageSize || 20 });
|
|
121
|
+
set({ cards: res.data?.list || [], cardsTotal: res.data?.total || 0, isCardLoading: false });
|
|
122
|
+
} catch (error: any) {
|
|
123
|
+
set({ isCardLoading: false, cardError: error.message || '加载卡片失败' });
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
loadVehicles: async (params) => {
|
|
128
|
+
set({ isVehicleLoading: true, vehicleError: null });
|
|
129
|
+
try {
|
|
130
|
+
const res = await vehicleApi.list({ pageNo: params?.pageNo || 1, pageSize: params?.pageSize || 20, plateNo: params?.plateNo, personName: params?.personName });
|
|
131
|
+
set({ vehicles: res.data?.list || [], vehiclesTotal: res.data?.total || 0, isVehicleLoading: false });
|
|
132
|
+
} catch (error: any) {
|
|
133
|
+
set({ isVehicleLoading: false, vehicleError: error.message || '加载车辆失败' });
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
refreshPerson: async (personId: string) => {
|
|
138
|
+
try {
|
|
139
|
+
const res = await personApi.getById(personId);
|
|
140
|
+
set((state) => ({ persons: state.persons.map((p) => p.personId === personId ? res.data : p) }));
|
|
141
|
+
} catch (error) { console.error(`刷新人员 ${personId} 失败`, error); }
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
refreshCard: async (cardNo: string) => {
|
|
145
|
+
try {
|
|
146
|
+
const res = await cardApi.getByCardNo(cardNo);
|
|
147
|
+
set((state) => ({ cards: state.cards.map((c) => c.cardNo === cardNo ? res.data : c) }));
|
|
148
|
+
} catch (error) { console.error(`刷新卡片 ${cardNo} 失败`, error); }
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
refreshVehicle: async (vehicleId: string) => {
|
|
152
|
+
try {
|
|
153
|
+
const res = await vehicleApi.getById(vehicleId);
|
|
154
|
+
set((state) => ({ vehicles: state.vehicles.map((v) => v.vehicleId === vehicleId ? res.data : v) }));
|
|
155
|
+
} catch (error) { console.error(`刷新车辆 ${vehicleId} 失败`, error); }
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
clearData: () => set({ organizations: [], persons: [], cards: [], vehicles: [] }),
|
|
159
|
+
}));
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享常量定义
|
|
3
|
+
* 集中管理全项目使用的常量,避免重复定义
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 车辆系统分类 ID(来自海康 API:getVehicleInfoPage 返回的 categoryCode)
|
|
8
|
+
* 注意:这是 API 返回的实际值,不能修改
|
|
9
|
+
*/
|
|
10
|
+
export const SYSTEM_CATEGORY_IDS = {
|
|
11
|
+
FIXED: '10', // 固定车
|
|
12
|
+
TEMPORARY: '11', // 临时车
|
|
13
|
+
SPECIAL: '14', // 特殊车辆
|
|
14
|
+
BLACKLIST: '9', // 黑名单
|
|
15
|
+
RESERVATION: '12', // 预约车
|
|
16
|
+
GROUP: '13', // 群组车
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 系统分类 ID 数组(用于判断是否是系统分类)
|
|
21
|
+
*/
|
|
22
|
+
export const SYSTEM_CATEGORY_ID_ARRAY = ['9', '10', '11', '12', '13', '14'];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 系统分类名称映射
|
|
26
|
+
*/
|
|
27
|
+
export const SYSTEM_CATEGORY_NAME_MAP: Record<string, string> = {
|
|
28
|
+
'9': '黑名单',
|
|
29
|
+
'10': '固定车',
|
|
30
|
+
'11': '临时车',
|
|
31
|
+
'12': '预约车',
|
|
32
|
+
'13': '群组车',
|
|
33
|
+
'14': '特殊车辆',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 获取车辆分类名称
|
|
38
|
+
*/
|
|
39
|
+
export function getCategoryName(categoryId: string | number): string {
|
|
40
|
+
const id = String(categoryId);
|
|
41
|
+
return SYSTEM_CATEGORY_NAME_MAP[id] || '未知分类';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 车辆分类选项(用于下拉选择)
|
|
46
|
+
*/
|
|
47
|
+
export const VEHICLE_CATEGORY_OPTIONS = [
|
|
48
|
+
{ value: SYSTEM_CATEGORY_IDS.FIXED, label: '固定车' },
|
|
49
|
+
{ value: SYSTEM_CATEGORY_IDS.TEMPORARY, label: '临时车' },
|
|
50
|
+
{ value: SYSTEM_CATEGORY_IDS.SPECIAL, label: '特殊车辆' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 性别选项
|
|
55
|
+
*/
|
|
56
|
+
export const GENDER_OPTIONS = [
|
|
57
|
+
{ value: '1', label: '男' },
|
|
58
|
+
{ value: '2', label: '女' },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 人员状态选项
|
|
63
|
+
*/
|
|
64
|
+
export const PERSON_STATUS_OPTIONS = [
|
|
65
|
+
{ value: '0', label: '正常' },
|
|
66
|
+
{ value: '1', label: '禁用' },
|
|
67
|
+
{ value: '2', label: '离职' },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 卡片状态选项
|
|
72
|
+
*/
|
|
73
|
+
export const CARD_STATUS_OPTIONS = [
|
|
74
|
+
{ value: '0', label: '正常' },
|
|
75
|
+
{ value: '1', label: '挂失' },
|
|
76
|
+
{ value: '2', label: '退卡' },
|
|
77
|
+
{ value: '3', label: '损坏' },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* API 服务端口常量
|
|
82
|
+
*/
|
|
83
|
+
export const PORTS = {
|
|
84
|
+
API_SERVER: 3000,
|
|
85
|
+
MANAGER: 3001,
|
|
86
|
+
WEB: 3030,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 默认分页配置
|
|
91
|
+
*/
|
|
92
|
+
export const PAGINATION = {
|
|
93
|
+
DEFAULT_PAGE_SIZE: 20,
|
|
94
|
+
MAX_PAGE_SIZE: 1000,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 卡片默认过期时间
|
|
99
|
+
*/
|
|
100
|
+
export const DEFAULT_CARD_END_DATE = '2037-12-31';
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 根组织代码
|
|
104
|
+
*/
|
|
105
|
+
export const ROOT_ORG_CODE = 'root00000000';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src"],
|
|
20
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
21
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
plugins: [react()],
|
|
6
|
+
server: {
|
|
7
|
+
port: parseInt(process.env.WEB_PORT || '3030'),
|
|
8
|
+
host: '0.0.0.0',
|
|
9
|
+
proxy: {
|
|
10
|
+
'/api': {
|
|
11
|
+
target: 'http://localhost:3000',
|
|
12
|
+
changeOrigin: true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
})
|