@deppon/create-deppon-app 2.2.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.
Files changed (57) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +63 -0
  3. package/deppon.js +640 -0
  4. package/package.json +51 -0
  5. package/template/.env +12 -0
  6. package/template/.env.dev-local.example +64 -0
  7. package/template/.env.development.example +64 -0
  8. package/template/.env.example +1 -0
  9. package/template/.env.production.example +64 -0
  10. package/template/.env.test.example +64 -0
  11. package/template/.eslintignore +2 -0
  12. package/template/.eslintrc.cjs +14 -0
  13. package/template/.prettierrc.js +3 -0
  14. package/template/.vscode/settings.json +8 -0
  15. package/template/Dockerfile +5 -0
  16. package/template/README.md +149 -0
  17. package/template/commitlint.config.js +11 -0
  18. package/template/gitignore +8 -0
  19. package/template/index.html +18 -0
  20. package/template/nginx.conf +70 -0
  21. package/template/npmrc +2 -0
  22. package/template/package.json +49 -0
  23. package/template/preview-server.js +117 -0
  24. package/template/public/favicon.ico +0 -0
  25. package/template/public/logo.png +0 -0
  26. package/template/src/App.vue +123 -0
  27. package/template/src/api/index.ts +13 -0
  28. package/template/src/api/prefercenter.ts +23 -0
  29. package/template/src/api/product.ts +16 -0
  30. package/template/src/api/user.ts +41 -0
  31. package/template/src/components/ExpandableMessage.vue +340 -0
  32. package/template/src/components/PageLayout.vue +43 -0
  33. package/template/src/config/dictionaryConfig.ts +24 -0
  34. package/template/src/directives/permission.ts +162 -0
  35. package/template/src/layouts/BaseLayout.vue +687 -0
  36. package/template/src/main.ts +27 -0
  37. package/template/src/router/index.ts +179 -0
  38. package/template/src/router/route.ts +61 -0
  39. package/template/src/stores/menu.ts +334 -0
  40. package/template/src/stores/product.ts +155 -0
  41. package/template/src/stores/route.ts +79 -0
  42. package/template/src/stores/user.ts +145 -0
  43. package/template/src/styles/index.ts +29 -0
  44. package/template/src/types/dictionary.d.ts +24 -0
  45. package/template/src/types/vite-env.d.ts +119 -0
  46. package/template/src/utils/dictionary.ts +188 -0
  47. package/template/src/utils/errorAnalyzer.ts +217 -0
  48. package/template/src/utils/messageVNode.ts +15 -0
  49. package/template/src/utils/request.ts +293 -0
  50. package/template/src/views/error/401.vue +30 -0
  51. package/template/src/views/error/403.vue +30 -0
  52. package/template/src/views/error/404.vue +30 -0
  53. package/template/src/views/home/index.vue +25 -0
  54. package/template/tsconfig.json +27 -0
  55. package/template/vite.config.ts +243 -0
  56. package/template/yarnrc +3 -0
  57. package/template/yarnrc.yml +7 -0
