Merge branch 'main' of http://62.234.3.186:3000/sxyanzhu/AIManage
commit
3cbf34d55c
|
@ -10,7 +10,7 @@
|
||||||
<script>
|
<script>
|
||||||
window.isV2=true
|
window.isV2=true
|
||||||
</script>
|
</script>
|
||||||
-->
|
-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|
|
@ -7,7 +7,35 @@ class MenuAPI {
|
||||||
*/
|
*/
|
||||||
static getRoutes() {
|
static getRoutes() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
resolve([
|
resolve([
|
||||||
|
{
|
||||||
|
path: "/home",
|
||||||
|
component: "Layout",
|
||||||
|
redirect: "/home/index2",
|
||||||
|
name: "/index2",
|
||||||
|
meta: {
|
||||||
|
title: "数据总览2",
|
||||||
|
icon: "homepage",
|
||||||
|
hidden: false,
|
||||||
|
roles: ["GUEST", "ADMIN", "ADMIN6","data_admin"],
|
||||||
|
alwaysShow: false,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "index2",
|
||||||
|
component: "dashboard/index2",
|
||||||
|
name: "dashboardIndex2",
|
||||||
|
meta: {
|
||||||
|
title: "模型管理",
|
||||||
|
icon: "user",
|
||||||
|
hidden: true,
|
||||||
|
roles: ["ADMIN", "GUEST","data_admin"],
|
||||||
|
keepAlive: true,
|
||||||
|
alwaysShow: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/modelMgr",
|
path: "/modelMgr",
|
||||||
component: "Layout",
|
component: "Layout",
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex" style="height: 100%;align-items: center;">
|
<div class="flex" style="height: 100%;align-items: center;">
|
||||||
<div style="flex-grow: 1;display: flex;align-items: center;padding-top: 3px;">
|
<div style="flex-grow: 1;display: flex;align-items: center;padding-top: 3px;" >
|
||||||
<span style="position: relative;top:7px;">
|
<span style="position: relative;" class="logo-small1">
|
||||||
<img src="@/assets/logojd.png" class="logo-image" />
|
<img src="@/assets/logo2.png" class="logo-image" />
|
||||||
</span>
|
<span class="logo-title" style="position: absolute;font-weight: bold;font-size: 14px;color: #003F88;left: 0;bottom: -13px;white-space: nowrap;">中国航天</span>
|
||||||
<span class="logo-title" style="font-weight: bold;font-size: 28px;margin-left: auto;margin-right: auto;color:#101010">超异构框架统一管理平台</span>
|
</span>
|
||||||
|
<span style="margin-left: 20px;margin-top: 10px;font-size: 12px;font-weight: bold;" class="logo-title2">
|
||||||
|
<div class="logo-title" style="font-weight: bold;font-size: 22px;color:#003F88"> {{ "中国航天某某某某研究院" }}</div>
|
||||||
|
<div >China Aerospace XXXX Research Institute</div>
|
||||||
|
</span>
|
||||||
|
<span class="logo-title" style="font-weight: bold;font-size: 28px;margin-left: auto;margin-right: auto;color:#101010">算法测试验证软件平台</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<!--全屏 -->
|
<!--全屏 -->
|
||||||
|
@ -17,10 +22,10 @@
|
||||||
<!-- 用户头像 -->
|
<!-- 用户头像 -->
|
||||||
<el-dropdown class="setting-item" trigger="click">
|
<el-dropdown class="setting-item" trigger="click">
|
||||||
<div class="flex-center h100% p10px">
|
<div class="flex-center h100% p10px">
|
||||||
<img
|
<img src="@/assets/images/avatar.png"
|
||||||
:src="userStore.user.avatar + '?imageView2/1/w/80/h/80'"
|
|
||||||
class="rounded-full mr-10px w24px w24px"
|
class="rounded-full mr-10px w24px w24px"
|
||||||
/>
|
/>
|
||||||
|
<!-- :src="userStore.user.avatar + '?imageView2/1/w/80/h/80'" -->
|
||||||
<span>{{ userStore.user.username }}</span>
|
<span>{{ userStore.user.username }}</span>
|
||||||
</div>
|
</div>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
|
@ -104,6 +109,7 @@ function resetPassword() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.setting-item {
|
.setting-item {
|
||||||
|
@ -119,8 +125,9 @@ function resetPassword() {
|
||||||
background: rgb(0 0 0 / 10%);
|
background: rgb(0 0 0 / 10%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.logo-image {
|
.logo-image {
|
||||||
height: 60px;
|
width: 50px;
|
||||||
|
//height: 68px;
|
||||||
}
|
}
|
||||||
.layout-top,
|
.layout-top,
|
||||||
.layout-mix {
|
.layout-mix {
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
<!-- 接口文档 -->
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<iframe
|
|
||||||
src="https://www.apifox.cn/apidoc/shared-195e783f-4d85-4235-a038-eec696de4ea5"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
frameborder="0"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/** 关闭tag标签 */
|
|
||||||
.app-container {
|
|
||||||
/* 50px = navbar = 50px */
|
|
||||||
height: calc(100vh - 50px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 开启tag标签 */
|
|
||||||
.hasTagsView {
|
|
||||||
.app-container {
|
|
||||||
/* 84px = navbar + tags-view = 50px + 34px */
|
|
||||||
height: calc(100vh - 84px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,27 +0,0 @@
|
||||||
<!-- 接口文档 -->
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<iframe
|
|
||||||
src="http://vapi.youlai.tech/doc.html"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
frameborder="0"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/** 关闭tag标签 */
|
|
||||||
.app-container {
|
|
||||||
/* 50px = navbar = 50px */
|
|
||||||
height: calc(100vh - 50px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 开启tag标签 */
|
|
||||||
.hasTagsView {
|
|
||||||
.app-container {
|
|
||||||
/* 84px = navbar + tags-view = 50px + 34px */
|
|
||||||
height: calc(100vh - 84px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,27 +0,0 @@
|
||||||
<!-- 接口文档 -->
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<iframe
|
|
||||||
src="http://vapi.youlai.tech/swagger-ui.html"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
frameborder="0"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/** 关闭tag标签 */
|
|
||||||
.app-container {
|
|
||||||
/* 50px = navbar = 50px */
|
|
||||||
height: calc(100vh - 50px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 开启tag标签 */
|
|
||||||
.hasTagsView {
|
|
||||||
.app-container {
|
|
||||||
/* 84px = navbar + tags-view = 50px + 34px */
|
|
||||||
height: calc(100vh - 84px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,146 +0,0 @@
|
||||||
import UserAPI from "@/api/user";
|
|
||||||
import type { UserForm } from "@/api/user/model";
|
|
||||||
import type { IModalConfig } from "@/components/PageModal/index.vue";
|
|
||||||
|
|
||||||
const modalConfig: IModalConfig<UserForm> = {
|
|
||||||
pageName: "sys:user",
|
|
||||||
dialog: {
|
|
||||||
title: "新增用户",
|
|
||||||
width: 800,
|
|
||||||
draggable: true,
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
labelWidth: 100,
|
|
||||||
},
|
|
||||||
formAction: UserAPI.add,
|
|
||||||
beforeSubmit(data) {
|
|
||||||
console.log("提交之前处理", data);
|
|
||||||
},
|
|
||||||
formItems: [
|
|
||||||
{
|
|
||||||
label: "用户名",
|
|
||||||
prop: "username",
|
|
||||||
rules: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
|
|
||||||
type: "input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入用户名",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "用户昵称",
|
|
||||||
prop: "nickname",
|
|
||||||
rules: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
|
||||||
type: "input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入用户昵称",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "所属部门",
|
|
||||||
prop: "deptId",
|
|
||||||
rules: [{ required: true, message: "所属部门不能为空", trigger: "blur" }],
|
|
||||||
type: "tree-select",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择所属部门",
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: "研筑科技",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: "研发部门",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 3,
|
|
||||||
label: "测试部门",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
filterable: true,
|
|
||||||
"check-strictly": true,
|
|
||||||
"render-after-expand": false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
label: "性别",
|
|
||||||
prop: "gender",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择",
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{ label: "男", value: 1 },
|
|
||||||
{ label: "女", value: 2 },
|
|
||||||
{ label: "未知", value: 0 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "角色",
|
|
||||||
prop: "roleIds",
|
|
||||||
rules: [{ required: true, message: "用户角色不能为空", trigger: "blur" }],
|
|
||||||
type: "select",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择",
|
|
||||||
multiple: true,
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{ label: "系统管理员", value: 2 },
|
|
||||||
{ label: "系统管理员1", value: 4 },
|
|
||||||
{ label: "系统管理员2", value: 5 },
|
|
||||||
{ label: "系统管理员3", value: 6 },
|
|
||||||
{ label: "系统管理员4", value: 7 },
|
|
||||||
{ label: "系统管理员5", value: 8 },
|
|
||||||
{ label: "系统管理员6", value: 9 },
|
|
||||||
{ label: "系统管理员7", value: 10 },
|
|
||||||
{ label: "系统管理员8", value: 11 },
|
|
||||||
{ label: "访问游客", value: 3 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
label: "手机号码",
|
|
||||||
prop: "mobile",
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
|
||||||
message: "请输入正确的手机号码",
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入手机号码",
|
|
||||||
maxlength: 11,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "邮箱",
|
|
||||||
prop: "email",
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/,
|
|
||||||
message: "请输入正确的邮箱地址",
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
type: "input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入邮箱",
|
|
||||||
maxlength: 50,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
type: "radio",
|
|
||||||
options: [
|
|
||||||
{ label: "正常", value: 1 },
|
|
||||||
{ label: "禁用", value: 0 },
|
|
||||||
],
|
|
||||||
initialValue: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default modalConfig;
|
|
|
@ -1,70 +0,0 @@
|
||||||
import UserAPI from "@/api/user";
|
|
||||||
import type { UserQuery } from "@/api/user/model";
|
|
||||||
import type { IContentConfig } from "@/components/PageContent/index.vue";
|
|
||||||
|
|
||||||
const contentConfig: IContentConfig<UserQuery> = {
|
|
||||||
pageName: "sys:user",
|
|
||||||
table: {
|
|
||||||
border: true,
|
|
||||||
highlightCurrentRow: true,
|
|
||||||
},
|
|
||||||
indexAction: function (params) {
|
|
||||||
if ("createAt" in params) {
|
|
||||||
const createAt = params.createAt as string[];
|
|
||||||
params.startTime = createAt[0];
|
|
||||||
params.endTime = createAt[1];
|
|
||||||
delete params.createAt;
|
|
||||||
}
|
|
||||||
return UserAPI.getPage(params);
|
|
||||||
},
|
|
||||||
deleteAction: UserAPI.deleteByIds,
|
|
||||||
exportAction: UserAPI.export,
|
|
||||||
pk: "id",
|
|
||||||
toolbar: [
|
|
||||||
"add",
|
|
||||||
"delete",
|
|
||||||
"export",
|
|
||||||
{
|
|
||||||
name: "import",
|
|
||||||
icon: "upload",
|
|
||||||
text: "导入",
|
|
||||||
auth: "import",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
cols: [
|
|
||||||
{ type: "selection", width: 50, align: "center" },
|
|
||||||
{ label: "编号", align: "center", prop: "id", width: 100, show: false },
|
|
||||||
{ label: "用户名", align: "center", prop: "username" },
|
|
||||||
{ label: "头像", align: "center", prop: "avatar", templet: "image" },
|
|
||||||
{ label: "用户昵称", align: "center", prop: "nickname", width: 120 },
|
|
||||||
{ label: "性别", align: "center", prop: "genderLabel", width: 100 },
|
|
||||||
{ label: "部门", align: "center", prop: "deptName", width: 120 },
|
|
||||||
{ label: "手机号码", align: "center", prop: "mobile", width: 120 },
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
align: "center",
|
|
||||||
prop: "status",
|
|
||||||
templet: "custom",
|
|
||||||
slotName: "status",
|
|
||||||
},
|
|
||||||
{ label: "创建时间", align: "center", prop: "createTime", width: 180 },
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
fixed: "right",
|
|
||||||
width: 220,
|
|
||||||
templet: "tool",
|
|
||||||
operat: [
|
|
||||||
{
|
|
||||||
name: "reset_pwd",
|
|
||||||
auth: "password:reset",
|
|
||||||
icon: "refresh-left",
|
|
||||||
text: "重置密码",
|
|
||||||
},
|
|
||||||
"edit",
|
|
||||||
"delete",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default contentConfig;
|
|
|
@ -1,111 +0,0 @@
|
||||||
import type { IContentConfig } from "@/components/PageContent/index.vue";
|
|
||||||
|
|
||||||
const contentConfig: IContentConfig = {
|
|
||||||
pageName: "sys:user",
|
|
||||||
table: {
|
|
||||||
showOverflowTooltip: true,
|
|
||||||
},
|
|
||||||
indexAction: function (params) {
|
|
||||||
// console.log("indexAction:", params);
|
|
||||||
return Promise.resolve({
|
|
||||||
total: 2,
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
username: "tom",
|
|
||||||
avatar:
|
|
||||||
"https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif",
|
|
||||||
percent: 99,
|
|
||||||
price: 10,
|
|
||||||
url: "https://www.baidu.com",
|
|
||||||
icon: "el-icon-setting",
|
|
||||||
gender: 1,
|
|
||||||
status: 1,
|
|
||||||
status2: 1,
|
|
||||||
createTime: 1715647982437,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
username: "jerry",
|
|
||||||
avatar:
|
|
||||||
"https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif",
|
|
||||||
percent: 88,
|
|
||||||
price: 999,
|
|
||||||
url: "https://www.google.com",
|
|
||||||
icon: "el-icon-user",
|
|
||||||
gender: 0,
|
|
||||||
status: 0,
|
|
||||||
status2: 0,
|
|
||||||
createTime: 1715648977426,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
modifyAction(data) {
|
|
||||||
// console.log("modifyAction:", data);
|
|
||||||
return Promise.resolve(null);
|
|
||||||
},
|
|
||||||
cols: [
|
|
||||||
{ type: "selection", width: 50, align: "center" },
|
|
||||||
{ label: "ID", align: "center", prop: "id", show: false },
|
|
||||||
{ label: "用户名", align: "center", prop: "username" },
|
|
||||||
{ label: "图片", align: "center", prop: "avatar", templet: "image" },
|
|
||||||
{
|
|
||||||
label: "百分比",
|
|
||||||
align: "center",
|
|
||||||
prop: "percent",
|
|
||||||
templet: "percent",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "价格",
|
|
||||||
align: "center",
|
|
||||||
prop: "price",
|
|
||||||
templet: "price",
|
|
||||||
priceFormat: "$",
|
|
||||||
},
|
|
||||||
{ label: "链接", align: "center", prop: "url", width: 180, templet: "url" },
|
|
||||||
{ label: "图标", align: "center", prop: "icon", templet: "icon" },
|
|
||||||
{
|
|
||||||
label: "列表值",
|
|
||||||
align: "center",
|
|
||||||
prop: "gender",
|
|
||||||
templet: "list",
|
|
||||||
selectList: { 0: "女", 1: "男" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "自定义",
|
|
||||||
align: "center",
|
|
||||||
prop: "status",
|
|
||||||
templet: "custom",
|
|
||||||
slotName: "status",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
align: "center",
|
|
||||||
prop: "status2",
|
|
||||||
templet: "switch",
|
|
||||||
activeValue: 1,
|
|
||||||
inactiveValue: 0,
|
|
||||||
activeText: "启用",
|
|
||||||
inactiveText: "禁用",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "创建时间",
|
|
||||||
align: "center",
|
|
||||||
prop: "createTime",
|
|
||||||
minWidth: 120,
|
|
||||||
templet: "date",
|
|
||||||
dateFormat: "YYYY/MM/DD HH:mm:ss",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
align: "center",
|
|
||||||
fixed: "right",
|
|
||||||
width: 150,
|
|
||||||
templet: "tool",
|
|
||||||
operat: ["edit", "delete"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default contentConfig;
|
|
|
@ -1,146 +0,0 @@
|
||||||
import UserAPI from "@/api/user";
|
|
||||||
import type { UserForm } from "@/api/user/model";
|
|
||||||
import type { IModalConfig } from "@/components/PageModal/index.vue";
|
|
||||||
|
|
||||||
const modalConfig: IModalConfig<UserForm> = {
|
|
||||||
pageName: "sys:user",
|
|
||||||
pk: "id",
|
|
||||||
component: "drawer",
|
|
||||||
drawer: {
|
|
||||||
title: "修改用户",
|
|
||||||
size: 300,
|
|
||||||
},
|
|
||||||
formAction: function (data) {
|
|
||||||
return UserAPI.update(data.id as number, data);
|
|
||||||
},
|
|
||||||
beforeSubmit(data) {
|
|
||||||
console.log("提交之前处理", data);
|
|
||||||
},
|
|
||||||
formItems: [
|
|
||||||
{
|
|
||||||
label: "用户名",
|
|
||||||
prop: "username",
|
|
||||||
rules: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
|
|
||||||
type: "input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入用户名",
|
|
||||||
readonly: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "用户昵称",
|
|
||||||
prop: "nickname",
|
|
||||||
rules: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
|
||||||
type: "input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入用户昵称",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "所属部门",
|
|
||||||
prop: "deptId",
|
|
||||||
rules: [{ required: true, message: "所属部门不能为空", trigger: "blur" }],
|
|
||||||
type: "tree-select",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择所属部门",
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: "研筑科技",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: "研发部门",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 3,
|
|
||||||
label: "测试部门",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
filterable: true,
|
|
||||||
"check-strictly": true,
|
|
||||||
"render-after-expand": false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
label: "性别",
|
|
||||||
prop: "gender",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择",
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{ label: "男", value: 1 },
|
|
||||||
{ label: "女", value: 2 },
|
|
||||||
{ label: "未知", value: 0 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "角色",
|
|
||||||
prop: "roleIds",
|
|
||||||
rules: [{ required: true, message: "用户角色不能为空", trigger: "blur" }],
|
|
||||||
type: "select",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择",
|
|
||||||
multiple: true,
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{ label: "系统管理员", value: 2 },
|
|
||||||
{ label: "系统管理员1", value: 4 },
|
|
||||||
{ label: "系统管理员2", value: 5 },
|
|
||||||
{ label: "系统管理员3", value: 6 },
|
|
||||||
{ label: "系统管理员4", value: 7 },
|
|
||||||
{ label: "系统管理员5", value: 8 },
|
|
||||||
{ label: "系统管理员6", value: 9 },
|
|
||||||
{ label: "系统管理员7", value: 10 },
|
|
||||||
{ label: "系统管理员8", value: 11 },
|
|
||||||
{ label: "访问游客", value: 3 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
label: "手机号码",
|
|
||||||
prop: "mobile",
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
|
||||||
message: "请输入正确的手机号码",
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入手机号码",
|
|
||||||
maxlength: 11,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "邮箱",
|
|
||||||
prop: "email",
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/,
|
|
||||||
message: "请输入正确的邮箱地址",
|
|
||||||
trigger: "blur",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
type: "input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入邮箱",
|
|
||||||
maxlength: 50,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
type: "radio",
|
|
||||||
options: [
|
|
||||||
{ label: "正常", value: 1 },
|
|
||||||
{ label: "禁用", value: 0 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default modalConfig;
|
|
|
@ -1,83 +0,0 @@
|
||||||
import type { ISearchConfig } from "@/components/PageSearch/index.vue";
|
|
||||||
|
|
||||||
const searchConfig: ISearchConfig = {
|
|
||||||
pageName: "sys:user",
|
|
||||||
formItems: [
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
label: "关键字",
|
|
||||||
prop: "keywords",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "用户名/昵称/手机号",
|
|
||||||
clearable: true,
|
|
||||||
style: {
|
|
||||||
width: "200px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "tree-select",
|
|
||||||
label: "部门",
|
|
||||||
prop: "deptId",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择",
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: "研筑科技",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: "研发部门",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 3,
|
|
||||||
label: "测试部门",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
filterable: true,
|
|
||||||
"check-strictly": true,
|
|
||||||
"render-after-expand": false,
|
|
||||||
clearable: true,
|
|
||||||
style: {
|
|
||||||
width: "150px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "全部",
|
|
||||||
clearable: true,
|
|
||||||
style: {
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{ label: "启用", value: 1 },
|
|
||||||
{ label: "禁用", value: 0 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "date-picker",
|
|
||||||
label: "创建时间",
|
|
||||||
prop: "createAt",
|
|
||||||
attrs: {
|
|
||||||
type: "daterange",
|
|
||||||
"range-separator": "~",
|
|
||||||
"start-placeholder": "开始时间",
|
|
||||||
"end-placeholder": "截止时间",
|
|
||||||
"value-format": "YYYY-MM-DD",
|
|
||||||
style: {
|
|
||||||
width: "240px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default searchConfig;
|
|
|
@ -1,111 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-link
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/curd/index.vue"
|
|
||||||
type="primary"
|
|
||||||
target="_blank"
|
|
||||||
class="mb-10"
|
|
||||||
>
|
|
||||||
示例源码 请点击>>>>
|
|
||||||
</el-link>
|
|
||||||
|
|
||||||
<!-- 搜索 -->
|
|
||||||
<page-search
|
|
||||||
ref="searchRef"
|
|
||||||
:search-config="searchConfig"
|
|
||||||
@query-click="handleQueryClick"
|
|
||||||
@reset-click="handleResetClick"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<page-content
|
|
||||||
ref="contentRef"
|
|
||||||
:content-config="contentConfig"
|
|
||||||
@add-click="handleAddClick"
|
|
||||||
@edit-click="handleEditClick"
|
|
||||||
@export-click="handleExportClick"
|
|
||||||
@search-click="handleSearchClick"
|
|
||||||
@toolbar-click="handleToolbarClick"
|
|
||||||
@operat-click="handleOperatClick"
|
|
||||||
>
|
|
||||||
<template #status="scope">
|
|
||||||
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
|
||||||
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</page-content>
|
|
||||||
|
|
||||||
<!-- 新增 -->
|
|
||||||
<page-modal
|
|
||||||
ref="addModalRef"
|
|
||||||
:modal-config="addModalConfig"
|
|
||||||
@submit-click="handleSubmitClick"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 编辑 -->
|
|
||||||
<page-modal
|
|
||||||
ref="editModalRef"
|
|
||||||
:modal-config="editModalConfig"
|
|
||||||
@submit-click="handleSubmitClick"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import UserAPI from "@/api/user";
|
|
||||||
import type { IObject, IOperatData } from "@/hooks/usePage";
|
|
||||||
import usePage from "@/hooks/usePage";
|
|
||||||
import addModalConfig from "./config/add";
|
|
||||||
import contentConfig from "./config/content";
|
|
||||||
import editModalConfig from "./config/edit";
|
|
||||||
import searchConfig from "./config/search";
|
|
||||||
|
|
||||||
const {
|
|
||||||
searchRef,
|
|
||||||
contentRef,
|
|
||||||
addModalRef,
|
|
||||||
editModalRef,
|
|
||||||
handleQueryClick,
|
|
||||||
handleResetClick,
|
|
||||||
handleAddClick,
|
|
||||||
// handleEditClick,
|
|
||||||
handleSubmitClick,
|
|
||||||
handleExportClick,
|
|
||||||
handleSearchClick,
|
|
||||||
} = usePage();
|
|
||||||
// 编辑
|
|
||||||
async function handleEditClick(row: IObject) {
|
|
||||||
// 根据id获取数据进行填充
|
|
||||||
const data = await UserAPI.getFormData(row.id);
|
|
||||||
editModalRef.value?.setModalVisible(data);
|
|
||||||
}
|
|
||||||
// 其他工具栏
|
|
||||||
function handleToolbarClick(name: string) {
|
|
||||||
console.log(name);
|
|
||||||
if (name === "import") {
|
|
||||||
ElMessage.success("点击了导入按钮");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 其他操作列
|
|
||||||
function handleOperatClick(data: IOperatData) {
|
|
||||||
console.log(data);
|
|
||||||
// 重置密码
|
|
||||||
if (data.name === "reset_pwd") {
|
|
||||||
ElMessageBox.prompt(
|
|
||||||
"请输入用户「" + data.row.username + "」的新密码",
|
|
||||||
"重置密码",
|
|
||||||
{
|
|
||||||
confirmButtonText: "确定",
|
|
||||||
cancelButtonText: "取消",
|
|
||||||
}
|
|
||||||
).then(({ value }) => {
|
|
||||||
if (!value) {
|
|
||||||
ElMessage.warning("请输入新密码");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 发送网络请求
|
|
||||||
ElMessage.success("密码重置成功,新密码是:" + value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,32 +0,0 @@
|
||||||
<!-- 字典组件示例 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
const stringValue = ref("1"); // 性别(值为String)
|
|
||||||
const numberValue = ref(1); // 性别(值为Number)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-link
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/dict.vue"
|
|
||||||
type="primary"
|
|
||||||
target="_blank"
|
|
||||||
class="mb-[20px]"
|
|
||||||
>示例源码 请点击>>>></el-link
|
|
||||||
>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item label="性别">
|
|
||||||
<dictionary v-model="stringValue" type-code="gender" />
|
|
||||||
<el-link :underline="false" type="primary" class="ml-5"
|
|
||||||
>值为String: const value = ref("1");
|
|
||||||
</el-link>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="性别">
|
|
||||||
<dictionary v-model="numberValue" type-code="gender" />
|
|
||||||
<el-link :underline="false" type="success" class="ml-5"
|
|
||||||
>值为Number: const value = ref(1);
|
|
||||||
</el-link>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<!-- 图标选择器示例 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
const iconName = ref("edit");
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-link
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/icon-selector.vue"
|
|
||||||
type="primary"
|
|
||||||
target="_blank"
|
|
||||||
class="mb-10"
|
|
||||||
>示例源码 请点击>>>></el-link
|
|
||||||
>
|
|
||||||
<icon-select v-model="iconName" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,156 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="icons-container">
|
|
||||||
<el-tabs type="border-card">
|
|
||||||
<el-tab-pane label="Icons">
|
|
||||||
<div class="grid">
|
|
||||||
<div
|
|
||||||
v-for="item of svg_icons"
|
|
||||||
:key="item"
|
|
||||||
@click="handleClipboard(generateIconCode(item), $event)"
|
|
||||||
>
|
|
||||||
<el-tooltip
|
|
||||||
effect="dark"
|
|
||||||
:content="generateIconCode(item)"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<div class="icon-item">
|
|
||||||
<svg-icon :icon-class="item" />
|
|
||||||
<span>{{ item }}</span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="Element-UI Icons">
|
|
||||||
<div class="grid">
|
|
||||||
<div
|
|
||||||
v-for="(icon, name) of icons"
|
|
||||||
:key="name"
|
|
||||||
@click="handleClipboard(generateElementIconCode(name), $event)"
|
|
||||||
>
|
|
||||||
<el-tooltip
|
|
||||||
effect="dark"
|
|
||||||
:content="generateElementIconCode(name)"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<div class="icon-item">
|
|
||||||
<el-icon :size="20">
|
|
||||||
<component :is="icon" />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ name }}</span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
|
||||||
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
// eslint-disable-next-line
|
|
||||||
name: "Icons",
|
|
||||||
inheritAttrs: false,
|
|
||||||
});
|
|
||||||
const svg_icons: string[] = [
|
|
||||||
"api",
|
|
||||||
"cascader",
|
|
||||||
"client",
|
|
||||||
"close",
|
|
||||||
"close_all",
|
|
||||||
"close_left",
|
|
||||||
"close_other",
|
|
||||||
"close_right",
|
|
||||||
"dict",
|
|
||||||
"document",
|
|
||||||
"download",
|
|
||||||
"drag",
|
|
||||||
"edit",
|
|
||||||
"exit-fullscreen",
|
|
||||||
"eye-open",
|
|
||||||
"eye",
|
|
||||||
"fullscreen",
|
|
||||||
"github",
|
|
||||||
"homepage",
|
|
||||||
"language",
|
|
||||||
"link",
|
|
||||||
"menu",
|
|
||||||
"message",
|
|
||||||
"money",
|
|
||||||
"monitor",
|
|
||||||
"order",
|
|
||||||
"password",
|
|
||||||
"peoples",
|
|
||||||
"perm",
|
|
||||||
"publish",
|
|
||||||
"role",
|
|
||||||
"security",
|
|
||||||
"size",
|
|
||||||
"skill",
|
|
||||||
"system",
|
|
||||||
"tree",
|
|
||||||
"user",
|
|
||||||
"uv",
|
|
||||||
"verify-code",
|
|
||||||
];
|
|
||||||
const icons = ref(ElementPlusIconsVue);
|
|
||||||
|
|
||||||
const { copy } = useClipboard();
|
|
||||||
|
|
||||||
function generateIconCode(symbol: any) {
|
|
||||||
return `<svg-icon icon-class="${symbol}" />`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateElementIconCode(symbol: any) {
|
|
||||||
return `<el-icon><${symbol} /></el-icon>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClipboard(text: any, event: any) {
|
|
||||||
// clipboard(text, event);
|
|
||||||
copy(text)
|
|
||||||
.then(() => {
|
|
||||||
ElMessage.success("Copy successfully");
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
ElMessage.warning("Copy failed");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.icons-container {
|
|
||||||
margin: 10px 20px 0;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
position: relative;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-item {
|
|
||||||
float: left;
|
|
||||||
width: 100px;
|
|
||||||
height: 85px;
|
|
||||||
margin: 20px;
|
|
||||||
font-size: 30px;
|
|
||||||
color: var(--el-text-color-regular);
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: block;
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<iframe
|
|
||||||
src="https://juejin.cn/post/7228990409909108793"
|
|
||||||
frameborder="0"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/** 关闭tag标签 */
|
|
||||||
.app-container {
|
|
||||||
/* 50px = navbar = 50px */
|
|
||||||
height: calc(100vh - 50px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 开启tag标签 */
|
|
||||||
.hasTagsView {
|
|
||||||
.app-container {
|
|
||||||
/* 84px = navbar + tags-view = 50px + 34px */
|
|
||||||
height: calc(100vh - 84px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<template>
|
|
||||||
<div style="padding: 30px">
|
|
||||||
<el-alert :closable="false" title="菜单三级-1" type="error" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<template>
|
|
||||||
<div style="padding: 30px">
|
|
||||||
<el-alert :closable="false" title="菜单三级-2" type="warning" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<template>
|
|
||||||
<div style="padding: 30px">
|
|
||||||
<el-alert :closable="false" title="菜单二级" type="success">
|
|
||||||
<router-view />
|
|
||||||
</el-alert>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,15 +0,0 @@
|
||||||
<template>
|
|
||||||
<div style="padding: 30px">
|
|
||||||
<el-link
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/multi-level/level1.vue"
|
|
||||||
type="primary"
|
|
||||||
target="_blank"
|
|
||||||
class="mb-10"
|
|
||||||
>示例源码 请点击>>>></el-link
|
|
||||||
>
|
|
||||||
|
|
||||||
<el-alert :closable="false" title="菜单一级">
|
|
||||||
<router-view />
|
|
||||||
</el-alert>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,186 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import FileAPI from "@/api/file";
|
|
||||||
|
|
||||||
const imgUrl = ref("");
|
|
||||||
const canvas = ref();
|
|
||||||
let ctx: CanvasRenderingContext2D;
|
|
||||||
|
|
||||||
// 正在绘制中,用来控制 move 和 end 事件
|
|
||||||
let painting = false;
|
|
||||||
|
|
||||||
// 获取触发点相对被触发dom的左、上角距离
|
|
||||||
const getOffset = (event: MouseEvent | TouchEvent) => {
|
|
||||||
let offset: [number, number];
|
|
||||||
if ((event as MouseEvent).offsetX) {
|
|
||||||
// pc端
|
|
||||||
const { offsetX, offsetY } = event as MouseEvent;
|
|
||||||
offset = [offsetX, offsetY];
|
|
||||||
} else {
|
|
||||||
// 移动端
|
|
||||||
const { top, left } = canvas.value.getBoundingClientRect();
|
|
||||||
const offsetX = (event as TouchEvent).touches[0].clientX - left;
|
|
||||||
const offsetY = (event as TouchEvent).touches[0].clientY - top;
|
|
||||||
offset = [offsetX, offsetY];
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 绘制起点
|
|
||||||
let startX = 0,
|
|
||||||
startY = 0;
|
|
||||||
|
|
||||||
// 鼠标/触摸 按下时,保存 触发点相对被触发dom的左、上 距离
|
|
||||||
const onEventStart = (event: MouseEvent | TouchEvent) => {
|
|
||||||
[startX, startY] = getOffset(event);
|
|
||||||
painting = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEventMove = (event: MouseEvent | TouchEvent) => {
|
|
||||||
if (painting) {
|
|
||||||
// 鼠标/触摸 移动时,保存 移动点相对 被触发dom的左、上 距离
|
|
||||||
const [endX, endY] = getOffset(event);
|
|
||||||
paint(startX, startY, endX, endY, ctx);
|
|
||||||
|
|
||||||
// 每次绘制 或 清除结束后,起点要重置为上次的终点
|
|
||||||
startX = endX;
|
|
||||||
startY = endY;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEventEnd = () => {
|
|
||||||
if (painting) {
|
|
||||||
painting = false; // 停止绘制
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
ctx = canvas.value.getContext("2d") as CanvasRenderingContext2D;
|
|
||||||
});
|
|
||||||
const handleToFile = async () => {
|
|
||||||
if (isCanvasBlank(canvas.value)) {
|
|
||||||
ElMessage({
|
|
||||||
type: "warning",
|
|
||||||
message: "当前签名文件为空",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const file = dataURLtoFile(canvas.value.toDataURL(), "签名.png");
|
|
||||||
|
|
||||||
if (!file) return;
|
|
||||||
const data = await FileAPI.upload(file);
|
|
||||||
handleClearSign();
|
|
||||||
imgUrl.value = data.url;
|
|
||||||
};
|
|
||||||
const handleClearSign = () => {
|
|
||||||
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
|
|
||||||
};
|
|
||||||
const isCanvasBlank = (canvas: HTMLCanvasElement) => {
|
|
||||||
const blank = document.createElement("canvas"); //系统获取一个空canvas对象
|
|
||||||
blank.width = canvas.width;
|
|
||||||
blank.height = canvas.height;
|
|
||||||
return canvas.toDataURL() == blank.toDataURL(); //比较值相等则为空
|
|
||||||
};
|
|
||||||
|
|
||||||
// 保存为图片
|
|
||||||
const handleSaveImg = () => {
|
|
||||||
if (isCanvasBlank(canvas.value)) {
|
|
||||||
ElMessage({
|
|
||||||
type: "warning",
|
|
||||||
message: "当前签名文件为空",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const el = document.createElement("a");
|
|
||||||
// 设置 href 为图片经过 base64 编码后的字符串,默认为 png 格式
|
|
||||||
el.href = canvas.value.toDataURL();
|
|
||||||
el.download = "签名";
|
|
||||||
// 创建一个点击事件并对 a 标签进行触发
|
|
||||||
const event = new MouseEvent("click");
|
|
||||||
el.dispatchEvent(event);
|
|
||||||
};
|
|
||||||
// 转为file格式,可传递给后端
|
|
||||||
const dataURLtoFile = (dataurl: string, filename: string) => {
|
|
||||||
const arr: string[] = dataurl.split(",");
|
|
||||||
if (!arr.length) return;
|
|
||||||
|
|
||||||
const mime = arr[0].match(/:(.*?);/);
|
|
||||||
if (mime) {
|
|
||||||
const bstr = atob(arr[1]);
|
|
||||||
let n = bstr.length;
|
|
||||||
const u8arr = new Uint8Array(n);
|
|
||||||
while (n--) {
|
|
||||||
u8arr[n] = bstr.charCodeAt(n);
|
|
||||||
}
|
|
||||||
return new File([u8arr], filename, { type: mime[1] });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// canvas 画图
|
|
||||||
function paint(
|
|
||||||
startX: number,
|
|
||||||
startY: number,
|
|
||||||
endX: number,
|
|
||||||
endY: number,
|
|
||||||
ctx: CanvasRenderingContext2D
|
|
||||||
) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.globalAlpha = 1;
|
|
||||||
ctx.lineWidth = 2;
|
|
||||||
ctx.strokeStyle = "#000";
|
|
||||||
ctx.moveTo(startX, startY);
|
|
||||||
ctx.lineTo(endX, endY);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="canvas-dom">
|
|
||||||
<h3>基于canvas实现的签名组件</h3>
|
|
||||||
<header>
|
|
||||||
<el-button type="primary" @click="handleSaveImg">保存为图片</el-button>
|
|
||||||
<el-button @click="handleToFile"> 保存到后端 </el-button>
|
|
||||||
<el-button @click="handleClearSign"> 清空签名 </el-button>
|
|
||||||
</header>
|
|
||||||
<canvas
|
|
||||||
ref="canvas"
|
|
||||||
height="200"
|
|
||||||
width="500"
|
|
||||||
@mousedown="onEventStart"
|
|
||||||
@mousemove.stop.prevent="onEventMove"
|
|
||||||
@mouseup="onEventEnd"
|
|
||||||
@touchstart="onEventStart"
|
|
||||||
@touchmove.stop.prevent="onEventMove"
|
|
||||||
@touchend="onEventEnd"
|
|
||||||
>
|
|
||||||
</canvas>
|
|
||||||
<img v-if="imgUrl" :src="imgUrl" alt="签名" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.canvas-dom {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
border: 1px solid #e6e6e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
margin: 8px;
|
|
||||||
|
|
||||||
.eraser-option {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
label {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,112 +0,0 @@
|
||||||
import UserAPI from "@/api/user";
|
|
||||||
import type { ISelectConfig } from "@/components/TableSelect/index.vue";
|
|
||||||
|
|
||||||
const selectConfig: ISelectConfig = {
|
|
||||||
pk: "id",
|
|
||||||
width: "70%",
|
|
||||||
placeholder: "请选择用户",
|
|
||||||
formItems: [
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
label: "关键字",
|
|
||||||
prop: "keywords",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "用户名/昵称/手机号",
|
|
||||||
clearable: true,
|
|
||||||
style: {
|
|
||||||
width: "200px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "tree-select",
|
|
||||||
label: "部门",
|
|
||||||
prop: "deptId",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请选择",
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: "研筑科技",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: "研发部门",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 3,
|
|
||||||
label: "测试部门",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
filterable: true,
|
|
||||||
"check-strictly": true,
|
|
||||||
"render-after-expand": false,
|
|
||||||
clearable: true,
|
|
||||||
style: {
|
|
||||||
width: "150px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "全部",
|
|
||||||
clearable: true,
|
|
||||||
style: {
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{ label: "启用", value: 1 },
|
|
||||||
{ label: "禁用", value: 0 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "date-picker",
|
|
||||||
label: "创建时间",
|
|
||||||
prop: "createAt",
|
|
||||||
attrs: {
|
|
||||||
type: "daterange",
|
|
||||||
"range-separator": "~",
|
|
||||||
"start-placeholder": "开始时间",
|
|
||||||
"end-placeholder": "截止时间",
|
|
||||||
"value-format": "YYYY-MM-DD",
|
|
||||||
style: {
|
|
||||||
width: "240px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
indexAction: function (params) {
|
|
||||||
if ("createAt" in params) {
|
|
||||||
const createAt = params.createAt as string[];
|
|
||||||
params.startTime = createAt[0];
|
|
||||||
params.endTime = createAt[1];
|
|
||||||
delete params.createAt;
|
|
||||||
}
|
|
||||||
return UserAPI.getPage(params);
|
|
||||||
},
|
|
||||||
tableColumns: [
|
|
||||||
{ type: "selection", width: 50, align: "center" },
|
|
||||||
{ label: "编号", align: "center", prop: "id", width: 100 },
|
|
||||||
{ label: "用户名", align: "center", prop: "username" },
|
|
||||||
{ label: "用户昵称", align: "center", prop: "nickname", width: 120 },
|
|
||||||
{ label: "性别", align: "center", prop: "genderLabel", width: 100 },
|
|
||||||
{ label: "部门", align: "center", prop: "deptName", width: 120 },
|
|
||||||
{ label: "手机号码", align: "center", prop: "mobile", width: 120 },
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
align: "center",
|
|
||||||
prop: "status",
|
|
||||||
templet: "custom",
|
|
||||||
slotName: "status",
|
|
||||||
},
|
|
||||||
{ label: "创建时间", align: "center", prop: "createTime", width: 180 },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default selectConfig;
|
|
|
@ -1,51 +0,0 @@
|
||||||
<!-- 列表选择器示例 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
import selectConfig from "./config/select";
|
|
||||||
|
|
||||||
interface IUser {
|
|
||||||
id: number;
|
|
||||||
username: string;
|
|
||||||
nickname: string;
|
|
||||||
mobile: string;
|
|
||||||
genderLabel: string;
|
|
||||||
avatar: string;
|
|
||||||
email: string | null;
|
|
||||||
status: number;
|
|
||||||
deptName: string;
|
|
||||||
roleNames: string;
|
|
||||||
createTime: string;
|
|
||||||
}
|
|
||||||
const selectedUser = ref<IUser>();
|
|
||||||
function handleConfirm(data: IUser[]) {
|
|
||||||
selectedUser.value = data[0];
|
|
||||||
}
|
|
||||||
const text = computed(() => {
|
|
||||||
return selectedUser.value
|
|
||||||
? `${selectedUser.value.username} - ${selectedUser.value.genderLabel} - ${selectedUser.value.deptName}`
|
|
||||||
: "";
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-link
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/table-select/index.vue"
|
|
||||||
type="primary"
|
|
||||||
target="_blank"
|
|
||||||
class="mb-10"
|
|
||||||
>
|
|
||||||
示例源码 请点击>>>>
|
|
||||||
</el-link>
|
|
||||||
<table-select
|
|
||||||
:text="text"
|
|
||||||
:select-config="selectConfig"
|
|
||||||
@confirm-click="handleConfirm"
|
|
||||||
>
|
|
||||||
<template #status="scope">
|
|
||||||
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
|
||||||
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</table-select>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,37 +0,0 @@
|
||||||
<!-- 文件上传组件(单图+多图)示例 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
import SingleUpload from "@/components/Upload/SingleUpload.vue";
|
|
||||||
import MultiUpload from "@/components/Upload/MultiUpload.vue";
|
|
||||||
|
|
||||||
const singlePicUrl = ref(
|
|
||||||
"https://oss.youlai.tech/youlai-boot/2023/05/20/2b6d8b49fa1047348a0a41cef5aaf69e.gif"
|
|
||||||
);
|
|
||||||
// 这里放外链图片,防止被删
|
|
||||||
const multiPicUrls = ref([
|
|
||||||
"https://s2.loli.net/2023/05/24/yNsxFC8rLHMZQcK.jpg",
|
|
||||||
"https://s2.loli.net/2023/05/24/RuHFMwW4rG5lIqs.jpg",
|
|
||||||
"https://s2.loli.net/2023/05/24/ZPiGbcpR91WqInB.jpg",
|
|
||||||
"https://s2.loli.net/2023/05/24/e1bcnEq3MFdmlNL.jpg",
|
|
||||||
"https://s2.loli.net/2023/05/24/wZTSPj1yDQNcuhU.jpg",
|
|
||||||
]);
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-link
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/upload.vue"
|
|
||||||
type="primary"
|
|
||||||
target="_blank"
|
|
||||||
class="mb-10"
|
|
||||||
>示例源码 请点击>>>></el-link
|
|
||||||
>
|
|
||||||
|
|
||||||
<el-form>
|
|
||||||
<el-form-item label="单图上传">
|
|
||||||
<single-upload v-model="singlePicUrl" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="多图上传">
|
|
||||||
<multi-upload v-model="multiPicUrls" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<!-- wangEditor富文本编辑器示例 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
import Editor from "@/components/WangEditor/index.vue";
|
|
||||||
|
|
||||||
const value = ref("初始内容");
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-link
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/wang-editor.vue"
|
|
||||||
type="primary"
|
|
||||||
target="_blank"
|
|
||||||
class="mb-[20px]"
|
|
||||||
>示例源码 请点击>>>></el-link
|
|
||||||
>
|
|
||||||
<editor v-model="value" style="height: calc(100vh - 180px)" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,268 +0,0 @@
|
||||||
<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
|
|
||||||
>
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-card>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="16">
|
|
||||||
<el-input v-model="socketEndpoint" class="w-220px" />
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
class="ml-5"
|
|
||||||
@click="connectWebSocket"
|
|
||||||
:disabled="isConnected"
|
|
||||||
>连接</el-button
|
|
||||||
>
|
|
||||||
<el-button
|
|
||||||
type="danger"
|
|
||||||
@click="disconnectWebSocket"
|
|
||||||
:disabled="!isConnected"
|
|
||||||
>断开</el-button
|
|
||||||
>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<el-col :span="8" class="text-right">
|
|
||||||
连接状态:
|
|
||||||
<el-tag class="ml-2" type="success" v-if="isConnected"
|
|
||||||
>已连接</el-tag
|
|
||||||
>
|
|
||||||
<el-tag class="ml-2" type="info" v-else>已断开</el-tag>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="mt-5">
|
|
||||||
<el-form label-width="90px">
|
|
||||||
<el-form-item label="消息内容">
|
|
||||||
<el-input type="textarea" v-model="topicMessage" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="sendToAll" type="primary">发送广播</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="mt-5">
|
|
||||||
<el-form label-width="90px">
|
|
||||||
<el-form-item label="消息内容">
|
|
||||||
<el-input type="textarea" v-model="queneMessage" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="消息接收人">
|
|
||||||
<el-input v-model="receiver" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="sendToUser" type="primary"
|
|
||||||
>发送点对点消息</el-button
|
|
||||||
>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-card>
|
|
||||||
<div class="message-container">
|
|
||||||
<div
|
|
||||||
v-for="(message, index) in messages"
|
|
||||||
:key="index"
|
|
||||||
:class="{
|
|
||||||
'tip-message': message.type === 'tip',
|
|
||||||
message: message.type !== 'tip',
|
|
||||||
'message--sent': message.sender === userStore.user.username,
|
|
||||||
'message--received': message.sender !== userStore.user.username,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div v-if="message.type != 'tip'" class="message-content">
|
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
'message-sender':
|
|
||||||
message.sender === userStore.user.username,
|
|
||||||
'message-receiver':
|
|
||||||
message.sender !== userStore.user.username,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ message.sender }}
|
|
||||||
</div>
|
|
||||||
<div class="color-#333">{{ message.content }}</div>
|
|
||||||
</div>
|
|
||||||
<div v-else>{{ message.content }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- websocket 示例 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
import SockJS from "sockjs-client/dist/sockjs.min.js";
|
|
||||||
//import SockJS from "sockjs-client";
|
|
||||||
import Stomp from "stompjs";
|
|
||||||
|
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
|
||||||
import { TOKEN_KEY } from "@/enums/CacheEnum";
|
|
||||||
|
|
||||||
const userStore = useUserStoreHook();
|
|
||||||
|
|
||||||
const isConnected = ref(false);
|
|
||||||
// const socketEndpoint = ref("http://47.117.115.107:8989/ws"); // 线上
|
|
||||||
const socketEndpoint = ref("http://localhost:8989/ws"); // 本地
|
|
||||||
|
|
||||||
const receiver = ref("root");
|
|
||||||
|
|
||||||
interface MessageType {
|
|
||||||
type?: string; // 消息类型: tip-提示消息
|
|
||||||
sender?: string;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = ref<MessageType[]>([]);
|
|
||||||
|
|
||||||
const topicMessage = ref(
|
|
||||||
"亲爱的大冤种们,由于一只史诗级的BUG,系统版本已经被迫回退到了0.0.1。"
|
|
||||||
); // 广播消息
|
|
||||||
|
|
||||||
const queneMessage = ref(
|
|
||||||
"hi , " +
|
|
||||||
receiver.value +
|
|
||||||
" , 我是" +
|
|
||||||
userStore.user.username +
|
|
||||||
" , 想和你交个朋友 ! "
|
|
||||||
);
|
|
||||||
|
|
||||||
function sendToAll() {
|
|
||||||
stompClient.send("/app/sendToAll", {}, topicMessage.value);
|
|
||||||
messages.value.push({
|
|
||||||
sender: userStore.user.username,
|
|
||||||
content: topicMessage.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendToUser() {
|
|
||||||
stompClient.send("/app/sendToUser/" + receiver.value, {}, queneMessage.value);
|
|
||||||
messages.value.push({
|
|
||||||
sender: userStore.user.username,
|
|
||||||
content: queneMessage.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let stompClient: Stomp.Client;
|
|
||||||
|
|
||||||
function connectWebSocket() {
|
|
||||||
let socket = new SockJS(socketEndpoint.value);
|
|
||||||
|
|
||||||
stompClient = Stomp.over(socket);
|
|
||||||
|
|
||||||
stompClient.connect(
|
|
||||||
{ Authorization: localStorage.getItem(TOKEN_KEY) },
|
|
||||||
() => {
|
|
||||||
isConnected.value = true;
|
|
||||||
messages.value.push({
|
|
||||||
sender: "Server",
|
|
||||||
content: "Websocket 已连接",
|
|
||||||
type: "tip",
|
|
||||||
});
|
|
||||||
|
|
||||||
stompClient.subscribe("/topic/notice", (res: any) => {
|
|
||||||
messages.value.push({
|
|
||||||
sender: "Server",
|
|
||||||
content: res.body,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
stompClient.subscribe("/user/queue/greeting", (res) => {
|
|
||||||
const messageData = JSON.parse(res.body) as MessageType;
|
|
||||||
messages.value.push({
|
|
||||||
sender: messageData.sender,
|
|
||||||
content: messageData.content,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.log("连接失败: " + error);
|
|
||||||
isConnected.value = false; // 更新连接状态
|
|
||||||
messages.value.push({
|
|
||||||
sender: "Server",
|
|
||||||
content: "Websocket 已断开",
|
|
||||||
type: "tip",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnectWebSocket() {
|
|
||||||
if (stompClient && stompClient.connected) {
|
|
||||||
stompClient.disconnect(() => {
|
|
||||||
isConnected.value = false; // 更新连接状态
|
|
||||||
messages.value.push({
|
|
||||||
sender: "Server",
|
|
||||||
content: "Websocket 已断开",
|
|
||||||
type: "tip",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
connectWebSocket();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.message-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message--sent {
|
|
||||||
align-self: flex-end;
|
|
||||||
background-color: #dcf8c6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message--received {
|
|
||||||
align-self: flex-start;
|
|
||||||
background-color: #e8e8e8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-sender {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-receiver {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip-message {
|
|
||||||
align-self: center;
|
|
||||||
padding: 5px 10px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-style: italic;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,198 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="flow-demo1">
|
|
||||||
<div style="display: flex;height:100%">
|
|
||||||
<NodePanel :lf="lf" ref="nodePanel" class="node-panel"/>
|
|
||||||
<el-card class="flow-card">
|
|
||||||
<div ref="flow" class="viewport" id="flowMain" @click="hideProp"/>
|
|
||||||
</el-card>
|
|
||||||
<el-card v-if="showProp" :key="nodePropKey" class="card-prop">
|
|
||||||
<template #header>节点属性</template>
|
|
||||||
<div>
|
|
||||||
节点ID:{{nodeData.properties.node }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
节点名称:{{nodeData.text.value }}
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import LogicFlow from '@logicflow/core';
|
|
||||||
// 引入整体背景的样式
|
|
||||||
import '@logicflow/core/dist/style/index.css';
|
|
||||||
import { Menu } from "@logicflow/extension";
|
|
||||||
import AiNodeExtension from '@/components/flow/index'
|
|
||||||
import RegisteMenu from '@/components/flow/menu.js'
|
|
||||||
import NodePanel from './nodePanel.vue'
|
|
||||||
const themeApprove = {
|
|
||||||
rect: { // 矩形样式
|
|
||||||
radius: 8,
|
|
||||||
stroke: '#3CB371'
|
|
||||||
},
|
|
||||||
circle: {
|
|
||||||
r: 25,
|
|
||||||
stroke: '#FF6347'
|
|
||||||
},
|
|
||||||
polygon: {
|
|
||||||
stroke: '#6495ED',
|
|
||||||
},
|
|
||||||
polyline: {
|
|
||||||
strokeWidth: 1,
|
|
||||||
stroke: 'red'
|
|
||||||
},
|
|
||||||
edgeText: {
|
|
||||||
background: {
|
|
||||||
fill: 'white',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
let showProp=ref(false)
|
|
||||||
let nodeData=reactive(null)
|
|
||||||
const nodePanel=ref()
|
|
||||||
const config = {
|
|
||||||
stopScrollGraph: true,
|
|
||||||
stopZoomGraph: true,
|
|
||||||
grid: {
|
|
||||||
size: 10,
|
|
||||||
visible: true,
|
|
||||||
type: 'mesh',
|
|
||||||
config: {
|
|
||||||
color: '#DCDCDC',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
keyboard: { enabled: true },
|
|
||||||
style: themeApprove,
|
|
||||||
plugins: [AiNodeExtension,Menu]
|
|
||||||
};
|
|
||||||
const flow = ref()
|
|
||||||
let lf = ref(null)
|
|
||||||
let nodePropKey=ref(1)
|
|
||||||
const hideProp=(e)=>{
|
|
||||||
if(e.target.classList.contains("lf-drag-able")){
|
|
||||||
showProp.value=false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const initEvent=(lf)=>{
|
|
||||||
lf.on("element:click",node=>{
|
|
||||||
nodeData=node.data
|
|
||||||
showProp.value=true
|
|
||||||
nodePropKey.value++
|
|
||||||
console.log("-->",nodeData)
|
|
||||||
});
|
|
||||||
lf.on("node:dnd-add",data=>{
|
|
||||||
nodeData=data.data;
|
|
||||||
showProp.value=true
|
|
||||||
nodePropKey.value++
|
|
||||||
doUpdateState()
|
|
||||||
});
|
|
||||||
lf.on("node:delete",data=>{
|
|
||||||
if(data.data.text.value=="目标绘图"){
|
|
||||||
ElMessage.error("目标绘图节点不能删除");
|
|
||||||
setTimeout(() => {
|
|
||||||
lf.undo();
|
|
||||||
}, 400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
doUpdateState()
|
|
||||||
});
|
|
||||||
lf.on("node:contextmenu",(d,e,p)=>{
|
|
||||||
if(d.data.text.value=="目标绘图"){
|
|
||||||
setTimeout(()=>{
|
|
||||||
document.querySelector(".lf-menu").style.display="none";
|
|
||||||
},0)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const doUpdateState=()=>{
|
|
||||||
nodePanel.value.updateNode(lf.value.getGraphData());
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
const logicFlow = new LogicFlow({
|
|
||||||
...config,
|
|
||||||
container: flow.value
|
|
||||||
})
|
|
||||||
lf.value = logicFlow
|
|
||||||
RegisteMenu(logicFlow)
|
|
||||||
initEvent(logicFlow);
|
|
||||||
window.lf=lf
|
|
||||||
logicFlow.render({
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
id: 'node_1',
|
|
||||||
type: 'ai-node',
|
|
||||||
x: 150,
|
|
||||||
y: 20,
|
|
||||||
fill:'#409effaa',
|
|
||||||
text: '图像缩放',
|
|
||||||
properties:{
|
|
||||||
node:201
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'node_2',
|
|
||||||
type: 'ai-node',
|
|
||||||
x: 150,
|
|
||||||
y: 80,
|
|
||||||
fill:'#50e904',
|
|
||||||
text: '目标绘图',
|
|
||||||
properties:{
|
|
||||||
node:900
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
edges: [
|
|
||||||
|
|
||||||
]
|
|
||||||
})
|
|
||||||
doUpdateState();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang='scss'>
|
|
||||||
.flow-demo1 {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
padding:0px 12px;
|
|
||||||
.viewport {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-anchor {
|
|
||||||
stroke: #999;
|
|
||||||
stroke-width: 1;
|
|
||||||
fill: #d9d9d9;
|
|
||||||
cursor: crosshair;
|
|
||||||
rx: 3;
|
|
||||||
ry: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-anchor:hover {
|
|
||||||
fill: #ff7f0e;
|
|
||||||
stroke: #ff7f0e;
|
|
||||||
}
|
|
||||||
.flow-card{
|
|
||||||
height: 80%;
|
|
||||||
width:calc(100% - 450px);
|
|
||||||
margin: 0px 12px;
|
|
||||||
.el-card__body{
|
|
||||||
height:100%;
|
|
||||||
padding:4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.node-panel{
|
|
||||||
width:240px;
|
|
||||||
.el-tabs__item{
|
|
||||||
padding: 0px 10px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-prop{
|
|
||||||
height: 300px;
|
|
||||||
width:200px;
|
|
||||||
min-width: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,87 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="node-panel">
|
|
||||||
<el-tabs type="border-card">
|
|
||||||
<el-tab-pane label="前处理算子">
|
|
||||||
<div v-for="(it1, idx1) in list1" :key="idx1" class="div-item" @mousedown="dragNode(it1, 1)" v-show="it1.show">
|
|
||||||
{{ it1.text }}
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="后处理算子">
|
|
||||||
<div v-for="(it2, idx2) in list2" :key="idx2" class="div-item item2" @mousedown="dragNode(it2, 2)" v-show="it2.show">
|
|
||||||
{{ it2.text }}
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const props = defineProps({
|
|
||||||
lf: {
|
|
||||||
type: Object,
|
|
||||||
require: true,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const dragNode = (it, t) => {
|
|
||||||
props.lf.dnd.startDrag({
|
|
||||||
type: 'ai-node',
|
|
||||||
text: it.text,
|
|
||||||
fill: t == 1 ? '#409effaa' : '#ccccccaa',
|
|
||||||
properties:{
|
|
||||||
node:it.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const list1 = reactive([
|
|
||||||
{ text: "图像缩放", id: 201,show:true },
|
|
||||||
{ text: "RGB12图像格式转换", id: 202,show:true },
|
|
||||||
{ text: "RGB24图像格式转换", id: 203,show:true },
|
|
||||||
{ text: "RGB36图像格式转换", id: 204,show:true },
|
|
||||||
{ text: "RGB48图像格式转换", id: 205,show:true },
|
|
||||||
{ text: "RGB72图像格式转换", id: 206,show:true }
|
|
||||||
])
|
|
||||||
|
|
||||||
const list2 = reactive([
|
|
||||||
{ text: "VIT推理算子1", id: 301,show:true },
|
|
||||||
{ text: "VIT推理算子2", id: 302,show:true },
|
|
||||||
{ text: "VIT后处理算子1", id: 303,show:true },
|
|
||||||
{ text: "VIT后处理算子2", id: 304,show:true },
|
|
||||||
{ text: "VIT后处理算子3", id: 305,show:true },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const updateNode=(nodes)=>{
|
|
||||||
let nds=nodes.nodes.map(d=>d.properties.node);
|
|
||||||
list2.forEach(d=>{
|
|
||||||
d.show=nds.indexOf(d.id)==-1
|
|
||||||
})
|
|
||||||
list1.forEach(d=>{
|
|
||||||
d.show=nds.indexOf(d.id)==-1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
updateNode
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<style lang='scss'>
|
|
||||||
.node-panel {
|
|
||||||
.div-item {
|
|
||||||
font-size: 12px;
|
|
||||||
border: solid 1px #888;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
line-height: 30px;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: #409effaa;
|
|
||||||
user-select: none;
|
|
||||||
cursor: move;
|
|
||||||
|
|
||||||
&.item2 {
|
|
||||||
background: #ccccccaa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="split-split1-index">
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<svg-icon icon-class="build" style="width:100px;height:100px;" />
|
|
||||||
</div>
|
|
||||||
<div style="text-align: center;">
|
|
||||||
建设中。。。。。
|
|
||||||
</div>
|
|
||||||
<iframe :src="url" style="width:100%;height:100%;position: absolute;top:0px;"></iframe>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
let url=ref("")
|
|
||||||
onMounted(()=>{
|
|
||||||
url.value="./onnx/onnx.html?url=/ai/siamRPN_192.onnx"
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style scoped lang='scss'>
|
|
||||||
.split-split1-index{
|
|
||||||
position: relative;
|
|
||||||
padding: 12px 24px;
|
|
||||||
height:100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue