Commit 90d6ce00 authored by guxukai's avatar guxukai

feat: 添加组件动态导入

parent b9844f3c
......@@ -23,6 +23,7 @@
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@types/lodash": "^4.14.178",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-babel": "^5.0.0-alpha.5",
......
export const zookeeper = router => {
import { Router } from 'vue-router'
export const zookeeper = (router: Router): void => {
// 添加异常处理
const originalPush = router.push
router.push = to => {
......
<template>
<div>com1</div>
</template>
<script>
export default {
name: 'com1',
}
</script>
import { upperFirst, camelCase } from 'lodash'
const path = require('path-browserify')
const requireModules = require.context('./', true, /index\.(vue)$/iu)
export const modules: any = {}
requireModules.keys().forEach(filePath => {
const component = requireModules(filePath)
let name = path.resolve(filePath, '..')
name = upperFirst(
camelCase(
name
.split('/')
.pop()
.replace(/\.\w+$/u, ''),
),
)
modules[name] = component.default || component
})
<template>
<div class="home">
<h1 class="font-bold underline text-[20px] text-red-100">Hello world!</h1>
<com1 />
</div>
</template>
<script>
export default {
name: 'home',
}
<script lang="ts">
import { defineComponent } from 'vue'
import { modules } from './component/index'
export default defineComponent({
components: {
...modules,
},
})
</script>
<script setup lang="ts"></script>
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
import { App } from '@vue/runtime-core'
const path = require('path-browserify')
export default {
install: function (app) {
const globalComponents = require.context('./', true, /index\.(vue|js)$/iu)
install: function (app: App) {
const globalComponents = require.context('./', true, /\.(vue|js)$/iu)
globalComponents.keys().forEach(filePath => {
const component = globalComponents(filePath)
let name = path.resolve(filePath, '..')
......
......@@ -24,7 +24,7 @@
class="py-4 text-lg whitespace-nowrap font-bold"
:class="[isCollapse ? 'text-center bg-[#151930] text-white' : '']"
>
{{ isCollapse ? 'A' : 'admin' }}
{{ isCollapse ? PROJECT_NAME[0] : PROJECT_NAME }}
</p>
<div
class="mb-4 bg-gray-200 dark:bg-[#151930] transition transition-all duration-300"
......@@ -128,6 +128,8 @@ import { onBeforeRouteUpdate } from 'vue-router'
import { ref, toRaw, onMounted, getCurrentInstance } from 'vue'
import { ArrowRight } from '@element-plus/icons-vue'
import AsideMenuList from '@shared/layouts/components/aside-menu-list/index.vue'
const PROJECT_NAME = process.env.PROJECT_NAME
let store = useStore()
let { proxy } = getCurrentInstance()
......@@ -171,7 +173,7 @@ onMounted(() => {
store.commit('layout/changeBreadcrumb', result)
})
// 折叠asdie
// 折叠aside
let isCollapse = ref(false)
const fold = function () {
isCollapse.value = !isCollapse.value
......
import Axios, { AxiosRequestConfig, CancelToken } from 'axios'
import { flatry } from '@shared/utils/util'
import { Dict } from '@shared/utils/types'
export const UnauthorizedHttpCode = 401
export const UnprocessableEntityHttpCode = 422
export const HttpGetMethod = ['GET', 'HEAD']
export type HttpRequestConfig = AxiosRequestConfig & {
[key: string]: unknown
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type HttpResponse<T = any> = {
data: T
status: number
statusText: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
headers: any
config: HttpRequestConfig
request?: unknown
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type HttpError<T = any> = Error & {
config: HttpRequestConfig
code?: string
request?: unknown
response?: HttpResponse<T>
}
export type HttpInstance = {
apiGet: <T>(url: string, params?: unknown, historyBack?: boolean) => Promise<T>
apiPost: <T>(url: string, data: unknown, params?: unknown, historyBack?: boolean) => Promise<T>
apiPut: <T>(url: string, data: unknown, params?: unknown, historyBack?: boolean) => Promise<T>
apiDelete: <T>(url: string, params?: unknown, historyBack?: boolean) => Promise<T>
apiDownload: (options: DownloadRequestOption) => Promise<boolean>
}
export type HttpParams = Record<string, unknown> | URLSearchParams | undefined
export type HttpMethod = 'GET' | 'POST' | 'PUT'
export type httpBaseUrl = string | null | undefined
export type httpOnRequest = (config: HttpRequestConfig) => HttpRequestConfig
export type httpOnSuccess = (response: HttpResponse) => HttpResponse | Promise<HttpResponse>
export type httpOnError = (error: HttpError) => HttpError | Promise<HttpError> | null
export type httpParamsSerializer = (params: HttpParams) => string
export const requestFlatry = <T>(
promise: Promise<T>,
): Promise<{ data: T | undefined; error: HttpError | undefined }> => {
return flatry<T, HttpError>(promise)
}
export const bindUnprocessableEntityErrors = (
error: HttpError,
setError: (field: string, message: string) => void,
clearErrors: (field?: string | string[] | undefined) => void,
): Dict<string> | null => {
const result: Dict<string> = {}
clearErrors()
if (!error || !error.response || !error.response.status || error.response.status !== 422) {
return null
}
const violations: {
field: string
message: string
}[] = Array.isArray(error.response.data.violations)
? error.response.data.violations
: Array.isArray(error.response.data)
? error.response.data
: []
if (violations.length === 0) {
return null
}
violations.map(violation => {
const field = violation.field.split('.').pop() as string
setError(field, violation.message)
result[field] = violation.message
})
return result
}
const saveFile = (response: HttpResponse): boolean => {
let filename = response.headers['x-suggested-filename']
if (!filename) {
filename = response.headers['content-disposition'].match(/filename="(.+)"/)[1]
}
if (filename) {
const url = URL.createObjectURL(
new Blob([response.data], {
type: response.headers['content-type'],
}),
)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', decodeURIComponent(filename))
link.click()
URL.revokeObjectURL(url)
return true
} else {
return false
}
}
const createAxios = (
baseUrl: httpBaseUrl,
onRequest?: httpOnRequest,
onSuccess?: httpOnSuccess,
onError?: httpOnError,
paramsSerializer?: httpParamsSerializer,
) => {
const axiosInstance = Axios.create({
baseURL: baseUrl || undefined,
paramsSerializer: paramsSerializer,
})
axiosInstance.interceptors.request.use(
config => {
return onRequest ? onRequest(config as HttpRequestConfig) : config
},
(error: HttpError) => {
if (Axios.isCancel(error)) {
return
}
return onError ? onError(error) : error
},
)
axiosInstance.interceptors.response.use(
response => {
return onSuccess ? onSuccess(response as HttpResponse) : response
},
(error: HttpError) => {
if (Axios.isCancel(error)) {
return
}
return onError ? onError(error) : error
},
)
return axiosInstance
}
type RequestOption = {
url: string
params?: Dict | Dict[] | null
data?: Dict | Dict[] | null
historyBack?: boolean
cancelToken?: CancelToken
}
type DownloadRequestOption = RequestOption & {
method: HttpMethod
timeout?: number
}
export const createHttp = (
baseUrl: httpBaseUrl,
onRequest: httpOnRequest,
onSuccess: httpOnSuccess,
onError: httpOnError,
paramsSerializer: httpParamsSerializer,
): HttpInstance => {
const axios = createAxios(baseUrl, onRequest, onSuccess, onError, paramsSerializer)
return {
apiGet: (url: string, params: unknown = null, historyBack = true) => {
const config = {
params: params,
__historyBack: historyBack,
}
return axios.get(url, config)
},
apiPost: (url: string, data: unknown, params: unknown = null, historyBack = false) => {
const config = {
params: params,
__historyBack: historyBack,
}
return axios.post(url, data, config)
},
apiPut: (url: string, data: unknown, params: unknown = null, historyBack = false) => {
const config = {
params: params,
__historyBack: historyBack,
}
return axios.put(url, data, config)
},
apiDelete: (url: string, params: unknown = null, historyBack = false) => {
const config = {
params: params,
__historyBack: historyBack,
}
return axios.delete(url, config)
},
apiDownload: async (options: DownloadRequestOption) => {
const config = {
url: options.url,
method: options.method,
timeout: options.timeout || 120 * 1000,
params: options.params,
data: options.data,
responseType: 'blob',
cancelToken: options.cancelToken,
__historyBack: options.historyBack || false,
} as AxiosRequestConfig
const { data, error } = await requestFlatry<HttpResponse>(axios.request(config))
if (data) {
return saveFile(data)
}
return !error
},
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Dict<T = any> = {
[key in StringOrNumber]: T
}
export type StringOrNumber = string | number
export const humanFileSize = (size: number): string => {
const i = Math.floor(Math.log(size) / Math.log(1024))
return (size / Math.pow(1024, i)).toFixed(2) + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]
}
export type UploadOption = {
action: string
method?: 'POST' | 'PUT'
fieldName?: string
onBefore?: (file: File, xhr: XMLHttpRequest, formData: FormData) => void
onProgress?: (precent: number) => void
onSuccess?: <E, T>(response: E) => T
onError?: (error: string) => void
onAbort?: () => void
}
const upload = (
file: File,
options: {
onBefore: ((file: File, xhr: XMLHttpRequest, formData: FormData) => void) | undefined
onProgress: ((precent: number) => void) | undefined
fieldName: string
onError: ((error: string) => void) | undefined
method: string
action: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onSuccess: (response: any) => unknown
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener(
'progress',
event => {
options.onProgress && options.onProgress((event.loaded / event.total) * 100)
},
false,
)
xhr.addEventListener(
'load',
() => {
resolve(options.onSuccess ? options.onSuccess(xhr.response) : xhr.response)
},
false,
)
xhr.addEventListener(
'error',
() => {
options.onError && options.onError('文件上传错误')
reject(xhr.response)
},
false,
)
xhr.addEventListener(
'abort',
() => {
options.onError && options.onError('文件上传取消')
reject(xhr.response)
},
false,
)
const formData = new FormData()
formData.append(options.fieldName || 'file', file, file.name)
xhr.open(options.method || 'POST', options.action)
options.onBefore && options.onBefore(file, xhr, formData)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
xhr.send(formData)
})
}
export { upload }
import { Dict } from '@shared/utils/types'
export const sleep = (delay: number): Promise<null> => {
return new Promise(resolve => {
setTimeout(resolve, delay)
})
}
export const flatry = <T, E>(promise: Promise<T>): Promise<{ data: T | undefined; error: E | undefined }> => {
return promise.then(data => ({ data, error: undefined })).catch(error => ({ data: undefined, error }))
}
type tabledObjectOptions = {
name: string
property: string
callback?: (obj: Dict) => string
}
export type tabledObjectResult = {
name: string
value: string
}
export const tabledObject = (object: Dict, options: tabledObjectOptions[] = []): tabledObjectResult[] => {
const result: tabledObjectResult[] = []
options.forEach(option => {
if (object.hasOwnProperty(option.property)) {
if (option.callback) {
result.push({ name: option.name, value: option.callback(object[option.property]) })
} else {
result.push({ name: option.name, value: object[option.property] })
}
}
})
return result
}
......@@ -10,16 +10,19 @@
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"noImplicitAny": false,
"resolveJsonModule": true,
"baseUrl": ".",
"types": [
"webpack-env"
"webpack-env",
"node"
],
"paths": {
"@/*": [
"src/*"
],
"@shared/*": ["src/shared/*"],
"@account/*": ["src/account/*"],
"@account/*": ["src/account/*"]
},
"lib": [
"esnext",
......@@ -33,7 +36,8 @@
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
"tests/**/*.tsx",
"node_modules/@types/node/globals.d.ts"
],
"exclude": [
"node_modules"
......
......@@ -18,7 +18,7 @@ module.exports = {
publicPath: `/${sign}/`,
pages: {
[sign]: {
entry: `src/pages/${sign}/main.js`,
entry: `src/pages/${sign}/main.ts`,
template: `src/pages/${sign}/index.html`,
filename: 'index.html',
title: sign,
......
......@@ -1293,6 +1293,11 @@
resolved "https://registry.nlark.com/@types/json-schema/download/@types/json-schema-7.0.9.tgz?cache=0&sync_timestamp=1629708189890&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
integrity sha1-l+3JA36gw4WFMgsolk3eOznkZg0=
"@types/lodash@^4.14.178":
version "4.14.178"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==
"@types/mime@^1":
version "1.3.2"
resolved "https://registry.nlark.com/@types/mime/download/@types/mime-1.3.2.tgz?cache=0&sync_timestamp=1629708336928&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fmime%2Fdownload%2F%40types%2Fmime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment