Compare commits

..

22 Commits

Author SHA1 Message Date
一勺
4212d01c46
Merge pull request #5 from element-plus-x/feat-20250617-coder_zr
Feat 20250617 coder zr
2025-06-18 20:55:21 +08:00
ZRMYDYCG
057699d0ac docs: 📝 文档更新 2025-06-18 20:51:04 +08:00
ZRMYDYCG
2e8939f861 chore: 配置项目版本发版工具 2025-06-18 20:45:26 +08:00
ZRMYDYCG
e6fd749305 feat: 新增路径别名 2025-06-18 20:29:29 +08:00
ZRMYDYCG
0916259a15 docs: 📝添加开发者描述 2025-06-18 20:09:51 +08:00
ZRMYDYCG
aac5448146 perf: 优化插件注册流程,提供暴露实例钩子 2025-06-18 19:53:55 +08:00
ZRMYDYCG
7905dbe470 feat: dialog 样式行为优化 2025-06-18 19:04:27 +08:00
Json_Lee
682d9a408e fix(api): 🧩 隐藏接口地址 2025-06-16 11:40:27 +08:00
何嘉悦
98a71d2061 docs: 📚 更新交流3群二维码图 2025-06-16 10:18:22 +08:00
Json_Lee
206595146e Merge branch 'main' of https://github.com/HeJiaYue520/ruoyi-element-ai 2025-06-11 09:43:20 +08:00
何嘉悦
5305747594 chore: 🔨 其他修改,注释 2025-06-08 17:28:34 +08:00
何嘉悦
a8ff983504
Update README.md
更新文档
2025-06-08 13:22:42 +08:00
何嘉悦
ecd17198ed
Update README.md
更新文档
2025-06-08 13:14:09 +08:00
何嘉悦
9f462bc41e
Update README.md
更新文档
2025-06-07 19:16:42 +08:00
何嘉悦
fd4320f4a8
Update README.md
文档更新
2025-06-07 00:56:51 +08:00
何嘉悦
edd482977b
Update README.md
更新文档
2025-06-07 00:55:10 +08:00
何嘉悦
ae1498c1c8 docs: 📚 文档更新 2025-06-07 00:43:31 +08:00
何嘉悦
5079db4ecd docs: 📚 文档更新 2025-06-07 00:42:21 +08:00
何嘉悦
8a808cfdcc docs: 📚 文档更新 2025-06-07 00:39:51 +08:00
何嘉悦
b8f2bc9ff2
Update README.md
更新文档
2025-06-07 00:35:58 +08:00
何嘉悦
8ba495e2e9 fix: 修复提价bug 2025-06-07 00:31:23 +08:00
何嘉悦
4de275159d docs: 📚 更新文档 2025-06-07 00:27:47 +08:00
24 changed files with 1878 additions and 138 deletions

View File

@ -11,6 +11,4 @@ VITE_WEB_ENV = 'development'
VITE_WEB_BASE_API = '/dev-api' VITE_WEB_BASE_API = '/dev-api'
# 本地接口 # 本地接口
# VITE_API_URL = http://122.51.75.95:6039 VITE_API_URL = *
VITE_API_URL = http://129.211.24.7:6039
# VITE_API_URL = https://mp.pandarobot.chat

View File

@ -13,8 +13,4 @@ VITE_WEB_BASE_API = '/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli # 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip VITE_BUILD_COMPRESS = gzip
# VITE_API_URL = http://122.51.75.95:6039 VITE_API_URL = *
# VITE_API_URL = http://129.211.24.7:6039
VITE_API_URL = https://mp.pandarobot.chat

33
.release-it.json Normal file
View File

