diff --git a/CHANGELOG.md b/CHANGELOG.md
index a154f7fb..456b93d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# CHANGE LOG
+## 4.3.1
+
+根据反馈,尽可能的补充了一些代码注释。
+
+### Feats
+
+- 标签页右键菜单新增关闭当前页功能,优化了文案
+- `utils/basic` 包中的部分方法改为 effect 执行逻辑,避免使用 ref 注册的 dom 不能正确的被获取的问题
+- 新增 `scopeDispose`, `watchEffectWithTarget` 方法
+- `utils/cache` 新增 `hasStorage` 方法
+- 现在标签页会缓存,不再随着刷新后丢失
+- 新增 maximize 方法,并且基于该方法实现 LayoutContent 全屏效果
+
+### Fixes
+
+- 修复标签页右键菜单闪烁问题
+
## 4.3.0
提供了专用于一些模板的 `hooks`,可以通过这些方法调用模板的特定功能。并且该功能后续是模板维护的重点。
diff --git a/package.json b/package.json
index f7524f3f..4f399009 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ray-template",
"private": false,
- "version": "4.3.0",
+ "version": "4.3.1",
"type": "module",
"engines": {
"node": ">=16.0.0",
diff --git a/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx b/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx
index d583938f..4c27a463 100644
--- a/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx
+++ b/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx
@@ -15,7 +15,7 @@ import { NInput, NForm, NFormItem, NButton, NSpace } from 'naive-ui'
import AppAvatar from '@/app-components/app/AppAvatar/index'
import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar'
-import { rules, useCondition } from '@/app-components/app/AppLockScreen/hook'
+import { rules, useCondition } from '@/app-components/app/AppLockScreen/shared'
import { useSettingGetters, useSettingActions } from '@/store'
import type { FormInst, InputInst } from 'naive-ui'
diff --git a/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx b/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx
index 9b23a573..8b8efb0e 100644
--- a/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx
+++ b/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx
@@ -16,7 +16,7 @@ import AppAvatar from '@/app-components/app/AppAvatar/index'
import dayjs from 'dayjs'
import { useSigningActions, useSettingActions } from '@/store'
-import { rules, useCondition } from '@/app-components/app/AppLockScreen/hook'
+import { rules, useCondition } from '@/app-components/app/AppLockScreen/shared'
import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar'
import { useDevice } from '@/hooks/web/index'
@@ -98,6 +98,9 @@ export default defineComponent({
},
render() {
const { isTabletOrSmaller } = this
+ const { HH_MM, AM_PM, YY_MM_DD, DDD } = this
+ const hmSplit = HH_MM.split(':')
+ const { unlockScreen, backToSigning } = this
return (
@@ -110,8 +113,8 @@ export default defineComponent({
: '',
]}
>
-
{this.HH_MM?.split(':')[0]}
-
{this.HH_MM?.split(':')[1]}
+
{hmSplit[0]}
+
{hmSplit[1]}
@@ -129,24 +132,16 @@ export default defineComponent({
maxlength={12}
onKeydown={(e: KeyboardEvent) => {
if (e.code === 'Enter') {
- this.unlockScreen()
+ unlockScreen()
}
}}
/>
-
+
返回登陆
-
+
进入系统
@@ -154,10 +149,10 @@ export default defineComponent({
- {this.HH_MM} {this.AM_PM}
+ {HH_MM} {AM_PM}
- {this.YY_MM_DD} {this.DDD}
+ {YY_MM_DD} {DDD}
diff --git a/src/app-components/app/AppLockScreen/hook.ts b/src/app-components/app/AppLockScreen/shared.ts
similarity index 100%
rename from src/app-components/app/AppLockScreen/hook.ts
rename to src/app-components/app/AppLockScreen/shared.ts
diff --git a/src/app-components/provider/AppStyleProvider/index.tsx b/src/app-components/provider/AppStyleProvider/index.tsx
index bac79840..f6341a96 100644
--- a/src/app-components/provider/AppStyleProvider/index.tsx
+++ b/src/app-components/provider/AppStyleProvider/index.tsx
@@ -18,7 +18,7 @@ import { useSettingGetters } from '@/store'
import type { SettingState } from '@/store/modules/setting/type'
-const AppStyleProvider = defineComponent({
+export default defineComponent({
name: 'AppStyleProvider',
setup(_, { expose }) {
const { getAppTheme } = useSettingGetters()
@@ -33,22 +33,22 @@ const AppStyleProvider = defineComponent({
const primaryColorOverride = getStorage(
'piniaSettingStore',
'localStorage',
- )
+ ) // 获取缓存 naive ui 配置项
if (primaryColorOverride) {
- const _p = get(
+ const p = get(
primaryColorOverride,
'primaryColorOverride.common.primaryColor',
primaryColor,
- )
- const _fp = colorToRgba(_p, 0.38)
+ ) // 获取主色调
+ const fp = colorToRgba(p, 0.38) // 将主色调任意颜色转换为 rgba 格式
/** 设置全局主题色 css 变量 */
- body.style.setProperty('--ray-theme-primary-color', _p)
+ body.style.setProperty('--ray-theme-primary-color', p) // 主色调
body.style.setProperty(
'--ray-theme-primary-fade-color',
- _fp || primaryFadeColor,
- )
+ fp || primaryFadeColor,
+ ) // 降低透明度后的主色调
}
}
@@ -73,8 +73,8 @@ const AppStyleProvider = defineComponent({
* 根据 getAppTheme 进行初始化
*/
const body = document.body
- const darkClassName = 'ray-template--dark'
- const lightClassName = 'ray-template--light'
+ const darkClassName = 'ray-template--dark' // 暗色类名
+ const lightClassName = 'ray-template--light' // 明亮色类名
bool
? removeClass(body, lightClassName)
@@ -86,6 +86,7 @@ const AppStyleProvider = defineComponent({
syncPrimaryColorToBody()
hiddenLoadingAnimation()
+ // 当切换主题时,更新 body 当前的注入 class
watch(
() => getAppTheme.value,
(ndata) => {
@@ -102,5 +103,3 @@ const AppStyleProvider = defineComponent({
return
},
})
-
-export default AppStyleProvider
diff --git a/src/app-components/provider/AppWatermarkProvider/index.tsx b/src/app-components/provider/AppWatermarkProvider/index.tsx
index bc92390f..cf749c5e 100644
--- a/src/app-components/provider/AppWatermarkProvider/index.tsx
+++ b/src/app-components/provider/AppWatermarkProvider/index.tsx
@@ -9,6 +9,14 @@
* @remark 今天也是元气满满撸代码的一天
*/
+/**
+ *
+ * 全局水印注入
+ *
+ * 该组件启用时,会在全局(包括首页)展示
+ * 如果你不希望在登录页显示,可以手动将该组件放置于 Layout 中
+ */
+
import { NWatermark } from 'naive-ui'
import { APP_WATERMARK_CONFIG } from '@/app-config/appConfig'
diff --git a/src/axios/instance.ts b/src/axios/instance.ts
index 923d200d..54695935 100644
--- a/src/axios/instance.ts
+++ b/src/axios/instance.ts
@@ -36,20 +36,17 @@ const { createAxiosInstance, beforeFetch, fetchError } = useAxiosInterceptor()
// 请求拦截器
server.interceptors.request.use(
(request) => {
- // 生成 request instance
- createAxiosInstance(request, 'requestInstance')
- // 初始化拦截器所有已注入方法
- setupRequestInterceptor()
- // 执行拦截器所有已注入方法
- beforeFetch('requestInstance', 'implementRequestInterceptorArray', 'ok')
+ createAxiosInstance(request, 'requestInstance') // 生成 request instance
+
+ setupRequestInterceptor() // 初始化拦截器所有已注入方法
+
+ beforeFetch('requestInstance', 'implementRequestInterceptorArray', 'ok') // 执行拦截器所有已注入方法
return request
},
(error) => {
- // 初始化拦截器所有已注入方法(错误状态)
- setupRequestErrorInterceptor()
- // 执行所有已注入方法
- fetchError('requestError', error, 'implementRequestInterceptorErrorArray')
+ setupRequestErrorInterceptor() // 初始化拦截器所有已注入方法(错误状态)
+ fetchError('requestError', error, 'implementRequestInterceptorErrorArray') // 执行所有已注入方法
return Promise.reject(error)
},
@@ -58,17 +55,17 @@ server.interceptors.request.use(
// 响应拦截器
server.interceptors.response.use(
(response) => {
- createAxiosInstance(response, 'responseInstance')
- setupResponseInterceptor()
- beforeFetch('responseInstance', 'implementResponseInterceptorArray', 'ok')
+ createAxiosInstance(response, 'responseInstance') // 创建响应实例
+ setupResponseInterceptor() // 注入响应成功待执行队列
+ beforeFetch('responseInstance', 'implementResponseInterceptorArray', 'ok') // 执行响应成功拦截器
const { data } = response
return Promise.resolve(data)
},
(error) => {
- setupResponseErrorInterceptor()
- fetchError('responseError', error, 'implementResponseInterceptorErrorArray')
+ setupResponseErrorInterceptor() // 注入响应失败待执行队列
+ fetchError('responseError', error, 'implementResponseInterceptorErrorArray') // 执行响应失败后拦截器
return Promise.reject(error)
},
diff --git a/src/components/RQRCode/src/index.tsx b/src/components/RQRCode/src/index.tsx
index 63b78f9f..aae7f18b 100644
--- a/src/components/RQRCode/src/index.tsx
+++ b/src/components/RQRCode/src/index.tsx
@@ -19,7 +19,11 @@ import { AwesomeQR } from 'awesome-qr'
import { isValueType, downloadAnyFile } from '@/utils/basic'
import { call } from '@/utils/vue/index'
-import type { QRCodeRenderResponse, GIFBuffer } from './type'
+import type {
+ QRCodeRenderResponse,
+ GIFBuffer,
+ DownloadFilenameType,
+} from './type'
import type { WatchStopHandle } from 'vue'
const readGIFAsArrayBuffer = (url: string): Promise => {
@@ -114,7 +118,7 @@ export default defineComponent({
}
}
- const downloadQRCode = (fileName?: string) => {
+ const downloadQRCode = (fileName?: DownloadFilenameType) => {
if (qrcodeURL.value && isValueType(qrcodeURL.value, 'String')) {
downloadAnyFile(
qrcodeURL.value,
diff --git a/src/components/RQRCode/src/type.ts b/src/components/RQRCode/src/type.ts
index 6cffb552..2a0d31f4 100644
--- a/src/components/RQRCode/src/type.ts
+++ b/src/components/RQRCode/src/type.ts
@@ -22,7 +22,11 @@ export type QRCodeInst = {
*
* 如果未设置名称,则默认以 时间戳.png 命名
*/
- downloadQRCode: (fileName?: string) => void
+ downloadQRCode: (fileName?: DownloadFilenameType) => void
}
export type GIFBuffer = string | ArrayBuffer | null
+
+export type DefaultDownloadImageType = 'png' | 'jpg' | 'jpeg' | 'webp'
+
+export type DownloadFilenameType = `${string}.${DefaultDownloadImageType}`
diff --git a/src/hooks/template/useMainPage.ts b/src/hooks/template/useMainPage.ts
index 4ca284ce..e29a66ac 100644
--- a/src/hooks/template/useMainPage.ts
+++ b/src/hooks/template/useMainPage.ts
@@ -9,23 +9,46 @@
* @remark 今天也是元气满满撸代码的一天
*/
-import { getVariableToRefs, setVariable } from '@/global-variable/index'
+import { setVariable } from '@/global-variable/index'
import { LAYOUT_CONTENT_REF } from '@/app-config/routerConfig'
-import { useFullscreen } from 'vue-hooks-plus'
-import { useI18n } from '@/hooks/web/index'
+import { addStyle, removeStyle } from '@/utils/element'
+import { unrefElement } from '@/utils/vue/index'
-import type { AppMenuOption } from '@/types/modules/app'
import type { Ref } from 'vue'
export function useMainPage() {
+ /**
+ *
+ * @param wait 延迟时长
+ *
+ * 刷新当前路由
+ */
const reload = (wait = 800) => {
setVariable('globalMainLayoutLoad', false)
setTimeout(() => setVariable('globalMainLayoutLoad', true), wait)
}
+ /**
+ *
+ * @param full 是否网页全屏内容区域
+ *
+ * 该方法仅针对于 LayoutContent 区域,并且依赖全局属性 layoutContentMaximize
+ */
const maximize = (full: boolean) => {
- // setVariable('layoutContentMaximize', full)
+ const contentEl = unrefElement(LAYOUT_CONTENT_REF as Ref)
+
+ if (contentEl) {
+ const { left, top } = contentEl.getBoundingClientRect()
+
+ full
+ ? addStyle(contentEl, {
+ transform: `translate(-${left}px, -${top}px)`,
+ })
+ : removeStyle(contentEl, ['transform'])
+ }
+
+ setVariable('layoutContentMaximize', full)
}
return {
diff --git a/src/hooks/template/useMenuTag.ts b/src/hooks/template/useMenuTag.ts
index c207f4c5..b5491117 100644
--- a/src/hooks/template/useMenuTag.ts
+++ b/src/hooks/template/useMenuTag.ts
@@ -11,7 +11,6 @@
import { useMenuGetters, useMenuActions } from '@/store'
import { ROOT_ROUTE } from '@/app-config/appConfig'
-import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
import type { MenuTagOptions, Key } from '@/types/modules/app'
@@ -93,8 +92,13 @@ export function useMenuTag() {
* 关闭所有标签并且导航至 root path
*/
const closeAll = () => {
+ const option = getMenuTagOptions.value.find((curr) => curr.key === path)
+
+ if (option) {
+ changeMenuModelValue(path, option)
+ }
+
emptyMenuTagOptions()
- redirectRouterToDashboard(true)
}
/**
diff --git a/src/hooks/web/useDayjs.ts b/src/hooks/web/useDayjs.ts
index d719a3da..1e2d92ec 100644
--- a/src/hooks/web/useDayjs.ts
+++ b/src/hooks/web/useDayjs.ts
@@ -22,6 +22,12 @@ import type { DayjsLocal } from '@/dayjs/type'
* - locale: 切换 dayjs 语言配置
*/
export const useDayjs = () => {
+ /**
+ *
+ * @param key 当前语言
+ *
+ * 手动配置 dayjs 语言(国际化)
+ */
const locale = (key: DayjsLocal) => {
const locale = DAYJS_LOCAL_MAP[key]
diff --git a/src/hooks/web/useDevice.ts b/src/hooks/web/useDevice.ts
index a0df97a4..dc364192 100644
--- a/src/hooks/web/useDevice.ts
+++ b/src/hooks/web/useDevice.ts
@@ -15,14 +15,17 @@
*/
import { useWindowSize } from '@vueuse/core'
+import { watchEffectWithTarget } from '@/utils/vue/index'
export function useDevice() {
const { width, height } = useWindowSize()
const isTabletOrSmaller = ref(false)
- watchEffect(() => {
+ const update = () => {
isTabletOrSmaller.value = width.value <= 768
- })
+ }
+
+ watchEffectWithTarget(update)
return {
width,
diff --git a/src/icons/close_left.svg b/src/icons/close_left.svg
new file mode 100644
index 00000000..841aba44
--- /dev/null
+++ b/src/icons/close_left.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/src/icons/close_right.svg b/src/icons/close_right.svg
new file mode 100644
index 00000000..132dc0c0
--- /dev/null
+++ b/src/icons/close_right.svg
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/src/icons/fullscreen_fold.svg b/src/icons/fullscreen_fold.svg
new file mode 100644
index 00000000..cc57c57e
--- /dev/null
+++ b/src/icons/fullscreen_fold.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/src/icons/other.svg b/src/icons/other.svg
index 517ad080..dc5d686a 100644
--- a/src/icons/other.svg
+++ b/src/icons/other.svg
@@ -1,9 +1,11 @@
\ No newline at end of file
diff --git a/src/icons/out.svg b/src/icons/out.svg
new file mode 100644
index 00000000..7e9e7f63
--- /dev/null
+++ b/src/icons/out.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/src/icons/reload.svg b/src/icons/reload.svg
index ac935338..9bfaa195 100644
--- a/src/icons/reload.svg
+++ b/src/icons/reload.svg
@@ -1,3 +1,6 @@
-