diff --git a/package.json b/package.json index 6f59ee8..5ef8e1d 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@vueuse/integrations": "^13.3.0", "element-plus": "^2.9.11", "hook-fetch": "^1.1.3", + "moment": "^2.30.1", "nprogress": "^0.2.0", "pinia": "^3.0.3", "pinia-plugin-persistedstate": "^4.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49c232a..4f0092f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: hook-fetch: specifier: ^1.1.3 version: 1.1.3(react@19.1.0)(typescript-api-pro@0.0.7)(vue@3.5.16(typescript@5.8.3)) + moment: + specifier: ^2.30.1 + version: 2.30.1 nprogress: specifier: ^0.2.0 version: 0.2.0 @@ -110,7 +113,7 @@ importers: version: 6.0.0(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-recommended-scss: specifier: ^15.0.1 - version: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)) + version: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-recommended-vue: specifier: ^1.6.0 version: 1.6.0(postcss-html@1.8.0)(stylelint@16.20.0(typescript@5.8.3)) @@ -119,7 +122,7 @@ importers: version: 38.0.0(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-standard-scss: specifier: ^15.0.1 - version: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)) + version: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)) typescript: specifier: ~5.8.3 version: 5.8.3 @@ -128,7 +131,7 @@ importers: version: 0.0.7 unocss: specifier: 66.1.3 - version: 66.1.3(postcss@8.5.4)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) + version: 66.1.3(postcss@5.2.18)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) unplugin-auto-import: specifier: ^19.3.0 version: 19.3.0(@nuxt/kit@3.17.5)(@vueuse/core@13.3.0(vue@3.5.16(typescript@5.8.3))) @@ -789,56 +792,67 @@ packages: resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.41.1': resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.41.1': resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.41.1': resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.41.1': resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.41.1': resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.41.1': resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.41.1': resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.41.1': resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.41.1': resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.41.1': resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==} @@ -1189,41 +1203,49 @@ packages: resolution: {integrity: sha512-7vFWHLCCNFLEQlmwKQfVy066ohLLArZl+AV/AdmrD1/pD1FlmqM+FKbtnONnIwbHtgetFUCV/SRi1q4D49aTlw==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.7.11': resolution: {integrity: sha512-tYkGIx8hjWPh4zcn17jLEHU8YMmdP2obRTGkdaB3BguGHh31VCS3ywqC4QjTODjmhhNyZYkj/1Dz/+0kKvg9YA==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.7.11': resolution: {integrity: sha512-6F328QIUev29vcZeRX6v6oqKxfUoGwIIAhWGD8wSysnBYFY0nivp25jdWmAb1GildbCCaQvOKEhCok7YfWkj4Q==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.7.11': resolution: {integrity: sha512-NqhWmiGJGdzbZbeucPZIG9Iav4lyYLCarEnxAceguMx9qlpeEF7ENqYKOwB8Zqk7/CeuYMEcLYMaW2li6HyDzQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.7.11': resolution: {integrity: sha512-J2RPIFKMdTrLtBdfR1cUMKl8Gcy05nlQ+bEs/6al7EdWLk9cs3tnDREHZ7mV9uGbeghpjo4i8neNZNx3PYUY9w==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.7.11': resolution: {integrity: sha512-bDpGRerHvvHdhun7MmFUNDpMiYcJSqWckwAVVRTJf8F+RyqYJOp/mx04PDc7DhpNPeWdnTMu91oZRMV+gGaVcQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.7.11': resolution: {integrity: sha512-G9U7bVmylzRLma3cK39RBm3guoD1HOvY4o0NS4JNm37AD0lS7/xyMt7kn0JejYyc0Im8J+rH69/dXGM9DAJcSQ==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.7.11': resolution: {integrity: sha512-7qL20SBKomekSunm7M9Fe5L93bFbn+FbHiGJbfTlp0RKhPVoJDP73vOxf1QrmJHyDPECsGWPFnKa/f8fO2FsHw==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.7.11': resolution: {integrity: sha512-jisvIva8MidjI+B1lFRZZMfCPaCISePgTyR60wNT1MeQvIh5Ksa0G3gvI+Iqyj3jqYbvOHByenpa5eDGcSdoSg==} @@ -3522,6 +3544,9 @@ packages: mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -6091,13 +6116,13 @@ snapshots: transitivePeerDependencies: - vue - '@unocss/postcss@66.1.3(postcss@8.5.4)': + '@unocss/postcss@66.1.3(postcss@5.2.18)': dependencies: '@unocss/config': 66.1.3 '@unocss/core': 66.1.3 '@unocss/rule-utils': 66.1.3 css-tree: 3.1.0 - postcss: 8.5.4 + postcss: 5.2.18 tinyglobby: 0.2.14 '@unocss/preset-attributify@66.1.3': @@ -8934,6 +8959,8 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 + moment@2.30.1: {} + mri@1.2.0: {} mrmime@2.0.1: {} @@ -9213,9 +9240,9 @@ snapshots: dependencies: postcss: 8.5.4 - postcss-scss@4.0.9(postcss@8.5.4): + postcss-scss@4.0.9(postcss@5.2.18): dependencies: - postcss: 8.5.4 + postcss: 5.2.18 postcss-selector-parser@6.1.2: dependencies: @@ -9806,14 +9833,14 @@ snapshots: stylelint: 16.20.0(typescript@5.8.3) stylelint-order: 6.0.4(stylelint@16.20.0(typescript@5.8.3)) - stylelint-config-recommended-scss@15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)): + stylelint-config-recommended-scss@15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)): dependencies: - postcss-scss: 4.0.9(postcss@8.5.4) + postcss-scss: 4.0.9(postcss@5.2.18) stylelint: 16.20.0(typescript@5.8.3) stylelint-config-recommended: 16.0.0(stylelint@16.20.0(typescript@5.8.3)) stylelint-scss: 6.12.0(stylelint@16.20.0(typescript@5.8.3)) optionalDependencies: - postcss: 8.5.4 + postcss: 5.2.18 stylelint-config-recommended-vue@1.6.0(postcss-html@1.8.0)(stylelint@16.20.0(typescript@5.8.3)): dependencies: @@ -9827,13 +9854,13 @@ snapshots: dependencies: stylelint: 16.20.0(typescript@5.8.3) - stylelint-config-standard-scss@15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)): + stylelint-config-standard-scss@15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)): dependencies: stylelint: 16.20.0(typescript@5.8.3) - stylelint-config-recommended-scss: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)) + stylelint-config-recommended-scss: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-standard: 38.0.0(stylelint@16.20.0(typescript@5.8.3)) optionalDependencies: - postcss: 8.5.4 + postcss: 5.2.18 stylelint-config-standard@38.0.0(stylelint@16.20.0(typescript@5.8.3)): dependencies: @@ -10189,12 +10216,12 @@ snapshots: universalify@2.0.1: {} - unocss@66.1.3(postcss@8.5.4)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)): + unocss@66.1.3(postcss@5.2.18)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)): dependencies: '@unocss/astro': 66.1.3(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) '@unocss/cli': 66.1.3 '@unocss/core': 66.1.3 - '@unocss/postcss': 66.1.3(postcss@8.5.4) + '@unocss/postcss': 66.1.3(postcss@5.2.18) '@unocss/preset-attributify': 66.1.3 '@unocss/preset-icons': 66.1.3 '@unocss/preset-mini': 66.1.3 diff --git a/src/hooks/useCurrentInstance.ts b/src/hooks/useCurrentInstance.ts new file mode 100644 index 0000000..a36d1a8 --- /dev/null +++ b/src/hooks/useCurrentInstance.ts @@ -0,0 +1,53 @@ +import type { ComponentInternalInstance } from 'vue'; +import type { RouteLocationNormalizedLoaded, Router } from 'vue-router'; +import { getCurrentInstance } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; + +interface Utils { + $is: { + [key: string]: (...args: any[]) => boolean; + }; + $dataHelpers: { + [key: string]: (...args: any[]) => any; + }; + $common: { + [key: string]: (...args: any[]) => any; + }; +} + +interface UseCurrentInstanceReturn { + currentInstance: ComponentInternalInstance; + proxy: ComponentInternalInstance['proxy'] & Utils; + router: Router; + route: RouteLocationNormalizedLoaded; + $is: Utils['$is']; + $dataHelpers: Utils['$dataHelpers']; + $common: Utils['$common']; +} + +export function useCurrentInstance(): UseCurrentInstanceReturn { + const router = useRouter(); + const route = useRoute(); + + const currentInstance = getCurrentInstance(); + + if (!currentInstance) { + throw new Error('useCurrentInstance必须在setup函数中调用💘'); + } + + const { proxy } = currentInstance as ComponentInternalInstance & { proxy: Utils }; + + const $is = proxy.$is; + const $dataHelpers = proxy.$dataHelpers; + const $common = proxy.$common; + + return { + currentInstance, + proxy, + router, + route, + $is, + $dataHelpers, + $common, + }; +} diff --git a/src/main.ts b/src/main.ts index 62222b4..9afc4fe 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,8 @@ -// 引入ElementPlus所有图标 -import * as ElementPlusIconsVue from '@element-plus/icons-vue'; import { ElMessage } from 'element-plus'; import { createApp } from 'vue'; import ElementPlusX from 'vue-element-plus-x'; import App from './App.vue'; +import { registerPlugins } from './plugins'; import router from './routers'; import store from './stores'; import './styles/index.scss'; @@ -14,13 +13,13 @@ import 'virtual:svg-icons-register'; const app = createApp(App); +// 插件安装 +registerPlugins(app); + app.use(router); app.use(ElMessage); app.use(ElementPlusX); -// 注册ElementPlus所有图标 -for (const [key, component] of Object.entries(ElementPlusIconsVue)) { - app.component(key, component); -} + app.use(store); app.mount('#app'); diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 0000000..511f400 --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,19 @@ +import type { App, Plugin } from 'vue'; + +interface PluginModule { default: Plugin } + +const modules = import.meta.glob('./modules/*.ts', { eager: true }); + +export function registerPlugins(app: App) { + Object.values(modules).forEach((module) => { + if (typeof module.default === 'object' && module.default && typeof module.default.install === 'function') { + app.use(module.default); + } + else if (typeof module.default === 'function') { + app.use(module.default); + } + else { + console.warn('插件模块无效:', module); + } + }); +} diff --git a/src/plugins/modules/elementPlusIconsPlugin.ts b/src/plugins/modules/elementPlusIconsPlugin.ts new file mode 100644 index 0000000..be0ded4 --- /dev/null +++ b/src/plugins/modules/elementPlusIconsPlugin.ts @@ -0,0 +1,13 @@ +import type { App } from 'vue'; +import * as ElementPlusIconsVue from '@element-plus/icons-vue'; + +const elementPlusIconsPlugin = { + install(app: App) { + // 注册所有ElementPlus图标 + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } + }, +}; + +export default elementPlusIconsPlugin; diff --git a/src/plugins/modules/utilsPlugin.ts b/src/plugins/modules/utilsPlugin.ts new file mode 100644 index 0000000..890c2ee --- /dev/null +++ b/src/plugins/modules/utilsPlugin.ts @@ -0,0 +1,26 @@ +import type { App, InjectionKey } from 'vue'; +import * as common from '../../utils/helper/common'; +import * as dataHelpers from '../../utils/helper/dataHelpers'; +import * as is from '../../utils/helper/is'; + +export const COMMON_KEY: InjectionKey = Symbol('$common'); +export const IS_KEY: InjectionKey = Symbol('$is'); +export const DATA_HELPERS_KEY: InjectionKey = Symbol('$dataHelpers'); + +const utilsPlugin = { + install(app: App) { + // common工具 + app.config.globalProperties.$common = common; + app.provide(COMMON_KEY, common); + + // 类型检测工具 + app.config.globalProperties.$is = is; + app.provide(IS_KEY, is); + + // 数据处理工具 + app.config.globalProperties.$dataHelpers = dataHelpers; + app.provide(DATA_HELPERS_KEY, dataHelpers); + }, +}; + +export default utilsPlugin; diff --git a/src/utils/helper/common.ts b/src/utils/helper/common.ts new file mode 100644 index 0000000..7356741 --- /dev/null +++ b/src/utils/helper/common.ts @@ -0,0 +1,7 @@ +/** + * 非处理数据的功能性工具汇总 + * + */ +export function test() { + return ''; +} diff --git a/src/utils/helper/dataHelpers.ts b/src/utils/helper/dataHelpers.ts new file mode 100644 index 0000000..49f026e --- /dev/null +++ b/src/utils/helper/dataHelpers.ts @@ -0,0 +1,30 @@ +import type { MomentInput } from 'moment'; +import moment from 'moment'; + +/** + * 数据处理工具汇总 + * formatDate 日期格式化 + */ + +/** + * 日期格式化字符串形式 + * @param date 日期输入(支持时间戳、日期字符串、Date 对象或 Moment 对象) + * @param format 模式,默认为 'YYYY-MM-DD HH:mm:ss' + * @returns 格式化时间字符串,若输入无效则返回空字符串 + */ +export function formatDate( + date: MomentInput | null | undefined, + format: string = 'YYYY-MM-DD HH:mm:ss', +): string { + if (!date) { + return ''; + } + + const momentDate = moment(date); + if (!momentDate.isValid()) { + console.warn('Invalid date input:', date); + return ''; + } + + return momentDate.format(format); +} diff --git a/src/utils/helper/is.ts b/src/utils/helper/is.ts new file mode 100644 index 0000000..4e3d24f --- /dev/null +++ b/src/utils/helper/is.ts @@ -0,0 +1,59 @@ +/* + * 数据类型检测工具汇总 + * * * @function isArray: 检测给定的值是否为数组 + * * * @function isObject: 检测给定的值是否为对象(注意:在JavaScript中,数组和null也会被认为是对象) + * * * @function isString: 检测给定的值是否为字符串 + * * * @function isNumber: 检测给定的值是否为数字 + * * * @function isFunction: 检测给定的值是否为函数(包括异步函数) + * * * @function isAsyncFunction: 检测给定的值是否为异步函数 + * * * @function isRegExp: 检测给定的值是否为正则表达式对象 + * * * @function isDef: 检测给定的值是否已定义(即不是undefined) + * * * @function isUnDef: 检测给定的值是否未定义(即为undefined) + * * * @function isNull: 检测给定的值是否为null + */ + +type DataType = 'Array' | 'Object' | 'String' | 'Number' | 'Function' | 'AsyncFunction' | 'RegExp' | 'Undefined' | 'Null'; + +export function dataTypeCheck(value: unknown, type: T): value is T extends 'Array' ? unknown[] : T extends 'Object' ? object : T extends 'String' ? string : T extends 'Number' ? number : T extends 'Function' ? (...args: any[]) => any : T extends 'AsyncFunction' ? (...args: any[]) => Promise : T extends 'RegExp' ? RegExp : T extends 'Undefined' ? undefined : T extends 'Null' ? null : never { + return Object.prototype.toString.call(value) === `[object ${type}]`; +} + +export function isArray(value: unknown): value is unknown[] { + return dataTypeCheck(value, 'Array'); +} + +export function isObject(value: unknown): value is object { + return dataTypeCheck(value, 'Object'); +} + +export function isString(value: unknown): value is string { + return dataTypeCheck(value, 'String'); +} + +export function isNumber(value: unknown): value is number { + return dataTypeCheck(value, 'Number'); +} + +export function isFunction(value: unknown): value is (...args: any[]) => any { + return dataTypeCheck(value, 'Function') || isAsyncFunction(value); +} + +export function isAsyncFunction(value: unknown): value is (...args: any[]) => Promise { + return dataTypeCheck(value, 'AsyncFunction'); +} + +export function isRegExp(value: unknown): value is RegExp { + return dataTypeCheck(value, 'RegExp'); +} + +export function isDef(value: T): value is NonNullable { + return !dataTypeCheck(value, 'Undefined'); +} + +export function isUnDef(value: unknown): value is undefined { + return !isDef(value); +} + +export function isNull(value: unknown): value is null { + return dataTypeCheck(value, 'Null'); +}