This commit is contained in:
XiaoDaiGua-Ray 2023-11-02 17:51:12 +08:00
parent a840693455
commit 36d646d8ba
66 changed files with 644 additions and 432 deletions

13
.vscode/settings.json vendored
View File

@ -17,5 +17,16 @@
"@mock": "/mock" "@mock": "/mock"
}, },
"alias-skip.allowedsuffix": ["ts", "tsx"], "alias-skip.allowedsuffix": ["ts", "tsx"],
"alias-skip.rootpath": "package.json" "alias-skip.rootpath": "package.json",
"cSpell.words": [
"Clickoutside",
"macarons",
"menutag",
"ndata",
"persistedstate",
"Popselect",
"siderbar",
"WUJIE",
"zlevel"
]
} }

View File

@ -1,5 +1,27 @@
# CHANGE LOG # CHANGE LOG
## 4.2.9
主要更新了命名问题。并且使用单词检查器,扫描整个项目替换了拼写错误的单词。
### Feats
- setVariable 方法新增回调函数参数
- 优化 getVariable ts 类型提示,并且使用 readonly 方法包裹
- 重命名 globalVariableToRefs 为 getVariableToRefs并且使用 readonly 方法包裹
- renderNode 方法新增对于 Slot 类型参数支持
- 新增 BasicTypes 类型
- 提取 RIcon 组件 props 单独维护
- 将 api 管理,提取到 `src` 下维护
- getAppLocalMessages 获取路径方式由相对路径改为绝对路径
- 将 hooks 包中的 variable 方法提取至 `src` 下管理,并且更改名称为 `global-variable`
- 调整 GlobalSearch 样式
### Fixes
- 修复 downloadBase64File 下载后不能正常移除 `a` 标签问题
- 修复 downloadAnyFile 存在的可能未加载完成而提前执行方法的问题
## 4.2.8 ## 4.2.8
我好像犯了一个很愚蠢的错误,那就是使用 useFullscreen 方法的时候总是会弹出提示。所以紧急修复了这个很愚蠢的问题,并且移除了这个方法。 我好像犯了一个很愚蠢的错误,那就是使用 useFullscreen 方法的时候总是会弹出提示。所以紧急修复了这个很愚蠢的问题,并且移除了这个方法。
@ -30,7 +52,7 @@
- RChart - RChart
- 修复 animation false 状态渲染异常问题 - 修复 animation false 状态渲染异常问题
- 修复响应式代理 echart instance 时,导致部分方法异常问题 - 修复响应式代理 echart instance 时,导致部分方法异常问题
## 4.2.7 ## 4.2.7
主要是做了一些统一命名的事情,以前由于写的比较放浪形骸现在正在慢慢更改这个大问题。 主要是做了一些统一命名的事情,以前由于写的比较放浪形骸现在正在慢慢更改这个大问题。

View File

@ -1,7 +1,7 @@
{ {
"name": "ray-template", "name": "ray-template",
"private": false, "private": false,
"version": "4.2.8", "version": "4.2.9",
"type": "module", "type": "module",
"engines": { "engines": {
"node": ">=16.0.0", "node": ">=16.0.0",

View File

@ -16,7 +16,7 @@ export default defineComponent({
<AppGlobalSpin> <AppGlobalSpin>
{{ {{
default: () => <RouterView />, default: () => <RouterView />,
description: () => 'lodaing...', description: () => 'loading...',
}} }}
</AppGlobalSpin> </AppGlobalSpin>
</AppNaiveGlobalProvider> </AppNaiveGlobalProvider>

View File

@ -27,7 +27,7 @@ import { getStorage } from '@/utils/cache'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import type { AvatarProps, SpaceProps } from 'naive-ui' import type { AvatarProps, SpaceProps } from 'naive-ui'
import type { SigninCallback } from '@/store/modules/signin/type' import type { SigningCallback } from '@/store/modules/signing/type'
const AppAvatar = defineComponent({ const AppAvatar = defineComponent({
name: 'AppAvatar', name: 'AppAvatar',
@ -48,7 +48,7 @@ const AppAvatar = defineComponent({
}, },
}, },
setup(props) { setup(props) {
const signin = getStorage<SigninCallback>(APP_CATCH_KEY.signin) const signing = getStorage<SigningCallback>(APP_CATCH_KEY.signing)
const cssVars = computed(() => { const cssVars = computed(() => {
const vars = { const vars = {
'--app-avatar-cursor': props.cursor, '--app-avatar-cursor': props.cursor,
@ -58,7 +58,7 @@ const AppAvatar = defineComponent({
}) })
return { return {
signin, signing,
cssVars, cssVars,
} }
}, },
@ -74,12 +74,12 @@ const AppAvatar = defineComponent({
<NAvatar <NAvatar
// eslint-disable-next-line prettier/prettier, @typescript-eslint/no-explicit-any // eslint-disable-next-line prettier/prettier, @typescript-eslint/no-explicit-any
{...(this.$props as any)} {...(this.$props as any)}
src={this.signin?.avatar} src={this.signing?.avatar}
objectFit="cover" objectFit="cover"
round round
size={this.avatarSize} size={this.avatarSize}
/> />
<div class="app-avatar__name">{this.signin?.name}</div> <div class="app-avatar__name">{this.signing?.name}</div>
</NSpace> </NSpace>
) )
}, },

View File

@ -15,20 +15,20 @@ import { NInput, NForm, NFormItem, NButton, NSpace } from 'naive-ui'
import AppAvatar from '@/app-components/app/AppAvatar/index' import AppAvatar from '@/app-components/app/AppAvatar/index'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useSetting, useSignin } from '@/store' import { useSetting, useSigning } from '@/store'
import { rules, useCondition } from '@/app-components/app/AppLockScreen/hook' import { rules, useCondition } from '@/app-components/app/AppLockScreen/hook'
import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar' import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar'
import { useDevice } from '@/hooks/web/index' import { useDevice } from '@/hooks/web/index'
import type { FormInst, InputInst } from 'naive-ui' import type { FormInst, InputInst } from 'naive-ui'
const UnlockScreen = defineComponent({ export default defineComponent({
name: 'UnlockScreen', name: 'UnlockScreen',
setup() { setup() {
const formRef = ref<FormInst | null>(null) const formRef = ref<FormInst | null>(null)
const inputInstRef = ref<InputInst | null>(null) const inputInstRef = ref<InputInst | null>(null)
const { logout } = useSignin() const { logout } = useSigning()
const { changeSwitcher } = useSetting() const { changeSwitcher } = useSetting()
const { setLockAppScreen } = useAppLockScreen() const { setLockAppScreen } = useAppLockScreen()
const { isTabletOrSmaller } = useDevice() const { isTabletOrSmaller } = useDevice()
@ -55,7 +55,7 @@ const UnlockScreen = defineComponent({
}, 86_400_000) }, 86_400_000)
/** 退出登陆并且回到登陆页 */ /** 退出登陆并且回到登陆页 */
const backToSignin = () => { const backToSigning = () => {
window.$dialog.warning({ window.$dialog.warning({
title: '警告', title: '警告',
content: '是否返回到登陆页?', content: '是否返回到登陆页?',
@ -89,7 +89,7 @@ const UnlockScreen = defineComponent({
return { return {
...toRefs(state), ...toRefs(state),
backToSignin, backToSigning,
unlockScreen, unlockScreen,
formRef, formRef,
inputInstRef, inputInstRef,
@ -138,7 +138,7 @@ const UnlockScreen = defineComponent({
<NButton <NButton
type="primary" type="primary"
text text
onClick={this.backToSignin.bind(this)} onClick={this.backToSigning.bind(this)}
> >
</NButton> </NButton>
@ -165,5 +165,3 @@ const UnlockScreen = defineComponent({
) )
}, },
}) })
export default UnlockScreen

View File

@ -99,12 +99,12 @@ export const APP_MENU_CONFIG: Readonly<AppMenuConfig> = {
* , key * , key
* *
* : * :
* - signin: 登陆信息缓存 key * - signing: 登陆信息缓存 key
* - localeLanguage: 国际化默认缓存 key * - localeLanguage: 国际化默认缓存 key
* - token: token key * - token: token key
*/ */
export const APP_CATCH_KEY = { export const APP_CATCH_KEY = {
signin: 'signin', signing: 'signing',
localeLanguage: 'localeLanguage', localeLanguage: 'localeLanguage',
token: 'token', token: 'token',
} as const } as const

View File

@ -17,6 +17,6 @@
export const APP_REGEX: Record<string, RegExp> = { export const APP_REGEX: Record<string, RegExp> = {
/** css 尺寸单位匹配 */ /** css 尺寸单位匹配 */
validerCSSUnit: validCSSUnit:
/^\d+(\.\d+)?(px|em|rem|%|vw|vh|vmin|vmax|cm|mm|in|pt|pc|ch|ex|q|s|ms|deg|rad|turn|grad|hz|khz|dpi|dpcm|dppx|fr|auto)$/, /^\d+(\.\d+)?(px|em|rem|%|vw|vh|vmin|vmax|cm|mm|in|pt|pc|ch|ex|q|s|ms|deg|rad|turn|grad|hz|khz|dpi|dpcm|dppx|fr|auto)$/,
} }

View File

@ -26,7 +26,7 @@ export default class RequestCanceler {
} }
/** 是否需要加入取消请求表中 */ /** 是否需要加入取消请求表中 */
private isApending(config: AppRawRequestConfig) { private isAppending(config: AppRawRequestConfig) {
return config.cancelConfig?.needCancel ?? true return config.cancelConfig?.needCancel ?? true
} }
@ -55,7 +55,7 @@ export default class RequestCanceler {
* @remark signal , * @remark signal ,
*/ */
addPendingRequest(config: AppRawRequestConfig) { addPendingRequest(config: AppRawRequestConfig) {
if (this.isApending(config)) { if (this.isAppending(config)) {
const requestKey = this.generateRequestKey(config) const requestKey = this.generateRequestKey(config)
if (!this.pendingRequest.has(requestKey)) { if (!this.pendingRequest.has(requestKey)) {

View File

@ -44,7 +44,7 @@ const axiosFetchError: AxiosFetchError = {
requestError: null, requestError: null,
responseError: null, responseError: null,
} }
/** 请求队列(区分 reslove 与 reject 状态) */ /** 请求队列(区分 resolve 与 reject 状态) */
const implement: ImplementQueue = { const implement: ImplementQueue = {
implementRequestInterceptorArray: [], implementRequestInterceptorArray: [],
implementResponseInterceptorArray: [], implementResponseInterceptorArray: [],

View File

@ -34,10 +34,10 @@ export const setupChartTheme = () => {
import.meta.glob('@/echart-themes/**/*.json', { import.meta.glob('@/echart-themes/**/*.json', {
eager: true, eager: true,
}) })
const regx = /\/([^/]+)\.json$/ const regex = /\/([^/]+)\.json$/
const rawThemes = Object.keys(themeRawModules).reduce((pre, curr) => { const rawThemes = Object.keys(themeRawModules).reduce((pre, curr) => {
const name = curr.match(regx)?.[1] const name = curr.match(regex)?.[1]
if (name) { if (name) {
pre.push({ pre.push({

View File

@ -56,7 +56,6 @@ import type { AnyFC } from '@/types/modules/utils'
import type { DebouncedFunc } from 'lodash-es' import type { DebouncedFunc } from 'lodash-es'
import type { UseResizeObserverReturn } from '@vueuse/core' import type { UseResizeObserverReturn } from '@vueuse/core'
import type { ECharts, EChartsCoreOption } from 'echarts/core' import type { ECharts, EChartsCoreOption } from 'echarts/core'
import type { RenderVNodeType } from '@use-utils/vue/renderNode'
import type { DropdownProps, DropdownOption } from 'naive-ui' import type { DropdownProps, DropdownOption } from 'naive-ui'
const defaultChartOptions = { const defaultChartOptions = {
@ -77,7 +76,7 @@ export default defineComponent({
const rayChartWrapperRef = ref<HTMLElement>() const rayChartWrapperRef = ref<HTMLElement>()
const echartInstanceRef = ref<ECharts>() // echart 实例 const echartInstanceRef = ref<ECharts>() // echart 实例
let resizeThrottleReturn: DebouncedFunc<AnyFC> | null // resize 防抖方法实例 let resizeThrottleReturn: DebouncedFunc<AnyFC> | null // resize 防抖方法实例
let resizeOvserverReturn: UseResizeObserverReturn | null let resizeObserverReturn: UseResizeObserverReturn | null
const { echartTheme } = APP_THEME const { echartTheme } = APP_THEME
let watchCallback: WatchStopHandle | null let watchCallback: WatchStopHandle | null
let echartInst: ECharts | null // 无代理响应式代理缓存 echart inst let echartInst: ECharts | null // 无代理响应式代理缓存 echart inst
@ -85,19 +84,16 @@ export default defineComponent({
{ {
label: '下载图片', label: '下载图片',
key: 'downloadChart', key: 'downloadChart',
disabled: disabled: !(
echartInstanceRef.value && echartInstanceRef.value.getDom() echartInstanceRef.value && echartInstanceRef.value.getDom()
? false ),
: true,
}, },
]) ])
const cssVarsRef = computed(() => { const cssVarsRef = computed(() => {
const cssVars = { return {
'--ray-chart-width': completeSize(props.width), '--ray-chart-width': completeSize(props.width),
'--ray-chart-height': completeSize(props.height), '--ray-chart-height': completeSize(props.height),
} }
return cssVars
}) })
/** /**
@ -257,14 +253,16 @@ export default defineComponent({
} }
} }
const isDispose = () => !!(echartInst && echartInst.getDom())
/** /**
* *
* `chart` , * `chart` ,
*/ */
const destroyChart = () => { const destroyChart = () => {
if (echartInst && echartInst.getDom()) { if (isDispose()) {
echartInst.clear() echartInst!.clear()
echartInst.dispose() echartInst!.dispose()
echartInstanceRef.value = void 0 echartInstanceRef.value = void 0
} }
} }
@ -277,11 +275,11 @@ export default defineComponent({
} }
const dropdownSelect = (key: string | number, option: DropdownOption) => { const dropdownSelect = (key: string | number, option: DropdownOption) => {
if (key === 'downloadChart' && echartInst && echartInst.getDom()) { if (key === 'downloadChart' && isDispose()) {
const { filename, ...args } = props.downloadOptions const { filename, ...args } = props.downloadOptions
downloadBase64File( downloadBase64File(
echartInst.getDataURL(args), echartInst!.getDataURL(args),
filename ?? `${new Date().getTime()}`, filename ?? `${new Date().getTime()}`,
) )
} }
@ -310,7 +308,7 @@ export default defineComponent({
resizeThrottleReturn = throttle(resizeChart, props.throttleWait) resizeThrottleReturn = throttle(resizeChart, props.throttleWait)
/** 监听内容区域尺寸变化更新 chart */ /** 监听内容区域尺寸变化更新 chart */
resizeOvserverReturn = useResizeObserver( resizeObserverReturn = useResizeObserver(
props.observer || rayChartWrapperRef, props.observer || rayChartWrapperRef,
resizeThrottleReturn, resizeThrottleReturn,
) )
@ -323,7 +321,7 @@ export default defineComponent({
/** 注销防抖 */ /** 注销防抖 */
resizeThrottleReturn?.cancel() resizeThrottleReturn?.cancel()
/** 注销 observer 监听 */ /** 注销 observer 监听 */
resizeOvserverReturn?.stop?.() resizeObserverReturn?.stop?.()
} }
/** 监听全局主题变化, 然后重新渲染对应主题 echarts */ /** 监听全局主题变化, 然后重新渲染对应主题 echarts */
@ -362,9 +360,9 @@ export default defineComponent({
if (props.watchOptions) { if (props.watchOptions) {
watchCallback = watch( watchCallback = watch(
() => props.options, () => props.options,
(noptions) => { (ndata) => {
/** 重新组合 options */ /** 重新组合 options */
const options = combineChartOptions(noptions) const options = combineChartOptions(ndata)
const setOpt = Object.assign( const setOpt = Object.assign(
props.setChartOptions, props.setChartOptions,
defaultChartOptions, defaultChartOptions,
@ -432,13 +430,13 @@ export default defineComponent({
bordered={bordered} bordered={bordered}
> >
{{ {{
default: () => ( default: renderNode(
<div class="ray-chart__container" ref="rayChartRef"></div> <div class="ray-chart__container" ref="rayChartRef"></div>,
), ),
header: renderNode(title, { header: renderNode(title, {
defaultElement: <div style="display: none;"></div>, defaultElement: <div style="display: none;"></div>,
}), }),
'header-extra': renderNode(cardExtra as RenderVNodeType, { 'header-extra': renderNode(cardExtra, {
defaultElement: ( defaultElement: (
<RMoreDropdown <RMoreDropdown
iconSize={18} iconSize={18}
@ -446,6 +444,7 @@ export default defineComponent({
options={dropdownOptions ?? moreDropDownOptions} options={dropdownOptions ?? moreDropDownOptions}
trigger="click" trigger="click"
onSelect={dropdownSelect.bind(this)} onSelect={dropdownSelect.bind(this)}
placement="bottom-end"
/> />
), ),
}), }),

View File

@ -27,13 +27,13 @@ import RIcon from '@/components/RIcon'
import { call } from '@/utils/vue/index' import { call } from '@/utils/vue/index'
import props from './props' import props from './props'
const RCollapseGrid = defineComponent({ export default defineComponent({
name: 'RCollapseGrid', name: 'RCollapseGrid',
props, props,
setup(props) { setup(props) {
const modelCollapsed = ref(!props.open) const modelCollapsed = ref(!props.open)
const handleCollapse = () => { const collapseClick = () => {
modelCollapsed.value = !modelCollapsed.value modelCollapsed.value = !modelCollapsed.value
const { onUpdateValue, 'onUpdate:value': _onUpdateValue } = props const { onUpdateValue, 'onUpdate:value': _onUpdateValue } = props
@ -48,7 +48,7 @@ const RCollapseGrid = defineComponent({
} }
const CollapseIcon = () => ( const CollapseIcon = () => (
<div class="collapse-icon" onClick={handleCollapse.bind(this)}> <div class="collapse-icon" onClick={collapseClick.bind(this)}>
<span> <span>
{modelCollapsed.value {modelCollapsed.value
? props.collapseToggleText[0] ? props.collapseToggleText[0]
@ -66,7 +66,7 @@ const RCollapseGrid = defineComponent({
return { return {
modelCollapsed, modelCollapsed,
handleCollapse, collapseClick,
CollapseIcon, CollapseIcon,
} }
}, },
@ -97,5 +97,3 @@ const RCollapseGrid = defineComponent({
) )
}, },
}) })
export default RCollapseGrid

View File

@ -13,57 +13,11 @@ import './index.scss'
import { call } from '@/utils/vue/index' import { call } from '@/utils/vue/index'
import { completeSize } from '@/utils/element' import { completeSize } from '@/utils/element'
import props from './props'
import type { PropType } from 'vue' export default defineComponent({
import type { MaybeArray } from '@/types/modules/utils'
const RIcon = defineComponent({
name: 'RIcon', name: 'RIcon',
props: { props,
color: {
type: String,
default: 'currentColor',
},
prefix: {
type: String,
default: 'icon',
},
name: {
type: String,
required: true,
},
size: {
type: [Number, String],
default: 14,
},
width: {
type: [Number, String],
default: 0,
},
height: {
type: [Number, String],
default: 0,
},
customClassName: {
/** 自定义 class name */
type: String,
default: null,
},
depth: {
/** 图标深度 */
type: Number,
default: 1,
},
cursor: {
/** 鼠标指针样式 */
type: String,
default: 'default',
},
onClick: {
type: [Function, Array] as PropType<MaybeArray<(e: MouseEvent) => void>>,
default: null,
},
},
setup(props) { setup(props) {
const symbolId = computed(() => `#${props.prefix}-${props.name}`) const symbolId = computed(() => `#${props.prefix}-${props.name}`)
const cssVars = computed(() => { const cssVars = computed(() => {
@ -111,5 +65,3 @@ const RIcon = defineComponent({
) )
}, },
}) })
export default RIcon

View File

@ -0,0 +1,61 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-Ray>
*
* @date 2023-10-27
*
* @workspace ray-template
*
* @remark
*/
import type { PropType } from 'vue'
import type { MaybeArray } from '@/types/modules/utils'
const props = {
color: {
type: String,
default: 'currentColor',
},
prefix: {
type: String,
default: 'icon',
},
name: {
type: String,
required: true,
},
size: {
type: [Number, String],
default: 14,
},
width: {
type: [Number, String],
default: 0,
},
height: {
type: [Number, String],
default: 0,
},
customClassName: {
/** 自定义 class name */
type: String,
default: null,
},
depth: {
/** 图标深度 */
type: Number,
default: 1,
},
cursor: {
/** 鼠标指针样式 */
type: String,
default: 'default',
},
onClick: {
type: [Function, Array] as PropType<MaybeArray<(e: MouseEvent) => void>>,
default: null,
},
}
export default props

View File

@ -17,7 +17,7 @@ import { completeSize, on, off } from '@use-utils/element'
import { call } from '@/utils/vue/index' import { call } from '@/utils/vue/index'
import props from './props' import props from './props'
const RIframe = defineComponent({ export default defineComponent({
name: 'RIframe', name: 'RIframe',
props, props,
setup(props, { expose }) { setup(props, { expose }) {
@ -61,7 +61,6 @@ const RIframe = defineComponent({
on(iframeRef.value, 'load', iframeLoadSuccess.bind(this)) on(iframeRef.value, 'load', iframeLoadSuccess.bind(this))
on(iframeRef.value, 'error', iframeLoadError) on(iframeRef.value, 'error', iframeLoadError)
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
off(iframeRef.value, 'load', iframeLoadSuccess) off(iframeRef.value, 'load', iframeLoadSuccess)
off(iframeRef.value, 'error', iframeLoadError) off(iframeRef.value, 'error', iframeLoadError)
@ -101,5 +100,3 @@ const RIframe = defineComponent({
) )
}, },
}) })
export default RIframe

View File

@ -13,20 +13,20 @@ import { NDropdown } from 'naive-ui'
import RIcon from '@/components/RIcon/index' import RIcon from '@/components/RIcon/index'
import props from './props' import props from './props'
import { renderNode } from '@use-utils/vue/index'
export default defineComponent({ export default defineComponent({
name: 'RMoreDropdown', name: 'RMoreDropdown',
props, props,
render() { render() {
const { iconSize, cursor } = this const { iconSize, cursor } = this
const { default: $default } = this.$slots
return ( return (
<NDropdown {...this.$props} {...this.$attrs}> <NDropdown {...this.$props} {...this.$attrs} placement="bottom-start">
{this.$slots.default ? ( {renderNode($default, {
this.$slots.default() defaultElement: <RIcon name="more" size={iconSize} cursor={cursor} />,
) : ( })}
<RIcon name="more" size={iconSize} cursor={cursor} />
)}
</NDropdown> </NDropdown>
) )
}, },

View File

@ -27,7 +27,7 @@ const props = {
watchText: { watchText: {
/** /**
* *
* Atuo watch QR code text * Auto watch QR code text
* If update text, then re-render QR code * If update text, then re-render QR code
* *
* @default true * @default true

View File

@ -13,7 +13,7 @@ import './index.scss'
import { NCard, NDataTable, NDropdown, NSpace } from 'naive-ui' import { NCard, NDataTable, NDropdown, NSpace } from 'naive-ui'
import Size from './components/Size' import Size from './components/Size'
import Screenfull from './components/Fullscreen' import Fullscreen from './components/Fullscreen'
import C from './components/C' import C from './components/C'
import Print from './components/Print' import Print from './components/Print'
@ -118,7 +118,7 @@ export default defineComponent({
<> <>
<Print {...p} /> <Print {...p} />
<Size {...p} onChangeSize={changeTableSize.bind(this)} /> <Size {...p} onChangeSize={changeTableSize.bind(this)} />
<Screenfull /> <Fullscreen />
<C {...p} onUpdateColumn={updateTableColumn.bind(this)} /> <C {...p} onUpdateColumn={updateTableColumn.bind(this)} />
</> </>
) )

View File

@ -37,12 +37,17 @@ export const setupDirectives = (app: App<Element>) => {
const regexDirectiveName = /^([^-]+-)*[^-]+$/ const regexDirectiveName = /^([^-]+-)*[^-]+$/
forIn(directivesModules, (value, key) => { forIn(directivesModules, (value, key) => {
const dname = key.match(regexExtractDirectiveName)?.[0] const directiveBindName = key.match(regexExtractDirectiveName)?.[0]
if (typeof dname === 'string' && regexDirectiveName.test(dname)) { if (
app.directive(dname, value()) typeof directiveBindName === 'string' &&
regexDirectiveName.test(directiveBindName)
) {
app.directive(directiveBindName, value())
} else { } else {
console.error(`[setupDirectives] ${dname} is not a valid directive name`) console.error(
`[setupDirectives] ${directiveBindName} is not a valid directive name`,
)
} }
}) })
} }

View File

@ -0,0 +1,30 @@
## global-variable
简单的管理全局响应式变量,但是不支持缓存。
创建该模块是因为在有时候不需要 `pinia` 这么复杂流程的全局变量。所以该模块管理的都是一些简单的全局变量,并且不支持缓存。
## 添加全局变量
1. 首先在 `variableState` 变量中添加对应变量
2. 调用对应方法获取、更新变量
```ts
// 添加变量
const variableState = reactive({
globalSpinning: false,
globalDrawerValue: false,
yourGlobalValue: 'demo value',
})
// 更新变量
setVariable(key, value)
// 获取非响应式变量
const yourGlobalValue = getVariable('yourGlobalValue') // Readonly<demo value>
// 获取响应式变量
const yourGlobalValue = getVariableToRefs('yourGlobalValue') // Readonly<Ref<demo value>>
```
> 注意,避免滥用该模块。可能会导致管理不当引起内存泄漏。

View File

@ -0,0 +1,18 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-Ray>
*
* @date 2023-09-11
*
* @workspace ray-template
*
* @remark
*/
import { setVariable, getVariable, getVariableToRefs } from './variable'
import type { VariableState, VariableStateKey } from './variable'
export { setVariable, getVariable, getVariableToRefs }
export type { VariableState, VariableStateKey }

View File

@ -0,0 +1,62 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-Ray>
*
* @date 2023-09-11
*
* @workspace ray-template
*
* @remark
*/
/**
*
* pinia 使
*
*
* 使
* @example
*
*
* getVariable('target key', 'default value')
*
*
* getVariableToRefs('target key')
*
* state
* setVariable('key', 'value')
*/
import type { AnyFC } from '@/types/modules/utils'
const variableState = reactive({
globalSpinning: false,
globalDrawerValue: false,
})
export type VariableState = typeof variableState
export type VariableStateKey = keyof VariableState
export function setVariable<T extends VariableStateKey>(
key: T,
value: VariableState[T],
cb?: AnyFC,
) {
variableState[key] = value
cb?.()
}
export function getVariable<T extends VariableStateKey>(
key: VariableStateKey,
defaultValue?: VariableState[T],
) {
const v = variableState[key]
return v ? readonly(variableState)[key] : defaultValue
}
export function getVariableToRefs<K extends VariableStateKey>(key: K) {
return readonly(toRef<VariableState, K>(variableState, key))
}

View File

@ -1,18 +0,0 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-Ray>
*
* @date 2023-09-11
*
* @workspace ray-template
*
* @remark
*/
import {
setVariable,
getVariable,
globalVariableToRefs,
} from './useGlobalVariable'
export { setVariable, getVariable, globalVariableToRefs }

View File

@ -1,41 +0,0 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-Ray>
*
* @date 2023-09-11
*
* @workspace ray-template
*
* @remark
*/
/**
*
* pinia 使
*
*
*
*/
/** 全局响应式变量 */
const variableState = reactive({
globalSpinning: false,
globalDrawerValue: false,
})
type VariableStateKey = keyof typeof variableState
export function setVariable(
key: VariableStateKey,
value: (typeof variableState)[VariableStateKey],
) {
variableState[key] = value
}
export function getVariable(key: VariableStateKey) {
return variableState[key] as (typeof variableState)[VariableStateKey]
}
export function globalVariableToRefs<K extends VariableStateKey>(key: K) {
return readonly(toRef<typeof variableState, K>(variableState, key))
}

View File

@ -23,9 +23,9 @@ import type { DayjsLocal } from '@/dayjs/type'
*/ */
export const useDayjs = () => { export const useDayjs = () => {
const locale = (key: DayjsLocal) => { const locale = (key: DayjsLocal) => {
const mapkey = DAYJS_LOCAL_MAP[key] const locale = DAYJS_LOCAL_MAP[key]
mapkey ? dayjs.locale(mapkey) : dayjs.locale(DEFAULT_DAYJS_LOCAL) locale ? dayjs.locale(locale) : dayjs.locale(DEFAULT_DAYJS_LOCAL)
} }
return { return {

6
src/icons/empty.svg Normal file
View File

@ -0,0 +1,6 @@
<svg t="1698916917467" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="10617" width="64" height="64">
<path
d="M776.145455 81.454545l214.10909 296.727273 10.472728 13.963637v552.727272H23.272727V392.145455l10.472728-13.963637L247.854545 81.454545h528.29091zM335.127273 406.109091H51.2v510.836364h921.6V406.109091H684.218182c-5.818182 36.072727-22.109091 69.818182-48.872727 96.581818-33.745455 33.745455-79.127273 52.363636-125.672728 52.363636-47.709091 0-91.927273-18.618182-125.672727-52.363636-25.6-26.763636-43.054545-60.509091-48.872727-96.581818z m621.381818-27.927273L762.181818 109.381818h-500.363636L67.490909 378.181818h293.271273c0 39.563636 16.256 76.8 44.183273 104.727273 27.927273 27.927273 66.327273 44.218182 104.727272 44.218182s76.8-16.290909 104.727273-44.218182c27.927273-27.927273 44.322909-65.163636 44.322909-104.727273H956.509091z"
fill="currentColor" p-id="10618"></path>
</svg>

After

Width:  |  Height:  |  Size: 961 B

View File

@ -17,7 +17,7 @@ import SiderBarLogo from './components/SiderBarLogo/index'
import { useMenu } from '@/store' import { useMenu } from '@/store'
import { APP_MENU_CONFIG } from '@/app-config/appConfig' import { APP_MENU_CONFIG } from '@/app-config/appConfig'
import { useDevice } from '@/hooks/web/index' import { useDevice } from '@/hooks/web/index'
import { globalVariableToRefs, setVariable } from '@/hooks/variable/index' import { getVariableToRefs, setVariable } from '@/global-variable/index'
import type { MenuInst } from 'naive-ui' import type { MenuInst } from 'naive-ui'
import type { NaiveMenuOptions } from '@/types/modules/component' import type { NaiveMenuOptions } from '@/types/modules/component'
@ -49,7 +49,7 @@ export default defineComponent({
const modelCollapsed = computed(() => menuStore.collapsed) const modelCollapsed = computed(() => menuStore.collapsed)
const { isTabletOrSmaller } = useDevice() const { isTabletOrSmaller } = useDevice()
const modelGlobalDrawerValue = computed({ const modelGlobalDrawerValue = computed({
get: () => globalVariableToRefs('globalDrawerValue').value, get: () => getVariableToRefs('globalDrawerValue').value,
set: (val) => { set: (val) => {
setVariable('globalDrawerValue', val) setVariable('globalDrawerValue', val)
}, },
@ -63,7 +63,7 @@ export default defineComponent({
}) })
} }
const BaseicMenu = () => ( const BasicMenu = () => (
<NLayoutSider <NLayoutSider
bordered bordered
showTrigger={!isTabletOrSmaller.value} showTrigger={!isTabletOrSmaller.value}
@ -93,23 +93,24 @@ export default defineComponent({
return { return {
menuRef, menuRef,
isTabletOrSmaller, isTabletOrSmaller,
BaseicMenu, BasicMenu: BasicMenu,
modelGlobalDrawerValue, modelGlobalDrawerValue,
} }
}, },
render() { render() {
const { isTabletOrSmaller, BaseicMenu } = this const { isTabletOrSmaller, BasicMenu } = this
return !isTabletOrSmaller ? ( return !isTabletOrSmaller ? (
<BaseicMenu /> <BasicMenu />
) : ( ) : (
<NDrawer <NDrawer
class="app-menu__drawer" class="app-menu__drawer"
v-model:show={this.modelGlobalDrawerValue} v-model:show={this.modelGlobalDrawerValue}
placement="left" placement="left"
displayDirective="show" displayDirective="show"
autoFocus={false}
> >
<BaseicMenu /> <BasicMenu />
</NDrawer> </NDrawer>
) )
}, },

View File

@ -8,7 +8,7 @@ $menuTagWrapperWidth: 76px;
align-items: center; align-items: center;
padding: 4px 0; padding: 4px 0;
& .menu-tag-sapce { & .menu-tag-space {
width: calc(100% - $space * 2); width: calc(100% - $space * 2);
padding: $space; padding: $space;

View File

@ -35,6 +35,7 @@ import { hasClass } from '@/utils/element'
import { redirectRouterToDashboard } from '@/router/helper/routerCopilot' import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
import { ROOT_ROUTE } from '@/app-config/appConfig' import { ROOT_ROUTE } from '@/app-config/appConfig'
import { queryElements } from '@use-utils/element' import { queryElements } from '@use-utils/element'
import { renderNode } from '@/utils/vue/index'
import type { MenuOption, ScrollbarInst } from 'naive-ui' import type { MenuOption, ScrollbarInst } from 'naive-ui'
import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app' import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app'
@ -58,7 +59,10 @@ export default defineComponent({
const { path } = ROOT_ROUTE const { path } = ROOT_ROUTE
const exclude = ['closeAll', 'closeRight', 'closeLeft', 'closeOther'] const exclude = ['closeAll', 'closeRight', 'closeLeft', 'closeOther']
let currentContentmenuIndex = -1 // 当前右键标签页索引位置 let currentContextmenuIndex = -1 // 当前右键标签页索引位置
const iconConfig = {
size: 16,
}
const modelMenuTagOptions = computed(() => const modelMenuTagOptions = computed(() =>
menuTagOptions.value.map((curr, _idx, currentArray) => { menuTagOptions.value.map((curr, _idx, currentArray) => {
if (curr.key === menuKey.value && curr.key !== path) { if (curr.key === menuKey.value && curr.key !== path) {
@ -86,7 +90,7 @@ export default defineComponent({
h( h(
RIcon, RIcon,
{ {
size: 16, size: iconConfig.size,
name: 'reload', name: 'reload',
}, },
{}, {},
@ -99,7 +103,7 @@ export default defineComponent({
h( h(
RIcon, RIcon,
{ {
size: 16, size: iconConfig.size,
name: 'other', name: 'other',
}, },
{}, {},
@ -112,7 +116,7 @@ export default defineComponent({
h( h(
RIcon, RIcon,
{ {
size: 16, size: iconConfig.size,
name: 'right_arrow', name: 'right_arrow',
}, },
{}, {},
@ -125,7 +129,7 @@ export default defineComponent({
h( h(
RIcon, RIcon,
{ {
size: 16, size: iconConfig.size,
name: 'left_arrow', name: 'left_arrow',
}, },
{}, {},
@ -142,7 +146,7 @@ export default defineComponent({
h( h(
RIcon, RIcon,
{ {
size: 16, size: iconConfig.size,
name: 'close', name: 'close',
}, },
{}, {},
@ -176,16 +180,16 @@ export default defineComponent({
* menuKey , * menuKey ,
*/ */
const length = moreOptions.value.length const length = moreOptions.value.length
const routeItem = modelMenuTagOptions.value[currentContentmenuIndex] const routeItem = modelMenuTagOptions.value[currentContextmenuIndex]
spliceMenTagOptions(currentContentmenuIndex + 1, length - 1) spliceMenTagOptions(currentContextmenuIndex + 1, length - 1)
if (menuKey.value !== routeItem.key) { if (menuKey.value !== routeItem.key) {
changeMenuModelValue(routeItem.key, routeItem) changeMenuModelValue(routeItem.key, routeItem)
} }
}, },
closeLeft: () => { closeLeft: () => {
spliceMenTagOptions(0, currentContentmenuIndex) spliceMenTagOptions(0, currentContextmenuIndex)
}, },
closeOther: () => { closeOther: () => {
/** /**
@ -194,7 +198,7 @@ export default defineComponent({
* *
* menuKey , * menuKey ,
*/ */
const routeItem = modelMenuTagOptions.value[currentContentmenuIndex] const routeItem = modelMenuTagOptions.value[currentContextmenuIndex]
if (menuKey.value !== routeItem.key) { if (menuKey.value !== routeItem.key) {
emptyMenuTagOptions() emptyMenuTagOptions()
@ -306,7 +310,7 @@ export default defineComponent({
e.preventDefault() e.preventDefault()
actionState.actionDropdownShow = false actionState.actionDropdownShow = false
currentContentmenuIndex = idx currentContextmenuIndex = idx
nextTick().then(() => { nextTick().then(() => {
actionState.actionDropdownShow = true actionState.actionDropdownShow = true
@ -318,31 +322,31 @@ export default defineComponent({
const setDisabledAccordionToIndex = () => { const setDisabledAccordionToIndex = () => {
const length = modelMenuTagOptions.value.length - 1 const length = modelMenuTagOptions.value.length - 1
if (currentContentmenuIndex === length) { if (currentContextmenuIndex === length) {
setMoreOptionsDisabled('closeRight', true) setMoreOptionsDisabled('closeRight', true)
} else if (currentContentmenuIndex < length) { } else if (currentContextmenuIndex < length) {
setMoreOptionsDisabled('closeRight', false) setMoreOptionsDisabled('closeRight', false)
} }
if (currentContentmenuIndex === 0) { if (currentContextmenuIndex === 0) {
setMoreOptionsDisabled('closeLeft', true) setMoreOptionsDisabled('closeLeft', true)
} else if (currentContentmenuIndex > 0) { } else if (currentContextmenuIndex > 0) {
setMoreOptionsDisabled('closeLeft', false) setMoreOptionsDisabled('closeLeft', false)
} }
} }
/** /**
* *
* , currentContentmenuIndex * , currentContextmenuIndex
* *
* *
*/ */
const setCurrentContentmenuIndex = () => { const setCurrentContextmenuIndex = () => {
const index = modelMenuTagOptions.value.findIndex( const index = modelMenuTagOptions.value.findIndex(
(curr) => curr.key === menuKey.value, (curr) => curr.key === menuKey.value,
) )
currentContentmenuIndex = index currentContextmenuIndex = index
setDisabledAccordionToIndex() setDisabledAccordionToIndex()
} }
@ -445,13 +449,18 @@ export default defineComponent({
rootPath: path, rootPath: path,
actionState, actionState,
handleContextMenu, handleContextMenu,
setCurrentContentmenuIndex, setCurrentContextmenuIndex,
menuTagMouseenter, menuTagMouseenter,
menuTagMouseleave, menuTagMouseleave,
MENU_TAG_DATA, MENU_TAG_DATA,
} }
}, },
render() { render() {
const iconConfig = {
width: 20,
height: 28,
}
return ( return (
<NLayoutHeader> <NLayoutHeader>
<div class="menu-tag"> <div class="menu-tag">
@ -468,7 +477,7 @@ export default defineComponent({
onSelect={this.actionDropdownSelect.bind(this)} onSelect={this.actionDropdownSelect.bind(this)}
/> />
<NSpace <NSpace
class="menu-tag-sapce" class="menu-tag-space"
wrap={false} wrap={false}
align="center" align="center"
justify="space-between" justify="space-between"
@ -477,8 +486,8 @@ export default defineComponent({
> >
<RIcon <RIcon
name="expanded" name="expanded"
width="20" width={iconConfig.width}
height="28" height={iconConfig.height}
customClassName="menu-tag__left-arrow" customClassName="menu-tag__left-arrow"
onClick={this.scrollX.bind(this, 'left')} onClick={this.scrollX.bind(this, 'left')}
/> />
@ -511,9 +520,7 @@ export default defineComponent({
[this.MENU_TAG_DATA]: curr.path, [this.MENU_TAG_DATA]: curr.path,
}} }}
> >
{typeof curr.label === 'string' {renderNode(curr.label)}
? curr.label
: curr.label?.()}
</NTag> </NTag>
))} ))}
</NSpace> </NSpace>
@ -521,8 +528,8 @@ export default defineComponent({
<div class="menu-tag__right-wrapper"> <div class="menu-tag__right-wrapper">
<RIcon <RIcon
name="expanded" name="expanded"
width="20" width={iconConfig.width}
height="28" height={iconConfig.height}
customClassName="menu-tag__right-arrow" customClassName="menu-tag__right-arrow"
onClick={this.scrollX.bind(this, 'right')} onClick={this.scrollX.bind(this, 'right')}
/> />
@ -534,10 +541,10 @@ export default defineComponent({
> >
<RIcon <RIcon
name="more" name="more"
width="20" width={iconConfig.width}
height="28" height={iconConfig.height}
customClassName="menu-tag__right-setting" customClassName="menu-tag__right-setting"
onClick={this.setCurrentContentmenuIndex.bind(this)} onClick={this.setCurrentContextmenuIndex.bind(this)}
/> />
</RMoreDropdown> </RMoreDropdown>
</div> </div>

View File

@ -1,16 +1,16 @@
$globalSearchWidth: 650px; $globalSearchWidth: 650px;
.global-seach { .global-search {
position: fixed; position: fixed;
width: $globalSearchWidth; width: $globalSearchWidth;
left: 50%; left: 50%;
margin-left: calc(0px - $globalSearchWidth / 2); margin-left: calc(0px - $globalSearchWidth / 2);
top: 60px; top: 60px;
& .global-seach__wrapper { & .global-search__wrapper {
box-sizing: border-box; box-sizing: border-box;
& .global-seach__card { & .global-search__card {
width: $globalSearchWidth; width: $globalSearchWidth;
border-radius: 6px; border-radius: 6px;
padding: 12px; padding: 12px;
@ -19,13 +19,28 @@ $globalSearchWidth: 650px;
color: var(--ray-theme-primary-color); color: var(--ray-theme-primary-color);
} }
& .global-seach__card-header { & .global-search__card-header {
margin-bottom: 12px; margin-bottom: 12px;
} }
& .global-seach__card-content { & .global-search__card-content {
height: auto; height: auto;
max-height: calc(100% - 98px); max-height: calc(100% - 98px);
padding: 8px 0;
& .global-search__empty {
margin: 24px;
}
& .global-search__empty-content {
font-size: 18px;
color: #969faf;
font-weight: 600;
& .ray-icon {
color: #969faf;
}
}
& .content-item { & .content-item {
padding: 12px; padding: 12px;
@ -39,7 +54,7 @@ $globalSearchWidth: 650px;
} }
} }
& .global-seach__card-footer { & .global-search__card-footer {
width: 100%; width: 100%;
& .card-footer__tip-wrapper { & .card-footer__tip-wrapper {
@ -67,12 +82,12 @@ $globalSearchWidth: 650px;
} }
} }
.global-seach--dark { .global-search--dark {
@include useAppTheme("dark") { @include useAppTheme('dark') {
& .global-seach__card { & .global-search__card {
background-color: #242424; background-color: #242424;
& .global-seach__card-content .content-item { & .global-search__card-content .content-item {
background-color: #2f2f2f; background-color: #2f2f2f;
&.content-item--active, &.content-item--active,
@ -84,12 +99,12 @@ $globalSearchWidth: 650px;
} }
} }
.global-seach--light { .global-search--light {
@include useAppTheme("light") { @include useAppTheme('light') {
& .global-seach__card { & .global-search__card {
background-color: #f9f9f9; background-color: #f9f9f9;
& .global-seach__card-content .content-item { & .global-search__card-content .content-item {
background-color: #ffffff; background-color: #ffffff;
&.content-item--active, &.content-item--active,

View File

@ -11,7 +11,15 @@
import './index.scss' import './index.scss'
import { NInput, NModal, NResult, NScrollbar, NSpace } from 'naive-ui' import {
NInput,
NModal,
NResult,
NScrollbar,
NSpace,
NDivider,
NButton,
} from 'naive-ui'
import RIcon from '@/components/RIcon/index' import RIcon from '@/components/RIcon/index'
import { on, off, queryElements, addClass, removeClass } from '@/utils/element' import { on, off, queryElements, addClass, removeClass } from '@/utils/element'
@ -24,7 +32,7 @@ import type { AppRouteMeta } from '@/router/type'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types/modules/app'
export default defineComponent({ export default defineComponent({
name: 'GlobalSeach', name: 'GlobalSearch',
props: { props: {
show: { show: {
type: Boolean, type: Boolean,
@ -51,7 +59,7 @@ export default defineComponent({
searchValue: null, searchValue: null,
searchOptions: [] as AppMenuOption[], searchOptions: [] as AppMenuOption[],
}) })
const tiptextOptions = [ const helperTipOptions = [
{ {
icon: 'cmd / ctrl + k', icon: 'cmd / ctrl + k',
label: '唤起', label: '唤起',
@ -131,7 +139,7 @@ export default defineComponent({
} }
nextTick().then(() => { nextTick().then(() => {
autoFouceSearchItem() autoFocusingSearchItem()
}) })
} }
@ -151,7 +159,7 @@ export default defineComponent({
} }
/** 自动聚焦检索项 */ /** 自动聚焦检索项 */
const autoFouceSearchItem = () => { const autoFocusingSearchItem = () => {
const currentOption = state.searchOptions[searchElementIndex] const currentOption = state.searchOptions[searchElementIndex]
const preOption = state.searchOptions[preSearchElementIndex] const preOption = state.searchOptions[preSearchElementIndex]
@ -239,9 +247,24 @@ export default defineComponent({
break break
} }
autoFouceSearchItem() autoFocusingSearchItem()
} }
const SearchItem = ({ menuOption }: { menuOption: AppMenuOption }) => (
<NSpace
align="center"
wrapItem={false}
class="content-item"
{...{
onClick: handleSearchItemClick.bind(this, menuOption),
data_path: menuOption.path,
}}
>
<div class="content-item-icon">{RenderPreIcon(menuOption.meta)}</div>
<div class="content-item-label">{menuOption.breadcrumbLabel}</div>
</NSpace>
)
watchEffect(() => { watchEffect(() => {
if (isTabletOrSmaller.value) { if (isTabletOrSmaller.value) {
modelShow.value = false modelShow.value = false
@ -264,24 +287,30 @@ export default defineComponent({
return { return {
...toRefs(state), ...toRefs(state),
modelShow, modelShow,
tiptextOptions, helperTipOptions,
handleSearchMenuOptions: debounce(handleSearchMenuOptions, 300), handleSearchMenuOptions: debounce(handleSearchMenuOptions, 300),
handleSearchItemClick, handleSearchItemClick,
RenderPreIcon, RenderPreIcon,
isTabletOrSmaller, isTabletOrSmaller,
SearchItem,
} }
}, },
render() { render() {
const { isTabletOrSmaller } = this const { isTabletOrSmaller, searchOptions } = this
const { SearchItem } = this
return isTabletOrSmaller ? ( return isTabletOrSmaller ? (
<div></div> <div style="display: none;"></div>
) : ( ) : (
<NModal v-model:show={this.modelShow} transform-origin="center" show> <NModal
<div class="global-seach global-seach--dark global-seach--light"> v-model:show={this.modelShow}
<div class="global-seach__wrapper"> transformOrigin="center"
<div class="global-seach__card"> displayDirective="if"
<div class="global-seach__card-header"> >
<div class="global-search global-search--dark global-search--light">
<div class="global-search__wrapper">
<div class="global-search__card">
<div class="global-search__card-header">
<NInput <NInput
size="large" size="large"
v-model:value={this.searchValue} v-model:value={this.searchValue}
@ -293,44 +322,39 @@ export default defineComponent({
}} }}
</NInput> </NInput>
</div> </div>
<NScrollbar class="global-seach__card-content"> <NScrollbar class="global-search__card-content">
{this.searchOptions.length ? ( {searchOptions.length ? (
<NSpace vertical wrapItem={false} size={[8, 8]}> <NSpace vertical wrapItem={false} size={[8, 8]}>
{this.searchOptions.map((curr) => ( {searchOptions.map((curr) => (
<NSpace <SearchItem menuOption={curr} />
align="center"
wrapItem={false}
class="content-item"
{...{
onClick: this.handleSearchItemClick.bind(this, curr),
data_path: curr.path,
}}
>
<div class="content-item-icon">
{this.RenderPreIcon(curr.meta)}
</div>
<div class="content-item-label">
{curr.breadcrumbLabel}
</div>
</NSpace>
))} ))}
</NSpace> </NSpace>
) : ( ) : (
<NResult size="large" description="暂无搜索结果"> <NResult size="large" class="global-search__empty">
{{ {{
icon: () => '', icon: () => null,
default: () => (
<NSpace
wrapItem={false}
justify="center"
class="global-search__empty-content"
>
<RIcon name="empty" size="24" />
</NSpace>
),
}} }}
</NResult> </NResult>
)} )}
</NScrollbar> </NScrollbar>
<div class="global-seach__card-footer"> <div class="global-search__card-footer">
<NSpace <NSpace
class="card-footer__tip-wrapper" class="card-footer__tip-wrapper"
align="center" align="center"
wrapItem={false} wrapItem={false}
size={[24, 8]} size={[24, 8]}
> >
{this.tiptextOptions.map((curr) => ( {this.helperTipOptions.map((curr) => (
<div class="tip-wrapper-item"> <div class="tip-wrapper-item">
<div class="item-icon"> <div class="item-icon">
{curr.plain ? ( {curr.plain ? (
@ -339,7 +363,7 @@ export default defineComponent({
<RIcon name={curr.icon} size="18" /> <RIcon name={curr.icon} size="18" />
)} )}
</div> </div>
<div class="item-laebl">{curr.label}</div> <div class="item-label">{curr.label}</div>
</div> </div>
))} ))}
</NSpace> </NSpace>

View File

@ -1,4 +1,4 @@
import { useSetting, useSignin } from '@/store' import { useSetting, useSigning } from '@/store'
import { useI18n } from '@/hooks/web/index' import { useI18n } from '@/hooks/web/index'
import type { IconOptionsFC, IconOptions } from './type' import type { IconOptionsFC, IconOptions } from './type'
@ -24,8 +24,8 @@ export const createAvatarOptions = () => [
const avatarDropdownActionMap = { const avatarDropdownActionMap = {
logout: () => { logout: () => {
const signinStore = useSignin() const signingStore = useSigning()
const { logout } = signinStore const { logout } = signingStore
window.$dialog.warning({ window.$dialog.warning({
title: '提示', title: '提示',

View File

@ -21,10 +21,10 @@ import './index.scss'
import { NLayoutHeader, NSpace, NDropdown } from 'naive-ui' import { NLayoutHeader, NSpace, NDropdown } from 'naive-ui'
import RIcon from '@/components/RIcon/index' import RIcon from '@/components/RIcon/index'
import TootipIcon from '@/layout/components/SiderBar/components/TooltipIcon/index' import TooltipIcon from '@/layout/components/SiderBar/components/TooltipIcon/index'
import SettingDrawer from './components/SettingDrawer/index' import SettingDrawer from './components/SettingDrawer/index'
import Breadcrumb from './components/Breadcrumb/index' import Breadcrumb from './components/Breadcrumb/index'
import GlobalSeach from './components/GlobalSeach/index' import GlobalSearch from './components/GlobalSearch/index'
import AppAvatar from '@/app-components/app/AppAvatar/index' import AppAvatar from '@/app-components/app/AppAvatar/index'
import { useSetting } from '@/store' import { useSetting } from '@/store'
@ -36,7 +36,7 @@ import {
createRightIconOptions, createRightIconOptions,
} from './hook' } from './hook'
import { useDevice } from '@/hooks/web/index' import { useDevice } from '@/hooks/web/index'
import { globalVariableToRefs, setVariable } from '@/hooks/variable/index' import { getVariableToRefs, setVariable } from '@/global-variable/index'
import { useFullscreen } from 'vue-hooks-plus' import { useFullscreen } from 'vue-hooks-plus'
import { useI18n } from '@/hooks/web/index' import { useI18n } from '@/hooks/web/index'
@ -61,7 +61,7 @@ export default defineComponent({
} }
const globalSearchShown = ref(false) const globalSearchShown = ref(false)
const { isTabletOrSmaller } = useDevice() const { isTabletOrSmaller } = useDevice()
const globalDrawerValue = globalVariableToRefs('globalDrawerValue') const globalDrawerValue = getVariableToRefs('globalDrawerValue')
/** /**
* *
@ -135,7 +135,7 @@ export default defineComponent({
render() { render() {
return ( return (
<NLayoutHeader class="layout-header" bordered> <NLayoutHeader class="layout-header" bordered>
<GlobalSeach v-model:show={this.globalSearchShown} /> <GlobalSearch v-model:show={this.globalSearchShown} />
<NSpace <NSpace
class="layout-header__method" class="layout-header__method"
align="center" align="center"
@ -147,7 +147,7 @@ export default defineComponent({
itemStyle={this.spaceItemStyle} itemStyle={this.spaceItemStyle}
> >
{this.leftIconOptions.map((curr) => ( {this.leftIconOptions.map((curr) => (
<TootipIcon <TooltipIcon
iconName={curr.name} iconName={curr.name}
tooltipText={ tooltipText={
isRef(curr.tooltip) ? curr.tooltip.value : curr.tooltip isRef(curr.tooltip) ? curr.tooltip.value : curr.tooltip
@ -164,7 +164,7 @@ export default defineComponent({
itemStyle={this.spaceItemStyle} itemStyle={this.spaceItemStyle}
> >
{this.rightTooltipIconOptions.map((curr) => ( {this.rightTooltipIconOptions.map((curr) => (
<TootipIcon <TooltipIcon
iconName={curr.name} iconName={curr.name}
tooltipText={ tooltipText={
isRef(curr.tooltip) ? curr.tooltip.value : curr.tooltip isRef(curr.tooltip) ? curr.tooltip.value : curr.tooltip

View File

@ -33,7 +33,7 @@ const ContentWrapper = defineComponent({
const { reloadRouteSwitch, contentTransition } = storeToRefs(settingStore) const { reloadRouteSwitch, contentTransition } = storeToRefs(settingStore)
const spinning = ref(false) const spinning = ref(false)
const thmeOverridesSpin: GlobalThemeOverrides['Spin'] = { const themeOverridesSpin: GlobalThemeOverrides['Spin'] = {
opacitySpinning: '0', opacitySpinning: '0',
} }
@ -52,7 +52,7 @@ const ContentWrapper = defineComponent({
return { return {
reloadRouteSwitch, reloadRouteSwitch,
spinning, spinning,
thmeOverridesSpin, themeOverridesSpin,
contentTransition, contentTransition,
} }
}, },
@ -62,7 +62,7 @@ const ContentWrapper = defineComponent({
show={this.spinning || !this.reloadRouteSwitch} show={this.spinning || !this.reloadRouteSwitch}
description="loading..." description="loading..."
size="large" size="large"
themeOverrides={this.thmeOverridesSpin} themeOverrides={this.themeOverridesSpin}
> >
<AppRequestCancelerProvider /> <AppRequestCancelerProvider />
{this.reloadRouteSwitch ? ( {this.reloadRouteSwitch ? (

View File

@ -76,7 +76,7 @@ export const getAppLocalMessages = async (
const message = {} as AppCurrentAppMessages const message = {} as AppCurrentAppMessages
for (const curr of localOptions) { for (const curr of localOptions) {
const msg: AppLocalesModules = await import(`./lang/${curr.key}.ts`) const msg: AppLocalesModules = await import(`@/locales/lang/${curr.key}.ts`)
const key = curr.key const key = curr.key
if (key) { if (key) {
@ -123,7 +123,7 @@ export const naiveLocales = (key: string) => {
* *
* @returns * @returns
* *
* @remak , `main.ts` , `i18n` * @remark , `main.ts` , `i18n`
*/ */
export const getAppDefaultLanguage = () => { export const getAppDefaultLanguage = () => {
const language = getStorage( const language = getStorage(

View File

@ -18,5 +18,7 @@
"CalculatePrecision": "Precision", "CalculatePrecision": "Precision",
"Directive": "Directive", "Directive": "Directive",
"RouterDemo": "Same Level Router Demo", "RouterDemo": "Same Level Router Demo",
"Mock": "Mock" "Mock": "Mock",
"QRCode": "QRCode",
"SvgIcon": "SVG Icon"
} }

View File

@ -18,5 +18,7 @@
"CalculatePrecision": "数字精度", "CalculatePrecision": "数字精度",
"Directive": "指令", "Directive": "指令",
"RouterDemo": "页面详情模式", "RouterDemo": "页面详情模式",
"Mock": "mock 数据" "Mock": "mock 数据",
"QRCode": "二维码",
"SvgIcon": "SVG图标"
} }

View File

@ -2,7 +2,7 @@ import App from './App'
import '@/styles/base.scss' import '@/styles/base.scss'
import 'virtual:svg-icons-register' // `vite-plugin-svg-icons` 脚本 import 'virtual:svg-icons-register' // vite-plugin-svg-icons 脚本,启用 svg 雪碧图
import { setupRouter } from './router/index' import { setupRouter } from './router/index'
import { setupStore } from './store/index' import { setupStore } from './store/index'
@ -40,8 +40,8 @@ const setupTemplate = async () => {
/** /**
* *
* `wujie-micro` * wujie-micro
* 注意: 此处的 `instance` `app` * __WUJIE_MOUNT
*/ */
const setupWujieTemplate = async () => { const setupWujieTemplate = async () => {
let instance: AppType<Element> let instance: AppType<Element>
@ -62,7 +62,6 @@ const setupWujieTemplate = async () => {
/** /**
* *
* 使, `setupTemplate`
* , * ,
* *
* @example * @example

View File

@ -11,7 +11,7 @@
import { permissionRouter } from './permission' import { permissionRouter } from './permission'
import { SETUP_ROUTER_ACTION, SUPER_ADMIN } from '@/app-config/routerConfig' import { SETUP_ROUTER_ACTION, SUPER_ADMIN } from '@/app-config/routerConfig'
import { useSignin } from '@/store' import { useSigning } from '@/store'
import { useVueRouter } from '@/hooks/web/index' import { useVueRouter } from '@/hooks/web/index'
import { ROOT_ROUTE } from '@/app-config/appConfig' import { ROOT_ROUTE } from '@/app-config/appConfig'
import { setStorage } from '@/utils/cache' import { setStorage } from '@/utils/cache'
@ -29,14 +29,14 @@ import type { AppMenuOption } from '@/types/modules/app'
* , * ,
*/ */
export const validRole = (meta: AppRouteMeta) => { export const validRole = (meta: AppRouteMeta) => {
const { signinCallback } = storeToRefs(useSignin()) const { signingCallback } = storeToRefs(useSigning())
const modelRole = computed(() => signinCallback.value.role) const modelRole = computed(() => signingCallback.value.role)
const { role: metaRole } = meta const { role: metaRole } = meta
if (SUPER_ADMIN?.length && SUPER_ADMIN.includes(modelRole.value)) { if (SUPER_ADMIN?.length && SUPER_ADMIN.includes(modelRole.value)) {
return true return true
} else { } else {
// 如果 role 为 undefind 或者空数组, 则认为该路由不做权限过滤 // 如果 role 为 undefined 或者空数组, 则认为该路由不做权限过滤
if (!metaRole || !metaRole?.length) { if (!metaRole || !metaRole?.length) {
return true return true
} }

View File

@ -8,7 +8,7 @@ const qrcode: AppRouteRecordRaw = {
name: 'RQRCode', name: 'RQRCode',
component: () => import('@/views/demo/qrcode/index'), component: () => import('@/views/demo/qrcode/index'),
meta: { meta: {
noLocalTitle: '二维码', i18nKey: t('menu.QRCode'),
icon: 'other', icon: 'other',
order: 3, order: 3,
}, },

View File

@ -8,7 +8,7 @@ const previewSVGIcons: AppRouteRecordRaw = {
name: 'PreviewSVGIcons', name: 'PreviewSVGIcons',
component: () => import('@/views/demo/svg-icons/index'), component: () => import('@/views/demo/svg-icons/index'),
meta: { meta: {
noLocalTitle: 'SVG图标', i18nKey: t('menu.SvgIcon'),
icon: 'other', icon: 'other',
order: 3, order: 3,
}, },

View File

@ -29,7 +29,7 @@
import { NSpin } from 'naive-ui' import { NSpin } from 'naive-ui'
import { spinProps } from 'naive-ui' import { spinProps } from 'naive-ui'
import { globalVariableToRefs } from '@/hooks/variable/index' import { getVariableToRefs } from '@/global-variable/index'
const GlobalSpin = defineComponent({ const GlobalSpin = defineComponent({
name: 'GlobalSpin', name: 'GlobalSpin',
@ -40,7 +40,7 @@ const GlobalSpin = defineComponent({
const overrides = { const overrides = {
opacitySpinning: '0.3', opacitySpinning: '0.3',
} }
const spinValue = globalVariableToRefs('globalSpinning') const spinValue = getVariableToRefs('globalSpinning')
return { return {
spinValue, spinValue,

View File

@ -1,6 +1,6 @@
## 描述 ## 描述
> pinia store 仓库包。存放全局公共状态。 > pinia store 仓库包。存放全局公共状态。如果不需要 pinia 但是又希望使用全局变量,可以使用 `global-variable` 管理数据。
## 约束 ## 约束

View File

@ -20,7 +20,7 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export { useSetting } from './modules/setting/index' // import { useSetting } from '@/store' 即可使用 export { useSetting } from './modules/setting/index' // import { useSetting } from '@/store' 即可使用
export { useMenu } from './modules/menu/index' export { useMenu } from './modules/menu/index'
export { useSignin } from './modules/signin/index' export { useSigning } from './modules/signing/index'
export { useKeepAlive } from './modules/keep-alive/index' export { useKeepAlive } from './modules/keep-alive/index'
import type { App } from 'vue' import type { App } from 'vue'

View File

@ -171,7 +171,7 @@ export const hasMenuIcon = (option: AppMenuOption) => {
return () => icon return () => icon
} }
/** 获取缓存的 menu key, 如果未获取到则使用 ROOTROUTE path 当作默认激活路由菜单 */ /** 获取缓存的 menu key, 如果未获取到则使用 ROOT_ROUTE path 当作默认激活路由菜单 */
export const getCatchMenuKey = () => { export const getCatchMenuKey = () => {
const { path: rootPath } = ROOT_ROUTE const { path: rootPath } = ROOT_ROUTE
const cacheMenuKey = getStorage<AppMenuKey>( const cacheMenuKey = getStorage<AppMenuKey>(

View File

@ -108,15 +108,15 @@ export const useMenu = defineStore(
/** /**
* *
* @param optins menu tag option(s) * @param options menu tag option(s)
* @param isAppend true: (push), false: * @param isAppend true: (push), false:
*/ */
const setMenuTagOptions = ( const setMenuTagOptions = (
optins: MenuTagOptions | MenuTagOptions[], options: MenuTagOptions | MenuTagOptions[],
isAppend = true, isAppend = true,
) => { ) => {
const isArray = Array.isArray(optins) const isArray = Array.isArray(options)
const arr = isArray ? [...optins] : [optins] const arr = isArray ? [...options] : [options]
isAppend isAppend
? menuState.menuTagOptions.push(...arr) ? menuState.menuTagOptions.push(...arr)

View File

@ -1,16 +0,0 @@
export interface SigninForm extends UnknownObjectKey {
name: string
pwd: string
}
export interface SigninCallback extends UnknownObjectKey {
role: string
name: string
avatar?: string
}
export interface SigninResponse extends UnknownObjectKey {
code: number
data: SigninCallback
message: string
}

View File

@ -16,20 +16,20 @@
* *
* 使 sessionStorage * 使 sessionStorage
* *
* signinCallback * SigningCallback
*/ */
import { isEmpty } from 'lodash-es' import { isEmpty } from 'lodash-es'
import { removeStorage } from '@/utils/cache' import { removeStorage } from '@/utils/cache'
import type { import type {
SigninForm, SigningForm,
SigninCallback, SigningCallback,
SigninResponse, SigningResponse,
} from '@/store/modules/signin/type' } from '@/store/modules/signing/type'
export const useSignin = defineStore( export const useSigning = defineStore(
'signin', 'signing',
() => { () => {
const state = reactive({ const state = reactive({
/** /**
@ -37,22 +37,22 @@ export const useSignin = defineStore(
* () * ()
* role , 如果需要更改请同步更改: router/basic.tsrouter/permission.ts * role , 如果需要更改请同步更改: router/basic.tsrouter/permission.ts
*/ */
signinCallback: {} as SigninCallback, signingCallback: {} as SigningCallback,
}) })
/** /**
* *
* @param signinForm * @param SigningForm
* @returns * @returns
* *
* @remark 0: 登陆成功, 1: 登陆失败 * @remark 0: 登陆成功, 1: 登陆失败
*/ */
const signin = (signinForm: SigninForm): Promise<SigninResponse> => { const signing = (SigningForm: SigningForm): Promise<SigningResponse> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!isEmpty(signinForm)) { if (!isEmpty(SigningForm)) {
state.signinCallback = { state.signingCallback = {
role: 'admin', role: 'admin',
name: signinForm.name, name: SigningForm.name,
avatar: avatar:
'https://usc1.contabostorage.com/c2e495d7890844d392e8ec0c6e5d77eb:image/longmao.navigator.png', 'https://usc1.contabostorage.com/c2e495d7890844d392e8ec0c6e5d77eb:image/longmao.navigator.png',
} }
@ -60,7 +60,7 @@ export const useSignin = defineStore(
resolve({ resolve({
code: 0, code: 0,
message: '登陆成功', message: '登陆成功',
data: state.signinCallback, data: state.signingCallback,
}) })
} else { } else {
reject({ reject({
@ -86,14 +86,14 @@ export const useSignin = defineStore(
return { return {
...toRefs(state), ...toRefs(state),
signin, signing,
logout, logout,
} }
}, },
{ {
persist: { persist: {
key: 'piniaSigninStore', key: 'piniaSigningStore',
paths: ['signinCallback'], paths: ['signingCallback'],
storage: sessionStorage, storage: sessionStorage,
}, },
}, },

View File

@ -0,0 +1,16 @@
export interface SigningForm extends UnknownObjectKey {
name: string
pwd: string
}
export interface SigningCallback extends UnknownObjectKey {
role: string
name: string
avatar?: string
}
export interface SigningResponse extends UnknownObjectKey {
code: number
data: SigningCallback
message: string
}

View File

@ -1,10 +1,13 @@
.n-spin-container, // layout content 区域默认开启继承宽高避免 global lodaing 丢失高度
.n-spin-container .n-spin-content { .r-layout-full__viewer-content .n-spin-container,
.r-layout-full__viewer-content .n-spin-container .n-spin-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
// 拓展 AppMenu Item样式
.r-menu--app:not(.n-menu--collapsed) .n-menu-item-content.n-menu-item-content--selected:before, .r-menu--app:not(.n-menu--collapsed) .n-menu-item-content.n-menu-item-content--selected:before,
.r-menu--app:not(.n-menu--collapsed) .n-menu-item-content:hover:before { .r-menu--app:not(.n-menu--collapsed) .n-menu-item-content:hover:before {
border-left: 4px solid var(--ray-theme-primary-color); border-left: 4px solid var(--ray-theme-primary-color);
transition: border-left 0.1s;
} }

View File

@ -14,33 +14,69 @@ export type EventListenerOrEventListenerObject =
| EventListener | EventListener
| EventListenerObject | EventListenerObject
export type ValidteValueType = export type ValidateValueType =
| 'Object' | 'BigUint64Array'
| 'Undefined' | 'BigInt64Array'
| 'Null'
| 'Boolean'
| 'Number'
| 'String'
| 'Symbol'
| 'Function'
| 'Date'
| 'Array'
| 'RegExp'
| 'Map'
| 'Set'
| 'WeakMap'
| 'WeakSet'
| 'ArrayBuffer'
| 'DataView'
| 'Int8Array'
| 'Uint8Array'
| 'Uint8ClampedArray'
| 'Int16Array'
| 'Uint16Array'
| 'Int32Array'
| 'Uint32Array'
| 'Float32Array'
| 'Float64Array' | 'Float64Array'
| 'Float32Array'
| 'Uint32Array'
| 'Int32Array'
| 'Uint16Array'
| 'Int16Array'
| 'Uint8ClampedArray'
| 'Uint8Array'
| 'Int8Array'
| 'DataView'
| 'ArrayBuffer'
| 'WeakSet'
| 'WeakMap'
| 'Set'
| 'Map'
| 'Error'
| 'Date'
| 'RegExp'
| 'Object'
| 'Array'
| 'Function'
| 'BigInt'
| 'Symbol'
| 'String'
| 'Number'
| 'Boolean'
| 'Null'
| 'Undefined'
export type BasicTypes =
| undefined
| null
| boolean
| number
| string
| symbol
| bigint
| Function
| any[]
| object
| RegExp
| Date
| Error
| Map<any, any>
| Set<any>
| WeakMap<object, any>
| WeakSet<object>
| ArrayBuffer
| DataView
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Float32Array
| Float64Array
| BigInt64Array
| BigUint64Array
export type WordArray = CryptoJS.lib.WordArray export type WordArray = CryptoJS.lib.WordArray

View File

@ -1,6 +1,7 @@
import type { import type {
ValidteValueType, ValidateValueType,
DownloadAnyFileDataType, DownloadAnyFileDataType,
BasicTypes,
} from '@/types/modules/utils' } from '@/types/modules/utils'
/** /**
@ -17,7 +18,7 @@ export const getAppEnvironment = () => {
* *
* @param data * @param data
* *
* @returns formate binary to base64 of the image * @returns format binary to base64 of the image
*/ */
export const arrayBufferToBase64Image = (data: ArrayBuffer): string | null => { export const arrayBufferToBase64Image = (data: ArrayBuffer): string | null => {
if (!data || data.byteLength) { if (!data || data.byteLength) {
@ -61,9 +62,9 @@ export const downloadBase64File = (base64: string, fileName: string) => {
* @param value * @param value
* @param type * @param type
*/ */
export const isValueType = <T>( export const isValueType = <T extends BasicTypes>(
value: unknown, value: unknown,
type: ValidteValueType, type: ValidateValueType,
): value is T => { ): value is T => {
const valid = Object.prototype.toString.call(value) const valid = Object.prototype.toString.call(value)
@ -117,32 +118,52 @@ export const downloadAnyFile = (
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
let blobData!: Blob let blobData!: Blob
if (typeof data === 'string') { try {
// 处理 Base64 数据 if (typeof data === 'string') {
downloadBase64File(data, fileName) downloadBase64File(data, fileName)
resolve() resolve()
} else if (data instanceof ArrayBuffer) {
// 处理 ArrayBuffer 数据 return
blobData = new Blob([new Uint8Array(data)], { }
type: 'application/octet-stream',
if (data instanceof ArrayBuffer) {
blobData = new Blob([new Uint8Array(data)], {
type: 'application/octet-stream',
})
} else if (data instanceof File || data instanceof Blob) {
blobData = data
} else {
reject(new Error('Unsupported data type'))
return
}
const url = URL.createObjectURL(blobData)
const link = document.createElement('a')
link.href = url
link.download = fileName
link.style.display = 'none'
const remove = () => {
URL.revokeObjectURL(url)
document.body.removeChild(link)
}
link.addEventListener('load', () => {
remove()
resolve()
}) })
} else {
// 处理 Blob 和 File 数据 link.addEventListener('error', (error) => {
blobData = data remove()
reject(error)
})
document.body.appendChild(link)
link.click()
} catch (error) {
reject(error)
} }
const url = URL.createObjectURL(blobData)
const link = document.createElement('a')
link.href = url
link.download = fileName
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
resolve()
}) })
} }

View File

@ -305,7 +305,7 @@ export const completeSize = (size: number | string, unit = 'px') => {
return size.toString() + unit return size.toString() + unit
} else if ( } else if (
isValueType<string>(size, 'String') && isValueType<string>(size, 'String') &&
APP_REGEX.validerCSSUnit.test(size) APP_REGEX.validCSSUnit.test(size)
) { ) {
return size return size
} else { } else {

View File

@ -170,7 +170,7 @@ export const divide = (...args: CurrencyArguments[]) => {
/** /**
* *
* () * ()
* undefind null 0 * undefined null 0
* *
* @example * @example
* distribute(0, 1) => [0] * distribute(0, 1) => [0]

View File

@ -11,32 +11,34 @@
import { isValueType } from '@/utils/basic' import { isValueType } from '@/utils/basic'
import type { VNode, VNodeChild } from 'vue' import type { VNode, Slot } from 'vue'
export type RenderVNodeType = export type RenderVNodeType<T = unknown> =
| VNode | VNode
| VNodeChild
| (() => VNode) | (() => VNode)
| string | string
| number | number
| undefined | undefined
| null | null
| JSX.Element | JSX.Element
| Slot<T>
export type DefaultElement = NonNullable< export type DefaultElement<T = unknown> = NonNullable<
Omit<RenderVNodeType, 'string' | 'number'> Omit<RenderVNodeType<T>, 'string' | 'number'>
> >
export interface RenderNodeOptions<T extends DefaultElement> { export interface RenderNodeOptions<T extends DefaultElement> {
defaultElement?: T defaultElement?: T
} }
export type RenderNodeReturn = ReturnType<typeof renderNode>
export function renderNode<T extends DefaultElement>( export function renderNode<T extends DefaultElement>(
vnode: RenderVNodeType, vnode: RenderVNodeType,
options?: RenderNodeOptions<T>, options?: RenderNodeOptions<T>,
) { ) {
if (!vnode) { if (!vnode) {
const { defaultElement } = options ?? {} const { defaultElement = null } = options ?? {}
return typeof defaultElement === 'function' return typeof defaultElement === 'function'
? defaultElement ? defaultElement

View File

@ -1,4 +1,5 @@
import './index.scss' import './index.scss'
import { import {
NCard, NCard,
NLayout, NLayout,

View File

@ -1,7 +1,7 @@
import './index.scss' import './index.scss'
import { NCard, NLayout, NSpace, NInput, NButton } from 'naive-ui' import { NCard, NLayout, NSpace, NInput, NButton } from 'naive-ui'
import { getWeather, getTypicode } from '@/axios/api/demo/test' import { getWeather, getTypicode } from '@/api/demo/test'
import { useRequest, useHookPlusRequest } from '@/axios/index' import { useRequest, useHookPlusRequest } from '@/axios/index'
const Axios = defineComponent({ const Axios = defineComponent({

View File

@ -14,9 +14,9 @@ import RTable from '@/components/RTable/index'
import RCollapseGrid from '@/components/RCollapseGrid/index' import RCollapseGrid from '@/components/RCollapseGrid/index'
import { useHookPlusRequest } from '@/axios/index' import { useHookPlusRequest } from '@/axios/index'
import { getPersonList } from '@/axios/api/demo/mock/person' import { getPersonList } from '@/api/demo/mock/person'
import type { Person } from '@/axios/api/demo/mock/person' import type { Person } from '@/api/demo/mock/person'
const MockDemo = defineComponent({ const MockDemo = defineComponent({
name: 'MockDemo', name: 'MockDemo',

View File

@ -1,11 +1,11 @@
import { NForm, NFormItem, NInput, NButton } from 'naive-ui' import { NForm, NFormItem, NInput, NButton } from 'naive-ui'
import { setStorage } from '@/utils/cache' import { setStorage } from '@/utils/cache'
import { useSignin } from '@/store' import { useSigning } from '@/store'
import { useI18n } from '@/hooks/web/index' import { useI18n } from '@/hooks/web/index'
import { APP_CATCH_KEY, ROOT_ROUTE } from '@/app-config/appConfig' import { APP_CATCH_KEY, ROOT_ROUTE } from '@/app-config/appConfig'
import { useVueRouter } from '@/hooks/web/index' import { useVueRouter } from '@/hooks/web/index'
import { setVariable, globalVariableToRefs } from '@/hooks/variable/index' import { setVariable, getVariableToRefs } from '@/global-variable/index'
import type { FormInst } from 'naive-ui' import type { FormInst } from 'naive-ui'
@ -15,19 +15,19 @@ export default defineComponent({
const loginFormRef = ref<FormInst>() const loginFormRef = ref<FormInst>()
const { t } = useI18n() const { t } = useI18n()
const signinStore = useSignin() const signingStore = useSigning()
const { signin } = signinStore const { signing } = signingStore
const { path } = ROOT_ROUTE const { path } = ROOT_ROUTE
const globalSpinning = globalVariableToRefs('globalSpinning') const globalSpinning = getVariableToRefs('globalSpinning')
const useSigninForm = () => ({ const useSigningForm = () => ({
name: 'Ray Admin', name: 'Ray Admin',
pwd: '123456', pwd: '123456',
}) })
const { router } = useVueRouter() const { router } = useVueRouter()
const signinForm = ref(useSigninForm()) const signingForm = ref(useSigningForm())
const rules = { const rules = {
name: { name: {
@ -48,16 +48,16 @@ export default defineComponent({
if (!valid) { if (!valid) {
setVariable('globalSpinning', true) setVariable('globalSpinning', true)
signin(signinForm.value) signing(signingForm.value)
.then((res) => { .then((res) => {
if (res.code === 0) { if (res.code === 0) {
setTimeout(() => { setTimeout(() => {
setVariable('globalSpinning', false) setVariable('globalSpinning', false)
window.$message.success(`欢迎${signinForm.value.name}登陆~`) window.$message.success(`欢迎${signingForm.value.name}登陆~`)
setStorage(APP_CATCH_KEY.token, 'tokenValue') setStorage(APP_CATCH_KEY.token, 'tokenValue')
setStorage(APP_CATCH_KEY.signin, res.data) setStorage(APP_CATCH_KEY.signing, res.data)
router.push(path) router.push(path)
}, 2 * 1000) }, 2 * 1000)
@ -71,7 +71,7 @@ export default defineComponent({
} }
return { return {
signinForm, signingForm,
loginFormRef, loginFormRef,
handleLogin, handleLogin,
rules, rules,
@ -82,16 +82,16 @@ export default defineComponent({
const { $t, globalSpinning } = this const { $t, globalSpinning } = this
return ( return (
<NForm model={this.signinForm} ref="loginFormRef" rules={this.rules}> <NForm model={this.signingForm} ref="loginFormRef" rules={this.rules}>
<NFormItem label={$t('views.login.index.Name')} path="name"> <NFormItem label={$t('views.login.index.Name')} path="name">
<NInput <NInput
v-model:value={this.signinForm.name} v-model:value={this.signingForm.name}
placeholder={$t('views.login.index.NamePlaceholder')} placeholder={$t('views.login.index.NamePlaceholder')}
/> />
</NFormItem> </NFormItem>
<NFormItem label={$t('views.login.index.Password')} path="pwd"> <NFormItem label={$t('views.login.index.Password')} path="pwd">
<NInput <NInput
v-model:value={this.signinForm.pwd} v-model:value={this.signingForm.pwd}
type="password" type="password"
showPasswordOn="click" showPasswordOn="click"
placeholder={$t('views.login.index.PasswordPlaceholder')} placeholder={$t('views.login.index.PasswordPlaceholder')}

View File

@ -19,8 +19,8 @@
"@/*": ["src/*"], "@/*": ["src/*"],
"@use-utils": ["src/utils"], "@use-utils": ["src/utils"],
"@use-utils/*": ["src/utils/*"], "@use-utils/*": ["src/utils/*"],
"@use-api": ["src/axios/api"], "@use-api": ["src/api"],
"@use-api/*": ["src/axios/api/*"], "@use-api/*": ["src/api/*"],
"@use-images": ["src/assets/images"], "@use-images": ["src/assets/images"],
"@use-images/*": ["src/assets/images"], "@use-images/*": ["src/assets/images"],
"@use-micro/*": ["src/micro/*"], "@use-micro/*": ["src/micro/*"],

View File

@ -19,17 +19,17 @@ export const htmlTitlePlugin = (title: string) => {
* @remark css , * @remark css ,
*/ */
export const mixinCSSPlugin = (options?: string[]) => { export const mixinCSSPlugin = (options?: string[]) => {
const defaultOptions = [] if (!Array.isArray(options)) {
throw TypeError(
if (Array.isArray(options)) { 'mixinCSSPlugin: The mixinCSSPlugin argument must be an array!',
defaultOptions.push(...options) )
} }
const mixisString = defaultOptions.reduce((pre, curr) => { const mixinString = options.reduce((pre, curr) => {
const temp = `@import "${curr}";` const temp = `@import "${curr}";`
return (pre += temp) return (pre += temp)
}, '') }, '')
return mixisString as string return mixinString as string
} }

View File

@ -86,7 +86,7 @@ export interface LibItem {
/** /**
* *
* whether replace old import statement, default `command === 'build'`, * whether replace old import statement, default `command === 'build'`,
* that means in vite serve default to `false`, in vite build default to `ture` * that means in vite serve default to `false`, in vite build default to `true`
*/ */
replaceOldImport?: boolean replaceOldImport?: boolean
/** /**
@ -117,7 +117,7 @@ export interface ImpConfig {
/** /**
* *
* By default `vite-plugin-imp` ignores all files inside node_modules. * By default `vite-plugin-imp` ignores all files inside node_modules.
* You can enable this option to avoid unexpected untranspiled code from third-party dependencies. * You can enable this option to avoid unexpected untranslated code from third-party dependencies.
* *
* Transpiling all the dependencies could slow down the build process, though. * Transpiling all the dependencies could slow down the build process, though.
* If build performance is a concern, you can explicitly transpile only some of the dependencies * If build performance is a concern, you can explicitly transpile only some of the dependencies

View File

@ -166,7 +166,6 @@ export default function (mode: string): PluginOption[] {
'echarts', 'echarts',
'xlsx', 'xlsx',
'axios', 'axios',
'screenfull',
'print-js', 'print-js',
'clipboard', 'clipboard',
'lodash-es', 'lodash-es',