@ -0,0 +1,33 @@
{
"plugins": {
"@release-it/conventional-changelog": {
"preset": {
"name": "conventionalcommits",
"types": [
{ "type": "feat", "section": "✨ Features | 新功能" },
{ "type": "fix", "section": "🐛 Bug Fixes | Bug 修复" },
{ "type": "chore", "section": "🎫 Chores | 其他更新" },
{ "type": "docs", "section": "📝 Documentation | 文档" },
{ "type": "style", "section": "💄 Styles | 风格" },
{ "type": "refactor", "section": "♻ Code Refactoring | 代码重构" },
{ "type": "perf", "section": "⚡ Performance Improvements | 性能优化" },
{ "type": "test", "section": "✅ Tests | 测试" },
{ "type": "revert", "section": "⏪ Reverts | 回退" },
{ "type": "build", "section": "👷‍ Build System | 构建" },
{ "type": "ci", "section": "🔧 Continuous Integration | CI 配置" },
{ "type": "config", "section": "🔨 CONFIG | 配置" }
]
},
"infile": "CHANGELOG.md",
"ignoreRecommendedBump": true,
"strictSemVer": true
}
},
"git": {
"commitMessage": "chore: Release v${version}"
},
"github": {
"release": true,
"draft": false
}
}

View File

