Former-commit-id: ecbba4384964ab453d345736cd790ddd7e9e84b1
This commit is contained in:
april
2023-06-05 19:29:15 +08:00
15 changed files with 1847 additions and 883 deletions

View File

@@ -1,7 +1,7 @@
<p align="center">
<img src="https://img.shields.io/badge/Vue-3.3.1-brightgreen.svg"/>
<img src="https://img.shields.io/badge/Vite-4.3.5-green.svg"/>
<img src="https://img.shields.io/badge/Element Plus-2.3.4-blue.svg"/>
<img src="https://img.shields.io/badge/Element Plus-2.3.6-blue.svg"/>
<img src="https://img.shields.io/badge/license-MIT-green.svg"/>
<a href="https://gitee.com/youlaiorg" target="_blank">
<img src="https://img.shields.io/badge/Author-有来开源组织-orange.svg"/>
@@ -11,6 +11,7 @@
<a target="_blank" href="https://juejin.cn/post/7228990409909108793">vue3-element-admin官方文档</a> | <a target="_blank" href="http://vue3.youlai.tech">在线预览</a>
</p>
## 项目介绍
[vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) 是基于 Vue3 + Vite4+ TypeScript5 + Element-Plus + Pinia 等最新主流技术栈构建的后台管理前端模板(配套后端源码)。
@@ -45,7 +46,7 @@
| 项目 | Gitee | Github |GitCode |
| --- | --- | --- | --- |
| 前端 | [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) |[vue3-element-admin](https://gitcode.net/youlai/vue3-element-admin)|
| 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/hxrui/youlai-boot.git) |[youlai-boot](https://gitcode.net/youlai/youlai-boot)|
| 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot.git) |[youlai-boot](https://gitcode.net/youlai/youlai-boot)|
## 环境准备
@@ -94,9 +95,30 @@ server {
proxy_pass http://vapi.youlai.tech/; # vapi.youlai.tech替换成你的后端API地址
}
}
```
```
## 注意事项
- **自动导入插件自动生成默认关闭**
模板项目的组件类型声明已自动生成。如果添加和使用新的组件,请按照图示方法开启自动生成。在自动生成完成后,记得将其设置为 `false`,避免重复执行引发冲突。
![](https://s2.loli.net/2023/06/03/lrcsHzInYV6wWqo.png)
- **项目启动浏览器访问空白**
请升级浏览器尝试,低版本浏览器内核可能不支持某些新的 JavaScript 语法,比如可选链操作符 `?.`
- **项目同步仓库更新升级**
项目同步仓库更新升级之后,建议 `pnpm install` 安装更新依赖之后启动 。
- **其他问题**
如果有其他问题或者建议,建议 [ISSUE](https://gitee.com/youlaiorg/vue3-element-admin/issues/new)
## 接口支持
- **接口调用地址**[https://vapi.youlai.tech](https://vapi.youlai.tech)
@@ -105,8 +127,8 @@ server {
- **本地接口**:默认使用线上接口,你可以通过以下步骤完成本地接口环境搭建:
> 1. 获取基于 `Java 、SpringBoot` 开发的后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot.git) 源码 ;
> 2. 根据后端工程说明文档 [README.md](https://gitee.com/youlaiorg/youlai-boot#%E9%A1%B9%E7%9B%AE%E8%BF%90%E8%A1%8C) 完成本地启动;
> 3. 替换 [vite.config.ts](vite.config.ts) 的代理目标地址 [vapi.youlai.tech](vapi.youlai.tech) 为本地的 [localhost:8989](localhost:8989).
>2. 根据后端工程说明文档 [README.md](https://gitee.com/youlaiorg/youlai-boot#%E9%A1%B9%E7%9B%AE%E8%BF%90%E8%A1%8C) 完成本地启动;
> 3. 替换 [vite.config.ts](vite.config.ts) 的代理目标地址 `vapi.youlai.tech` 为本地的 `localhost:8989`
@@ -118,6 +140,8 @@ server {
- [Husky + Lint-staged + Commitlint + Commitizen + cz-git 配置 Git 提交规范](https://blog.csdn.net/u013737132/article/details/130191363)
## 提交规范
执行 `pnpm run commit` 唤起 git commit 交互,根据提示完成信息的输入和选择。
@@ -136,4 +160,3 @@ server {
![](https://s2.loli.net/2023/05/28/7vNjHTotb2h9zBD.png)

View File

@@ -48,7 +48,7 @@
"axios": "^1.4.0",
"codemirror": "^5.65.13",
"echarts": "^5.2.2",
"element-plus": "^2.3.4",
"element-plus": "^2.3.6",
"lodash-es": "^4.17.21",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
@@ -102,4 +102,4 @@
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git",
"author": "有来开源组织",
"license": "MIT"
}
}

View File

@@ -2,6 +2,7 @@
* 登录用户信息
*/
export interface UserInfo {
userId: number;
nickname: string;
avatar: string;
roles: string[];

View File

@@ -0,0 +1,27 @@
import request from "@/utils/request";
/**
* 发送消息给所有人
*
* @param file
*/
export function sendToAll(message: string) {
return request({
url: "/websocket/sendToAll",
method: "post",
params: { message: message },
});
}
/**
* 发送消息给指定用户
*
* @param
*/
export function sendToUser(userId: number, message: string) {
return request({
url: "/websocket/sendToUser/" + userId,
method: "post",
params: { message: message },
});
}

View File

@@ -1,6 +1,6 @@
<template>
<a
href="https://github.com/hxrui"
href="https://github.com/haoxianrui"
target="_blank"
class="github-corner"
aria-label="View source on Github"

View File

@@ -55,7 +55,7 @@ const props = defineProps({
},
});
const emit = defineEmits(["pagination"]);
const emit = defineEmits(["pagination", "update:page", "update:limit"]);
const currentPage = useVModel(props, "page", emit);

View File

@@ -88,13 +88,19 @@ function logout() {
<router-link to="/">
<el-dropdown-item>{{ $t("navbar.dashboard") }}</el-dropdown-item>
</router-link>
<a target="_blank" href="https://github.com/hxrui">
<a
target="_blank"
href="https://github.com/youlaitech/vue3-element-admin"
>
<el-dropdown-item>Github</el-dropdown-item>
</a>
<a target="_blank" href="https://gitee.com/haoxr">
<el-dropdown-item>{{ $t("navbar.gitee") }}</el-dropdown-item>
</a>
<a target="_blank" href="https://www.cnblogs.com/haoxianrui/">
<a
target="_blank"
href="https://juejin.cn/post/7228990409909108793"
>
<el-dropdown-item>{{ $t("navbar.document") }}</el-dropdown-item>
</a>
<el-dropdown-item divided @click="logout">

View File

@@ -12,6 +12,7 @@ import { useStorage } from "@vueuse/core";
export const useUserStore = defineStore("user", () => {
// state
const userId = ref();
const token = useStorage("accessToken", "");
const nickname = ref("");
const avatar = ref("");
@@ -49,6 +50,7 @@ export const useUserStore = defineStore("user", () => {
if (!data.roles || data.roles.length <= 0) {
reject("getUserInfo: roles must be a non-null array!");
}
userId.value = data.userId;
nickname.value = data.nickname;
avatar.value = data.avatar;
roles.value = data.roles;
@@ -95,6 +97,10 @@ export const useUserStore = defineStore("user", () => {
getInfo,
logout,
resetToken,
/**
* 当前登录用户ID
*/
userId,
};
});

File diff suppressed because it is too large Load Diff

View File

@@ -5,79 +5,78 @@
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
export {};
declare module '@vue/runtime-core' {
declare module "@vue/runtime-core" {
export interface GlobalComponents {
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
GithubCorner: typeof import('./../components/GithubCorner/index.vue')['default']
Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
IEpCaretTop: typeof import('~icons/ep/caret-top')['default']
IEpClose: typeof import('~icons/ep/close')['default']
IEpCollection: typeof import('~icons/ep/collection')['default']
IEpDelete: typeof import('~icons/ep/delete')['default']
IEpDownload: typeof import('~icons/ep/download')['default']
IEpEdit: typeof import('~icons/ep/edit')['default']
IEpPlus: typeof import('~icons/ep/plus')['default']
IEpPosition: typeof import('~icons/ep/position')['default']
IEpRefresh: typeof import('~icons/ep/refresh')['default']
IEpRefreshLeft: typeof import('~icons/ep/refresh-left')['default']
IEpSearch: typeof import('~icons/ep/search')['default']
IEpSetting: typeof import('~icons/ep/setting')['default']
IEpTop: typeof import('~icons/ep/top')['default']
IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default']
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default']
Pagination: typeof import('./../components/Pagination/index.vue')['default']
RightPanel: typeof import('./../components/RightPanel/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Screenfull: typeof import('./../components/Screenfull/index.vue')['default']
SingleUpload: typeof import('./../components/Upload/SingleUpload.vue')['default']
SizeSelect: typeof import('./../components/SizeSelect/index.vue')['default']
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
TagInput: typeof import('./../components/TagInput/index.vue')['default']
WangEditor: typeof import('./../components/WangEditor/index.vue')['default']
Breadcrumb: typeof import("./../components/Breadcrumb/index.vue")["default"];
ElAlert: typeof import("element-plus/es")["ElAlert"];
ElBreadcrumb: typeof import("element-plus/es")["ElBreadcrumb"];
ElBreadcrumbItem: typeof import("element-plus/es")["ElBreadcrumbItem"];
ElButton: typeof import("element-plus/es")["ElButton"];
ElCard: typeof import("element-plus/es")["ElCard"];
ElCol: typeof import("element-plus/es")["ElCol"];
ElDialog: typeof import("element-plus/es")["ElDialog"];
ElDivider: typeof import("element-plus/es")["ElDivider"];
ElDropdown: typeof import("element-plus/es")["ElDropdown"];
ElDropdownItem: typeof import("element-plus/es")["ElDropdownItem"];
ElDropdownMenu: typeof import("element-plus/es")["ElDropdownMenu"];
ElForm: typeof import("element-plus/es")["ElForm"];
ElFormItem: typeof import("element-plus/es")["ElFormItem"];
ElIcon: typeof import("element-plus/es")["ElIcon"];
ElInput: typeof import("element-plus/es")["ElInput"];
ElInputNumber: typeof import("element-plus/es")["ElInputNumber"];
ElLink: typeof import("element-plus/es")["ElLink"];
ElMenu: typeof import("element-plus/es")["ElMenu"];
ElMenuItem: typeof import("element-plus/es")["ElMenuItem"];
ElOption: typeof import("element-plus/es")["ElOption"];
ElPagination: typeof import("element-plus/es")["ElPagination"];
ElPopover: typeof import("element-plus/es")["ElPopover"];
ElRadio: typeof import("element-plus/es")["ElRadio"];
ElRadioGroup: typeof import("element-plus/es")["ElRadioGroup"];
ElRow: typeof import("element-plus/es")["ElRow"];
ElScrollbar: typeof import("element-plus/es")["ElScrollbar"];
ElSelect: typeof import("element-plus/es")["ElSelect"];
ElSubMenu: typeof import("element-plus/es")["ElSubMenu"];
ElSwitch: typeof import("element-plus/es")["ElSwitch"];
ElTable: typeof import("element-plus/es")["ElTable"];
ElTableColumn: typeof import("element-plus/es")["ElTableColumn"];
ElTag: typeof import("element-plus/es")["ElTag"];
ElTooltip: typeof import("element-plus/es")["ElTooltip"];
ElTree: typeof import("element-plus/es")["ElTree"];
ElTreeSelect: typeof import("element-plus/es")["ElTreeSelect"];
ElUpload: typeof import("element-plus/es")["ElUpload"];
GithubCorner: typeof import("./../components/GithubCorner/index.vue")["default"];
Hamburger: typeof import("./../components/Hamburger/index.vue")["default"];
IconSelect: typeof import("./../components/IconSelect/index.vue")["default"];
IEpCaretBottom: typeof import("~icons/ep/caret-bottom")["default"];
IEpCaretTop: typeof import("~icons/ep/caret-top")["default"];
IEpClose: typeof import("~icons/ep/close")["default"];
IEpCollection: typeof import("~icons/ep/collection")["default"];
IEpDelete: typeof import("~icons/ep/delete")["default"];
IEpDownload: typeof import("~icons/ep/download")["default"];
IEpEdit: typeof import("~icons/ep/edit")["default"];
IEpPlus: typeof import("~icons/ep/plus")["default"];
IEpPosition: typeof import("~icons/ep/position")["default"];
IEpRefresh: typeof import("~icons/ep/refresh")["default"];
IEpRefreshLeft: typeof import("~icons/ep/refresh-left")["default"];
IEpSearch: typeof import("~icons/ep/search")["default"];
IEpSetting: typeof import("~icons/ep/setting")["default"];
IEpTop: typeof import("~icons/ep/top")["default"];
IEpUploadFilled: typeof import("~icons/ep/upload-filled")["default"];
LangSelect: typeof import("./../components/LangSelect/index.vue")["default"];
MultiUpload: typeof import("./../components/Upload/MultiUpload.vue")["default"];
Pagination: typeof import("./../components/Pagination/index.vue")["default"];
RightPanel: typeof import("./../components/RightPanel/index.vue")["default"];
RouterLink: typeof import("vue-router")["RouterLink"];
RouterView: typeof import("vue-router")["RouterView"];
SingleUpload: typeof import("./../components/Upload/SingleUpload.vue")["default"];
SizeSelect: typeof import("./../components/SizeSelect/index.vue")["default"];
SvgIcon: typeof import("./../components/SvgIcon/index.vue")["default"];
TagInput: typeof import("./../components/TagInput/index.vue")["default"];
WangEditor: typeof import("./../components/WangEditor/index.vue")["default"];
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
vLoading: typeof import("element-plus/es")["ElLoadingDirective"];
}
}

View File

@@ -1,8 +1,10 @@
<script lang="ts">
export default { name: "Dashboard" };
</script>
<script setup lang="ts">
defineOptions({
// eslint-disable-next-line vue/no-reserved-component-names
name: "Dashboard",
inheritAttrs: false,
});
import { useUserStore } from "@/store/modules/user";
import { useTransition, TransitionPresets } from "@vueuse/core";

View File

@@ -0,0 +1,119 @@
<!-- websocket 示例 -->
<script setup lang="ts">
import { sendToAll, sendToUser } from "@/api/websocket";
const inputVal = ref("初始内容");
const topicMsgs = ref<string[]>(["接收到一条主题消息"]); // 主题消息列表
const p2pMsgs = ref<string[]>(["接收到一条点对线消息"]); // 点对点消息列表
import { useUserStore } from "@/store/modules/user";
const userId = useUserStore().userId;
import { useWebSocket } from "@vueuse/core";
const { data, status, close, send, open } = useWebSocket(
"ws://localhost:8989/ws",
{
onConnected(ws) {
console.log("订阅主题");
// 连接建立后发送订阅消息
ws.send(JSON.stringify({ type: "subscribe", topic: "/topic/all" }));
},
onMessage(ws, event) {
// 获取接收到的消息内容
const message = event.data;
// 处理消息内容
console.log("Received message:", message);
},
}
);
// 监听 WebSocket 连接状态变化
watch(status, (newStatus) => {
if (newStatus === "OPEN") {
// 连接已打开,订阅主题
console.log(" 连接已打开,订阅主题");
const subscribeMessage = {
type: "subscribe",
channel: "/topic/all",
};
send(JSON.stringify(subscribeMessage));
} else if (newStatus === "CLOSED") {
// 连接已关闭,执行相应的清理操作
// ...
}
});
// 监听 WebSocket 接收到的消息
watch(data, (newData) => {
console.log("Received data:", newData);
// 解析消息体
const message = JSON.parse(newData);
// 判断消息主题并处理
if (message.topic === "topic1") {
// 处理来自 topic1 的消息
console.log("Received message from topic1:", message);
} else if (message.topic === "topic2") {
// 处理来自 topic2 的消息
console.log("Received message from topic2:", message);
}
// 可以根据需要添加更多的判断逻辑来处理其他主题的消息
});
function handleSendToAll() {
sendToAll(inputVal.value);
}
function handleSendToUser() {
sendToUser(userId, inputVal.value);
}
onMounted(() => {});
</script>
<template>
<div class="app-container">
<el-link
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/websocket.vue"
type="primary"
target="_blank"
class="mb-[20px]"
>示例源码 请点击>>>></el-link
>
<div>
<div class="search">
<el-form :inline="true">
<el-form-item> <el-input v-model="inputVal" /></el-form-item>
<el-form-item
><el-button @click="handleSendToUser">发送点对点消息</el-button>
<el-button @click="handleSendToAll">发送广播消息</el-button>
</el-form-item>
</el-form>
</div>
<el-row :gutter="20">
<el-col :span="12">
<el-card>
<template #header>点对点消息接收 </template>
<div v-for="(msg, index) in p2pMsgs" :key="index">
{{ msg }}
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<template #header> 广播消息接收 </template>
<div v-for="(msg, index) in topicMsgs" :key="index">
{{ msg }}
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</template>

View File

@@ -196,6 +196,8 @@ function resetForm() {
formData.visible = 1;
formData.sort = 1;
formData.perm = undefined;
formData.component = undefined;
formData.path = undefined;
}
onMounted(() => {

View File

@@ -15,7 +15,7 @@
"paths": {
"@/*": ["src/*"]
},
"types": ["vite/client", "element-plus/global", "unplugin-icons/types/vue"],
"types": ["vite/client", "unplugin-icons/types/vue"],
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
"allowSyntheticDefaultImports": true /* */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */

View File

@@ -71,7 +71,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
IconsResolver({}),
],
vueTemplate: true, // 是否在 vue 模板中自动导入
dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // 自动导入组件类型声明文件位置,默认根目录; false 关闭自动生成
dts: false, // 关闭自动生成
//dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // 自动导入组件类型声明文件位置,默认根目录
}),
Components({
@@ -83,7 +84,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
// 自动导入 Element Plus 组件
ElementPlusResolver(),
],
dts: path.resolve(pathSrc, "types", "components.d.ts"), // 自动导入组件类型声明文件位置,默认根目录; false 关闭自动生成
dts: false, // 关闭自动生成
// dts: path.resolve(pathSrc, "types", "components.d.ts"), // 自动导入组件类型声明文件位置,默认根目录
}),
Icons({
@@ -149,7 +151,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
"@wangeditor/editor",
"@wangeditor/editor-for-vue",
"vue-i18n",
'codemirror'
"codemirror",
],
},
};