@@ -0,0 +1,188 @@
1
+ /**
2
+ * 前端字典配置
3
+ * 用于存储和管理前端字典数据,支持根据 key 动态获取字典
4
+ */
5
+
6
+ import type { DictionaryItem, DictionaryConfig } from '@/types/dictionary';
7
+
8
+ // 重新导出类型,保持向后兼容
9
+ export type { DictionaryItem, DictionaryConfig };
10
+
11
+ /**
12
+ * 从配置文件导入字典配置
13
+ */
14
+ import { dictionaryConfig as configData } from '@/config/dictionaryConfig';
15
+
16
+ /**
17
+ * 前端字典配置数据
18
+ * 配置数据从 @/config/dictionaryConfig 导入
19
+ * 可以通过 setDictionary、setDictionaries 等方法动态修改
20
+ */
21
+ const dictionaryConfig: DictionaryConfig = { ...configData };
22
+
23
+ /**
24
+ * 根据 key 获取字典列表
25
+ * @param key - 字典 key
26
+ * @returns 返回字典项数组,如果不存在则返回空数组
27
+ */
28
+ export const getDictionary = (key: string): DictionaryItem[] => {
29
+ return dictionaryConfig[key] || [];
30
+ };
31
+
32
+ /**
33
+ * 根据 key 获取字典选项(用于下拉框等组件)
34
+ * @param key - 字典 key
35
+ * @param filterDisabled - 是否过滤禁用的项(默认 true)
36
+ * @returns 返回选项数组,格式为 { label, value, ... }
37
+ */
38
+ export const getDictionaryOptions = (
39
+ key: string,
40
+ filterDisabled: boolean = true
41
+ ): Array<{ label: string; value: string; [key: string]: any }> => {
42
+ const dictList = getDictionary(key);
43
+ let result = dictList.map(item => ({
44
+ label: item.label,
45
+ value: item.code,
46
+ ...item,
47
+ }));
48
+
49
+ // 过滤禁用的项
50
+ if (filterDisabled) {
51
+ result = result.filter(item => !item.disabled);
52
+ }
53
+
54
+ // 按 sort 排序
55
+ result.sort((a, b) => {
56
+ const sortA = a.sort ?? 999;
57
+ const sortB = b.sort ?? 999;
58
+ return sortA - sortB;
59
+ });
60
+
61
+ return result;
62
+ };
63
+
64
+ /**
65
+ * 根据 key 和 code 获取字典项
66
+ * @param key - 字典 key
67
+ * @param code - 字典编码
68
+ * @returns 返回字典项,如果不存在则返回 undefined
69
+ */
70
+ export const getDictionaryItem = (key: string, code: string): DictionaryItem | undefined => {
71
+ const dictList = getDictionary(key);
72
+ return dictList.find(item => item.code === code);
73
+ };
74
+
75
+ /**
76
+ * 根据 key 和 code 获取字典标签
77
+ * @param key - 字典 key
78
+ * @param code - 字典编码
79
+ * @param defaultValue - 默认值(如果找不到对应的字典项)
80
+ * @returns 返回字典标签,如果不存在则返回默认值或 code
81
+ */
82
+ export const getDictionaryLabel = (
83
+ key: string,
84
+ code: string,
85
+ defaultValue?: string
86
+ ): string => {
87
+ const item = getDictionaryItem(key, code);
88
+ return item?.label || defaultValue || code;
89
+ };
90
+
91
+ /**
92
+ * 设置字典配置
93
+ * @param key - 字典 key
94
+ * @param dictList - 字典项数组
95
+ */
96
+ export const setDictionary = (key: string, dictList: DictionaryItem[]): void => {
97
+ dictionaryConfig[key] = dictList;
98
+ };
99
+
100
+ /**
101
+ * 批量设置字典配置
102
+ * @param config - 字典配置对象
103
+ */
104
+ export const setDictionaries = (config: DictionaryConfig): void => {
105
+ Object.assign(dictionaryConfig, config);
106
+ };
107
+
108
+ /**
109
+ * 添加字典项
110
+ * @param key - 字典 key
111
+ * @param item - 字典项
112
+ */
113
+ export const addDictionaryItem = (key: string, item: DictionaryItem): void => {
114
+ if (!dictionaryConfig[key]) {
115
+ dictionaryConfig[key] = [];
116
+ }
117
+ // 检查是否已存在相同的 code
118
+ const index = dictionaryConfig[key].findIndex(d => d.code === item.code);
119
+ if (index >= 0) {
120
+ // 如果存在,则更新
121
+ dictionaryConfig[key][index] = item;
122
+ } else {
123
+ // 如果不存在,则添加
124
+ dictionaryConfig[key].push(item);
125
+ }
126
+ };
127
+
128
+ /**
129
+ * 移除字典项
130
+ * @param key - 字典 key
131
+ * @param code - 字典编码
132
+ */
133
+ export const removeDictionaryItem = (key: string, code: string): void => {
134
+ if (!dictionaryConfig[key]) {
135
+ return;
136
+ }
137
+ dictionaryConfig[key] = dictionaryConfig[key].filter(item => item.code !== code);
138
+ };
139
+
140
+ /**
141
+ * 移除整个字典
142
+ * @param key - 字典 key
143
+ */
144
+ export const removeDictionary = (key: string): void => {
145
+ delete dictionaryConfig[key];
146
+ };
147
+
148
+ /**
149
+ * 获取所有字典 key
150
+ * @returns 返回所有字典 key 数组
151
+ */
152
+ export const getAllDictionaryKeys = (): string[] => {
153
+ return Object.keys(dictionaryConfig);
154
+ };
155
+
156
+ /**
157
+ * 检查字典是否存在
158
+ * @param key - 字典 key
159
+ * @returns 返回是否存在
160
+ */
161
+ export const hasDictionary = (key: string): boolean => {
162
+ return key in dictionaryConfig && Array.isArray(dictionaryConfig[key]) && dictionaryConfig[key].length > 0;
163
+ };
164
+
165
+ /**
166
+ * 清空所有字典配置
167
+ */
168
+ export const clearAllDictionaries = (): void => {
169
+ Object.keys(dictionaryConfig).forEach(key => {
170
+ delete dictionaryConfig[key];
171
+ });
172
+ };
173
+
174
+ /**
175
+ * 导出字典配置(用于备份或调试)
176
+ * @returns 返回当前所有字典配置
177
+ */
178
+ export const exportDictionaryConfig = (): DictionaryConfig => {
179
+ return JSON.parse(JSON.stringify(dictionaryConfig));
180
+ };
181
+
182
+ /**
183
+ * 导入字典配置(用于恢复或初始化)
184
+ * @param config - 字典配置对象
185
+ */
186
+ export const importDictionaryConfig = (config: DictionaryConfig): void => {
187
+ Object.assign(dictionaryConfig, config);
188
+ };
@@ -0,0 +1,217 @@
1
+ /**
2
+ * 错误信息分析工具类
3
+ * 用于分析和序列化来自 resInterceptor.js 的错误对象
4
+ */
5
+
6
+ /**
7
+ * 安全的序列化函数,处理循环引用和不可序列化的值
8
+ * @param obj - 要序列化的对象
9
+ * @param space - JSON 格式化的缩进空格数
10
+ * @returns 序列化后的 JSON 字符串
11
+ */
12
+ export function safeStringify(obj: any, space: number = 2): string {
13
+ const seen = new WeakSet();
14
+ return JSON.stringify(
15
+ obj,
16
+ (key, value) => {
17
+ // 跳过函数
18
+ if (typeof value === 'function') {
19
+ return '[Function]';
20
+ }
21
+ // 跳过 undefined(返回 undefined 会从结果中移除该属性)
22
+ if (value === undefined) {
23
+ return undefined;
24
+ }
25
+ // 处理循环引用
26
+ if (typeof value === 'object' && value !== null) {
27
+ if (seen.has(value)) {
28
+ return '[Circular]';
29
+ }
30
+ seen.add(value);
31
+ }
32
+ return value;
33
+ },
34
+ space
35
+ );
36
+ }
37
+
38
+ /**
39
+ * 兼容的复制函数,支持多种复制方式
40
+ * @param text - 要复制的文本
41
+ * @returns Promise<void>
42
+ */
43
+ export async function copyToClipboard(text: string): Promise<void> {
44
+ // 方法1: 使用 Clipboard API(现代浏览器)
45
+ if (navigator.clipboard && navigator.clipboard.writeText) {
46
+ try {
47
+ await navigator.clipboard.writeText(text);
48
+ return;
49
+ } catch (clipboardErr) {
50
+ console.warn('Clipboard API 失败,尝试备用方法:', clipboardErr);
51
+ }
52
+ }
53
+
54
+ // 方法2: 使用 document.execCommand(兼容旧浏览器)
55
+ const textarea = document.createElement('textarea');
56
+ textarea.value = text;
57
+ textarea.style.position = 'fixed';
58
+ textarea.style.left = '-9999px';
59
+ textarea.style.top = '-9999px';
60
+ document.body.appendChild(textarea);
61
+ textarea.select();
62
+ textarea.setSelectionRange(0, text.length);
63
+
64
+ try {
65
+ const success = document.execCommand('copy');
66
+ document.body.removeChild(textarea);
67
+ if (!success) {
68
+ throw new Error('execCommand 复制失败');
69
+ }
70
+ } catch (execErr) {
71
+ document.body.removeChild(textarea);
72
+ throw execErr;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * 错误对象结构(根据 resInterceptor.js)
78
+ */
79
+ export interface ErrorInfo {
80
+ // 响应信息
81
+ status?: number;
82
+ statusText?: string;
83
+ response?: any;
84
+ // 请求信息
85
+ request?: {
86
+ url?: string;
87
+ method?: string;
88
+ params?: any;
89
+ data?: any;
90
+ headers?: any;
91
+ timeout?: number;
92
+ };
93
+ }
94
+
95
+ /**
96
+ * 分析错误对象,提取关键信息
97
+ * @param error - 错误对象(来自 resInterceptor.js)
98
+ * @returns 分析后的错误信息对象
99
+ */
100
+ export function analyzeError(error: any): {
101
+ errorMessage: string;
102
+ status: number | undefined;
103
+ statusText: string | undefined;
104
+ requestUrl: string | undefined;
105
+ requestMethod: string | undefined;
106
+ hasRequestData: boolean;
107
+ hasResponseData: boolean;
108
+ errorKeys: string[];
109
+ } {
110
+ return {
111
+ errorMessage: error?.response?.message || error?.statusText || '未知错误',
112
+ status: error?.status,
113
+ statusText: error?.statusText,
114
+ requestUrl: error?.request?.url || error?.config?.url,
115
+ requestMethod: error?.request?.method || error?.config?.method,
116
+ hasRequestData: !!(error?.request?.data || error?.config?.data),
117
+ hasResponseData: !!error?.response,
118
+ errorKeys: Object.keys(error || {}),
119
+ };
120
+ }
121
+
122
+ /**
123
+ * 按照 resInterceptor.js 的结构组织错误信息
124
+ * @param error - 错误对象(来自 resInterceptor.js)
125
+ * @returns 按照 resInterceptor.js 结构组织的错误信息对象
126
+ */
127
+ export function formatErrorInfo(error: any): ErrorInfo {
128
+ // 如果错误对象已经是 resInterceptor.js 的结构,直接使用
129
+ if (error?.status !== undefined && error?.request !== undefined) {
130
+ // 确保 request 对象存在且包含必要的字段
131
+ const request = error.request || {};
132
+
133
+ return {
134
+ // 只返回 error 中存在的字段,不强制添加 status 和 statusText
135
+ ...(error.status !== undefined && { status: error.status }),
136
+ ...(error.statusText !== undefined && { statusText: error.statusText }),
137
+ response: error.response,
138
+ request: {
139
+ url: request.url || '',
140
+ method: request.method || 'GET',
141
+ params: request.params || {},
142
+ data: request.data || {},
143
+ headers: request.headers || {},
144
+ timeout: request.timeout,
145
+ },
146
+ };
147
+ }
148
+
149
+ // 否则,从 error.response 和 error.config 中提取信息(兼容 axios 原始错误)
150
+ const res = error?.response || error;
151
+
152
+ // 获取完整的请求 headers(优先使用 _requestHeaders)
153
+ const requestHeaders = res?.config?._requestHeaders || res?.config?.headers || {};
154
+
155
+ // 清理 headers,移除 Axios 内部使用的字段
156
+ const cleanedHeaders: any = {};
157
+ const excludeKeys = ['common', 'delete', 'get', 'head', 'post', 'put', 'patch'];
158
+
159
+ for (const key in requestHeaders) {
160
+ if (!excludeKeys.includes(key)) {
161
+ cleanedHeaders[key] = requestHeaders[key];
162
+ }
163
+ }
164
+
165
+ return {
166
+ // 只返回存在的字段,不强制添加 status 和 statusText
167
+ ...(res?.status !== undefined && { status: res.status }),
168
+ ...(res?.statusText !== undefined && { statusText: res.statusText }),
169
+ response: res?.data,
170
+ // 请求信息(从 config 中获取,包含完整的 headers)
171
+ request: {
172
+ url: res?.config?.url || res?.config?.baseURL || '',
173
+ method: res?.config?.method?.toUpperCase() || 'GET',
174
+ params: res?.config?.params || {},
175
+ data: res?.config?.data || {},
176
+ headers: cleanedHeaders,
177
+ timeout: res?.config?.timeout,
178
+ },
179
+ };
180
+ }
181
+
182
+ /**
183
+ * 序列化错误对象为可复制的 JSON 字符串
184
+ * 按照 resInterceptor.js 的结构组织错误信息
185
+ * @param error - 错误对象(来自 resInterceptor.js)
186
+ * @returns 序列化后的 JSON 字符串
187
+ */
188
+ export function serializeError(error: any): string {
189
+ try {
190
+ const errorInfo = formatErrorInfo(error);
191
+ return safeStringify(errorInfo, 2);
192
+ } catch (stringifyErr) {
193
+ console.error('序列化错误失败:', stringifyErr);
194
+ // 如果序列化失败,返回简单的错误信息
195
+ return JSON.stringify({
196
+ error: '序列化失败',
197
+ message: error?.message || String(error),
198
+ timestamp: new Date().toISOString(),
199
+ }, null, 2);
200
+ }
201
+ }
202
+
203
+ /**
204
+ * 复制错误信息到剪贴板
205
+ * @param error - 错误对象(来自 resInterceptor.js)或错误消息字符串
206
+ * @returns Promise<void>
207
+ */
208
+ export async function copyErrorToClipboard(error: any): Promise<void> {
209
+ // 如果传入的是字符串,直接复制字符串
210
+ if (typeof error === 'string') {
211
+ await copyToClipboard(error);
212
+ return;
213
+ }
214
+ // 如果是对象,序列化后复制
215
+ const errorText = serializeError(error);
216
+ await copyToClipboard(errorText);
217
+ }
@@ -0,0 +1,15 @@
1
+ import { h } from 'vue';
2
+ import ExpandableMessage from '@/components/ExpandableMessage.vue';
3
+
4
+ /**
5
+ * 创建可展开/收缩的错误消息 vnode
6
+ * @param errorMessage - 错误消息文本
7
+ * @param maxLines - 默认显示的最大行数,超出后显示展开按钮,默认 3
8
+ * @returns 返回一个 vnode
9
+ */
10
+ export const createExpandableMessageVNode = (errorMessage: string, maxLines: number = 3) => {
11
+ return h(ExpandableMessage, {
12
+ message: errorMessage,
13
+ maxLines: maxLines
14
+ });
15
+ };