姜玉琦 2024-11-25 23:14:17 +08:00
commit 3cbf34d55c
30 changed files with 46 additions and 2343 deletions

View File

@ -10,7 +10,7 @@
<script>
window.isV2=true
</script>
-->
-->
</head>
<body>
<div id="app">

View File

@ -7,7 +7,35 @@ class MenuAPI {
*/
static getRoutes() {
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",
component: "Layout",

View File

@ -1,10 +1,15 @@
<template>
<div class="flex" style="height: 100%;align-items: center;">
<div style="flex-grow: 1;display: flex;align-items: center;padding-top: 3px;">
<span style="position: relative;top:7px;">
<img src="@/assets/logojd.png" class="logo-image" />
</span>
<span class="logo-title" style="font-weight: bold;font-size: 28px;margin-left: auto;margin-right: auto;color:#101010">超异构框架统一管理平台</span>
<div style="flex-grow: 1;display: flex;align-items: center;padding-top: 3px;" >
<span style="position: relative;" class="logo-small1">
<img src="@/assets/logo2.png" class="logo-image" />
<span class="logo-title" style="position: absolute;font-weight: bold;font-size: 14px;color: #003F88;left: 0;bottom: -13px;white-space: nowrap;">中国航天</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>
<!--全屏 -->
@ -17,10 +22,10 @@
<!-- 用户头像 -->
<el-dropdown class="setting-item" trigger="click">
<div class="flex-center h100% p10px">
<img
:src="userStore.user.avatar + '?imageView2/1/w/80/h/80'"
<img src="@/assets/images/avatar.png"
class="rounded-full mr-10px w24px w24px"
/>
<!-- :src="userStore.user.avatar + '?imageView2/1/w/80/h/80'" -->
<span>{{ userStore.user.username }}</span>
</div>
<template #dropdown>
@ -104,6 +109,7 @@ function resetPassword() {
});
});
}
</script>
<style lang="scss" scoped>
.setting-item {
@ -119,8 +125,9 @@ function resetPassword() {
background: rgb(0 0 0 / 10%);
}
}
.logo-image {
height: 60px;
.logo-image {
width: 50px;
//height: 68px;
}
.layout-top,
.layout-mix {

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,5 +0,0 @@
<template>
<div style="padding: 30px">
<el-alert :closable="false" title="菜单三级-1" type="error" />
</div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<div style="padding: 30px">
<el-alert :closable="false" title="菜单三级-2" type="warning" />
</div>
</template>

View File

@ -1,7 +0,0 @@
<template>
<div style="padding: 30px">
<el-alert :closable="false" title="菜单二级" type="success">
<router-view />
</el-alert>
</div>
</template>

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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