diff --git a/.eslintrc.js b/.eslintrc.js index f63fef1..1c45832 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,6 +30,7 @@ module.exports = { defineEmits: 'readonly', defineExpose: 'readonly', withDefaults: 'readonly', + NodeJS: true, }, rules: { // @typescript-eslint diff --git a/packages/nice2cu-ui-mobile-example/src/router/routes.ts b/packages/nice2cu-ui-mobile-example/src/router/routes.ts index 34ba01a..4180771 100644 --- a/packages/nice2cu-ui-mobile-example/src/router/routes.ts +++ b/packages/nice2cu-ui-mobile-example/src/router/routes.ts @@ -188,4 +188,12 @@ export default [ title: '索引列表', }, }, + { + path: '/toast', + name: 'Toast', + component: () => import(/* webpackChunkName: "Toast" */ '@views/componentsViews/Toast.vue'), + meta: { + title: '消息提示', + }, + }, ]; diff --git a/packages/nice2cu-ui-mobile-example/src/views/HomeIndex.vue b/packages/nice2cu-ui-mobile-example/src/views/HomeIndex.vue index 2120c4d..43f3a36 100644 --- a/packages/nice2cu-ui-mobile-example/src/views/HomeIndex.vue +++ b/packages/nice2cu-ui-mobile-example/src/views/HomeIndex.vue @@ -19,5 +19,6 @@ + diff --git a/packages/nice2cu-ui-mobile-example/src/views/componentsViews/Toast.vue b/packages/nice2cu-ui-mobile-example/src/views/componentsViews/Toast.vue new file mode 100644 index 0000000..d17fd39 --- /dev/null +++ b/packages/nice2cu-ui-mobile-example/src/views/componentsViews/Toast.vue @@ -0,0 +1,126 @@ + + + diff --git a/packages/nice2cu-ui/src/index.ts b/packages/nice2cu-ui/src/index.ts index 0ce912a..24c1c76 100644 --- a/packages/nice2cu-ui/src/index.ts +++ b/packages/nice2cu-ui/src/index.ts @@ -22,3 +22,4 @@ export * from './nDivider'; export * from './nTag'; export * from './nCard'; export * from './nIndexList'; +export * from './nToast'; diff --git a/packages/nice2cu-ui/src/nLoading/LoadingProps.ts b/packages/nice2cu-ui/src/nLoading/LoadingProps.ts index fc82134..7935c82 100644 --- a/packages/nice2cu-ui/src/nLoading/LoadingProps.ts +++ b/packages/nice2cu-ui/src/nLoading/LoadingProps.ts @@ -1,11 +1,3 @@ -/* - * @Author: linMeow linjinnan1998@sina.com - * @Date: 2022-11-26 13:35:08 - * @LastEditors: linMeow linjinnan1998@sina.com - * @LastEditTime: 2023-07-03 17:19:45 - * @FilePath: /nice2cu/packages/nice2cu-ui/src/nLoading/LoadingProps.ts - * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE - */ import { ExtractPropTypes, PropType } from 'vue'; export function typeValidator(type: string): boolean { diff --git a/packages/nice2cu-ui/src/nToast/Toast.vue b/packages/nice2cu-ui/src/nToast/Toast.vue new file mode 100644 index 0000000..0333c64 --- /dev/null +++ b/packages/nice2cu-ui/src/nToast/Toast.vue @@ -0,0 +1,107 @@ + + + + diff --git a/packages/nice2cu-ui/src/nToast/ToastProps.ts b/packages/nice2cu-ui/src/nToast/ToastProps.ts new file mode 100644 index 0000000..8fcdc4f --- /dev/null +++ b/packages/nice2cu-ui/src/nToast/ToastProps.ts @@ -0,0 +1,62 @@ +import { ExtractPropTypes, PropType, VNode } from 'vue'; + +const typeValidator = (position: string) => { + return ['default', 'success', 'warning', 'error'].includes(position); +}; + +export function loadingTypeValidator(type: string): boolean { + return ['circle', 'time', 'wave', 'point', 'rever', 'bounce', 'battery'].includes(type); +} + +export const ToastProps = { + id: { + type: [String, Number], + default: '', + }, + type: { + type: String as PropType<'default' | 'success' | 'warning' | 'error'>, + default: 'default', + validator: typeValidator, + }, + content: { + type: [String, Function, Object] as PropType VNode)>, + defualt: '', + }, + duration: { + type: Number, + default: 2000, + }, + customStyle: { + type: Object, + default: {}, + }, + icon: { + type: String, + defalt: '', + }, + iconSize: { + type: [String, Number], + default: '36px', + }, + iconPrefix: { + type: String, + default: 'nice2cu-icon', + }, + loading: { + type: Boolean, + default: false, + }, + loadingType: { + type: String as PropType<'circle' | 'time' | 'wave' | 'point' | 'rever' | 'bounce' | 'battery'>, + default: 'circle', + validator: loadingTypeValidator, + }, + hasMask: { + type: Boolean, + default: false, + }, + clearToast: Function, + onClose: Function, +}; + +export type ToastPropsType = ExtractPropTypes; diff --git a/packages/nice2cu-ui/src/nToast/index.ts b/packages/nice2cu-ui/src/nToast/index.ts new file mode 100644 index 0000000..a585c8c --- /dev/null +++ b/packages/nice2cu-ui/src/nToast/index.ts @@ -0,0 +1,130 @@ +import { createVNode, render, toRaw, toRefs } from 'vue'; +import _toast from './Toast.vue'; +import { withInstall } from '../../utils/withInstall'; +import { CreateComponent } from '../../utils/components'; + +const nToast = withInstall(_toast); + +const defaultToastOptions = { + id: '', + type: 'default', + content: '', + icon: '', + iconSize: '30px', + iconPrefix: 'nice2cu-icon', + loading: false, + duration: 2000, + hasMask: false, + customStyle: {}, + clearToast: null, + onClose: null, +}; +let cacheOptions: any[] = []; +let cacheComp: any[] = []; +let idsMap: string[] = []; +let optsMap: any[] = []; + +export default nToast; + +export { nToast }; + +const updateToast = (options: any, comp: any) => { + const container = document.getElementById(`NToast-${options.id}`); + if (container) { + const currentOpt = optsMap.find((item) => item.id === options.id); + if (currentOpt) { + options = { ...defaultToastOptions, ...currentOpt, ...options }; + } else { + options = { ...defaultToastOptions, ...options }; + } + + const compData = toRaw(comp.instance); + const state = toRefs(compData.state); + for (const key in state) { + if (Object.prototype.hasOwnProperty.call(options, key)) { + state[key].value = options[key]; + } + } + + return compData; + } +}; + +const clearToast = (id?: string) => { + if (id) { + const container = document.getElementById(`NToast-${id}`); + optsMap = optsMap.filter((item) => item.id !== id); + idsMap = idsMap.filter((item) => item !== id); + const cacheOptionsData = cacheOptions.filter((item) => item.id === id); + cacheOptions = cacheOptions.filter((item) => item.id !== id); + cacheComp = cacheComp.filter((item) => item.id !== id); + if (container) { + if (cacheOptionsData.length > 0 && cacheOptionsData[0].onClose) { + cacheOptionsData[0].onClose(); + } + document.body.removeChild(container); + } + } else { + idsMap.forEach((item) => { + const container = document.getElementById(`NToast-${item}`); + if (container) { + document.body.removeChild(container); + } + }); + cacheOptions.forEach((item) => { + if (item.onClose) { + item.onClose(); + } + }); + optsMap = []; + idsMap = []; + cacheOptions = []; + cacheComp = []; + } +}; + +const createdToast = (options: any) => { + options.clearToast = clearToast; + + let _id = null; + if (options.id) { + _id = options.id; + if (idsMap.find((item) => item === options.id)) { + const compData = cacheComp.find((item) => item.id === options.id); + return updateToast(options, compData.comp); + } + } else { + _id = new Date().getTime() + ''; + } + + options = { ...defaultToastOptions, ...options }; + options.id = _id; + + idsMap.push(options.id); + optsMap.push(options); + cacheOptions.push(options); + + const comp = CreateComponent(options, { wrapper: _toast, name: 'NToast' }); + + cacheComp.push({ id: options.id, comp }); + + return toRaw(comp.instance); +}; + +export const Toast = { + showToast(options: any) { + return createdToast(options); + }, + showToastLoading(options?: any) { + return createdToast({ ...{ content: '加载中...', loading: true }, ...options }); + }, + hideToastLoading(id?: string) { + return clearToast(id); + }, +}; + +declare module 'vue' { + export interface GlobalComponents { + nToast: typeof nToast; + } +} diff --git a/packages/nice2cu-ui/src/nToast/style/toast.less b/packages/nice2cu-ui/src/nToast/style/toast.less new file mode 100644 index 0000000..89ee44b --- /dev/null +++ b/packages/nice2cu-ui/src/nToast/style/toast.less @@ -0,0 +1,55 @@ +.n-toast { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + min-width: 100px; + background-color: rgba(0, 0, 0, .7); + color: #fff; + padding: 8px 12px; + border-radius: 5px; + z-index: 9999; + text-align: center; + font-size: 14px; + color: rgba(255, 255, 255, .87); + border-color: currentColor; + + &__mask { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 9998; + overflow: hidden; + user-select: none; + } + + &-inner { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 10px 0; + } +} + +.toast-fade-enter-active { + animation: toast-fade-in 0.2s; +} + +.toast-fade-leave-active { + animation: toast-fade-in 0.2s reverse; +} + +@keyframes toast-fade-in { + 0% { + transform: translate(-50%, -50%) scale(0); + opacity: 0; + } + + 100% { + transform: translate(-50%, -50%) scale(1); + opacity: 1; + } +} \ No newline at end of file diff --git a/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.ttf b/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.ttf index 12addc6..74d9b88 100644 Binary files a/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.ttf and b/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.ttf differ diff --git a/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff b/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff index 8c7098a..536b3d1 100644 Binary files a/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff and b/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff differ diff --git a/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff2 b/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff2 index a4dd794..5e6379d 100644 Binary files a/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff2 and b/packages/nice2cu-ui/theme-chalk/font/nice2cuUI-icon.woff2 differ diff --git a/packages/nice2cu-ui/theme-chalk/mixins/font.less b/packages/nice2cu-ui/theme-chalk/mixins/font.less index 995acbb..65c8a7c 100644 --- a/packages/nice2cu-ui/theme-chalk/mixins/font.less +++ b/packages/nice2cu-ui/theme-chalk/mixins/font.less @@ -13,6 +13,18 @@ -moz-osx-font-smoothing: grayscale; } +.n-error:before { + content: "\e7f9"; +} + +.n-success:before { + content: "\e7f7"; +} + +.n-warning:before { + content: "\e7f8"; +} + .n-sey-garbage-outline:before { content: "\e7f5"; } diff --git a/packages/nice2cu-ui/theme-chalk/mixins/variable.less b/packages/nice2cu-ui/theme-chalk/mixins/variable.less index f801d65..16b43fe 100644 --- a/packages/nice2cu-ui/theme-chalk/mixins/variable.less +++ b/packages/nice2cu-ui/theme-chalk/mixins/variable.less @@ -51,7 +51,7 @@ --button-small-padding: 0 12px; --button-small-height: 28px; --button-normal-padding: 0 15px; - --button-normal-height: 30px; + --button-normal-height: 34px; --button-large-padding: 0 18px; --button-large-height: 40px; --button-disabled-color: var(--color-disabled); diff --git a/packages/nice2cu-ui/utils/components.ts b/packages/nice2cu-ui/utils/components.ts new file mode 100644 index 0000000..bc95098 --- /dev/null +++ b/packages/nice2cu-ui/utils/components.ts @@ -0,0 +1,46 @@ +import { createApp, Component } from 'vue'; +import { isString, isFunction } from './tools'; + +export const CreateComponent = (options: any, component: any) => { + let elWrap: HTMLElement = document.body; + const wrapperContainer = (options.wrapper as string) || 'body'; + + if (wrapperContainer != 'body') { + if (isString(wrapperContainer)) { + elWrap = document.querySelector(wrapperContainer) as HTMLElement; + } else { + elWrap = options.wrapper as HTMLElement; + } + } + + const div = document.createElement('div'); + const name = component.name ? component.name + '-' : ''; + const id = options.id || new Date().getTime(); + div.id = name + id; + + let Wrapper = {}; + + if (isFunction(component.wrapper)) { + Wrapper = component.wrapper(elWrap, div); + } else { + Wrapper = component.wrapper; + } + + const instance: Component = createApp(Wrapper, options); + + const componens = component.components; + + componens && + componens.forEach((comp: Component) => { + instance.use(comp); + }); + elWrap.appendChild(div); + + return { + instance: instance.mount(div), + unmount: () => { + instance.unmount(); + elWrap.removeChild(div); + }, + }; +}; diff --git a/packages/nice2cu-ui/utils/tools.ts b/packages/nice2cu-ui/utils/tools.ts index 289b115..13e2d43 100644 --- a/packages/nice2cu-ui/utils/tools.ts +++ b/packages/nice2cu-ui/utils/tools.ts @@ -26,6 +26,8 @@ export const isPromise = (val: unknown): val is Promise => val instanceof P export const isSameVNodeType = (node1: VNode, node2: VNode) => node1.type === node2.type && node1.key === node2.key; +export const isFunction = (val: unknown): val is unknown => typeof val === 'function'; + export const isElement = (node: Element) => { const ELEMENT_NODE_TYPE = 1; return node.tagName !== 'HTML' && node.tagName !== 'BODY' && node.nodeType === ELEMENT_NODE_TYPE;