diff --git a/.env.production b/.env.production index d4ddf677..402ad3fd 100644 --- a/.env.production +++ b/.env.production @@ -1,6 +1,4 @@ #生产环境 -NODE_ENV = 'production' - VITE_APP_URL = '/' # office 服务代理地址 diff --git a/CHANGELOG.md b/CHANGELOG.md index ed90f8ec..ec6ff8b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # CHANGE LOG +## 4.3.4 + +更新了 MenuTag 的样式,现在有更加细腻的过渡动画。 + +针对 `utils` 下的方法,修复 `utils/element` 中的部分方法因为 `ref` 注册 `dom` 的时候不能正确的触发方法的问题。并且修复了部分方法类型的不准确问题;补充了一些示例。 + +由于 vite 不再支持显式声明 .env=production 配置文件 NODE_ENV=production,所以该版本移除了配置文件的 NODE_ENV 声明。 + +修复构建提示循环依赖问题。 + +### Feats + +- 更新了 MenuTag 的动画效果 +- 基于 `print-js` 与 `vue hooks` 开发新 `print` 方法,存放于 `utils/basic` +- 移除 .env.production 文件的 NODE_ENV 显式声明 +- 优化构建 chunk + +### Fixes + +- 修复 `utils/element` 方法不能正确获取 `ref` 绑定 `dom` 的问题 +- 修复设置界面抛出治毒警告问题 +- 修复构建提示循环依赖问题 + ## 4.3.3 紧跟尤大大脚步,更新 `vite` 版本至 `5.0.0` 版本!与此同时,更新了配套所有插件! diff --git a/README-ZH.md b/README-ZH.md index e197022c..b0a2346b 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -9,12 +9,13 @@ 简体中文 | [English](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README.md) -一个基于 vite4.x & ts(x) & pinia & vue3.x 的中后台模板 +一个 `免费`、`高效`、`特性完整` 并且基于 vite4.x & ts(x) & pinia & vue3.x 等最新技术的中后台模板。 ## ✨ 特性 +- **靠爱发电**:几乎包含市面常见的模板特性并且全部免费使用 - **最新技术栈**:使用 vue3.x/vite4.x/pinia 等前端前沿技术开发 - **TypeScript**:应用程序级 JavaScript 的语言 - **主题**:可配置的主题 diff --git a/README.md b/README.md index 0c788e43..aa9c0227 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,13 @@ English | [简体中文](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README-ZH.md) -A middle and backend template based on vite4.x & ts(x) & pinia & vue3.x +A `free`, `efficient`, `complete with features` middle and backend template based on the latest technologies such as vite4.x & ts(x) & pinia & vue3.x. ## ✨ Feature +- **Power by love**: Contains almost all common template features on the market and all are free to use. - **Latest Technology Stack**:Developed using front-end cutting-edge technologies such as vue3.x/vite4.x/pinia. - **TypeScript**:The language for application-level JavaScript. - **App Theme**:Configurable themes. diff --git a/package.json b/package.json index fdb45f6f..1bba92e4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ray-template", "private": false, - "version": "4.3.3", + "version": "4.3.4", "type": "module", "engines": { "node": "^18.0.0 || >=20.0.0", @@ -9,7 +9,7 @@ }, "scripts": { "dev": "vite", - "build": "vue-tsc --noEmit && vite build --mode production", + "build": "vue-tsc --noEmit && vite build", "preview": "vite preview", "test": "vue-tsc --noEmit && vite build --mode test", "dev-build": "vue-tsc --noEmit && vite build --mode development", diff --git a/src/components/RForm/index.ts b/src/components/RForm/index.ts new file mode 100644 index 00000000..aff8fcb6 --- /dev/null +++ b/src/components/RForm/index.ts @@ -0,0 +1,5 @@ +import RForm from './src/RForm' +import props from './src/props' + +export default RForm +export { props } diff --git a/src/components/RForm/src/RForm.tsx b/src/components/RForm/src/RForm.tsx new file mode 100644 index 00000000..65187728 --- /dev/null +++ b/src/components/RForm/src/RForm.tsx @@ -0,0 +1,25 @@ +/** + * + * @author Ray + * + * @date 2023-11-18 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import { NForm } from 'naive-ui' + +import props from './props' + +export default defineComponent({ + name: 'RForm', + props, + setup() { + return {} + }, + render() { + return + }, +}) diff --git a/src/components/RForm/src/props.ts b/src/components/RForm/src/props.ts new file mode 100644 index 00000000..26851747 --- /dev/null +++ b/src/components/RForm/src/props.ts @@ -0,0 +1,18 @@ +/** + * + * @author Ray + * + * @date 2023-11-18 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import { formProps } from 'naive-ui' + +const props = { + ...formProps, +} + +export default props diff --git a/src/components/RTable/src/components/Print.tsx b/src/components/RTable/src/components/Print.tsx index b2e5d71a..89ec9ef5 100644 --- a/src/components/RTable/src/components/Print.tsx +++ b/src/components/RTable/src/components/Print.tsx @@ -14,7 +14,7 @@ import RIcon from '@/components/RIcon/index' import config from '../config' import props from '../props' -import print from 'print-js' +import { print } from '@/utils/basic' import type { TableProvider } from '../type' @@ -39,7 +39,7 @@ export default defineComponent({ : '表格', }) - print(options) + print(document.getElementById(uuidTable), options) } return { diff --git a/src/hooks/template/index.ts b/src/hooks/template/index.ts index 922f22ea..eabdabe1 100644 --- a/src/hooks/template/index.ts +++ b/src/hooks/template/index.ts @@ -2,9 +2,10 @@ import { useAppMenu } from './useAppMenu' import { useMainPage } from './useMainPage' import { useMenuTag } from './useMenuTag' import { useRootRoute } from './useRootRoute' +import { useAppSetting } from './useAppSetting' export type { MaximizeOptions } from './useMainPage' export type { Target } from './useAppMenu' export type { CloseMenuTag } from './useMenuTag' -export { useAppMenu, useMainPage, useMenuTag, useRootRoute } +export { useAppMenu, useMainPage, useMenuTag, useRootRoute, useAppSetting } diff --git a/src/hooks/template/useAppSetting.ts b/src/hooks/template/useAppSetting.ts index 098a7981..b3863b51 100644 --- a/src/hooks/template/useAppSetting.ts +++ b/src/hooks/template/useAppSetting.ts @@ -11,7 +11,7 @@ import { useSettingActions } from '@/store' -export function useApp() { +export function useAppSetting() { /** * * @param theme 当前主题色 diff --git a/src/hooks/template/useMenuTag.ts b/src/hooks/template/useMenuTag.ts index 171bf007..234eb391 100644 --- a/src/hooks/template/useMenuTag.ts +++ b/src/hooks/template/useMenuTag.ts @@ -170,15 +170,16 @@ export function useMenuTag() { const normal = normalMenuTagOption(target, 'close') if (normal) { - const { index } = normal + const { index, option } = normal spliceMenTagOptions(index) - if (getMenuKey.value !== getRootPath.value) { - const length = getMenuTagOptions.value.length - const tag = getMenuTagOptions.value[length - 1] + if (option.key === getMenuKey.value) { + const tag = getMenuTagOptions.value[index - 1] - changeMenuModelValue(tag.key as string, tag) + if (tag) { + changeMenuModelValue(tag.key, tag) + } } } } @@ -216,7 +217,7 @@ export function useMenuTag() { if (index <= currentIndex) { if (getMenuKey.value !== option.key) { - changeMenuModelValue(option.key as string, option) + changeMenuModelValue(option.key, option) } } } @@ -245,7 +246,7 @@ export function useMenuTag() { if (currentIndex <= index) { if (getMenuKey.value !== option.key) { - changeMenuModelValue(option.key as string, option) + changeMenuModelValue(option.key, option) } } } diff --git a/src/layout/components/MenuTag/index.scss b/src/layout/components/MenuTag/index.scss index 53a88982..a22a1366 100644 --- a/src/layout/components/MenuTag/index.scss +++ b/src/layout/components/MenuTag/index.scss @@ -41,6 +41,46 @@ $menuTagWrapperWidth: 76px; } } +// 激活标签页关闭按钮样式 +.menu-tag { + .menu-tag__btn { + padding: 7px 10px; + + .menu-tag__btn-icon--hidden { + display: none !important; + } + + .menu-tag__btn-icon { + display: inline; + margin-left: 0; + width: 0; + height: 0; + transition: all 0.3s var(--r-bezier); + overflow: hidden; + opacity: 0; + + & .ray-icon { + transform: translate(-1px, 0px); + } + } + + &:hover { + .menu-tag__btn-icon { + width: 14px; + height: 14px; + margin-left: 5px; + font-size: 12px; + background-color: rgba(0, 0, 0, 0.12); + border-radius: 50%; + padding: 1px; + transition: all 0.3s var(--r-bezier); + opacity: 1; + } + } + } +} + +// 设置 dropdown animate svg 尺寸 .menu-tag__dropdown { & .menu-tag__icon { width: 18px; diff --git a/src/layout/components/MenuTag/index.tsx b/src/layout/components/MenuTag/index.tsx index 059fa2bd..a5e17e31 100644 --- a/src/layout/components/MenuTag/index.tsx +++ b/src/layout/components/MenuTag/index.tsx @@ -31,11 +31,17 @@ import './index.scss' -import { NScrollbar, NTag, NSpace, NLayoutHeader, NDropdown } from 'naive-ui' +import { + NScrollbar, + NSpace, + NLayoutHeader, + NDropdown, + NButton, + NIcon, +} from 'naive-ui' import RIcon from '@/components/RIcon/index' import RMoreDropdown from '@/components/RMoreDropdown/index' -// import Reload from '@/icons/reload.svg?component' import CloseRight from '@/icons/close_right.svg?component' import CloseLeft from '@/icons/close_left.svg?component' @@ -43,7 +49,6 @@ import { useMenuGetters, useMenuActions } from '@/store' import { uuid } from '@/utils/basic' import { hasClass } from '@/utils/element' import { queryElements } from '@use-utils/element' -import { renderNode } from '@/utils/vue/index' import { useMainPage } from '@/hooks/template/index' import { useMenuTag } from '@/hooks/template/index' import { throttle } from 'lodash-es' @@ -180,7 +185,7 @@ export default defineComponent({ const handleTagClick = (option: AppMenuOption) => { actionState.actionDropdownShow = false - changeMenuModelValue(option.key as string, option) + changeMenuModelValue(option.key, option) } /** @@ -194,9 +199,11 @@ export default defineComponent({ const scrollContentElement = Array.from( scroll.childNodes, ) as HTMLElement[] - const findElement = scrollContentElement.find((el) => - hasClass(el, 'n-scrollbar-container'), - ) + const findElement = scrollContentElement.find((el) => { + const has = hasClass(el, 'n-scrollbar-container') + + return has.value + }) return findElement } @@ -411,11 +418,12 @@ export default defineComponent({ height: 28, }, maximize, + getRootPath, } }, render() { - const { iconConfig } = this - const { maximize, closeCurrentMenuTag } = this + const { iconConfig, getRootPath, uuidScrollBar } = this + const { maximize, closeCurrentMenuTag, scrollX, $t } = this return ( @@ -453,7 +461,7 @@ export default defineComponent({ xScrollable ref="scrollRef" {...{ - id: this.uuidScrollBar, + id: uuidScrollBar, }} > {this.getMenuTagOptions.map((curr, idx) => ( - - {renderNode(curr.breadcrumbLabel)} - + {{ + default: () => ( + <> + + {{ + default: () => { + const { + breadcrumbLabel, + meta: { i18nKey }, + } = curr + + if (i18nKey) { + return $t(i18nKey) + } else { + return breadcrumbLabel + } + }, + }} + + {(curr.closeable || + this.getMenuTagOptions.length === 1) && + curr.key !== getRootPath ? ( + + + + ) : ( + // 默认使用一个空 NIcon 占位,避免不能正确的触发动画 + + )} + + ), + }} + ))} @@ -497,7 +549,7 @@ export default defineComponent({ width={iconConfig.width} height={iconConfig.height} customClassName="menu-tag__right-arrow" - onClick={this.scrollX.bind(this, 'right')} + onClick={scrollX.bind(this, 'right')} /> { this.updateContentTransition(value) @@ -139,7 +142,7 @@ const SettingDrawer = defineComponent({ this.changeSwitcher(bool, 'menuTagSwitch') } @@ -147,7 +150,7 @@ const SettingDrawer = defineComponent({ this.changeSwitcher(bool, 'breadcrumbSwitch') } @@ -155,7 +158,7 @@ const SettingDrawer = defineComponent({ this.changeSwitcher(bool, 'watermarkSwitch') } @@ -163,7 +166,7 @@ const SettingDrawer = defineComponent({ this.changeSwitcher(bool, 'copyrightSwitch') } diff --git a/src/store/index.ts b/src/store/index.ts index 06ffa54e..a5da64d6 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -19,22 +19,37 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' // 导出仓库实例,不建议直接使用 store -export { piniaSettingStore } from './modules/setting/index' // import { piniaSettingStore } from '@/store' 即可使用 -export { piniaMenuStore } from './modules/menu/index' -export { piniaSigningStore } from './modules/signing/index' -export { piniaKeepAliveStore } from './modules/keep-alive/index' +import { piniaSettingStore } from './modules/setting/index' // import { piniaSettingStore } from '@/store' 即可使用 +import { piniaMenuStore } from './modules/menu/index' +import { piniaSigningStore } from './modules/signing/index' +import { piniaKeepAliveStore } from './modules/keep-alive/index' // 导出 getters, actions -export { useMenuGetters, useMenuActions } from './hooks/useMenuStore' -export { useSettingGetters, useSettingActions } from './hooks/useSettingStore' -export { useSigningGetters, useSigningActions } from './hooks/useSigningStore' -export { +import { useMenuGetters, useMenuActions } from './hooks/useMenuStore' +import { useSettingGetters, useSettingActions } from './hooks/useSettingStore' +import { useSigningGetters, useSigningActions } from './hooks/useSigningStore' +import { useKeepAliveGetters, useKeepAliveActions, } from './hooks/useKeepAliveStore' import type { App } from 'vue' +export { + piniaSettingStore, + piniaMenuStore, + piniaSigningStore, + piniaKeepAliveStore, + useMenuGetters, + useMenuActions, + useSettingGetters, + useSettingActions, + useSigningGetters, + useSigningActions, + useKeepAliveGetters, + useKeepAliveActions, +} + /** * * 设置并且注册 pinia diff --git a/src/store/modules/menu/index.ts b/src/store/modules/menu/index.ts index 6ef8e418..16b9d47b 100644 --- a/src/store/modules/menu/index.ts +++ b/src/store/modules/menu/index.ts @@ -39,12 +39,8 @@ import { useVueRouter } from '@/hooks/web/index' import { throttle } from 'lodash-es' import { useKeepAliveActions } from '@/store' -import type { AppRouteMeta, AppRouteRecordRaw } from '@/router/type' -import type { - AppMenuOption, - MenuTagOptions, - AppMenuKey, -} from '@/types/modules/app' +import type { AppRouteRecordRaw } from '@/router/type' +import type { AppMenuOption, MenuTagOptions } from '@/types/modules/app' import type { MenuState } from '@/store/modules/menu/type' export const piniaMenuStore = defineStore( diff --git a/src/utils/basic.ts b/src/utils/basic.ts index b3233fb1..c8281fda 100644 --- a/src/utils/basic.ts +++ b/src/utils/basic.ts @@ -1,8 +1,13 @@ +import printJs from 'print-js' +import { unrefElement } from '@/utils/vue/index' +import { watchEffectWithTarget } from '@/utils/vue/index' + import type { ValidateValueType, DownloadAnyFileDataType, BasicTypes, } from '@/types/modules/utils' +import type { BasicTarget, TargetValue } from '@/types/modules/vue' /** * @@ -18,7 +23,10 @@ export const getAppEnvironment = () => { * * @param data 二进制流数据 * - * @returns format binary to base64 of the image + * 将 base64 格式文件转换为图片 + * + * @example + * arrayBufferToBase64Image('base64') => Image */ export const arrayBufferToBase64Image = (data: ArrayBuffer): string | null => { if (!data || data.byteLength) { @@ -42,7 +50,10 @@ export const arrayBufferToBase64Image = (data: ArrayBuffer): string | null => { * @param base64 base64 * @param fileName file name * - * @remark 下载 base64 文件 + * 该方法仅能下载 base64 文件,如果有其他的文件类型需要下载,请看 downloadAnyFile 方法 + * + * @example + * downloadBase64File('base64', 'file name') */ export const downloadBase64File = (base64: string, fileName: string) => { const link = document.createElement('a') @@ -61,6 +72,10 @@ export const downloadBase64File = (base64: string, fileName: string) => { * * @param value 目标值 * @param type 类型 + * + * @example + * isValueType('123', 'String') => true + * isValueType({}, 'Object') => true */ export const isValueType = ( value: unknown, @@ -73,9 +88,11 @@ export const isValueType = ( /** * - * @param length `uuid` 长度 - * @param radix `uuid` 基数 - * @returns `uuid` + * @param length uuid 长度 + * @param radix uuid 基数 + * + * @example + * uuid(8) => 'B8tGcl0FCKJkpO0V' */ export const uuid = (length = 16, radix = 62) => { // 定义可用的字符集,即 0-9, A-Z, a-z @@ -109,7 +126,11 @@ export const uuid = (length = 16, radix = 62) => { * @param data base64, Blob, ArrayBuffer type * @param fileName file name * - * @remark 支持下载任意类型的文件,包括 base64, Blob, ArrayBuffer + * 支持下载任意类型的文件,包括 base64, Blob, ArrayBuffer + * + * @example + * downloadAnyFile('base64', 'file name') + * downloadAnyFile('Blob', 'file name') */ export const downloadAnyFile = ( data: DownloadAnyFileDataType, @@ -167,3 +188,24 @@ export const downloadAnyFile = ( } }) } + +export function print>( + target: T, + options?: printJs.Configuration, +) { + const element = computed(() => unrefElement(target)) + const { printable, ...args } = options ?? {} + + const $print = (element: TargetValue) => { + printJs({ + ...args, + printable: element, + }) + } + + const watcher = watch(element, (ndata) => $print(ndata), { + immediate: true, + }) + + watchEffectWithTarget(watcher) +} diff --git a/src/utils/element.ts b/src/utils/element.ts index 30ddfd03..c941fa78 100644 --- a/src/utils/element.ts +++ b/src/utils/element.ts @@ -9,7 +9,7 @@ import type { ElementSelector, } from '@/types/modules/utils' import type { EventListenerTarget } from '@/types/modules/utils' -import type { BasicTarget } from '@/types/modules/vue' +import type { BasicTarget, TargetValue } from '@/types/modules/vue' /** * @@ -28,13 +28,21 @@ export const on = ( ) => { const targetElement = computed(() => unrefElement(target, window)) - const update = () => { - if (targetElement.value && event && handler) { - targetElement.value.addEventListener(event, handler, useCapture) + const update = < + T extends TargetValue, + >( + element: T, + ) => { + if (element && event && handler) { + element.addEventListener(event, handler, useCapture) } } - watchEffectWithTarget(update) + const watcher = watch(targetElement, (ndata) => update(ndata), { + immediate: true, + }) + + watchEffectWithTarget(watcher) } /** @@ -54,13 +62,21 @@ export const off = ( ) => { const targetElement = computed(() => unrefElement(target, window)) - const update = () => { - if (targetElement.value && event && handler) { - targetElement.value.removeEventListener(event, handler, useCapture) + const update = < + T extends TargetValue, + >( + element: T, + ) => { + if (element && event && handler) { + element.removeEventListener(event, handler, useCapture) } } - watchEffectWithTarget(update) + const watcher = watch(targetElement, (ndata) => update(ndata), { + immediate: true, + }) + + watchEffectWithTarget(watcher) } /** @@ -68,7 +84,11 @@ export const off = ( * @param target Target element dom * @param className 所需添加className,可: 'xxx xxx' | 'xxx' 格式添加(参考向元素绑定 css 语法) * - * @remark 添加元素className(可: 'xxx xxx' | 'xxx'格式添加) + * 添加元素className(可: 'xxx xxx' | 'xxx'格式添加) + * + * @example + * targetDom 当前 class: a-class b-class + * addClass(targetDom, 'c-class') => a-class b-class c-class */ export const addClass = ( target: BasicTarget, @@ -76,19 +96,25 @@ export const addClass = ( ) => { const targetElement = computed(() => unrefElement(target)) - const update = () => { - if (targetElement.value) { + const update = ( + element: TargetValue, + ) => { + if (element) { const classes = className.trim().split(' ') classes.forEach((item) => { if (item) { - targetElement.value!.classList.add(item) + element.classList.add(item) } }) } } - watchEffectWithTarget(update) + const watcher = watch(targetElement, (ndata) => update(ndata), { + immediate: true, + }) + + watchEffectWithTarget(watcher) } /** @@ -96,8 +122,12 @@ export const addClass = ( * @param target Target element dom * @param className 所需删除className,可: 'xxx xxx' | 'xxx' 格式删除(参考向元素绑定 css 语法) * - * @remark 删除元素className(可: 'xxx xxx' | 'xxx'格式删除) - * @remark 如果输入值为 removeAllClass 则会删除该元素所有 class name + * 删除元素className(可: 'xxx xxx' | 'xxx'格式删除) + * 如果输入值为 removeAllClass 则会删除该元素所有 class name + * + * @example + * targetDom 当前 class: a-class b-class + * removeClass(targetDom, 'a-class') => b-class */ export const removeClass = ( target: BasicTarget, @@ -105,10 +135,12 @@ export const removeClass = ( ) => { const targetElement = computed(() => unrefElement(target)) - const update = () => { - if (targetElement.value) { + const update = ( + element: TargetValue, + ) => { + if (element) { if (className === 'removeAllClass') { - const classList = targetElement.value.classList + const classList = element.classList classList.forEach((curr) => classList.remove(curr)) } else { @@ -116,14 +148,18 @@ export const removeClass = ( classes.forEach((item) => { if (item) { - targetElement.value!.classList.remove(item) + element.classList.remove(item) } }) } } } - watchEffectWithTarget(update) + const watcher = watch(targetElement, (ndata) => update(ndata), { + immediate: true, + }) + + watchEffectWithTarget(watcher) } /** @@ -131,25 +167,37 @@ export const removeClass = ( * @param target Target element dom * @param className 查询元素是否含有此className,可: 'xxx xxx' | 'xxx' 格式查询(参考向元素绑定 css 语法) * - * @returns 返回boolean + * 元素是否含有某个className(可: 'xxx xxx' | 'xxx' 格式查询) * - * @remark 元素是否含有某个className(可: 'xxx xxx' | 'xxx' 格式查询) + * @example + * hasClass(targetDom, 'matchClassName') => Ref | Ref */ -export const hasClass = (target: BasicTarget, className: string) => { - const targetElement = unrefElement(target) +export const hasClass = (target: BasicTarget, className: string) => { + const targetElement = computed(() => unrefElement(target)) + const hasClassRef = ref(false) - if (!targetElement) { - return false + const update = >(element: E) => { + if (!element) { + hasClassRef.value = false + } else { + const elementClassName = element.className + + const classes = className + .trim() + .split(' ') + .filter((item: string) => item !== '') + + hasClassRef.value = elementClassName.includes(classes.join(' ')) + } } - const elementClassName = targetElement.className + const watcher = watch(targetElement, (ndata) => update(ndata), { + immediate: true, + }) - const classes = className - .trim() - .split(' ') - .filter((item: string) => item !== '') + watchEffectWithTarget(watcher) - return elementClassName.includes(classes.join(' ')) + return hasClassRef } /** @@ -157,7 +205,6 @@ export const hasClass = (target: BasicTarget, className: string) => { * @param target Target element dom * @param styles 所需绑定样式(如果为字符串, 则必须以分号结尾每个行内样式描述) * - * * @example * style of string * ``` @@ -180,14 +227,13 @@ export const addStyle = ( styles: PartialCSSStyleDeclaration | string, ) => { const targetElement = computed(() => unrefElement(target)) - - if (!targetElement.value) { - return - } - let styleObj: PartialCSSStyleDeclaration - const update = () => { + const update = (element: TargetValue) => { + if (!element) { + return + } + if (isValueType(styles, 'String')) { styleObj = styles.split(';').reduce((pre, curr) => { const [key, value] = curr.split(':').map((s) => s.trim()) @@ -205,13 +251,17 @@ export const addStyle = ( Object.keys(styleObj).forEach((key) => { const value = styleObj[key] - if (key in targetElement.value!.style) { - targetElement.value!.style[key] = value + if (key in element!.style) { + element!.style[key] = value } }) } - watchEffectWithTarget(update) + const watcher = watch(targetElement, (ndata) => update(ndata), { + immediate: true, + }) + + watchEffectWithTarget(watcher) } /** @@ -220,6 +270,7 @@ export const addStyle = ( * @param styles 所需卸载样式 * * 当你发现不能正常的移除某些样式的时候,应该考虑是否是样式表兼容问题 + * * @example * removeStyle(['zIndex', 'z-index']) */ @@ -229,17 +280,21 @@ export const removeStyle = ( ) => { const targetElement = computed(() => unrefElement(target)) - if (!targetElement.value) { - return - } + const update = (element: TargetValue) => { + if (!element) { + return + } - const update = () => { styles.forEach((curr) => { - targetElement.value!.style.removeProperty(curr) + element.style.removeProperty(curr) }) } - watchEffectWithTarget(update) + const watcher = watch(targetElement, (ndata) => update(ndata), { + immediate: true, + }) + + watchEffectWithTarget(watcher) } /** @@ -249,6 +304,9 @@ export const removeStyle = ( * @returns 转换后的 rgba 颜色值 * * @remark 将任意颜色值转为 rgba + * + * @example + * colorToRgba('#123632', 0.8) => rgba(18, 54, 50, 0.8) */ export const colorToRgba = (color: string, alpha = 1) => { const hexPattern = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i diff --git a/src/utils/vue/index.ts b/src/utils/vue/index.ts index c47074de..c7239326 100644 --- a/src/utils/vue/index.ts +++ b/src/utils/vue/index.ts @@ -1,5 +1,7 @@ -export { call } from './call' -export { unrefElement } from './unrefElement' -export { renderNode } from './renderNode' -export { effectDispose } from './effectDispose' -export { watchEffectWithTarget } from './watchEffectWithTarget' +import { call } from './call' +import { unrefElement } from './unrefElement' +import { renderNode } from './renderNode' +import { effectDispose } from './effectDispose' +import { watchEffectWithTarget } from './watchEffectWithTarget' + +export { call, unrefElement, renderNode, effectDispose, watchEffectWithTarget } diff --git a/vite.config.ts b/vite.config.ts index e5d805f3..f303e0c2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -47,9 +47,21 @@ export default defineConfig(async ({ mode }) => { rollupOptions: { output: { manualChunks: (id) => { - if (id.includes('node_modules')) { - const index = id.includes('pnpm') ? 1 : 0 + const isUtils = () => id.includes('src/utils/') + const isHooks = () => + id.includes('src/hooks/template') || id.includes('src/hooks/web') + const isNodeModules = () => id.includes('node_modules') + const index = id.includes('pnpm') ? 1 : 0 + if (isUtils()) { + return 'utils' + } + + if (isHooks()) { + return 'hooks' + } + + if (isNodeModules()) { return id .toString() .split('node_modules/')[1] diff --git a/vite.pliugin.config.ts b/vite.pliugin.config.ts index 559c7dcf..96c96975 100644 --- a/vite.pliugin.config.ts +++ b/vite.pliugin.config.ts @@ -155,6 +155,7 @@ export default function (mode: string): PluginOption[] { customDomId: '__svg__icons__dom__', }), viteCDNPlugin({ + // modules 顺序 vue, vue-demi 必须保持当前顺序加载,否则会出现加载错误问题 modules: [ 'vue', 'vue-demi',