@ -6,18 +6,27 @@
</div> </div>
<div align="center">
<img src="https://cdn.element-plus-x.com/chat/1.webp" />
</div>
## 🚀 项目亮点 ## 🚀 项目亮点
**ruoyi-element-ai** 是基于 **Vue3.5** + **Element-Plus-X** + **TypeScript** 的企业级AI应用模板搭配 **ruoyi-ai** 后端,快速构建仿豆包/通义的全栈AI项目。 **ruoyi-element-ai** 是基于 **Vue3.5** + **Element-Plus-X** + **hook-fetch** + **TypeScript** + **Eslint9** 开发的企业级AI应用模板搭配 **ruoyi-ai** 后端,快速构建仿豆包/通义的全栈AI项目。
## 💡 核心优势 ## 💡 核心优势
- 最新技术栈Vue3.5/Vite5/Pinia3/TypeScript5.8 - 最新技术栈Vue3.5+/Vite6/Pinia3/TypeScript5
- 流式交互Hook-Fetch支持Server-Sent Events - 流式交互Hook-Fetch支持Server-Sent Events,插件化写法优雅的一批
- 企业级规范ESLint/Stylelint/husky/commitlint全链路校验 - 企业级规范ESLint/Stylelint/husky/commitlint全链路校验
- 开箱即用:内置动态路由、状态管理、组件库封装 - 全栈项目:集成 ruoyi-ai 后端服务,快速开发
- 开箱即用内置动态路由、状态管理、组件库封装、hooks封装
## 🎯 开发文档
帮助你快速熟悉项目
<div align="center"> <div align="center">
<img src="https://cdn.element-plus-x.com/chat/1.webp" calss="element-plus-x-bubble" /> <img src="https://cdn.element-plus-x.com/chat/docs1.webp" />
</div> </div>
## 🔗 关键链接 ## 🔗 关键链接
@ -25,9 +34,9 @@
| 功能模块 | 说明 | 链接 | | 功能模块 | 说明 | 链接 |
|------------------|-------------------------------|----------------------------------------------------------------------| |------------------|-------------------------------|----------------------------------------------------------------------|
| 🚀 前端仓库 | Gitee/GitHub 代码仓库(感谢 star🥰爱你爱你😘) | [Gitee](https://gitee.com/he-jiayue/ruoyi-element-ai) <br> [GitHub](https://github.com/element-plus-x/ruoyi-element-ai) | | 🚀 前端仓库 | Gitee/GitHub 代码仓库(感谢 star🥰爱你爱你😘) | [Gitee](https://gitee.com/he-jiayue/ruoyi-element-ai) <br> [GitHub](https://github.com/element-plus-x/ruoyi-element-ai) |
| 🛠️ 后端仓库 | 待补充支持Java/Node | 待更新 | | 🛠️ 后端仓库 | Java服务功能丰富强大 | [Gitee](https://gitee.com/ageerle/ruoyi-ai) <br> [GitHub](https://github.com/ageerle/ruoyi-ai) |
| 📚 前端文档 | 开发指南/组件说明 | [快速了解项目](https://chat-docs.element-plus-x.com) | | 📚 前端文档 | 开发指南/项目说明 | [快速了解项目](https://chat-docs.element-plus-x.com) |
| 📡 在线演示 | 实时交互体验 | [立即访问](https://chat.element-plus-x.com) | | 📡 在线演示 | 实时交互体验 | [在线预览](https://chat.element-plus-x.com) |
## 🧰 核心功能 ## 🧰 核心功能
@ -68,16 +77,51 @@ pnpm lint:stylelint # 样式格式化
pnpm cz # 规范提交自动执行lint pnpm cz # 规范提交自动执行lint
``` ```
开发模式配置远程服务器地址:
根目录下新建 `.env.development.local` 文件
```bash
VITE_API_URL = xxxxxxxxxxxxxxxxxxxxx
```
## 🪼 项目发版
项目使用 `release-it` 进行发版
默认更新次版本号,如果想每次更新修订号,可执行 pnpm release patch
```bash
# 更新主版本号
pnpm release major
# 更新次版本号
pnpm release minor
# 更新修订号
pnpm release patch
```
## 🧸 即将推出 (含 ruoyi-ai 接口联调)
- [x] 会话管理
- [x] 发送消息
- [x] 登录注册
- [ ] md 渲染
- [ ] 编辑输入框
- [ ] 文件上传
- [ ] 其他...
## 🤝 社区支持 ## 🤝 社区支持
| 👨‍👨‍👧‍👦 微信交流群 | 💩 作者微信 | | 👨‍👨‍👧‍👦 微信交流群 | 💩 作者微信 |
|---------------------------------------|-------------------------------------| |---------------------------------------|-------------------------------------|
| <img src="https://cdn.element-plus-x.com/vx-2025-06-06.png" alt="微信交流群" width="100%"><br>扫码加入交流群<br>获取最新动态与技术支持 | <img src="https://cdn.element-plus-x.com/element-plus-x-author-vx.png" alt="作者vx" width="100%" ><br>群过期或失效?<br>扫码添加作者微信 | | <img src="https://cdn.element-plus-x.com/vx-2025-06-16.png" alt="微信交流群" width="230px" height="300px" ><br>扫码加入交流群<br>获取最新动态与技术支持 | <img src="https://cdn.element-plus-x.com/vx.png" alt="作者vx" width="230px" height="300px" ><br>群过期或失效?<br>扫码添加作者微信 |
## 🌟 留下赞赏,助力开源 ## 🌹 赠人玫瑰,手有余香
<div align="center"> <div align="center">
<h3 style="color: #fa8c16; margin-bottom: 10px;">🙊感谢您的支持!🙊</h3>
<p>😍开源是热爱的坚持,您的每一份心意都让我们走得更远~😍</p> <p>😍开源是热爱的坚持,您的每一份心意都让我们走得更远~😍</p>
<p style="color: #666; margin: 15px 0;">💌 扫码赞赏,后续将截图保留,并公示赞赏记录💌<br>💖感谢每一份温暖助力 💖</p> <img src="https://cdn.element-plus-x.com/zs1.webp" alt="赞赏" width="230px" height="100%" />
<img src="https://cdn.element-plus-x.com/zs.png" alt="赞赏" width="180" style="margin-bottom: 20px; border-radius: 4px;" /> <h3 style="color: #fa8c16; margin-bottom: 10px;">🙊扫码赞赏,感谢您的支持!🙊</h3>
<p style="color: #666; margin: 15px 0;">💖感谢每一份温暖助力💖</p>
<p style="color: #666; margin: 15px 0;">💌 后续将截图保留,并公示赞赏记录 💌</p>
</div> </div>
</div> </div>

View File

@ -24,6 +24,7 @@
"preview": "vite preview", "preview": "vite preview",
"prepare": "husky", "prepare": "husky",
"lint": "eslint .", "lint": "eslint .",
"release": "release-it",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"fix": "eslint . --fix" "fix": "eslint . --fix"
}, },
@ -37,6 +38,7 @@
"@vueuse/integrations": "^13.3.0", "@vueuse/integrations": "^13.3.0",
"element-plus": "^2.9.11", "element-plus": "^2.9.11",
"hook-fetch": "^1.1.3", "hook-fetch": "^1.1.3",
"moment": "^2.30.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.3.0", "pinia-plugin-persistedstate": "^4.3.0",
@ -51,6 +53,7 @@
"@antfu/eslint-config": "^4.13.3", "@antfu/eslint-config": "^4.13.3",
"@changesets/cli": "^2.29.4", "@changesets/cli": "^2.29.4",
"@commitlint/config-conventional": "^19.8.1", "@commitlint/config-conventional": "^19.8.1",
"@release-it/conventional-changelog": "^10.0.1",
"@vitejs/plugin-vue": "^5.2.4", "@vitejs/plugin-vue": "^5.2.4",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"commitlint": "^19.8.1", "commitlint": "^19.8.1",
@ -59,6 +62,7 @@
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^16.1.0", "lint-staged": "^16.1.0",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"release-it": "^19.0.3",
"sass-embedded": "^1.89.1", "sass-embedded": "^1.89.1",
"stylelint": "^16.20.0", "stylelint": "^16.20.0",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",

1459
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
<script setup lang="ts"></script> <script setup lang="ts">
</script>
<template> <template>
<router-view /> <router-view />

View File

@ -113,8 +113,8 @@ function onAfterLeave() {
/* 动画样式(仅作用于弹框) */ /* 动画样式(仅作用于弹框) */
.dialog-zoom-enter-active, .dialog-zoom-enter-active,
.dialog-zoom-leave-active { .dialog-zoom-leave-active {
transition: all 0.3s ease-in-out;
transform-origin: center; transform-origin: center;
transition: all 0.3s ease-in-out;
} }
.dialog-zoom-enter-from, .dialog-zoom-enter-from,
.dialog-zoom-leave-to { .dialog-zoom-leave-to {
@ -141,8 +141,8 @@ function onAfterLeave() {
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
background-color: rgb(0 0 0 / 50%); background-color: rgb(0 0 0 / 50%);
backdrop-filter: blur(3px);
opacity: 1; opacity: 1;
backdrop-filter: blur(3px);
transition: opacity 0.3s; transition: opacity 0.3s;
} }
.mask[hidden] { .mask[hidden] {
@ -189,9 +189,9 @@ function onAfterLeave() {
height: 40px; height: 40px;
padding: 4px; padding: 4px;
background: var(--login-dialog-logo-background); background: var(--login-dialog-logo-background);
filter: drop-shadow(0 4px 4px rgb(0 0 0 / 10%));
border-radius: 12px; border-radius: 12px;
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 8%); box-shadow: 0 2px 12px 0 rgb(0 0 0 / 8%);
filter: drop-shadow(0 4px 4px rgb(0 0 0 / 10%));
} }
.left-section .logo-wrap .logo-text { .left-section .logo-wrap .logo-text {
font-size: 16px; font-size: 16px;

View File

@ -0,0 +1,53 @@
import type { ComponentInternalInstance } from 'vue';
import type { RouteLocationNormalizedLoaded, Router } from 'vue-router';
import { getCurrentInstance } from 'vue';
import { useRoute, useRouter } from 'vue-router';
interface Utils {
$is: {
[key: string]: (...args: any[]) => boolean;
};
$dataHelpers: {
[key: string]: (...args: any[]) => any;
};
$common: {
[key: string]: (...args: any[]) => any;
};
}
interface UseCurrentInstanceReturn {
currentInstance: ComponentInternalInstance;
proxy: ComponentInternalInstance['proxy'] & Utils;
router: Router;
route: RouteLocationNormalizedLoaded;
$is: Utils['$is'];
$dataHelpers: Utils['$dataHelpers'];
$common: Utils['$common'];
}
export function useCurrentInstance(): UseCurrentInstanceReturn {
const router = useRouter();
const route = useRoute();
const currentInstance = getCurrentInstance();
if (!currentInstance) {
throw new Error('useCurrentInstance必须在setup函数中调用💘');
}
const { proxy } = currentInstance as ComponentInternalInstance & { proxy: Utils };
const $is = proxy.$is;
const $dataHelpers = proxy.$dataHelpers;
const $common = proxy.$common;
return {
currentInstance,
proxy,
router,
route,
$is,
$dataHelpers,
$common,
};
}

View File

@ -329,12 +329,12 @@ function handleMenuCommand(command: string, item: ConversationItem<ChatSessionVo
0 10px 20px 0 rgb(0 0 0 / 10%), 0 10px 20px 0 rgb(0 0 0 / 10%),
0 0 1px 0 rgb(0 0 0 / 15%); 0 0 1px 0 rgb(0 0 0 / 15%);
opacity: 0; opacity: 0;
transition: opacity 0.3s ease 0.3s, transform 0.3s ease 0.3s;
// //
// //
transform: translateX(-100%); transform: translateX(-100%);
transition: opacity 0.3s ease 0.3s, transform 0.3s ease 0.3s;
/* 新增:未激活悬停时覆盖延迟 */ /* 新增:未激活悬停时覆盖延迟 */
&.no-delay { &.no-delay {
@ -358,10 +358,10 @@ function handleMenuCommand(command: string, item: ConversationItem<ChatSessionVo
// aside-container-suspended // aside-container-suspended
opacity: 1; opacity: 1;
transition: opacity 0.3s ease 0s, transform 0.3s ease 0s;
// 沿 // 沿
transform: translateX(15px); transform: translateX(15px);
transition: opacity 0.3s ease 0s, transform 0.3s ease 0s;
// - // -
.conversations-wrap { .conversations-wrap {

View File

@ -1,9 +1,8 @@
// 引入ElementPlus所有图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { createApp } from 'vue'; import { createApp } from 'vue';
import ElementPlusX from 'vue-element-plus-x'; import ElementPlusX from 'vue-element-plus-x';
import App from './App.vue'; import App from './App.vue';
import { registerPlugins } from './plugins';
import router from './routers'; import router from './routers';
import store from './stores'; import store from './stores';
import './styles/index.scss'; import './styles/index.scss';
@ -14,13 +13,13 @@ import 'virtual:svg-icons-register';
const app = createApp(App); const app = createApp(App);
// 插件安装
registerPlugins(app);
app.use(router); app.use(router);
app.use(ElMessage); app.use(ElMessage);
app.use(ElementPlusX); app.use(ElementPlusX);
// 注册ElementPlus所有图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(store); app.use(store);
app.mount('#app'); app.mount('#app');

19
src/plugins/index.ts Normal file
View File

@ -0,0 +1,19 @@
import type { App, Plugin } from 'vue';
interface PluginModule { default: Plugin }
const modules = import.meta.glob<PluginModule>('./modules/*.ts', { eager: true });
export function registerPlugins(app: App) {
Object.values(modules).forEach((module) => {
if (typeof module.default === 'object' && module.default && typeof module.default.install === 'function') {
app.use(module.default);
}
else if (typeof module.default === 'function') {
app.use(module.default);
}
else {
console.warn('插件模块无效:', module);
}
});
}

View File

@ -0,0 +1,13 @@
import type { App } from 'vue';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
const elementPlusIconsPlugin = {
install(app: App) {
// 注册所有ElementPlus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
},
};
export default elementPlusIconsPlugin;

View File

@ -0,0 +1,26 @@
import type { App, InjectionKey } from 'vue';
import * as common from '../../utils/helper/common';
import * as dataHelpers from '../../utils/helper/dataHelpers';
import * as is from '../../utils/helper/is';
export const COMMON_KEY: InjectionKey<typeof common> = Symbol('$common');
export const IS_KEY: InjectionKey<typeof is> = Symbol('$is');
export const DATA_HELPERS_KEY: InjectionKey<typeof dataHelpers> = Symbol('$dataHelpers');
const utilsPlugin = {
install(app: App) {
// common工具
app.config.globalProperties.$common = common;
app.provide(COMMON_KEY, common);
// 类型检测工具
app.config.globalProperties.$is = is;
app.provide(IS_KEY, is);
// 数据处理工具
app.config.globalProperties.$dataHelpers = dataHelpers;
app.provide(DATA_HELPERS_KEY, dataHelpers);
},
};
export default utilsPlugin;

View File

@ -16,9 +16,9 @@ export const layoutRouter: RouteRecordRaw[] = [
// title: '通用聊天页面', // title: '通用聊天页面',
isDefaultChat: true, isDefaultChat: true,
icon: 'HomeFilled', icon: 'HomeFilled',
isHide: '1', // isHide: '1', // 是否在菜单中隐藏[0是1否] 预留
isKeepAlive: '0', // 是否缓存路由数据[0是1否] // isKeepAlive: '0', // 是否缓存路由数据[0是1否] 预留
isFull: '1', // 是否缓存全屏[0是1否] // isFull: '1', // 是否全屏[0是1否] 预留
// enName: "Master Station", // 英文名称 预留 // enName: "Master Station", // 英文名称 预留
}, },
}, },

View File

@ -1,15 +1,15 @@
.el-form{ .el-form{
.el-form-item{ .el-form-item{
&:nth-last-child(1){ &:nth-last-child(1){
margin-bottom: 0px; margin-bottom: 0;
} }
} }
} }
// el 弹框不知道为啥宽度会变大 // el 弹框不知道为啥宽度会变大
.el-popup-parent--hidden { .el-popup-parent--hidden {
overflow: hidden;
width: 100% !important; width: 100% !important;
overflow: hidden;
} }
@ -22,29 +22,73 @@
// rounded-tooltip 提示框样式 // rounded-tooltip 提示框样式
.rounded-tooltip { .rounded-tooltip {
border-radius: 10px !important;
display: flex; display: flex;
align-items: center; align-items: center;
height: fit-content; height: fit-content;
border-radius: 10px !important;
} }
.rounded-tooltip-enter-from { .rounded-tooltip-enter-from {
transform: scale(0.9); /* 进入前:缩小隐藏 */
opacity: 0; opacity: 0;
transform: scale(0.9); /* 进入前:缩小隐藏 */
} }
.rounded-tooltip-enter-active, .rounded-tooltip-enter-active,
.rounded-tooltip-leave-active { .rounded-tooltip-leave-active {
transition: transform 0.3s, opacity 0.3s; /* 缓入动画 */ transition: transform 0.3s, opacity 0.3s; /* 缓入动画 */
} }
.rounded-tooltip-enter-to { .rounded-tooltip-enter-to {
transform: scale(1); /* 进入后:正常大小 */
opacity: 1; opacity: 1;
transform: scale(1); /* 进入后:正常大小 */
} }
.rounded-tooltip-leave-from { .rounded-tooltip-leave-from {
transform: scale(1); /* 离开前:正常大小 */
opacity: 1; opacity: 1;
transform: scale(1); /* 离开前:正常大小 */
} }
.rounded-tooltip-leave-to { .rounded-tooltip-leave-to {
transform: scale(0.9); /* 离开后:缩小隐藏 */
opacity: 0; opacity: 0;
transform: scale(0.9); /* 离开后:缩小隐藏 */
} }
/* S Dialog 样式行为 */
.dialog-fade-enter-active .el-dialog {
animation: dialog-open 0.2s cubic-bezier(0.33, 0.89, 0.25, 1.02);
}
.dialog-fade-leave-active {
animation: fade-out 0.2s linear;
}
.dialog-fade-leave-active .el-dialog {
animation: dialog-close 0.2s cubic-bezier(0.78, 0.14, 0.15, 0.86);
}
@keyframes dialog-open {
0% {
opacity: 0;
transform: scale(0.2);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes dialog-close {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.2);
}
}
// 遮罩层动画
@keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
/* E Dialog 样式行为 */

View File

@ -13,7 +13,7 @@
--login-dialog-section-padding: 0px; --login-dialog-section-padding: 0px;
--login-dialog-border-radius: 24px; --login-dialog-border-radius: 24px;
--login-dialog-mode-toggle-color: #409eff; --login-dialog-mode-toggle-color: #409eff;
--login-dialog-logo-background: #fff; --login-dialog-logo-background: #ffffff;
--login-dialog-logo-text-color: #191919; --login-dialog-logo-text-color: #191919;

View File

@ -0,0 +1,7 @@
/**
*
*
*/
export function test() {
return '';
}

View File

@ -0,0 +1,30 @@
import type { MomentInput } from 'moment';
import moment from 'moment';
/**
*
* formatDate
*/
/**
*
* @param date Date Moment
* @param format 'YYYY-MM-DD HH:mm:ss'
* @returns
*/
export function formatDate(
date: MomentInput | null | undefined,
format: string = 'YYYY-MM-DD HH:mm:ss',
): string {
if (!date) {
return '';
}
const momentDate = moment(date);
if (!momentDate.isValid()) {
console.warn('Invalid date input:', date);
return '';
}
return momentDate.format(format);
}

59
src/utils/helper/is.ts Normal file
View File

@ -0,0 +1,59 @@
/*
*
* * * @function isArray: 检测给定的值是否为数组
* * * @function isObject: 检测给定的值是否为对象JavaScript中null也会被认为是对象
* * * @function isString: 检测给定的值是否为字符串
* * * @function isNumber: 检测给定的值是否为数字
* * * @function isFunction: 检测给定的值是否为函数
* * * @function isAsyncFunction: 检测给定的值是否为异步函数
* * * @function isRegExp: 检测给定的值是否为正则表达式对象
* * * @function isDef: 检测给定的值是否已定义undefined
* * * @function isUnDef: 检测给定的值是否未定义undefined
* * * @function isNull: 检测给定的值是否为null
*/
type DataType = 'Array' | 'Object' | 'String' | 'Number' | 'Function' | 'AsyncFunction' | 'RegExp' | 'Undefined' | 'Null';
export function dataTypeCheck<T extends DataType>(value: unknown, type: T): value is T extends 'Array' ? unknown[] : T extends 'Object' ? object : T extends 'String' ? string : T extends 'Number' ? number : T extends 'Function' ? (...args: any[]) => any : T extends 'AsyncFunction' ? (...args: any[]) => Promise<any> : T extends 'RegExp' ? RegExp : T extends 'Undefined' ? undefined : T extends 'Null' ? null : never {
return Object.prototype.toString.call(value) === `[object ${type}]`;
}
export function isArray(value: unknown): value is unknown[] {
return dataTypeCheck(value, 'Array');
}
export function isObject(value: unknown): value is object {
return dataTypeCheck(value, 'Object');
}
export function isString(value: unknown): value is string {
return dataTypeCheck(value, 'String');
}
export function isNumber(value: unknown): value is number {
return dataTypeCheck(value, 'Number');
}
export function isFunction(value: unknown): value is (...args: any[]) => any {
return dataTypeCheck(value, 'Function') || isAsyncFunction(value);
}
export function isAsyncFunction(value: unknown): value is (...args: any[]) => Promise<any> {
return dataTypeCheck(value, 'AsyncFunction');
}
export function isRegExp(value: unknown): value is RegExp {
return dataTypeCheck(value, 'RegExp');
}
export function isDef<T>(value: T): value is NonNullable<T> {
return !dataTypeCheck(value, 'Undefined');
}
export function isUnDef(value: unknown): value is undefined {
return !isDef(value);
}
export function isNull(value: unknown): value is null {
return dataTypeCheck(value, 'Null');
}

View File

@ -6,85 +6,68 @@
// biome-ignore lint: disable // biome-ignore lint: disable
export {} export {}
declare global { declare global {
const EffectScope: (typeof import("vue"))["EffectScope"]; const EffectScope: typeof import('vue')['EffectScope']
const ElMessage: (typeof import("element-plus/es"))["ElMessage"]; const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: (typeof import("element-plus/es"))["ElMessageBox"]; const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
const computed: (typeof import("vue"))["computed"]; const computed: typeof import('vue')['computed']
const createApp: (typeof import("vue"))["createApp"]; const createApp: typeof import('vue')['createApp']
const customRef: (typeof import("vue"))["customRef"]; const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: (typeof import("vue"))["defineAsyncComponent"]; const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: (typeof import("vue"))["defineComponent"]; const defineComponent: typeof import('vue')['defineComponent']
const effectScope: (typeof import("vue"))["effectScope"]; const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: (typeof import("vue"))["getCurrentInstance"]; const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: (typeof import("vue"))["getCurrentScope"]; const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: (typeof import("vue"))["h"]; const h: typeof import('vue')['h']
const inject: (typeof import("vue"))["inject"]; const inject: typeof import('vue')['inject']
const isProxy: (typeof import("vue"))["isProxy"]; const isProxy: typeof import('vue')['isProxy']
const isReactive: (typeof import("vue"))["isReactive"]; const isReactive: typeof import('vue')['isReactive']
const isReadonly: (typeof import("vue"))["isReadonly"]; const isReadonly: typeof import('vue')['isReadonly']
const isRef: (typeof import("vue"))["isRef"]; const isRef: typeof import('vue')['isRef']
const markRaw: (typeof import("vue"))["markRaw"]; const markRaw: typeof import('vue')['markRaw']
const nextTick: (typeof import("vue"))["nextTick"]; const nextTick: typeof import('vue')['nextTick']
const onActivated: (typeof import("vue"))["onActivated"]; const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: (typeof import("vue"))["onBeforeMount"]; const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: (typeof import("vue"))["onBeforeUnmount"]; const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: (typeof import("vue"))["onBeforeUpdate"]; const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: (typeof import("vue"))["onDeactivated"]; const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: (typeof import("vue"))["onErrorCaptured"]; const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: (typeof import("vue"))["onMounted"]; const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: (typeof import("vue"))["onRenderTracked"]; const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: (typeof import("vue"))["onRenderTriggered"]; const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: (typeof import("vue"))["onScopeDispose"]; const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: (typeof import("vue"))["onServerPrefetch"]; const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: (typeof import("vue"))["onUnmounted"]; const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: (typeof import("vue"))["onUpdated"]; const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: (typeof import("vue"))["onWatcherCleanup"]; const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: (typeof import("vue"))["provide"]; const provide: typeof import('vue')['provide']
const reactive: (typeof import("vue"))["reactive"]; const reactive: typeof import('vue')['reactive']
const readonly: (typeof import("vue"))["readonly"]; const readonly: typeof import('vue')['readonly']
const ref: (typeof import("vue"))["ref"]; const ref: typeof import('vue')['ref']
const resolveComponent: (typeof import("vue"))["resolveComponent"]; const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: (typeof import("vue"))["shallowReactive"]; const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: (typeof import("vue"))["shallowReadonly"]; const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: (typeof import("vue"))["shallowRef"]; const shallowRef: typeof import('vue')['shallowRef']
const toRaw: (typeof import("vue"))["toRaw"]; const toRaw: typeof import('vue')['toRaw']
const toRef: (typeof import("vue"))["toRef"]; const toRef: typeof import('vue')['toRef']
const toRefs: (typeof import("vue"))["toRefs"]; const toRefs: typeof import('vue')['toRefs']
const toValue: (typeof import("vue"))["toValue"]; const toValue: typeof import('vue')['toValue']
const triggerRef: (typeof import("vue"))["triggerRef"]; const triggerRef: typeof import('vue')['triggerRef']
const unref: (typeof import("vue"))["unref"]; const unref: typeof import('vue')['unref']
const useAttrs: (typeof import("vue"))["useAttrs"]; const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: (typeof import("vue"))["useCssModule"]; const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: (typeof import("vue"))["useCssVars"]; const useCssVars: typeof import('vue')['useCssVars']
const useId: (typeof import("vue"))["useId"]; const useId: typeof import('vue')['useId']
const useModel: (typeof import("vue"))["useModel"]; const useModel: typeof import('vue')['useModel']
const useSlots: (typeof import("vue"))["useSlots"]; const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: (typeof import("vue"))["useTemplateRef"]; const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: (typeof import("vue"))["watch"]; const watch: typeof import('vue')['watch']
const watchEffect: (typeof import("vue"))["watchEffect"]; const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: (typeof import("vue"))["watchPostEffect"]; const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: (typeof import("vue"))["watchSyncEffect"]; const watchSyncEffect: typeof import('vue')['watchSyncEffect']
} }
// for type re-export // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
Component, import('vue')
Slot,
Slots,
ComponentPublicInstance,
ComputedRef,
DirectiveBinding,
ExtractDefaultPropTypes,
ExtractPropTypes,
ExtractPublicPropTypes,
InjectionKey,
PropType,
Ref,
MaybeRef,
MaybeRefOrGetter,
VNode,
WritableComputedRef,
} from "vue";
import("vue");
} }

View File

@ -3,7 +3,7 @@
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable // biome-ignore lint: disable
export {}; export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {

View File

@ -13,6 +13,8 @@ export default defineConfig((cnf) => {
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),
"~": path.resolve(__dirname, "./src/assets"),
"GCnps": path.resolve(__dirname, "./src/components"),
}, },
}, },
css: { css: {