hlq-cli 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.
Files changed (88) hide show
  1. package/README.md +18 -0
  2. package/bin/index.js +16 -0
  3. package/lib/aesCreate.js +11 -0
  4. package/lib/axiosCreate.js +11 -0
  5. package/lib/create.js +172 -0
  6. package/lib/echartCreate.js +12 -0
  7. package/lib/jwtDecodeCreate.js +16 -0
  8. package/lib/rsaCreate.js +11 -0
  9. package/lib/stateCreate.js +34 -0
  10. package/lib/websocketCreate.js +16 -0
  11. package/package.json +21 -0
  12. package/templates/.env +1 -0
  13. package/templates/.env.dev +2 -0
  14. package/templates/.env.pro +2 -0
  15. package/templates/index.html +15 -0
  16. package/templates/package-lock.json +4058 -0
  17. package/templates/package.json +31 -0
  18. package/templates/public/config.js +1 -0
  19. package/templates/public/font/iconfont.css +579 -0
  20. package/templates/public/font/iconfont.js +1 -0
  21. package/templates/public/font/iconfont.ttf +0 -0
  22. package/templates/public/font/iconfont.woff +0 -0
  23. package/templates/public/font/iconfont.woff2 +0 -0
  24. package/templates/src/App.vue +35 -0
  25. package/templates/src/components/chart/barChart.vue +103 -0
  26. package/templates/src/components/chart/color.ts +43 -0
  27. package/templates/src/components/chart/lineChart.vue +114 -0
  28. package/templates/src/components/chart/mapChart.vue +135 -0
  29. package/templates/src/components/chart/mixedChart.vue +148 -0
  30. package/templates/src/components/chart/pieChart.vue +104 -0
  31. package/templates/src/components/chart/radarChart.vue +112 -0
  32. package/templates/src/components/chart/scatterChart.vue +144 -0
  33. package/templates/src/components/chart/sunburstChart.vue +183 -0
  34. package/templates/src/components/descript/index.vue +45 -0
  35. package/templates/src/components/dialog/index.vue +54 -0
  36. package/templates/src/components/drawer/index.vue +53 -0
  37. package/templates/src/components/form/component/cascader.vue +65 -0
  38. package/templates/src/components/form/component/checkbox.vue +31 -0
  39. package/templates/src/components/form/component/datePicker.vue +39 -0
  40. package/templates/src/components/form/component/dateRange.vue +36 -0
  41. package/templates/src/components/form/component/datetimePicker.vue +25 -0
  42. package/templates/src/components/form/component/fileUpload.vue +80 -0
  43. package/templates/src/components/form/component/formFun.ts +132 -0
  44. package/templates/src/components/form/component/imageUpload.vue +92 -0
  45. package/templates/src/components/form/component/input.vue +41 -0
  46. package/templates/src/components/form/component/location.vue +79 -0
  47. package/templates/src/components/form/component/radio.vue +31 -0
  48. package/templates/src/components/form/component/select.vue +66 -0
  49. package/templates/src/components/form/component/textarea.vue +26 -0
  50. package/templates/src/components/form/component/timePicker.vue +28 -0
  51. package/templates/src/components/form/component/upload.ts +20 -0
  52. package/templates/src/components/form/formInterface.ts +115 -0
  53. package/templates/src/components/form/index.vue +193 -0
  54. package/templates/src/components/form/item.vue +323 -0
  55. package/templates/src/components/groupForm/index.vue +91 -0
  56. package/templates/src/components/icon/index.vue +29 -0
  57. package/templates/src/components/layout/header.vue +238 -0
  58. package/templates/src/components/layout/index.vue +167 -0
  59. package/templates/src/components/layout/menu.vue +130 -0
  60. package/templates/src/components/layout/sideBarItem.vue +49 -0
  61. package/templates/src/components/searchBox/height.ts +9 -0
  62. package/templates/src/components/searchBox/index.vue +265 -0
  63. package/templates/src/components/table/index.vue +371 -0
  64. package/templates/src/components/table/table.ts +23 -0
  65. package/templates/src/components/tree/index.vue +222 -0
  66. package/templates/src/components/tree/lazyTree.vue +136 -0
  67. package/templates/src/data.d.ts +4 -0
  68. package/templates/src/main.ts +18 -0
  69. package/templates/src/router/index.ts +60 -0
  70. package/templates/src/store/menuInterface.ts +10 -0
  71. package/templates/src/store/permission.ts +59 -0
  72. package/templates/src/store/user.ts +24 -0
  73. package/templates/src/utils/alioss/index.ts +0 -0
  74. package/templates/src/utils/axios/http.ts +99 -0
  75. package/templates/src/utils/axios/index.ts +112 -0
  76. package/templates/src/utils/axios/service.ts +8 -0
  77. package/templates/src/utils/crypto/index.ts +28 -0
  78. package/templates/src/utils/rsa/index.ts +18 -0
  79. package/templates/src/utils/token/index.ts +6 -0
  80. package/templates/src/utils/tree/index.ts +74 -0
  81. package/templates/src/utils/websocket/index.ts +136 -0
  82. package/templates/src/views/login/index.vue +248 -0
  83. package/templates/src/views/templete/table.vue +122 -0
  84. package/templates/src/views/templete/tableConfig.ts +153 -0
  85. package/templates/tsconfig.app.json +19 -0
  86. package/templates/tsconfig.json +7 -0
  87. package/templates/tsconfig.node.json +23 -0
  88. package/templates/vite.config.ts +34 -0
@@ -0,0 +1,222 @@
1
+ <template>
2
+ <el-input
3
+ v-model="filterText"
4
+ style="max-width: 240px; width: 100%; margin-bottom: 12px"
5
+ v-if="hasSearch"
6
+ placeholder="请输入"
7
+ />
8
+ <div
9
+ ref="treeBox"
10
+ :style="`height: calc(100% - ${hasSearch ? 44 : 0}px);width: 100%;`"
11
+ >
12
+ <el-tree-v2
13
+ ref="treeRef"
14
+ style="height: 100%"
15
+ class="filter-tree"
16
+ :data="state.treeData"
17
+ :props="treeProps"
18
+ :current-node-key="
19
+ props.selectKey || (state.selectData && state.selectData.id)
20
+ "
21
+ :filter-method="filterNode"
22
+ :show-checkbox="selectType === 'checkbox'"
23
+ :highlight-current="selectType === 'radio'"
24
+ @current-change="nodeClick"
25
+ @node-expand="nodeExpand"
26
+ :expand-on-click-node="false"
27
+ :height="height"
28
+ :item-size="46"
29
+ :check-strictly="checkStrictly"
30
+ >
31
+ <template #default="{ node, data }">
32
+ <slot v-if="customLabel" :node="node" :data="data"></slot>
33
+ <div v-else class="ellipsis">
34
+ <span :title="node.label"> {{ node.label }} </span>
35
+ </div>
36
+ <div class="option">
37
+ <template v-if="!data.isLeaf && props.options?.includes('add')">
38
+ <Icon name="icon-line_plus" @click="addClick(data)"></Icon>
39
+ </template>
40
+ <template v-else>
41
+ <Icon
42
+ v-if="props.options?.includes('edit')"
43
+ name="icon-line_edit"
44
+ @click="editClick(data)"
45
+ ></Icon>
46
+ <el-popconfirm
47
+ title="确定要删除吗?"
48
+ @confirm="delClick(data)"
49
+ v-if="props.options?.includes('del')"
50
+ >
51
+ <template #reference>
52
+ <Icon name="icon-line_delete"></Icon>
53
+ </template>
54
+ </el-popconfirm>
55
+ </template>
56
+ </div>
57
+ </template>
58
+ </el-tree-v2>
59
+ </div>
60
+ </template>
61
+
62
+ <script lang="ts" setup>
63
+ import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
64
+ import Icon from '@/components/icon/index.vue'
65
+ type optionsType = 'add' | 'edit' | 'del'
66
+
67
+ interface Props {
68
+ treeData: Tree[]
69
+ children?: string
70
+ label?: string
71
+ nodeKey?: string // key
72
+ disabled?: string
73
+ isLeaf?: string
74
+ selectType?: 'checkbox' | 'radio' | ''
75
+ data?: Data // 全局数据data
76
+ params?: any
77
+ dataCode?: string // 全局数据code
78
+ selectKey?: string | number
79
+ options?: optionsType[]
80
+ checkedKey?: string[] | number[] // 默认勾选的节点
81
+ checkStrictly?: boolean // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
82
+ customLabel?: boolean
83
+ isLazy?: boolean
84
+ hasSearch?: boolean
85
+ }
86
+ const props = withDefaults(defineProps<Props>(), {
87
+ children: 'children',
88
+ disabled: 'disabled',
89
+ isLeaf: 'isLeaf',
90
+ selectType: 'radio',
91
+ params: {},
92
+ nodeKey: 'id',
93
+ checkStrictly: false,
94
+ isLazy: false,
95
+ hasSearch: true,
96
+ })
97
+ const treeProps = computed(() => {
98
+ return {
99
+ children: props.children,
100
+ label: props.label || state.label || 'label',
101
+ disabled: props.disabled,
102
+ isLeaf: props.isLeaf,
103
+ value: props.nodeKey,
104
+ }
105
+ })
106
+ interface Tree {
107
+ [key: string]: any
108
+ }
109
+ const state = reactive({
110
+ treeData: [] as Tree[],
111
+ selectData: {} as Tree,
112
+ label: 'label',
113
+ value: 'value',
114
+ defaultExpandAll: true,
115
+ oldparams: null,
116
+ params: {} as Data,
117
+ lazyData: {} as Tree,
118
+ monitor: [],
119
+ })
120
+ const filterText = ref('')
121
+ const treeRef = ref()
122
+ let parentFilter: string[] = []
123
+ watch(filterText, (val) => {
124
+ parentFilter = []
125
+ treeRef.value!.filter(val)
126
+ })
127
+ const treeBox = ref()
128
+ const height = computed(() => {
129
+ if (treeBox.value) {
130
+ const inputHeight = 40
131
+ return treeBox.value.offsetHeight - inputHeight
132
+ }
133
+ })
134
+ // 默认展开两层
135
+ const defaultExpandedKey = computed(() => {
136
+ const list: any = []
137
+ state.treeData.forEach((item: Tree) => {
138
+ list.push(item[props.nodeKey || 'id'])
139
+ if (item.children && item.children.length > 0) {
140
+ item.children.forEach((item2: Data) => {
141
+ list.push(item2[props.nodeKey || 'id'])
142
+ })
143
+ }
144
+ })
145
+ nextTick(() => {
146
+ if (treeRef.value) {
147
+ treeRef.value!.setExpandedKeys(list)
148
+ }
149
+ })
150
+ return list
151
+ })
152
+ const filterNode = (value: string, data: Tree) => {
153
+ if (!value) return true
154
+ if (
155
+ (data[props.label || state.label || 'label'].includes(value) ||
156
+ parentFilter.includes(data[props.nodeKey || 'id'])) &&
157
+ data.children
158
+ ) {
159
+ data.children.map((item: Tree) => {
160
+ parentFilter.push(item[props.nodeKey || 'id'])
161
+ })
162
+ }
163
+
164
+ if (!state.defaultExpandAll) {
165
+ if (data.children && data.children.length > 0) {
166
+ let flag = false
167
+ data.children.forEach((item: Tree) => {
168
+ if (filterNode(value, item)) flag = true
169
+ })
170
+
171
+ return flag
172
+ }
173
+ }
174
+ return (
175
+ data[props.label || state.label || 'label'].includes(value) ||
176
+ parentFilter.includes(data[props.nodeKey || 'id'])
177
+ )
178
+ }
179
+ const emits = defineEmits(['nodeClick', 'addClick', 'editClick', 'delClick'])
180
+ const nodeClick = (data: Tree) => {
181
+ emits('nodeClick', data)
182
+ state.selectData = data
183
+ }
184
+ // 新增
185
+ const addClick = (data: Tree) => {
186
+ emits('addClick', data)
187
+ }
188
+ // 编辑
189
+ const editClick = (data: Tree) => {
190
+ emits('editClick', data)
191
+ }
192
+ // 删除
193
+ const delClick = (data: Tree) => {
194
+ emits('delClick', data)
195
+ }
196
+ watch(
197
+ () => props.treeData,
198
+ () => {
199
+ state.treeData = props.treeData
200
+ },
201
+ {
202
+ deep: true,
203
+ },
204
+ )
205
+ watch(
206
+ () => props.checkedKey,
207
+ () => {
208
+ if (props.checkedKey && treeRef.value) {
209
+ nextTick(() => {
210
+ treeRef.value.setCheckedKeys(props.checkedKey)
211
+ })
212
+ }
213
+ },
214
+ {
215
+ deep: true,
216
+ immediate: true,
217
+ },
218
+ )
219
+ onMounted(() => {
220
+ defaultExpandedKey()
221
+ })
222
+ </script>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <el-input
3
+ v-model="filterText"
4
+ style="width: 240px; margin-bottom: 12px"
5
+ placeholder="请输入"
6
+ />
7
+ <el-tree
8
+ ref="treeRef"
9
+ class="filter-tree"
10
+ :props="treeProps"
11
+ lazy
12
+ :load="loadNode"
13
+ @current-change="nodeClick"
14
+ :filter-node-method="filterNode"
15
+ :show-checkbox="selectType === 'checkbox'"
16
+ :highlight-current="selectType === 'radio'"
17
+ :nodeKey="nodeKey || state.value"
18
+ :check-on-click-leaf="true"
19
+ :expand-on-click-node="false"
20
+ :current-node-key="state.clickData?.[nodeKey || state.value]"
21
+ >
22
+ <template #default="{ node, data }">
23
+ <slot :node="node" :data="data"></slot>
24
+ </template>
25
+ </el-tree>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ import { computed, onMounted, reactive, ref, watch } from 'vue'
30
+ interface Props {
31
+ children?: string
32
+ label?: string
33
+ nodeKey?: string // key
34
+ disabled?: string
35
+ isLeaf?: string
36
+ selectType?: 'checkbox' | 'radio' | ''
37
+ actionMethod?: string
38
+ loadMethod: (node: any) => Promise<Data[]>
39
+ }
40
+ const props = withDefaults(defineProps<Props>(), {
41
+ children: 'children',
42
+ disabled: 'disabled',
43
+ isLeaf: 'isLeaf',
44
+ selectType: 'radio',
45
+ })
46
+ const treeProps = computed(() => {
47
+ return {
48
+ children: props.children,
49
+ label: props.label || state.label || 'label',
50
+ disabled: props.disabled,
51
+ isLeaf: props.isLeaf,
52
+ }
53
+ })
54
+ const state = reactive({
55
+ clickData: {} as Data,
56
+ label: 'label',
57
+ value: 'value',
58
+ defaultExpandAll: true,
59
+ params: null,
60
+ })
61
+ const filterText = ref('')
62
+ const treeRef = ref()
63
+ let parentFilter: string[] = []
64
+ watch(filterText, (val) => {
65
+ parentFilter = []
66
+ treeRef.value!.filter(val)
67
+ })
68
+ const filterNode = (value: string, data: Data) => {
69
+ if (!value) return true
70
+ if (
71
+ (data[props.label || state.label || 'label'].includes(value) ||
72
+ parentFilter.includes(data[props.nodeKey || 'id'])) &&
73
+ data.children
74
+ ) {
75
+ data.children.map((item: Data) => {
76
+ parentFilter.push(item[props.nodeKey || 'id'])
77
+ })
78
+ }
79
+
80
+ if (!state.defaultExpandAll) {
81
+ if (data.children && data.children.length > 0) {
82
+ let flag = false
83
+ data.children.forEach((item: Data) => {
84
+ if (filterNode(value, item)) flag = true
85
+ })
86
+
87
+ return flag
88
+ }
89
+ }
90
+ return (
91
+ data[props.label || state.label || 'label'].includes(value) ||
92
+ parentFilter.includes(data[props.nodeKey || 'id'])
93
+ )
94
+ }
95
+ const emits = defineEmits(['nodeClick'])
96
+ const nodeClick = (data: Data) => {
97
+ emits('nodeClick', data)
98
+ state.clickData = data
99
+ }
100
+ let resolveFun: any
101
+ const loadNode = async (node: any, resolve: (data: Data[]) => void) => {
102
+ const list = await props.loadMethod(node)
103
+ if (node.level === 0 && !resolveFun) {
104
+ resolveFun = resolve
105
+ }
106
+ return resolve(list)
107
+ }
108
+ const reloadTree = () => {
109
+ const tree = treeRef.value
110
+ tree.store.root.setData([])
111
+ loadNode({ level: 0 }, resolveFun)
112
+ }
113
+ const setCurrentKey = (key?: string) => {
114
+ const tree = treeRef.value
115
+ tree.setCurrentKey(key || null)
116
+ }
117
+ // 重新渲染指定节点下的子节点
118
+ const reloadNodeChildren = async (nodeKey: string) => {
119
+ const tree = treeRef.value
120
+ if (tree) {
121
+ // 找到指定节点
122
+ const node = tree.getNode(nodeKey)
123
+ if (node) {
124
+ // 清空节点的子节点数据
125
+ node.childNodes = []
126
+ node.loaded = false
127
+ // 重新加载子节点
128
+ await loadNode(node, (data) => {
129
+ node.doCreateChildren(data)
130
+ })
131
+ }
132
+ }
133
+ }
134
+ defineExpose({ reloadTree, setCurrentKey, reloadNodeChildren })
135
+ onMounted(() => {})
136
+ </script>
@@ -0,0 +1,4 @@
1
+ // 无法指定的对象
2
+ interface Data {
3
+ [key: string]: any
4
+ }
@@ -0,0 +1,18 @@
1
+ import { createApp } from 'vue'
2
+ import App from './App.vue'
3
+ import ElementPlus from 'element-plus'
4
+ import 'element-plus/dist/index.css'
5
+ import '/public/font/iconfont.css'
6
+ import zhCn from 'element-plus/es/locale/lang/zh-cn'
7
+ const app = createApp(App)
8
+
9
+ import { usePermissionStore } from './store/permission.ts'
10
+ const store = usePermissionStore()
11
+ store.setRouterList()
12
+ import router from './router'
13
+ app
14
+ .use(router)
15
+ .use(ElementPlus, {
16
+ locale: zhCn,
17
+ })
18
+ .mount('#app')
@@ -0,0 +1,60 @@
1
+ import { createWebHistory, createRouter } from 'vue-router'
2
+ import Login from '../views/login/index.vue'
3
+ import Layout from '../components/layout/index.vue'
4
+ import { usePermissionStore } from '../store/permission.ts'
5
+ import { useUserStore } from '../store/user'
6
+ import { isEmpty } from 'lodash'
7
+
8
+ const routes = [
9
+ {
10
+ path: '/login',
11
+ name: 'login',
12
+ component: Login,
13
+ },
14
+ {
15
+ path: '/',
16
+ name: 'layout',
17
+ component: Layout,
18
+ children: [
19
+ {
20
+ path: '/table',
21
+ name: 'table',
22
+ component: () => import('../views/templete/table.vue'),
23
+ meta: {
24
+ title: '表格',
25
+ },
26
+ },
27
+ ],
28
+ },
29
+ {
30
+ path: '',
31
+ redirect: '/login',
32
+ },
33
+ ]
34
+ export const router = createRouter({
35
+ history: createWebHistory(),
36
+ routes,
37
+ })
38
+
39
+ router.beforeEach((to, from, next) => {
40
+ const store = useUserStore()
41
+ const permissionStore = usePermissionStore()
42
+ permissionStore.setRouterList()
43
+
44
+ // if (to.path === '/login') {
45
+ // store.clear()
46
+ // next()
47
+ // } else if (isEmpty(to.matched)) {
48
+ // store.clear()
49
+ // next('/login')
50
+ // } else {
51
+ // const token = store.token
52
+ // if (token) {
53
+ // next()
54
+ // } else {
55
+ // next('/login')
56
+ // }
57
+ // }
58
+ next()
59
+ })
60
+ export default router
@@ -0,0 +1,10 @@
1
+ // 菜单
2
+ interface menuItem {
3
+ menuPath: string // 路由
4
+ menuCode: string // 路由名称
5
+ menuName: string // 标题
6
+ menuFile?: string // 组件路径
7
+ icon?: string // 图标
8
+ children?: Array<menuItem> // 子菜单
9
+ menuIndex?: number // 菜单排序
10
+ }
@@ -0,0 +1,59 @@
1
+ import { defineStore } from 'pinia'
2
+ import router from '@/router'
3
+ import { type RouteRecordRaw } from 'vue-router'
4
+ import { type menuItem } from './menuInterface'
5
+ const modules = import.meta.glob('@/views/**/*.vue')
6
+ const initRouteList = (item: menuItem) => {
7
+ const route: RouteRecordRaw = {
8
+ path: item.menuPath,
9
+ name: item.menuCode,
10
+ meta: {
11
+ title: item.menuName,
12
+ code: item.menuFile,
13
+ path: item.menuPath,
14
+ },
15
+ children: [],
16
+ }
17
+ route.component = modules[`/src/views${item.menuFile}`]
18
+ let children: Array<RouteRecordRaw> = []
19
+ if (item.children && item.children.length > 0) {
20
+ item.children = item.children.sort(
21
+ (a: menuItem, b: menuItem) => (a.menuIndex || 99) - (b.menuIndex || 99),
22
+ )
23
+ item.children.forEach((i: menuItem) => {
24
+ children.push(initRouteList(i))
25
+ })
26
+ route.children = children
27
+ } else {
28
+ delete item.children
29
+ }
30
+ return route
31
+ }
32
+
33
+ export const usePermissionStore = defineStore('permission', {
34
+ state: () => ({
35
+ menuList: [] as Array<menuItem>,
36
+ currentRole: '',
37
+ }),
38
+ getters: {},
39
+ actions: {
40
+ setMenuList(menuList: Array<menuItem>) {
41
+ this.menuList = menuList.filter((item: menuItem) => item.menuPath)
42
+ },
43
+ setRouterList() {
44
+ this.menuList
45
+ .sort(
46
+ (a: menuItem, b: menuItem) =>
47
+ (a.menuIndex || 99) - (b.menuIndex || 99),
48
+ )
49
+ .forEach((item: menuItem) => {
50
+ if (initRouteList(item).path) {
51
+ router.addRoute('layout', initRouteList(item))
52
+ }
53
+ })
54
+ },
55
+ clear() {
56
+ this.menuList = []
57
+ },
58
+ },
59
+ })
@@ -0,0 +1,24 @@
1
+ import { defineStore } from 'pinia'
2
+ import { decode } from '@/utils/token'
3
+
4
+ const getOtherMessage = (token: string) => {
5
+ const tokenData: any = decode(token)
6
+ return tokenData
7
+ }
8
+ export const useUserStore = defineStore('user', {
9
+ state: () => ({
10
+ token: '',
11
+ otherMessage: {} as Data,
12
+ }),
13
+ getters: {},
14
+ actions: {
15
+ setToken(token: string) {
16
+ this.token = token
17
+ this.otherMessage = getOtherMessage(token)
18
+ },
19
+ clear() {
20
+ this.token = ''
21
+ this.otherMessage = {}
22
+ },
23
+ },
24
+ })
File without changes
@@ -0,0 +1,99 @@
1
+ import { request } from './index'
2
+
3
+ export interface HttpServiceConfig {
4
+ baseURL?: string
5
+ timeout?: number
6
+ headers?: Record<string, string>
7
+ preserveLongIntegers?: boolean
8
+ auto?: boolean
9
+ custom?: boolean
10
+ }
11
+
12
+ export class HttpService {
13
+ private baseURL: string
14
+ private timeout: number
15
+ private headers: Record<string, string>
16
+ private preserveLongIntegers: boolean
17
+ private auto: boolean
18
+ private custom: boolean
19
+
20
+ constructor(config: HttpServiceConfig = {}) {
21
+ this.baseURL = config.baseURL || ''
22
+ this.timeout = config.timeout || 10000
23
+ this.headers = config.headers || {}
24
+ this.preserveLongIntegers = config.preserveLongIntegers || false
25
+ this.auto = config.auto || false
26
+ this.custom = config.custom || false
27
+ }
28
+
29
+ /**
30
+ * API 调用
31
+ * @param {string} url
32
+ * @param {string} method
33
+ * @param {Data | Data[]} data
34
+ * @returns {Promise<Response>}
35
+ */
36
+ api = (
37
+ url: string,
38
+ method?: string,
39
+ data?: Data | Data[],
40
+ ): Promise<Response> => {
41
+ const parameter: Record<string, unknown> = {
42
+ url: url,
43
+ method: method || 'get',
44
+ data: {},
45
+ params: {},
46
+ }
47
+ if (method === 'get' || method === 'postParams') {
48
+ parameter.params = data || {}
49
+ if (method === 'postParams') {
50
+ parameter.method = 'post'
51
+ }
52
+ } else {
53
+ parameter.data = data || {}
54
+ }
55
+ return request(parameter as any)
56
+ }
57
+
58
+ /**
59
+ * 文件下载
60
+ * @param {string} url
61
+ * @param {string} fileName
62
+ * @param {any} data
63
+ */
64
+ async download(url: string, fileName?: string, data?: any) {
65
+ try {
66
+ const response: any = await this.api(url, 'get', data)
67
+ this.triggerDownload(response, fileName)
68
+ } catch (error) {
69
+ console.error('File download failed:', error)
70
+ }
71
+ }
72
+
73
+ /**
74
+ * POST 文件下载
75
+ */
76
+ async downloadPost(url: string, fileName?: string, data?: Data) {
77
+ try {
78
+ const response: any = await this.api(url, 'post', data || {})
79
+ this.triggerDownload(response, fileName)
80
+ } catch (error) {
81
+ console.error('File download failed:', error)
82
+ }
83
+ }
84
+
85
+ private triggerDownload(response: Blob, fileName?: string) {
86
+ const blob = new Blob([response])
87
+ const downloadUrl = window.URL.createObjectURL(blob)
88
+ const link = document.createElement('a')
89
+ link.href = downloadUrl
90
+ link.download = fileName || 'template.xlsx'
91
+ document.body.appendChild(link)
92
+ link.click()
93
+ link.remove()
94
+ window.URL.revokeObjectURL(downloadUrl)
95
+ }
96
+ }
97
+
98
+ // 默认实例(保持向后兼容)
99
+ export const httpService = new HttpService()