Merge remote-tracking branch 'upstream/main'

This commit is contained in:
yun 2024-01-10 10:59:09 +08:00
commit f60a107997
No known key found for this signature in database
GPG Key ID: 5249ADD7327F7ABF
92 changed files with 1172 additions and 699 deletions

View File

@ -9,7 +9,6 @@ on:
jobs: jobs:
build-and-deploy: build-and-deploy:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -1,5 +1,74 @@
# CHANGE LOG # CHANGE LOG
## 4.5.0
破坏性更新。
更新 `vue` 版本至 `3.4.0`,请查看相关变更:[Vue 3.4.0](https://github.com/vuejs/core/blob/main/CHANGELOG.md)。该版本破坏性更新了一些东西,例如:
- `JSX` 命名空间默认移除
- `ReactiveTransform` 移除
- `v-is` 指令移除
更新 `@vitejs/plugin-vue` 版本至 `5.0.0`,请查看相关变更:[vite-plugin-vue](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md)。该版本破坏性更新了一些东西,例如:
- 放弃 `ReactivityTransform` 支持
- 放弃 `node14, 16` 版本支持
- 放弃 `vite4.x` 版本支持
- 弃用 `defineModel` 配置,`vue3.4.0` 版本该功能已经稳定
解决 `sameLevel` 问题与核心包 `store/men` 的一些问题。提高了性能,避免了潜在的空引用问题。
是统一 `types` 包的导入导出行为。
## Feats
- 统一 `types` 包下的导出方式,现在统一为 `import type { xxx } from '@/types'` 导入
- `RQRCode` 组件
- 在 `loading` 状态下会模糊显示二维码,并且展示 `loading` 状态
- 优化显示 `error` 状态下默认按钮样式
- 更新 `README.md`, `README-ZH.md` 文档
- 取消 `changeModelValue` 节流锁
- 优化 `changeModelValue`, `updateMenuKeyWhenRouteUpdate` 方法,避免潜在的空引用问题
- 插件相关
- 更新 `vue` 版本至 `3.4.0`
- 更新 `@vitejs/plugin-vue-jsx` 版本至 `3.1.0`
- `RModal` 组件相关
- 新增 `memo` 配置项,用于配置是否记忆上一次的位置
- `RChart` 组件
- 补充部分注释
- 新增 `isDispose` 方法,检测当前图表是否被卸载
- 新增 `AppVersionProvider` 注入器,用于注入 `appVersion` 全局变量,检测是否需要重新加载页面
## Fixes
- 修复初始化的时候面包屑为空的问题
- 修复 `sameLevel` 平级模式面包屑追加问题
- `RModal` 组件
- 修复初始化配置 `show = true` 时,`dad` 属性不生效的问题
## 4.4.7
## Feats
- 更新 `vue` 版本至 `3.3.13` 版本
- 更新 `naive-ui` 版本至 `3.36.0`。新增了几个新组件
- 更新 `vite` 版本至 `5.0.10`
- `appConfig` 相关
- 移除 `APP_WATERMARK_CONFIG` 配置项,现在水印配置项会被 `watermarkConfig` 替代
- 移除 `ROOT_ROUTE` 配置项,现在根路由配置项会被 `appRootRoute` 替代
- `variable` 相关
- 移除 `variable` 管理 `ROO_ROUTE` 配置项
- `commit-message` 新增 `plugin` 更新相关前缀配置
- `RQRCode` 组件
- 调整 `loading` 透明度
- `downloadQRCode` 方法将会返回一个 `Promise` 对象
- `basic`
- `downloadAnyFile` 方法将会返回一个 `Promise` 对象
- `types`
- 新增 `ReturnPromiseType` 工具类型,用于获取函数返回值的 `Promise` 类型
- 新增 `ConditionalExclude` 工具类型,用于条件排除指定类型
## 4.4.6 ## 4.4.6
## Feats ## Feats

View File

@ -1,6 +1,22 @@
<div align="center"> <div align="center">
<a href="https://github.com/XiaoDaiGua-Ray/ray-template"> <img alt="Ray Template" width="200" height="200" src="https://r2chevereto.yka.moe/longmao.navigator.th.png"> </a> <br> <br> <a href="https://github.com/XiaoDaiGua-Ray/ray-template">
<a href="https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/LICENSE"><img src="https://img.shields.io/github/license/XiaoDaiGua-Ray/ray-template" alt="LICENSE"></a> <img
alt="Ray Template"
width="200"
height="200"
src="https://r2chevereto.yka.moe/longmao.navigator.th.png"
/>
</a>
<br />
<br />
<a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/vite.svg" alt="node compatibility"></a>
<a href="https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/LICENSE"
><img
src="https://img.shields.io/github/license/XiaoDaiGua-Ray/ray-template"
alt="LICENSE"
/></a>
<a href="#badge"><img src="https://img.shields.io/github/languages/top/XiaoDaiGua-Ray/ray-template" alt="language"></a>
<a href="https://www.npmjs.com/package/ray-template"><img src="https://img.shields.io/npm/v/ray-template" alt="npm package"></a>
</div> </div>
<div align="center"> <div align="center">
@ -9,32 +25,40 @@
简体中文 | [English](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README.md) 简体中文 | [English](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README.md)
一个 `免费`、`高效``特性完整` 并且基于 vite5.x & ts(x) & pinia & vue3.x 等最新技术的中后台模板。 一个 `完全免费`、`高效``特性完整` 并且基于 vite5.x & ts(x) & pinia & vue3.x 等最新技术的中后台模板。
</div> </div>
## 🌻 简介
`Ray Template`采用前沿前端技术,摒弃繁杂与臃肿,采用模块化设计,解耦数据、方法和视图,专注业务开发。提供丰富配置和丰富的模板 `Hooks`,支持个性化定制,满足你的项目需求。
## ✨ 特性 ## ✨ 特性
- **靠爱发电**:几乎包含市面常见的模板特性并且全部免费使用 - `全新技术栈:`使用 ts(x), vite5.x, vue3.x, pinia 等前端前沿技术开发
- **最新技术栈**:使用 vue3.x/vite5.x/pinia 等前端前沿技术开发 - `主题:`可配置的主题
- **TypeScript**:应用程序级 JavaScript 的语言 - `国际化:`内置完善的国际化方案
- **主题**:可配置的主题 - `权限:`内置完善的动态路由权限生成方案
- **国际化**:内置完善的国际化方案 - `组件:`二次封装了多个常用的组件
- **Mock 数据**:内置 Mock 数据方案 - `工具包:`常用的工具函数封装
- **权限**:内置完善的动态路由权限生成方案 - `缓存:`任意深度页面缓存
- **组件**:二次封装了多个常用的组件 - `模块化设计:`解耦管理的数据、方法、视图,放心二次开发
- **Axios 请求**:二次封装 axios 库,支持:取消、防抖、自动重复取消等功能 - `配置化:`支持丰富的配置项
- **缓存**:任意深度页面缓存 - `代码风格:`内置 prettier, eslint 等代码风格工具
- **SVG**:内置 svg icon 解决方案 - `多端适配:`支持 pc, phone, pad
- **独立的 Data Methods Views**:解耦管理的数据、方法、视图,放心二次开发 - `文档:`完善的文档
- **模板专属 hooks**:基于模板特性封装的 hooks 让你更加方便的使用模板一些功能 - `Mock 数据:`内置 Mock 数据方案
- `Axios 请求:`二次封装 axios 库,支持:取消、防抖、自动重复取消等功能
- `SVG`内置 svg icon 解决方案
- `Hooks`基于模板特性封装的 hooks 让你更加方便的使用模板一些功能
- `TypeScript`提供完整的类型
## 🪄 预览地址 ## 👀 预览地址
- [点击预览](https://xiaodaigua-ray.github.io/ray-template/#/) - [点击预览](https://xiaodaigua-ray.github.io/ray-template/#/)
- [点击预览(加速地址)](https://ray-template.yunkuangao.com/#/) - [点击预览(加速地址)](https://ray-template.yunkuangao.com/#/)
## 🦾 文档地址 ## 📌 文档地址
- [文档](https://xiaodaigua-ray.github.io/ray-template-doc/) - [文档](https://xiaodaigua-ray.github.io/ray-template-doc/)
- [文档(加速地址)](https://ray-template.yunkuangao.com/ray-template-doc/) - [文档(加速地址)](https://ray-template.yunkuangao.com/ray-template-doc/)
@ -50,8 +74,9 @@
- [Vue3](https://v3.vuejs.org/) - 熟悉 Vue 基础语法 - [Vue3](https://v3.vuejs.org/) - 熟悉 Vue 基础语法
- [TypeScript](https://www.typescriptlang.org/) - 熟悉 TypeScript 基本语法 - [TypeScript](https://www.typescriptlang.org/) - 熟悉 TypeScript 基本语法
- [Es6+](http://es6.ruanyifeng.com/) - 熟悉 es6 基本语法 - [Es6+](http://es6.ruanyifeng.com/) - 熟悉 es6 基本语法
- [Vue-Hooks-Plus] - 熟悉 vue-hooks-plus useRequest 方法的基本使用
- [Vue-Router-Next](https://next.router.vuejs.org/) - 熟悉 vue-router4.x 基本使用 - [Vue-Router-Next](https://next.router.vuejs.org/) - 熟悉 vue-router4.x 基本使用
- [Naive-UI](https://www.naiveui.com) - ui 基本使用 - [Naive-UI](https://www.naiveui.com) - naive ui 基本使用
- [Mock.js](https://github.com/nuysoft/Mock) - mockjs 基本语法 - [Mock.js](https://github.com/nuysoft/Mock) - mockjs 基本语法
- [Pinia](https://pinia.vuejs.org/zh/introduction.html) - 状态管理器 pinia 使用 - [Pinia](https://pinia.vuejs.org/zh/introduction.html) - 状态管理器 pinia 使用
- [TSX](https://github.com/vuejs/babel-plugin-jsx/blob/main/packages/babel-plugin-jsx/README-zh_CN.md) - tsx 基本语法 - [TSX](https://github.com/vuejs/babel-plugin-jsx/blob/main/packages/babel-plugin-jsx/README-zh_CN.md) - tsx 基本语法
@ -98,9 +123,9 @@ pnpm preview
pnpm report pnpm report
``` ```
### 开发 ### 快速开发
简介、易上手是该模板的核心思路。所以你可以放心的直接删除 `views/demo` `router/moduels/demo` 下的所有文件,这样就是一个干净的项目了 只需要删除 `views/demo`, `router/modules/demo` 下的文件即可得到一个干净的项目模板
## 🪴 项目活动 ## 🪴 项目活动

103
README.md
View File

@ -1,6 +1,22 @@
<div align="center"> <div align="center">
<a href="https://github.com/XiaoDaiGua-Ray/ray-template"> <img alt="Ray Template" width="200" height="200" src="https://r2chevereto.yka.moe/longmao.navigator.th.png"> </a> <br> <br> <a href="https://github.com/XiaoDaiGua-Ray/ray-template">
<a href="https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/LICENSE"><img src="https://img.shields.io/github/license/XiaoDaiGua-Ray/ray-template" alt="LICENSE"></a> <img
alt="Ray Template"
width="200"
height="200"
src="https://r2chevereto.yka.moe/longmao.navigator.th.png"
/>
</a>
<br />
<br />
<a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/vite.svg" alt="node compatibility"></a>
<a href="https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/LICENSE"
><img
src="https://img.shields.io/github/license/XiaoDaiGua-Ray/ray-template"
alt="LICENSE"
/></a>
<a href="#badge"><img src="https://img.shields.io/github/languages/top/XiaoDaiGua-Ray/ray-template" alt="language"></a>
<a href="https://www.npmjs.com/package/ray-template"><img src="https://img.shields.io/npm/v/ray-template" alt="npm package"></a>
</div> </div>
<div align="center"> <div align="center">
@ -9,35 +25,43 @@
English | [简体中文](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README-ZH.md) English | [简体中文](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README-ZH.md)
A `free`, `efficient`, `complete with features` middle and backend template based on the latest technologies such as vite5.x & ts(x) & pinia & vue3.x. A `completely free`, `efficient`, `feature complete` and based on vite5. x & ts(x) & pinia & vue3. x and other latest technology in the background template.
</div> </div>
## ✨ Feature ## 🌻 Intro
- **Power by love**: Contains almost all common template features on the market and all are free to use. `Ray Template` uses cutting-edge front-end technology, abandoning complexity and bloat, using modular design, decoupling data, methods and views, focusing on business development. Provide rich configuration and rich template `Hooks`, support personalized customization, to meet your project needs.
- **Latest Technology Stack**Developed using front-end cutting-edge technologies such as vue3.x/vite5.x/pinia.
- **TypeScript**The language for application-level JavaScript.
- **App Theme**Configurable themes.
- **Globalization**Built-in complete internationalization solution.
- **Mock Data**Built-in Mock data scheme.
- **Permissions**Built-in complete dynamic routing permission generation solution.
- **Components**Secondary encapsulation of multiple commonly used components.
- **Axios Request**Secondary encapsulation of the axios library, supporting functions such as cancellation, anti-shake, automatic repeat cancellation, etc.
- **Page Cache**Arbitrarily deep page cache.
- **SVG**Built-in svg icon solution.
- **Standalone Data Methods Views**Decoupled management of data, methods, and views allows for secondary development with confidence.
- **Template Specific Hooks** : Hooks based on the template feature package make it easier to use some of the features of the template.
## 🪄 Preview ## ✨ Features
- [Click to preview](https://xiaodaigua-ray.github.io/ray-template/#/) - `New technology stack:` using ts(x), vite5. x, vue3. x, pinia and other front-end cutting-edge technology development
- [Click to preview(Acceleration address)](https://ray-template.yunkuangao.com/#/) - `Theme:` configurable theme
- `Internationalization:` built-in perfect internationalization solution
- `Permissions:` built-in perfect dynamic routing permission generation solution
- `Components:` secondary encapsulation of multiple common components
- `Toolkit:` common tool function packaging
- `Cache:` arbitrary depth page caching
- `Modular design:` decoupling management data, methods, views, rest assured secondary development
- `Configurable:` support rich configuration items
- `Code style:` built-in prettier, eslint and other code style tools
- `Multi-terminal adaptation:` support pc, phone, pad
- `Documentation:` complete documentation
- `Mock data:` built-in Mock data solution
- `Axios request:` secondary encapsulation of axios library, support: cancel, jitter, automatic repeat cancellation and other functions
- `SVG:` built-in svg icon solution
- `Hooks:` based on the template characteristics of the encapsulated hooks to make it easier to use some functions of the template
- `TypeScript:` provide a complete type
## 🦾 Document ## 👀 Preview
- [Document](https://xiaodaigua-ray.github.io/ray-template-doc/) - [Preview](https://xiaodaigua-ray.github.io/ray-template/#/)
- [Document(Acceleration address)](https://ray-template.yunkuangao.com/ray-template-doc/) - [Preview(Acceleration address)](https://ray-template.yunkuangao.com/#/)
## 📌 Documentation
- [Documentation](https://xiaodaigua-ray.github.io/ray-template-doc/)
- [Documentation(Acceleration address)](https://ray-template.yunkuangao.com/ray-template-doc/)
## 🔋 Change Log ## 🔋 Change Log
@ -45,16 +69,17 @@ A `free`, `efficient`, `complete with features` middle and backend template base
## 🪴 Prepare ## 🪴 Prepare
- [Node](http://nodejs.org/) and [git](https://git-scm.com/) - Project development environment - [Node](http://nodejs.org/) and [git](https://git-scm.com/) - project development environment
- [Vite](https://vitejs.dev/) - Familiar with vite features - [Vite](https://vitejs.dev/) - familiar with vite features
- [Vue3](https://v3.vuejs.org/) - Familiar with Vue3.x basic syntax - [Vue3](https://v3.vuejs.org/) - familiar with Vue basic syntax
- [TypeScript](https://www.typescriptlang.org/) - Familiar with TypeScript basic syntax - [TypeScript](https://www.typescriptlang.org/) - familiar with TypeScript basic syntax
- [Es6+](http://es6.ruanyifeng.com/) - Familiar with es6 basic syntax - [Es6+](http://es6.ruanyifeng.com/) - familiar with es6 basic syntax
- [Vue-Router-Next](https://next.router.vuejs.org/) - Familiar with the basic use of vue-router4.x - [Vue-Hooks-Plus] - familiar with vue-hooks-plus useRequest method basic use
- [Naive-UI](https://www.naiveui.com) - UI basic usage - [Vue-Router-Next](https://next.router.vuejs.org/) - familiar with vue-router4.x basic use
- [Mock.js](https://github.com/nuysoft/Mock) - Mockjs basic syntax - [Naive-UI](https://www.naiveui.com) - naive ui basic use
- [Pinia](https://pinia.vuejs.org/zh/introduction.html) - State manager pinia uses - [Mock.js](https://github.com/nuysoft/Mock) - mockjs basic syntax
- [TSX](https://github.com/vuejs/babel-plugin-jsx/blob/main/packages/babel-plugin-jsx/README-zh_CN.md) - TSX basic syntax - [Pinia](https://pinia.vuejs.org/zh/introduction.html) - state manager pinia usage
- [TSX](https://github.com/vuejs/babel-plugin-jsx/blob/main/packages/babel-plugin-jsx/README-zh_CN.md) - tsx basic syntax
## 📦 Setup ## 📦 Setup
@ -80,7 +105,7 @@ pnpm i
pnpm dev pnpm dev
``` ```
### Project packaging ### Build project
```sh ```sh
pnpm build pnpm build
@ -92,23 +117,23 @@ pnpm build
pnpm preview pnpm preview
``` ```
### Volumetric analysis ### Report project
```sh ```sh
pnpm report pnpm report
``` ```
### Develop ### Development
Introduction and ease of use are the core ideas of this template. So you can safely delete all files under `views/demo` and `router/moduels/demo`, and you will have a clean project. Just delete the files under `views/demo`, `router/modules/demo` to get a clean project template.
## 🪴 Project Activities ## 🪴 Project Activities
![Alt](https://repobeats.axiom.co/api/embed/fab6071297ab281913a42f07a2779b488cfd62b8.svg 'Repobeats analytics image') ![Alt](https://repobeats.axiom.co/api/embed/fab6071297ab281913a42f07a2779b488cfd62b8.svg 'Repobeats analytics image')
### Contributor ### Contributors
Thanks for all their contributions 🐝! Thanks for all their contributions 🐝 !
<a href="https://github.com/XiaoDaiGua-Ray/ray-template/graphs/contributors"> <a href="https://github.com/XiaoDaiGua-Ray/ray-template/graphs/contributors">
<img src="https://contrib.rocks/image?repo=XiaoDaiGua-Ray/ray-template" /> <img src="https://contrib.rocks/image?repo=XiaoDaiGua-Ray/ray-template" />

2
cfg.ts
View File

@ -42,7 +42,7 @@ import { htmlTitlePlugin, mixinCSSPlugin } from './vite-plugins/index'
import { APP_THEME } from './src/app-config/designConfig' import { APP_THEME } from './src/app-config/designConfig'
import { PRE_LOADING_CONFIG, SIDE_BAR_LOGO } from './src/app-config/appConfig' import { PRE_LOADING_CONFIG, SIDE_BAR_LOGO } from './src/app-config/appConfig'
import type { AppConfigExport } from '@/types/modules/cfg' import type { AppConfigExport } from '@/types'
import type { BuildOptions } from 'vite' import type { BuildOptions } from 'vite'
const config: AppConfigExport = { const config: AppConfigExport = {

View File

@ -8,6 +8,7 @@
// style: 代码格式(不影响功能,例如空格、分号等格式修正) | Code format (no functional impact, such as space, semicolon, etc.) // style: 代码格式(不影响功能,例如空格、分号等格式修正) | Code format (no functional impact, such as space, semicolon, etc.)
// version: 更新迭代 package.json 版本号 | Update the package.json version number // version: 更新迭代 package.json 版本号 | Update the package.json version number
// build: 构建 | Build // build: 构建 | Build
// plugin: 更新插件版本 | Update plugin version
module.exports = { module.exports = {
ignores: [(commit) => commit.includes('init')], ignores: [(commit) => commit.includes('init')],
@ -33,6 +34,7 @@ module.exports = {
'style', 'style',
'version', 'version',
'build', 'build',
'plugin',
], ],
], ],
}, },

View File

@ -1,7 +1,7 @@
{ {
"name": "ray-template", "name": "ray-template",
"private": false, "private": false,
"version": "4.4.6", "version": "4.5.0",
"type": "module", "type": "module",
"engines": { "engines": {
"node": "^18.0.0 || >=20.0.0", "node": "^18.0.0 || >=20.0.0",
@ -44,11 +44,11 @@
"interactjs": "1.10.26", "interactjs": "1.10.26",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mockjs": "1.1.0", "mockjs": "1.1.0",
"naive-ui": "^2.35.0", "naive-ui": "^2.36.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.0", "pinia-plugin-persistedstate": "^3.2.0",
"print-js": "^1.6.0", "print-js": "^1.6.0",
"vue": "^3.3.11", "vue": "^3.4.0",
"vue-hooks-plus": "1.8.5", "vue-hooks-plus": "1.8.5",
"vue-i18n": "^9.8.0", "vue-i18n": "^9.8.0",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
@ -67,8 +67,8 @@
"@types/mockjs": "1.0.7", "@types/mockjs": "1.0.7",
"@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/eslint-plugin": "^6.5.0",
"@typescript-eslint/parser": "^6.5.0", "@typescript-eslint/parser": "^6.5.0",
"@vitejs/plugin-vue": "^4.4.1", "@vitejs/plugin-vue": "^5.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.2", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue-hooks-plus/resolvers": "1.2.4", "@vue-hooks-plus/resolvers": "1.2.4",
"@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.3",
@ -93,7 +93,7 @@
"typescript": "^5.2.2", "typescript": "^5.2.2",
"unplugin-auto-import": "^0.16.6", "unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.25.2", "unplugin-vue-components": "^0.25.2",
"vite": "^5.0.8", "vite": "^5.0.10",
"vite-plugin-cdn2": "0.15.2", "vite-plugin-cdn2": "0.15.2",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-ejs": "^1.7.0", "vite-plugin-ejs": "^1.7.0",

928
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -4,12 +4,14 @@ import AppStyleProvider from '@/app-components/provider/AppStyleProvider'
import AppLockScreen from '@/app-components/app/AppLockScreen' import AppLockScreen from '@/app-components/app/AppLockScreen'
import AppWatermarkProvider from '@/app-components/provider/AppWatermarkProvider' import AppWatermarkProvider from '@/app-components/provider/AppWatermarkProvider'
import AppGlobalSpin from '@/spin' import AppGlobalSpin from '@/spin'
import AppVersionProvider from '@/app-components/provider/AppVersionProvider'
export default defineComponent({ export default defineComponent({
name: 'App', name: 'App',
render() { render() {
return ( return (
<AppNaiveGlobalProvider> <AppNaiveGlobalProvider>
<AppVersionProvider />
<AppLockScreen /> <AppLockScreen />
<AppStyleProvider /> <AppStyleProvider />
<AppWatermarkProvider /> <AppWatermarkProvider />

View File

@ -1,6 +1,6 @@
import { request } from '@/axios' import { request } from '@/axios'
import type { BasicResponse, PaginationResponse } from '@/types/modules/axios' import type { PaginationResponse } from '@/types'
export interface MockListParams { export interface MockListParams {
page: number page: number

View File

@ -21,7 +21,7 @@
import { request } from '@/axios' import { request } from '@/axios'
import type { BasicResponse } from '@/types/modules/axios' import type { BasicResponse } from '@/types'
interface AxiosTestResponse extends UnknownObjectKey { interface AxiosTestResponse extends UnknownObjectKey {
data: UnknownObjectKey[] data: UnknownObjectKey[]

View File

@ -29,13 +29,7 @@ const AppRequestCancelerProvider = defineComponent({
expose() expose()
}, },
render() { render() {
return ( return <div class="app-style-provider"></div>
<div
style={{
display: 'none',
}}
></div>
)
}, },
}) })

View File

@ -9,8 +9,6 @@
* @remark * @remark
*/ */
import './index.scss'
import { get } from 'lodash-es' import { get } from 'lodash-es'
import { import {
addClass, addClass,

View File

@ -0,0 +1,70 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-Ray>
*
* @date 2024-01-01
*
* @workspace ray-template
*
* @remark
*/
/**
*
*
* 退
*/
import { RModal } from '@/components'
import { getStorage, setStorage } from '@/utils'
import { useSigningActions } from '@/store'
export default defineComponent({
name: 'AppVersionProvider',
setup() {
const storageKey = 'appVersionProvider'
const {
pkg: { version },
} = __APP_CFG__
const cacheVersion = getStorage<string>(storageKey, 'localStorage')
const modalShow = ref(false)
const { logout } = useSigningActions()
if (version !== cacheVersion || !cacheVersion) {
modalShow.value = true
setStorage<string>(storageKey, version, 'localStorage')
}
return {
modalShow,
logout,
}
},
render() {
const { logout } = this
return (
<div class="app-style-provider">
<RModal
v-model:show={this.modalShow}
closeOnEsc={false}
maskClosable={false}
preset="dialog"
closable={false}
title="发现新版本"
content="当前版本已更新,点击确认加载新版本~"
zIndex={999999999}
dad
positiveText="确认"
negativeText="取消"
onPositiveClick={logout}
onNegativeClick={() => {
this.modalShow = false
}}
/>
</div>
)
},
})

View File

@ -11,12 +11,8 @@
/** 系统配置 */ /** 系统配置 */
import type { import type { LayoutSideBarLogo, PreloadingConfig } from '@/types'
LayoutSideBarLogo, import type { AppMenuConfig, AppKeepAlive } from '@/types'
PreloadingConfig,
RootRoute,
} from '@/types/modules/cfg'
import type { AppMenuConfig, AppKeepAlive } from '@/types/modules/appConfig'
/** /**
* *
@ -44,21 +40,6 @@ export const PRE_LOADING_CONFIG: PreloadingConfig = {
titleColor: '#2d8cf0', titleColor: '#2d8cf0',
} }
/**
*
*
* ROOT_ROUTE Layout Root Path path
*
* globalRootRoute
*
*
* Root Route 使 useAppRoot
*/
export const ROOT_ROUTE: RootRoute = {
name: 'Dashboard',
path: '/dashboard',
}
/** /**
* *
* icon: LOGO , `RIcon` () * icon: LOGO , `RIcon` ()
@ -111,19 +92,3 @@ export const APP_CATCH_KEY = {
localeLanguage: 'localeLanguage', localeLanguage: 'localeLanguage',
token: 'token', token: 'token',
} as const } as const
/**
*
*
* 具体配置信息查看官网: https://www.naiveui.com/zh-CN/dark/components/watermark#API
*/
export const APP_WATERMARK_CONFIG = {
content: 'Trying be better~',
fontSize: 16,
lineHeight: 16,
width: 384,
height: 384,
xOffset: 12,
yOffset: 60,
rotate: -15,
} as const

View File

@ -11,7 +11,7 @@
/** 系统颜色风格配置入口 */ /** 系统颜色风格配置入口 */
import type { AppTheme } from '@/types/modules/cfg' import type { AppTheme } from '@/types'
export const APP_THEME: AppTheme = { export const APP_THEME: AppTheme = {
/** /**
@ -65,6 +65,8 @@ export const APP_THEME: AppTheme = {
* *
* echart * echart
* json xxx-dark.json * json xxx-dark.json
*
* [](https://xiaodaigua-ray.github.io/ray-template-doc/ray-template-docs/advanced/echart-themes.html)
*/ */
echartTheme: 'macarons', echartTheme: 'macarons',
} }

View File

@ -11,12 +11,8 @@
/** 国际化相关配置 */ /** 国际化相关配置 */
import type { import type { TemplateLocale, LocalOptions, DayjsLocalMap } from '@/types'
TemplateLocale, import type { ValueOf } from '@/types'
LocalOptions,
DayjsLocalMap,
} from '@/types/modules/appConfig'
import type { ValueOf } from '@/types/modules/helper'
/** /**
* *

View File

@ -9,7 +9,7 @@
* @remark * @remark
*/ */
import type { AxiosConfig } from '@/types/modules/appConfig' import type { AxiosConfig } from '@/types'
/** axios 相关配置 */ /** axios 相关配置 */
export const AXIOS_CONFIG: AxiosConfig = { export const AXIOS_CONFIG: AxiosConfig = {

View File

@ -32,7 +32,7 @@ import type {
AxiosFetchInstance, AxiosFetchInstance,
AxiosFetchError, AxiosFetchError,
} from '@/axios/type' } from '@/axios/type'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
/** 当前请求的实例 */ /** 当前请求的实例 */
const axiosFetchInstance: AxiosFetchInstance = { const axiosFetchInstance: AxiosFetchInstance = {

View File

@ -30,7 +30,7 @@ import type {
BeforeFetchFunction, BeforeFetchFunction,
FetchErrorFunction, FetchErrorFunction,
} from '@/axios/type' } from '@/axios/type'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
/** /**
* *

View File

@ -27,7 +27,7 @@ import type {
BeforeFetchFunction, BeforeFetchFunction,
FetchErrorFunction, FetchErrorFunction,
} from '@/axios/type' } from '@/axios/type'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
/** /**
* *

View File

@ -7,7 +7,7 @@ import type {
Axios, Axios,
AxiosResponse, AxiosResponse,
} from 'axios' } from 'axios'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
export type AxiosHeaderValue = export type AxiosHeaderValue =
| AxiosHeaders | AxiosHeaders

View File

@ -49,7 +49,7 @@ import { RMoreDropdown } from '@/components'
import { useSettingGetters } from '@/store' import { useSettingGetters } from '@/store'
import type { WatchStopHandle } from 'vue' import type { WatchStopHandle } from 'vue'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
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'
@ -124,9 +124,7 @@ export default defineComponent({
try { try {
echarts.use(props.use?.filter(Boolean)) echarts.use(props.use?.filter(Boolean))
} catch (e) { } catch (e) {
console.error( console.error('RChart register error: ', e)
'register chart Core error: wrong property and method passed in extend attribute',
)
} }
} }
@ -249,6 +247,7 @@ export default defineComponent({
} }
} }
// chart 是否已经销毁
const isDispose = () => !!(echartInst && echartInst.getDom()) const isDispose = () => !!(echartInst && echartInst.getDom())
/** /**
@ -270,6 +269,14 @@ export default defineComponent({
} }
} }
/**
*
* @param key moreDropDownOptions key
* @param option moreDropDownOptions current click option
*
* card
*
*/
const dropdownSelect = (key: string | number, option: DropdownOption) => { const dropdownSelect = (key: string | number, option: DropdownOption) => {
if (key === 'downloadChart' && isDispose()) { if (key === 'downloadChart' && isDispose()) {
const { filename, ...args } = props.downloadOptions const { filename, ...args } = props.downloadOptions
@ -303,7 +310,6 @@ export default defineComponent({
if (props.autoResize) { if (props.autoResize) {
resizeThrottleReturn = throttle(resizeChart, props.throttleWait) resizeThrottleReturn = throttle(resizeChart, props.throttleWait)
/** 监听内容区域尺寸变化更新 chart */ /** 监听内容区域尺寸变化更新 chart */
resizeObserverReturn = useResizeObserver( resizeObserverReturn = useResizeObserver(
props.observer || rayChartWrapperRef, props.observer || rayChartWrapperRef,
resizeThrottleReturn, resizeThrottleReturn,
@ -336,7 +342,6 @@ export default defineComponent({
} }
}, },
) )
/** /**
* *
* *
@ -350,7 +355,6 @@ export default defineComponent({
updateChartTheme() updateChartTheme()
}, },
) )
watchEffect(() => { watchEffect(() => {
/** 监听 options 变化 */ /** 监听 options 变化 */
if (props.watchOptions) { if (props.watchOptions) {
@ -367,6 +371,7 @@ export default defineComponent({
echartInst?.setOption(options, setOpt) echartInst?.setOption(options, setOpt)
}, },
{ {
// 深度监听 options
deep: true, deep: true,
}, },
) )
@ -383,6 +388,7 @@ export default defineComponent({
echart: echartInstanceRef, echart: echartInstanceRef,
dispose: unmount, dispose: unmount,
render: mount, render: mount,
isDispose,
}) })
onBeforeMount(async () => { onBeforeMount(async () => {

View File

@ -1,6 +1,6 @@
import type * as echarts from 'echarts/core' // `echarts` 核心模块 import type * as echarts from 'echarts/core' // `echarts` 核心模块
import type { PropType, VNode } from 'vue' import type { PropType, VNode } from 'vue'
import type { MaybeArray } from '@/types/modules/utils' import type { MaybeArray } from '@/types'
import type { import type {
LoadingOptions, LoadingOptions,
AutoResize, AutoResize,
@ -52,6 +52,7 @@ const props = {
type: Array as PropType<DropdownProps['options']>, type: Array as PropType<DropdownProps['options']>,
}, },
preset: { preset: {
// 是否启用预设样式
type: String as PropType<RChartPresetType>, type: String as PropType<RChartPresetType>,
}, },
contentStyle: { contentStyle: {
@ -153,6 +154,8 @@ const props = {
* *
* `echarts` * `echarts`
* *
*
*
*/ */
type: Array as PropType<EChartsExtensionInstallRegisters[]>, type: Array as PropType<EChartsExtensionInstallRegisters[]>,
default: () => [], default: () => [],

View File

@ -2,7 +2,7 @@ import { gridProps } from 'naive-ui'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import type { CollapseToggleText } from './type' import type { CollapseToggleText } from './type'
import type { AnyFC, MaybeArray } from '@/types/modules/utils' import type { AnyFC, MaybeArray } from '@/types'
const props = { const props = {
open: { open: {

View File

@ -10,7 +10,7 @@
*/ */
import type { PropType } from 'vue' import type { PropType } from 'vue'
import type { MaybeArray } from '@/types/modules/utils' import type { MaybeArray } from '@/types'
const props = { const props = {
color: { color: {

View File

@ -10,7 +10,7 @@
*/ */
import type { PropType } from 'vue' import type { PropType } from 'vue'
import type { MaybeArray } from '@/types/modules/utils' import type { MaybeArray } from '@/types'
import type { SpinProps } from 'naive-ui' import type { SpinProps } from 'naive-ui'
const props = { const props = {

View File

@ -32,6 +32,11 @@ export default defineComponent({
})) }))
const uuidEl = uuid() const uuidEl = uuid()
let intractable: null | ReturnType<typeof interact> let intractable: null | ReturnType<typeof interact>
// 记录拖拽的位置
const position = {
x: 0,
y: 0,
}
/** /**
* *
@ -44,7 +49,16 @@ export default defineComponent({
const target = document.getElementById(uuidEl) const target = document.getElementById(uuidEl)
if (target) { if (target) {
setupDraggable(target, props.preset).then((res) => { setupDraggable(target, props.preset, {
scheduler: (event) => {
const target = event.target
position.x += event.dx
position.y += event.dy
target.style.transform = `translate(${position.x}px, ${position.y}px)`
},
}).then((res) => {
intractable = res intractable = res
}) })
} }
@ -60,6 +74,12 @@ export default defineComponent({
) { ) {
nextTick(() => { nextTick(() => {
setupInteract() setupInteract()
const target = document.getElementById(uuidEl)
if (props.memo && target) {
target.style.transform = `translate(${position.x}px, ${position.y}px)`
}
}) })
} else { } else {
intractable?.unset() intractable?.unset()
@ -67,6 +87,9 @@ export default defineComponent({
intractable = null intractable = null
} }
}, },
{
immediate: true,
},
) )
return { return {

View File

@ -13,6 +13,16 @@ import { modalProps } from 'naive-ui'
const props = { const props = {
...modalProps, ...modalProps,
memo: {
/**
*
*
*
* @default true
*/
type: Boolean,
default: true,
},
/** /**
* *
* *

View File

@ -1,6 +1,11 @@
import interact from 'interactjs' import interact from 'interactjs'
import type { ModalProps } from 'naive-ui' import type { ModalProps } from 'naive-ui'
import type { AnyFC } from '@/types'
interface SetupDraggableOptions {
scheduler?: AnyFC
}
/** /**
* *
@ -15,7 +20,10 @@ import type { ModalProps } from 'naive-ui'
export const setupDraggable = ( export const setupDraggable = (
bindModal: HTMLElement, bindModal: HTMLElement,
preset: ModalProps['preset'], preset: ModalProps['preset'],
options?: SetupDraggableOptions,
): Promise<ReturnType<typeof interact>> => { ): Promise<ReturnType<typeof interact>> => {
const { scheduler } = options ?? {}
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
const allowFromStr = const allowFromStr =
@ -35,16 +43,7 @@ export const setupDraggable = (
], ],
listeners: { listeners: {
move: (event) => { move: (event) => {
const target = event.target scheduler?.(event)
const x =
(parseFloat(target.getAttribute('data-x')) || 0) + event.dx
const y =
(parseFloat(target.getAttribute('data-y')) || 0) + event.dy
target.style.transform = 'translate(' + x + 'px, ' + y + 'px)'
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}, },
}, },
}) })

View File

@ -23,4 +23,8 @@
color: #ffffff; color: #ffffff;
} }
} }
& .n-spin-content--spinning img {
filter: blur(6px);
}
} }

View File

@ -59,9 +59,6 @@ export default defineComponent({
const { expose } = ctx const { expose } = ctx
const qrcodeURL = ref<QRCodeRenderResponse>() const qrcodeURL = ref<QRCodeRenderResponse>()
const spinOverrides = {
opacitySpinning: '0.1',
}
let gifBuffer: GIFBuffer let gifBuffer: GIFBuffer
let watchCallback!: WatchStopHandle let watchCallback!: WatchStopHandle
@ -119,10 +116,12 @@ export default defineComponent({
const downloadQRCode = (fileName?: DownloadFilenameType) => { const downloadQRCode = (fileName?: DownloadFilenameType) => {
if (qrcodeURL.value && isValueType<string>(qrcodeURL.value, 'String')) { if (qrcodeURL.value && isValueType<string>(qrcodeURL.value, 'String')) {
downloadAnyFile( return downloadAnyFile(
qrcodeURL.value, qrcodeURL.value,
fileName || new Date().getTime() + '.png', fileName || new Date().getTime() + '.png',
) )
} else {
return Promise.reject()
} }
} }
@ -151,7 +150,6 @@ export default defineComponent({
return { return {
qrcodeURL, qrcodeURL,
spinOverrides,
errorActionClick, errorActionClick,
} }
}, },
@ -160,7 +158,6 @@ export default defineComponent({
<div class="ray-qrcode"> <div class="ray-qrcode">
<NSpin <NSpin
show={this.status === 'loading'} show={this.status === 'loading'}
themeOverrides={this.spinOverrides}
description={this.loadingDescription} description={this.loadingDescription}
> >
<img src={this.qrcodeURL as string | undefined} /> <img src={this.qrcodeURL as string | undefined} />
@ -180,7 +177,7 @@ export default defineComponent({
this.$slots.errorAction() this.$slots.errorAction()
) : ( ) : (
<> <>
<NButton text> <NButton text type="primary" color="#ffffff">
{{ {{
default: () => this.errorActionDescription, default: () => this.errorActionDescription,
icon: () => ( icon: () => (

View File

@ -11,7 +11,7 @@
import type { QRCodeStatus, QRCodeLevel } from './type' import type { QRCodeStatus, QRCodeLevel } from './type'
import type { PropType, VNode } from 'vue' import type { PropType, VNode } from 'vue'
import type { MaybeArray } from '@/types/modules/utils' import type { MaybeArray } from '@/types'
import type { Options } from 'awesome-qr' import type { Options } from 'awesome-qr'
const props = { const props = {

View File

@ -22,7 +22,7 @@ import { call, renderNode, uuid } from '@/utils'
import config from './config' import config from './config'
import type { DropdownOption, DataTableInst } from 'naive-ui' import type { DropdownOption, DataTableInst } from 'naive-ui'
import type { ComponentSize } from '@/types/modules/component' import type { ComponentSize } from '@/types'
import type { C as CType } from './type' import type { C as CType } from './type'
export default defineComponent({ export default defineComponent({

View File

@ -27,8 +27,8 @@ import { call } from '@/utils'
import type { TreeOption, TreeDropInfo } from 'naive-ui' import type { TreeOption, TreeDropInfo } from 'naive-ui'
import type { C } from '../type' import type { C } from '../type'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
import type { MaybeArray } from '@/types/modules/utils' import type { MaybeArray } from '@/types'
type FixedClick = (type: 'left' | 'right', option: C, index: number) => void type FixedClick = (type: 'left' | 'right', option: C, index: number) => void

View File

@ -16,8 +16,8 @@ import { call } from '@/utils'
import props from '../props' import props from '../props'
import config from '../config' import config from '../config'
import type { ComponentSize } from '@/types/modules/component' import type { ComponentSize } from '@/types'
import type { MaybeArray } from '@/types/modules/utils' import type { MaybeArray } from '@/types'
export default defineComponent({ export default defineComponent({
name: 'TableSizeSelect', name: 'TableSizeSelect',

View File

@ -12,10 +12,10 @@
import { dataTableProps } from 'naive-ui' import { dataTableProps } from 'naive-ui'
import type { PropType, VNode, VNodeChild } from 'vue' import type { PropType, VNode, VNodeChild } from 'vue'
import type { MaybeArray } from '@/types/modules/utils' import type { MaybeArray } from '@/types'
import type { DropdownOption, DataTableColumn } from 'naive-ui' import type { DropdownOption, DataTableColumn } from 'naive-ui'
import type { DownloadTableOptions, PrintTableOptions } from './type' import type { DownloadTableOptions, PrintTableOptions } from './type'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
const props = { const props = {
...dataTableProps, ...dataTableProps,

View File

@ -8,7 +8,7 @@ import type {
DataTableBaseColumn, DataTableBaseColumn,
} from 'naive-ui' } from 'naive-ui'
import type { VNode } from 'vue' import type { VNode } from 'vue'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
import type { PrintDomOptions } from '@/utils' import type { PrintDomOptions } from '@/utils'
export type TableActionIcon = string | (() => VNode) export type TableActionIcon = string | (() => VNode)

View File

@ -18,7 +18,7 @@ import { debounce } from 'lodash-es'
import { useEventListener } from '@vueuse/core' import { useEventListener } from '@vueuse/core'
import type { DebounceBindingOptions } from './type' import type { DebounceBindingOptions } from './type'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
import type { DebouncedFunc } from 'lodash-es' import type { DebouncedFunc } from 'lodash-es'
import type { CustomDirectiveFC } from '@/directives/type' import type { CustomDirectiveFC } from '@/directives/type'

View File

@ -1,5 +1,5 @@
import type { DebounceSettings } from 'lodash-es' import type { DebounceSettings } from 'lodash-es'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
export interface DebounceBindingOptions { export interface DebounceBindingOptions {
func: AnyFC func: AnyFC

View File

@ -18,7 +18,7 @@ import { throttle } from 'lodash-es'
import { useEventListener } from '@vueuse/core' import { useEventListener } from '@vueuse/core'
import type { ThrottleBindingOptions } from './type' import type { ThrottleBindingOptions } from './type'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
import type { DebouncedFunc } from 'lodash-es' import type { DebouncedFunc } from 'lodash-es'
import type { CustomDirectiveFC } from '@/directives/type' import type { CustomDirectiveFC } from '@/directives/type'

View File

@ -1,5 +1,5 @@
import type { ThrottleSettings } from 'lodash-es' import type { ThrottleSettings } from 'lodash-es'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
export interface ThrottleBindingOptions { export interface ThrottleBindingOptions {
func: AnyFC func: AnyFC

View File

@ -27,11 +27,7 @@
* createVariableState({ your state }) * createVariableState({ your state })
*/ */
import { ROOT_ROUTE, APP_WATERMARK_CONFIG } from '@/app-config/appConfig' import type { AnyFC } from '@/types'
import { cloneDeep } from 'lodash-es'
import type { AnyFC } from '@/types/modules/utils'
import type { Mutable } from '@/types/modules/helper'
/** /**
* *
@ -47,7 +43,6 @@ const variableState = reactive({
globalDrawerValue: false, // 全局抽屉控制器(小尺寸设备可用) globalDrawerValue: false, // 全局抽屉控制器(小尺寸设备可用)
globalMainLayoutLoad: true, // LayoutContent 区域加载控制器,会触发强制刷新 globalMainLayoutLoad: true, // LayoutContent 区域加载控制器,会触发强制刷新
layoutContentMaximize: false, // LayoutContent 区域全屏控制器 layoutContentMaximize: false, // LayoutContent 区域全屏控制器
globalRootRoute: cloneDeep(ROOT_ROUTE), // 全局根路由配置,同步至 ROOT_ROUTE
layoutContentSpinning: false, // LayoutContent 区域加载控制器,不会触发强制刷新 layoutContentSpinning: false, // LayoutContent 区域加载控制器,不会触发强制刷新
}) })

View File

@ -11,11 +11,10 @@
import { useEventListener, onClickOutside } from '@vueuse/core' import { useEventListener, onClickOutside } from '@vueuse/core'
import type { BasicTarget } from '@/types/modules/vue' import type { BasicTarget } from '@/types'
import type { import type {
MaybeElementRef, MaybeElementRef,
MaybeElement, MaybeElement,
UseEventSourceOptions,
MaybeRefOrGetter, MaybeRefOrGetter,
} from '@vueuse/core' } from '@vueuse/core'

View File

@ -11,7 +11,7 @@
import { useMenuGetters, useMenuActions } from '@/store' import { useMenuGetters, useMenuActions } from '@/store'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types'
export type Target = number | AppMenuOption export type Target = number | AppMenuOption

View File

@ -9,29 +9,29 @@
* @remark * @remark
*/ */
import { setVariable, getVariableToRefs } from '@/global-variable' import { useSettingGetters, useSettingActions } from '@/store'
import { cloneDeep } from 'lodash-es'
import type { DeepMutable } from '@/types/modules/helper' import type { AppRootRoute } from '@/store/modules/setting/type'
export function useAppRoot() { export function useAppRoot() {
const globalRootRoute = getVariableToRefs('globalRootRoute') const { getAppRootRoute } = useSettingGetters()
const { updateSettingState } = useSettingActions()
/** /**
* *
* @remark * @remark
*/ */
const getRootRoute = computed(() => globalRootRoute.value) const getRootRoute = getAppRootRoute
/** /**
* *
* @remark path * @remark path
*/ */
const getRootPath = computed(() => globalRootRoute.value.path) const getRootPath = computed(() => getAppRootRoute.value.path)
/** /**
* *
* @remark name * @remark name
*/ */
const getRootName = computed(() => globalRootRoute.value.name) const getRootName = computed(() => getAppRootRoute.value.name)
/** /**
* *
@ -42,11 +42,11 @@ export function useAppRoot() {
* @example * @example
* setRootRoute({ path: '/your root path', name: 'your root name' }) * setRootRoute({ path: '/your root path', name: 'your root name' })
*/ */
const setRootRoute = (route: DeepMutable<typeof globalRootRoute.value>) => { const setRootRoute = (route: AppRootRoute) => {
const routeRef = getVariableToRefs('globalRootRoute') updateSettingState(
const assignRoute = Object.assign(cloneDeep(routeRef.value), route) 'appRootRoute',
Object.assign({}, getAppRootRoute.value, route),
setVariable('globalRootRoute', assignRoute) )
} }
return { return {

View File

@ -12,7 +12,7 @@
import { useMenuGetters, useMenuActions } from '@/store' import { useMenuGetters, useMenuActions } from '@/store'
import { redirectRouterToDashboard } from '@/router/helper/routerCopilot' import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
import type { MenuTagOptions, Key } from '@/types/modules/app' import type { MenuTagOptions, Key } from '@/types'
export type CloseMenuTag = Key | MenuTagOptions export type CloseMenuTag = Key | MenuTagOptions

View File

@ -17,6 +17,10 @@
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import { watchEffectWithTarget } from '@/utils' import { watchEffectWithTarget } from '@/utils'
import type { UseWindowSizeOptions } from '@vueuse/core'
export interface UseDeviceOptions extends UseWindowSizeOptions {}
/** /**
* *
* *
@ -28,8 +32,8 @@ import { watchEffectWithTarget } from '@/utils'
* isTabletOrSmaller.value => true // 当前尺寸为平板或者更小 * isTabletOrSmaller.value => true // 当前尺寸为平板或者更小
* isTabletOrSmaller.value => false // 当前尺寸为桌面或者更大 * isTabletOrSmaller.value => false // 当前尺寸为桌面或者更大
*/ */
export function useDevice() { export function useDevice(options?: UseDeviceOptions) {
const { width, height } = useWindowSize() const { width, height } = useWindowSize(options)
const isTabletOrSmaller = ref(false) const isTabletOrSmaller = ref(false)
const update = () => { const update = () => {

View File

@ -13,7 +13,7 @@ import domToImage from 'dom-to-image'
import { unrefElement } from '@/utils' import { unrefElement } from '@/utils'
import type { Options as ReDomToImageOptions } from 'dom-to-image' import type { Options as ReDomToImageOptions } from 'dom-to-image'
import type { BasicTarget, TargetType } from '@/types/modules/vue' import type { BasicTarget, TargetType } from '@/types'
export type ImageType = keyof typeof domToImageMethods export type ImageType = keyof typeof domToImageMethods
@ -90,7 +90,8 @@ const domToImageMethods = {
* const { create, stop } = useDomToImage(refDom, { * const { create, stop } = useDomToImage(refDom, {
* beforeCreate: (element) => { ... }, * beforeCreate: (element) => { ... },
* created: (element, result) => { ... }, * created: (element, result) => { ... },
* createdError: (element, error) => { ... }, * createdError: (error) => { ... },
* finally: () => { ... },
* }) * })
*/ */
export const useDomToImage = <T extends HTMLElement>( export const useDomToImage = <T extends HTMLElement>(
@ -102,10 +103,11 @@ export const useDomToImage = <T extends HTMLElement>(
created, created,
createdError, createdError,
finally: _finally, finally: _finally,
imageType: _imageType,
} = options ?? {} } = options ?? {}
const run = ( const run = (
imageType: UseDomToImageOptions['imageType'] = 'jpeg', imageType?: UseDomToImageOptions['imageType'],
): Promise<DomToImageResult> => { ): Promise<DomToImageResult> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const element = unrefElement(target) const element = unrefElement(target)
@ -118,10 +120,7 @@ export const useDomToImage = <T extends HTMLElement>(
return reject('useDomToImage: element is undefined.') return reject('useDomToImage: element is undefined.')
} }
const type = imageType ?? options?.imageType domToImageMethods[imageType ?? _imageType ?? 'jpeg']?.(element, options)
const matchFc = domToImageMethods[type] || domToImageMethods['jpeg']
matchFc(element, options)
.then((res) => { .then((res) => {
created?.(res, element) created?.(res, element)

View File

@ -12,7 +12,7 @@
import { unrefElement, effectDispose, isValueType } from '@/utils' import { unrefElement, effectDispose, isValueType } from '@/utils'
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import type { BasicTarget } from '@/types/modules/vue' import type { BasicTarget } from '@/types'
export interface UseElementFullscreenOptions { export interface UseElementFullscreenOptions {
beforeEnter?: () => void beforeEnter?: () => void
@ -159,3 +159,7 @@ export const useElementFullscreen = (
toggleFullscreen, toggleFullscreen,
} }
} }
export type UseElementFullscreenReturnTypes = ReturnType<
typeof useElementFullscreen
>

View File

@ -18,7 +18,7 @@ const getI18nKey = (namespace: string | undefined, key: string) => {
return key return key
} }
if (key.startsWith(namespace)) { if (key[0] === namespace) {
return key return key
} }

View File

@ -2,7 +2,7 @@
import print from 'print-js' import print from 'print-js'
import { unrefElement } from '@/utils' import { unrefElement } from '@/utils'
import type { BasicTarget } from '@/types/modules/vue' import type { BasicTarget } from '@/types'
export interface UsePrintOptions export interface UsePrintOptions
extends Omit<print.Configuration, 'printable'> {} extends Omit<print.Configuration, 'printable'> {}

View File

@ -20,8 +20,8 @@ import { getVariableToRefs, setVariable } from '@/global-variable'
import { useMenuGetters, useMenuActions } from '@/store' import { useMenuGetters, useMenuActions } from '@/store'
import type { MenuInst } from 'naive-ui' import type { MenuInst } from 'naive-ui'
import type { NaiveMenuOptions } from '@/types/modules/component' import type { NaiveMenuOptions } from '@/types'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types'
export default defineComponent({ export default defineComponent({
name: 'AppMenu', name: 'AppMenu',

View File

@ -52,7 +52,7 @@ import { throttle } from 'lodash-es'
import { useAppRoot } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
import type { ScrollbarInst } from 'naive-ui' import type { ScrollbarInst } from 'naive-ui'
import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app' import type { MenuTagOptions, AppMenuOption } from '@/types'
export default defineComponent({ export default defineComponent({
name: 'AppMenuTag', name: 'AppMenuTag',
@ -423,7 +423,7 @@ export default defineComponent({
} }
}, },
render() { render() {
const { iconConfig, getRootPath, uuidScrollBar } = this const { iconConfig, getRootPath, uuidScrollBar, getMenuTagOptions } = this
const { maximize, closeCurrentMenuTag, scrollX, $t } = this const { maximize, closeCurrentMenuTag, scrollX, $t } = this
return ( return (
@ -472,7 +472,7 @@ export default defineComponent({
align="center" align="center"
justify="start" justify="start"
> >
{this.getMenuTagOptions.map((curr, idx) => ( {getMenuTagOptions.map((curr, idx) => (
<NButton <NButton
key={curr.key} key={curr.key}
class={['menu-tag__btn']} class={['menu-tag__btn']}
@ -502,8 +502,7 @@ export default defineComponent({
}, },
}} }}
</span> </span>
{(curr.closeable || {(curr.closeable || getMenuTagOptions.length === 1) &&
this.getMenuTagOptions.length === 1) &&
curr.key !== getRootPath ? ( curr.key !== getRootPath ? (
<NIcon <NIcon
class="menu-tag__btn-icon" class="menu-tag__btn-icon"

View File

@ -27,7 +27,7 @@ import { useMenuGetters, useMenuActions } from '@/store'
import { useDevice } from '@/hooks/web' import { useDevice } from '@/hooks/web'
import type { DropdownOption } from 'naive-ui' import type { DropdownOption } from 'naive-ui'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types'
export default defineComponent({ export default defineComponent({
name: 'SiderBarBreadcrumb', name: 'SiderBarBreadcrumb',

View File

@ -31,7 +31,7 @@ import { useDevice } from '@/hooks/web'
import { useEventListener } from '@vueuse/core' import { useEventListener } from '@vueuse/core'
import type { AppRouteMeta } from '@/router/type' import type { AppRouteMeta } from '@/router/type'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types'
export default defineComponent({ export default defineComponent({
name: 'GlobalSearch', name: 'GlobalSearch',

View File

@ -27,7 +27,7 @@ import { APP_THEME } from '@/app-config/designConfig'
import { useSettingGetters, useSettingActions } from '@/store' import { useSettingGetters, useSettingActions } from '@/store'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import type { Placement } from '@/types/modules/component' import type { Placement } from '@/types'
const SettingDrawer = defineComponent({ const SettingDrawer = defineComponent({
name: 'SettingDrawer', name: 'SettingDrawer',

View File

@ -22,7 +22,7 @@ import { getStorage } from '@/utils'
import { SYSTEM_DEFAULT_LOCAL } from '@/app-config/localConfig' import { SYSTEM_DEFAULT_LOCAL } from '@/app-config/localConfig'
import { APP_CATCH_KEY } from '@/app-config/appConfig' import { APP_CATCH_KEY } from '@/app-config/appConfig'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
import type { import type {
AppLocalesModules, AppLocalesModules,
AppLocalesDropdownMixedOption, AppLocalesDropdownMixedOption,

View File

@ -4,7 +4,7 @@ import type {
DropdownDividerOption, DropdownDividerOption,
DropdownRenderOption, DropdownRenderOption,
} from 'naive-ui' } from 'naive-ui'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
export interface AppCurrentAppMessages { export interface AppCurrentAppMessages {
'zh-CN': object 'zh-CN': object

View File

@ -1,6 +1,7 @@
import App from './App' import App from './App'
import '@/styles/base.scss' import '@/styles/base.scss'
import './app-components/provider/provider.scss'
import 'virtual:svg-icons-register' // vite-plugin-svg-icons 脚本,启用 svg 雪碧图 import 'virtual:svg-icons-register' // vite-plugin-svg-icons 脚本,启用 svg 雪碧图

View File

@ -21,7 +21,8 @@ import { cloneDeep } from 'lodash-es'
import type { AppRouteRecordRaw } from '@/router/type' import type { AppRouteRecordRaw } from '@/router/type'
const isRootPath = (path: string) => path.startsWith('/') // 是否为根路由
const isRootPath = (path: string) => path[0] === '/'
/** /**
* *
@ -41,31 +42,25 @@ const routePromotion = (
return [] return []
} }
const sourceArr = arr for (const curr of arr) {
const newPath = path + (isRootPath(curr.path) ? curr.path : '/' + curr.path)
sourceArr.forEach((curr) => {
if (curr.children?.length) { if (curr.children?.length) {
routePromotion( routePromotion(curr.children, result, newPath)
curr.children,
result,
path + (isRootPath(curr.path) ? curr.path : '/' + curr.path),
)
} else {
const newPath =
path + (isRootPath(curr.path) ? curr.path : '/' + curr.path)
const newCurr: AppRouteRecordRaw = { continue
} else {
result.push({
...curr, ...curr,
path: newPath, path: newPath,
} })
result.push(newCurr)
} }
}) }
return result return result
} }
// 获取所有已展开的路由
export const expandRoutes = (arr: AppRouteRecordRaw[]) => { export const expandRoutes = (arr: AppRouteRecordRaw[]) => {
if (!Array.isArray(arr)) { if (!Array.isArray(arr)) {
return [] return []

View File

@ -18,7 +18,7 @@ import { useAppRoot } from '@/hooks/template'
import type { Router } from 'vue-router' import type { Router } from 'vue-router'
import type { AppRouteMeta } from '@/router/type' import type { AppRouteMeta } from '@/router/type'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types'
/** /**
* *

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
import type { DefineComponent, VNode } from 'vue' import type { DefineComponent, VNode } from 'vue'
export type Component<T = any> = export type Component<T = any> =

View File

@ -79,7 +79,6 @@ export const useMenuGetters = () => {
export const useMenuActions = () => { export const useMenuActions = () => {
const { const {
changeMenuModelValue, changeMenuModelValue,
setupAppMenu,
collapsedMenu, collapsedMenu,
spliceMenTagOptions, spliceMenTagOptions,
emptyMenuTagOptions, emptyMenuTagOptions,
@ -88,7 +87,6 @@ export const useMenuActions = () => {
return { return {
changeMenuModelValue, changeMenuModelValue,
setupAppMenu,
collapsedMenu, collapsedMenu,
spliceMenTagOptions, spliceMenTagOptions,
emptyMenuTagOptions, emptyMenuTagOptions,

View File

@ -71,6 +71,12 @@ export const useSettingGetters = () => {
*/ */
const getWatermarkConfig = computed(() => variable.watermarkConfig) const getWatermarkConfig = computed(() => variable.watermarkConfig)
/**
*
* @remark app
*/
const getAppRootRoute = computed(() => variable.appRootRoute)
return { return {
getDrawerPlacement, getDrawerPlacement,
getPrimaryColorOverride, getPrimaryColorOverride,
@ -83,6 +89,7 @@ export const useSettingGetters = () => {
getContentTransition, getContentTransition,
getWatermarkSwitch, getWatermarkSwitch,
getWatermarkConfig, getWatermarkConfig,
getAppRootRoute,
} }
} }

View File

@ -21,7 +21,7 @@
import { APP_KEEP_ALIVE } from '@/app-config/appConfig' import { APP_KEEP_ALIVE } from '@/app-config/appConfig'
import type { KeepAliveStoreState } from './type' import type { KeepAliveStoreState } from './type'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types'
export const piniaKeepAliveStore = defineStore( export const piniaKeepAliveStore = defineStore(
'keepAlive', 'keepAlive',

View File

@ -39,11 +39,7 @@ const isMatch = (
return false return false
} }
if (node[key] === value) { return node[key] === value
return true
}
return false
} }
/** /**
@ -95,7 +91,7 @@ export const parseAndFindMatchingNodes = (
key: string | number, key: string | number,
value: string | number, value: string | number,
) => { ) => {
const temp = [] const temp: AppMenuOption[] = []
for (const it of options) { for (const it of options) {
const innerTemp = findMatchingNodes(it, key, value) const innerTemp = findMatchingNodes(it, key, value)

View File

@ -25,7 +25,7 @@
import { NEllipsis } from 'naive-ui' import { NEllipsis } from 'naive-ui'
import { setStorage } from '@/utils' import { setStorage, pick } from '@/utils'
import { validRole, validMenuItemShow } from '@/router/helper/routerCopilot' import { validRole, validMenuItemShow } from '@/router/helper/routerCopilot'
import { import {
parseAndFindMatchingNodes, parseAndFindMatchingNodes,
@ -35,18 +35,16 @@ import {
} from './helper' } from './helper'
import { useI18n } from '@/hooks/web' import { useI18n } from '@/hooks/web'
import { getAppRawRoutes } from '@/router/appRouteModules' import { getAppRawRoutes } from '@/router/appRouteModules'
import { throttle } from 'lodash-es'
import { useKeepAliveActions } from '@/store' import { useKeepAliveActions } from '@/store'
import type { AppRouteRecordRaw } from '@/router/type' import type { AppMenuOption, MenuTagOptions } from '@/types'
import type { AppMenuOption, MenuTagOptions } from '@/types/modules/app'
import type { MenuState } from '@/store/modules/menu/type' import type { MenuState } from '@/store/modules/menu/type'
import type { LocationQuery } from 'vue-router' import type { LocationQuery } from 'vue-router'
export const piniaMenuStore = defineStore( export const piniaMenuStore = defineStore(
'menu', 'menu',
() => { () => {
const router = useRouter() const { push, getRoutes } = useRouter()
const route = useRoute() const route = useRoute()
const { t } = useI18n() const { t } = useI18n()
const { setKeepAliveInclude } = useKeepAliveActions() const { setKeepAliveInclude } = useKeepAliveActions()
@ -86,8 +84,7 @@ export const piniaMenuStore = defineStore(
}) })
if (option.path === getCatchMenuKey()) { if (option.path === getCatchMenuKey()) {
/** 设置标签页(初始化时执行设置一次, 避免含有平级路由模式情况时出现不能正确设置标签页的情况) */ menuState.currentMenuOption = attr
setMenuTagOptionsWhenMenuValueChange(option.path, attr)
} }
attr.show = validMenuItemShow(attr) attr.show = validMenuItemShow(attr)
@ -121,21 +118,7 @@ export const piniaMenuStore = defineStore(
key: string | number, key: string | number,
option: AppMenuOption, option: AppMenuOption,
) => { ) => {
const { meta } = option as unknown as AppRouteRecordRaw
menuState.breadcrumbOptions = getCompleteRoutePath(menuState.options, key) menuState.breadcrumbOptions = getCompleteRoutePath(menuState.options, key)
if (meta.sameLevel) {
nextTick().then(() => {
const fd = menuState.breadcrumbOptions.find((curr) => {
return curr.path === option.path
})
if (!fd) {
menuState.breadcrumbOptions.push(option as unknown as AppMenuOption)
}
})
}
} }
/** /**
@ -204,46 +187,68 @@ export const piniaMenuStore = defineStore(
* *
* , key key * , key key
*/ */
if (!String(key).startsWith('/')) { if (String(key)[0] === '/') {
/** 根路由直接跳转 */
push({
path,
query,
})
} else {
/** 如果不是根路由, 则拼接完整路由并跳转 */ /** 如果不是根路由, 则拼接完整路由并跳转 */
const _path = getCompleteRoutePath(menuState.options, key) const _path = getCompleteRoutePath(menuState.options, key)
.map((curr) => curr.key) .map((curr) => curr.key)
.join('/') .join('/')
router.push({ push({
path: _path, path: _path,
query, query,
}) })
} else {
/** 根路由直接跳转 */
router.push({
path,
query,
})
} }
/** 检查是否为根路由 */ /** 检查是否为根路由 */
const count = (path.match(isRootPathReg) || []).length const count = (path.match(isRootPathReg) || []).length
const { sameLevel } = meta
/** 更新缓存队列 */ /** 更新缓存队列 */
setKeepAliveInclude(option as unknown as AppMenuOption) setKeepAliveInclude(option)
/** 更新浏览器标题 */ /** 更新浏览器标题 */
updateDocumentTitle(option as unknown as AppMenuOption) updateDocumentTitle(option)
if (!meta.sameLevel || (meta.sameLevel && count === 1)) { // 如果不为 sameLevel则会执行更新覆盖更新面包屑、添加标签菜单、更新缓存
if (!sameLevel || (sameLevel && count === 1)) {
/** 更新标签菜单 */ /** 更新标签菜单 */
setMenuTagOptionsWhenMenuValueChange(key, option) setMenuTagOptionsWhenMenuValueChange(key, option)
/** 更新面包屑 */ /** 更新面包屑 */
setBreadcrumbOptions(key, option) setBreadcrumbOptions(key, option)
menuState.menuKey = key menuState.menuKey = key
menuState.currentMenuOption = option
/** 缓存菜单 key(sessionStorage) */ /** 缓存菜单 key(sessionStorage) */
setStorage('menuKey', key) setStorage('menuKey', key)
} else { } else {
setBreadcrumbOptions(menuState.menuKey || '', option) // 使用 pick 提取仅需要的字段,避免 vue 抛错空引用,导致性能损耗
} const breadcrumbOption = pick(resolveOption(option), [
'breadcrumbLabel',
'children',
'key',
'meta',
'name',
'path',
'show',
])
// 查看是否重复
const find = menuState.breadcrumbOptions.find(
(curr) => curr.key === breadcrumbOption.key,
)
menuState.currentMenuOption = option // 如果未重复追加
if (!find) {
menuState.breadcrumbOptions.push(
breadcrumbOption as unknown as AppMenuOption,
)
}
}
} }
} }
@ -270,21 +275,22 @@ export const piniaMenuStore = defineStore(
combinePath = splitPath[splitPath.length - 1] combinePath = splitPath[splitPath.length - 1]
} }
// 如果当前菜单 key 与路由地址相同,说明不是手动更新 url 则不会触发更新 // 直接使用完整 url检查是否在 routes 中
if (combinePath === menuState.menuKey) { const findMenuOption = getRoutes().find((curr) => curr.path === routePath)
return
}
const findMenuOption = router
.getRoutes()
.find((curr) =>
count > 1 ? path === curr.path : combinePath === curr.path,
)
if (findMenuOption) { if (findMenuOption) {
// 使用 pick 提取仅需要的字段,避免 vue 抛错空引用,导致性能损耗
const pickOption = pick(findMenuOption, [
'children',
'meta',
'path',
'name',
'redirect',
]) as unknown as AppMenuOption
changeMenuModelValue( changeMenuModelValue(
count > 1 ? combinePath : path, count > 1 ? combinePath : path,
resolveOption(findMenuOption as unknown as AppMenuOption), resolveOption(pickOption),
query, query,
) )
} }
@ -320,6 +326,17 @@ export const piniaMenuStore = defineStore(
0, 0,
) )
// 初始化后更新面包屑、标签菜单
if (menuState.currentMenuOption) {
const { currentMenuOption } = menuState
setBreadcrumbOptions(currentMenuOption.key, currentMenuOption)
setMenuTagOptionsWhenMenuValueChange(
currentMenuOption.key,
currentMenuOption,
)
}
resolve() resolve()
}) })
} }
@ -384,8 +401,7 @@ export const piniaMenuStore = defineStore(
return { return {
...toRefs(menuState), ...toRefs(menuState),
changeMenuModelValue: throttle(changeMenuModelValue, 500), changeMenuModelValue,
setupAppMenu,
collapsedMenu, collapsedMenu,
spliceMenTagOptions, spliceMenTagOptions,
emptyMenuTagOptions, emptyMenuTagOptions,

View File

@ -1,8 +1,4 @@
import type { import type { AppMenuOption, MenuTagOptions, AppMenuKey } from '@/types'
AppMenuOption,
MenuTagOptions,
AppMenuKey,
} from '@/types/modules/app'
export interface MenuState { export interface MenuState {
menuKey: AppMenuKey menuKey: AppMenuKey

View File

@ -4,12 +4,10 @@ import { colorToRgba, setStorage } from '@/utils'
import { useI18n } from '@/hooks/web' import { useI18n } from '@/hooks/web'
import { APP_THEME } from '@/app-config/designConfig' import { APP_THEME } from '@/app-config/designConfig'
import { useDayjs } from '@/hooks/web' import { useDayjs } from '@/hooks/web'
import { APP_WATERMARK_CONFIG } from '@/app-config/appConfig'
import { cloneDeep } from 'lodash-es'
import type { SettingState } from '@/store/modules/setting/type' import type { SettingState } from '@/store/modules/setting/type'
import type { LocalKey } from '@/hooks/web' import type { LocalKey } from '@/hooks/web'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
export const piniaSettingStore = defineStore( export const piniaSettingStore = defineStore(
'setting', 'setting',
@ -37,7 +35,20 @@ export const piniaSettingStore = defineStore(
copyrightSwitch: true, // 底部区域开关 copyrightSwitch: true, // 底部区域开关
contentTransition: 'scale', // 切换过渡效果 contentTransition: 'scale', // 切换过渡效果
watermarkSwitch: false, // 水印开关, watermarkSwitch: false, // 水印开关,
watermarkConfig: cloneDeep(APP_WATERMARK_CONFIG), watermarkConfig: {
content: 'Trying be better~',
fontSize: 16,
lineHeight: 16,
width: 384,
height: 384,
xOffset: 12,
yOffset: 60,
rotate: -15,
},
appRootRoute: {
name: 'Dashboard',
path: '/dashboard',
},
}) })
/** 修改当前语言 */ /** 修改当前语言 */

View File

@ -1,6 +1,21 @@
import type { GlobalThemeOverrides } from 'naive-ui' import type { GlobalThemeOverrides } from 'naive-ui'
import type { Placement } from '@/types/modules/component' import type { Placement } from '@/types'
import type { APP_WATERMARK_CONFIG } from '@/app-config/appConfig'
export interface WatermarkConfig {
content: string
fontSize: number
lineHeight: number
width: number
height: number
xOffset: number
yOffset: number
rotate: number
}
export interface AppRootRoute {
name: string
path: string
}
export interface SettingState { export interface SettingState {
drawerPlacement: Placement drawerPlacement: Placement
@ -13,5 +28,6 @@ export interface SettingState {
watermarkSwitch: boolean watermarkSwitch: boolean
copyrightSwitch: boolean copyrightSwitch: boolean
contentTransition: string contentTransition: string
watermarkConfig: typeof APP_WATERMARK_CONFIG watermarkConfig: WatermarkConfig
appRootRoute: AppRootRoute
} }

16
src/types/global.d.ts vendored
View File

@ -8,12 +8,28 @@ import type {
LoadingBarApi, LoadingBarApi,
NotificationApi, NotificationApi,
} from 'naive-ui' } from 'naive-ui'
import type { NativeElements, ReservedProps, VNode } from 'vue'
export declare global { export declare global {
declare interface UnknownObjectKey { declare interface UnknownObjectKey {
[propName: string]: any [propName: string]: any
} }
namespace JSX {
export interface Element extends VNode {}
export interface ElementClass {
$props: {}
}
export interface ElementAttributesProperty {
$props: {}
}
export interface IntrinsicElements extends NativeElements {
// allow arbitrary elements
[name: string]: any
}
export interface IntrinsicAttributes extends ReservedProps {}
}
declare const __DEV__: boolean declare const __DEV__: boolean
declare const __APP_CFG__: AppConfig declare const __APP_CFG__: AppConfig

8
src/types/index.ts Normal file
View File

@ -0,0 +1,8 @@
export type * from './modules/app'
export type * from './modules/appConfig'
export type * from './modules/axios'
export type * from './modules/cfg'
export type * from './modules/component'
export type * from './modules/helper'
export type * from './modules/utils'
export type * from './modules/vue'

View File

@ -5,7 +5,7 @@ import type {
AliasOptions, AliasOptions,
UserConfigExport, UserConfigExport,
} from 'vite' } from 'vite'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
import type { GlobalThemeOverrides } from 'naive-ui' import type { GlobalThemeOverrides } from 'naive-ui'
export interface LayoutSideBarLogo { export interface LayoutSideBarLogo {

View File

@ -13,6 +13,18 @@ export type ConditionalKeys<Base, Condition> = NonNullable<
}[keyof Base] }[keyof Base]
> >
/**
*
*
*
* @example
* ConditionalExclude<{ a: string, b: number }, string> // { b: number }
*/
export type ConditionalExclude<Base, Condition> = Omit<
Base,
ConditionalKeys<Base, Condition>
>
/** /**
* *
* *
@ -68,3 +80,17 @@ export type DeepMutable<T> = {
? DeepMutable<T[P]> ? DeepMutable<T[P]>
: T[P] : T[P]
} }
/**
*
* Promise
*
* @example
* ReturnPromiseType<Promise<string>> // string
* ReturnPromiseType<Promise<string> | Promise<number>> // string | number
*/
export type ReturnPromiseType<T extends Promise<any>> = T extends Promise<
infer U
>
? U
: never

View File

@ -3,8 +3,8 @@ import type {
DownloadAnyFileDataType, DownloadAnyFileDataType,
BasicTypes, BasicTypes,
AnyFC, AnyFC,
} from '@/types/modules/utils' } from '@/types'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types'
/** /**
* *
@ -150,9 +150,7 @@ export const downloadAnyFile = (
try { try {
if (typeof data === 'string') { if (typeof data === 'string') {
downloadBase64File(data, fileName) downloadBase64File(data, fileName)
resolve() return resolve()
return
} }
if (data instanceof ArrayBuffer) { if (data instanceof ArrayBuffer) {
@ -162,9 +160,7 @@ export const downloadAnyFile = (
} else if (data instanceof File || data instanceof Blob) { } else if (data instanceof File || data instanceof Blob) {
blobData = data blobData = data
} else { } else {
reject(new Error('downloadAnyFile: Unsupported data type.')) return reject(new Error('downloadAnyFile: Unsupported data type.'))
return
} }
const url = URL.createObjectURL(blobData) const url = URL.createObjectURL(blobData)
@ -181,18 +177,18 @@ export const downloadAnyFile = (
link.addEventListener('load', () => { link.addEventListener('load', () => {
remove() remove()
resolve() return resolve()
}) })
link.addEventListener('error', (error) => { link.addEventListener('error', (error) => {
remove() remove()
reject(error) return reject(error)
}) })
document.body.appendChild(link) document.body.appendChild(link)
link.click() link.click()
} catch (error) { } catch (error) {
reject(error) return reject(error)
} }
}) })
} }

View File

@ -9,7 +9,7 @@
* @remark * @remark
*/ */
import type { StorageLike, RemoveStorageKey } from '@/types/modules/utils' import type { StorageLike, RemoveStorageKey } from '@/types'
/** /**
* *

View File

@ -4,7 +4,7 @@
// import MD5 from 'crypto-js/md5' // import MD5 from 'crypto-js/md5'
// import BASE64 from 'crypto-js/enc-base64' // import BASE64 from 'crypto-js/enc-base64'
// import type { WordArray, CipherParams } from '@/types/modules/utils' // import type { WordArray, CipherParams } from '@/types'
/** /**
* *

View File

@ -13,7 +13,7 @@ import { omit } from './basic'
import { useDomToImage, usePrint } from '@/hooks/web' import { useDomToImage, usePrint } from '@/hooks/web'
import type { UsePrintOptions, UseDomToImageOptions } from '@/hooks/web' import type { UsePrintOptions, UseDomToImageOptions } from '@/hooks/web'
import type { BasicTarget } from '@/types/modules/vue' import type { BasicTarget } from '@/types'
export interface PrintDomOptions { export interface PrintDomOptions {
printOptions?: Omit<UsePrintOptions, 'printable' | 'type' | 'base64'> printOptions?: Omit<UsePrintOptions, 'printable' | 'type' | 'base64'>

View File

@ -1,11 +1,8 @@
import { APP_REGEX } from '@/app-config/regexConfig' import { APP_REGEX } from '@/app-config/regexConfig'
import { effectDispose, unrefElement, isValueType } from '@/utils' import { effectDispose, unrefElement, isValueType } from '@/utils'
import type { import type { PartialCSSStyleDeclaration, ElementSelector } from '@/types'
PartialCSSStyleDeclaration, import type { BasicTarget } from '@/types'
ElementSelector,
} from '@/types/modules/utils'
import type { BasicTarget } from '@/types/modules/vue'
/** /**
* *

View File

@ -32,7 +32,7 @@ import { cloneDeep } from 'lodash-es'
import { isValueType } from '@/utils' import { isValueType } from '@/utils'
import type { Options } from 'currency.js' import type { Options } from 'currency.js'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
export type CurrencyArguments = string | number | currency export type CurrencyArguments = string | number | currency

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type { AnyFC, MaybeArray } from '@/types/modules/utils' import type { AnyFC, MaybeArray } from '@/types'
function call(funcs: MaybeArray<() => void>): void function call(funcs: MaybeArray<() => void>): void

View File

@ -11,7 +11,7 @@
import { getCurrentScope, onScopeDispose } from 'vue' import { getCurrentScope, onScopeDispose } from 'vue'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
/** /**
* *

View File

@ -9,7 +9,7 @@
* @remark * @remark
*/ */
import type { BasicTarget, TargetType, TargetValue } from '@/types/modules/vue' import type { BasicTarget, TargetType, TargetValue } from '@/types'
import type { ComponentPublicInstance } from 'vue' import type { ComponentPublicInstance } from 'vue'
/** /**

View File

@ -12,7 +12,7 @@
import { effectDispose } from './effectDispose' import { effectDispose } from './effectDispose'
import type { WatchOptionsBase } from 'vue' import type { WatchOptionsBase } from 'vue'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types'
/** /**
* *

View File

@ -20,7 +20,7 @@ import {
NFormItem, NFormItem,
} from 'naive-ui' } from 'naive-ui'
import type { ConditionalPick } from '@/types/modules/helper' import type { ConditionalPick } from '@/types'
import type { import type {
DebounceBindingOptions, DebounceBindingOptions,
ThrottleBindingOptions, ThrottleBindingOptions,

View File

@ -28,6 +28,16 @@ export default defineComponent({
render() { render() {
return ( return (
<NSpace vertical wrapItem={false}> <NSpace vertical wrapItem={false}>
<NCard title="props">
<NSpace vertical>
<h3>
memoryPosition: 是否记住上一次被拖拽的位置
true
</h3>
<h3>fullscreen: 全屏模态框</h3>
<h3>dad: 启用拖拽 false </h3>
</NSpace>
</NCard>
<RModal <RModal
v-model:show={this.modal1} v-model:show={this.modal1}
title="全屏模态框" title="全屏模态框"

View File

@ -58,7 +58,7 @@ export default defineComponent({
logoImage={LOGO} logoImage={LOGO}
status="error" status="error"
onReload={() => { onReload={() => {
window.$message.error('relod props') window.$message.info('reload event')
}} }}
/> />
<RQRCode <RQRCode

View File

@ -69,7 +69,7 @@ const RouterDemoHome = defineComponent({
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
dataSource.push({ dataSource.push({
name: '张三', name: '张三' + i,
address: 'New York No. 1 Lake Park', address: 'New York No. 1 Lake Park',
phone: '010-121212', phone: '010-121212',
key: i, key: i,

View File

@ -12,6 +12,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"lib": ["ESNext", "DOM", "es5", "es6", "dom.iterable", "es2022"], "lib": ["ESNext", "DOM", "es5", "es6", "dom.iterable", "es2022"],
"skipLibCheck": true, "skipLibCheck": true,
"jsxImportSource": "vue",
"baseUrl": "./", "baseUrl": "./",
"rootDir": "./", "rootDir": "./",
"paths": { "paths": {

View File

@ -5,7 +5,7 @@ import pkg from './package.json'
import vitePlugins from './vite.plugin.config' import vitePlugins from './vite.plugin.config'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(async ({ mode }) => { export default defineConfig(({ mode }) => {
const { dependencies, devDependencies, name, version } = pkg const { dependencies, devDependencies, name, version } = pkg
const { const {
server, server,