dev #1
|
@ -1,8 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# shellcheck source=./_/husky.sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
PATH="/usr/local/bin:$PATH"
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/sh
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Workaround for Windows 10, Git Bash and Pnpm
|
||||
if command_exists winpty && test -t 1; then
|
||||
exec < /dev/tty
|
||||
fi
|
|
@ -1,10 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
. "$(dirname "$0")/common.sh"
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
[ -n "$CI" ] && exit 0
|
||||
|
||||
PATH="/usr/local/bin:$PATH"
|
||||
|
||||
# Perform lint check on files in the staging area through .lintstagedrc configuration
|
||||
pnpm exec lint-staged
|
||||
pnpm exec lint-staged
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
/** @type {import("prettier").Config} */
|
||||
export default {
|
||||
printWidth: 230,
|
||||
printWidth: 200,
|
||||
bracketSpacing: true,
|
||||
singleQuote: false,
|
||||
arrowParens: "avoid",
|
||||
|
|
|
@ -4,13 +4,13 @@ import dayjs, { type Dayjs } from "dayjs";
|
|||
import duration from "dayjs/plugin/duration";
|
||||
import gradientString from "gradient-string";
|
||||
import boxen, { type Options as BoxenOptions } from "boxen";
|
||||
|
||||
dayjs.extend(duration);
|
||||
|
||||
const welcomeMessage = gradientString("cyan", "magenta").multiline(
|
||||
`您好! 欢迎使用 pure-admin 开源项目
|
||||
我们为您精心准备了下面两个贴心的保姆级文档
|
||||
https://pure-admin.github.io/pure-admin-doc
|
||||
https://pure-admin-utils.netlify.app`
|
||||
`您好! 欢迎使用 bunny-admin 后台管理
|
||||
项目访问地址如下:
|
||||
http://localhost:8848/`
|
||||
);
|
||||
|
||||
const boxenOptions: BoxenOptions = {
|
||||
|
@ -42,16 +42,7 @@ export function viteBuildInfo(): Plugin {
|
|||
getPackageSize({
|
||||
folder: outDir,
|
||||
callback: (size: string) => {
|
||||
console.log(
|
||||
boxen(
|
||||
gradientString("cyan", "magenta").multiline(
|
||||
`🎉 恭喜打包完成(总用时${dayjs
|
||||
.duration(endTime.diff(startTime))
|
||||
.format("mm分ss秒")},打包后的大小为${size})`
|
||||
),
|
||||
boxenOptions
|
||||
)
|
||||
);
|
||||
console.log(boxen(gradientString("cyan", "magenta").multiline(`🎉 恭喜打包完成(总用时${dayjs.duration(endTime.diff(startTime)).format("mm分ss秒")},打包后的大小为${size})`), boxenOptions));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,35 +1,80 @@
|
|||
// @ts-check
|
||||
// @see: https://cz-git.qbenben.com/zh/guide
|
||||
/** @type {import('cz-git').UserConfig} */
|
||||
|
||||
/** @type {import("@commitlint/types").UserConfig} */
|
||||
export default {
|
||||
ignores: [commit => commit.includes("init")],
|
||||
extends: ["@commitlint/config-conventional"],
|
||||
rules: {
|
||||
// @see: https://commitlint.js.org/#/reference-rules
|
||||
"body-leading-blank": [2, "always"],
|
||||
"footer-leading-blank": [1, "always"],
|
||||
"header-max-length": [2, "always", 108],
|
||||
"subject-empty": [2, "never"],
|
||||
"type-empty": [2, "never"],
|
||||
"subject-case": [0],
|
||||
"type-enum": [
|
||||
2,
|
||||
"always",
|
||||
[
|
||||
"feat",
|
||||
"fix",
|
||||
"perf",
|
||||
"style",
|
||||
"docs",
|
||||
"test",
|
||||
"refactor",
|
||||
"build",
|
||||
"ci",
|
||||
"chore",
|
||||
"revert",
|
||||
"wip",
|
||||
"workflow",
|
||||
"types",
|
||||
"release"
|
||||
]
|
||||
["init", "optimize", "feat", "fix", "fixbug", "media", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore", "revert", "wip", "workflow", "types", "release"]
|
||||
]
|
||||
},
|
||||
prompt: {
|
||||
messages: {
|
||||
type: "选择你要提交的类型 :",
|
||||
scope: "选择一个提交范围(可选):",
|
||||
customScope: "请输入自定义的提交范围 :",
|
||||
subject: "填写简短精炼的变更描述 :\n",
|
||||
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
|
||||
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
|
||||
footerPrefixsSelect: "选择关联issue前缀(可选):",
|
||||
customFooterPrefixs: "输入自定义issue前缀 :",
|
||||
footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
|
||||
confirmCommit: "是否提交或修改commit ?"
|
||||
},
|
||||
types: [
|
||||
{ value: "init", name: "初始化: ⏳ 初始化项目", emoji: "⏳" },
|
||||
{ value: "optimize", name: "优化代码: ♻️ 优化项目代码", emoji: "♻️" },
|
||||
{ value: "feat", name: "特性: 🚀 新增功能", emoji: "🚀" },
|
||||
{ value: "media", name: "媒体: 🎁 新增媒体资源", emoji: "🎁" },
|
||||
{ value: "fix", name: "修复: 🧩 修复缺陷", emoji: "🧩" },
|
||||
{ value: "docs", name: "文档: 📚 文档变更", emoji: "📚" },
|
||||
{ value: "style", name: "格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "🎨" },
|
||||
{ value: "fixbug", name: "bug: 🐛 修改bug", emoji: "🐛" },
|
||||
{ value: "refactor", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)", emoji: "♻️" },
|
||||
{ value: "perf", name: "性能: ⚡️ 性能优化", emoji: "⚡️" },
|
||||
{ value: "test", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: "✅" },
|
||||
{ value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)", emoji: "📦️" },
|
||||
{ value: "ci", name: "集成: 🎡 修改 CI 配置、脚本", emoji: "🎡" },
|
||||
{ value: "revert", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" },
|
||||
{ value: "chore", name: "其他: 🔨 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: "🔨" }
|
||||
],
|
||||
useEmoji: true,
|
||||
themeColorCode: "",
|
||||
scopes: [],
|
||||
allowCustomScopes: true,
|
||||
allowEmptyScopes: true,
|
||||
customScopesAlign: "bottom",
|
||||
customScopesAlias: "custom",
|
||||
emptyScopesAlias: "empty",
|
||||
upperCaseSubject: false,
|
||||
allowBreakingChanges: ["feat", "fix"],
|
||||
breaklineNumber: 100,
|
||||
breaklineChar: "|",
|
||||
skipQuestions: [],
|
||||
issuePrefixs: [{ value: "closed", name: "closed: ISSUES has been processed" }],
|
||||
customIssuePrefixsAlign: "top",
|
||||
emptyIssuePrefixsAlias: "skip",
|
||||
customIssuePrefixsAlias: "custom",
|
||||
allowCustomIssuePrefixs: true,
|
||||
allowEmptyIssuePrefixs: true,
|
||||
confirmColorize: true,
|
||||
maxHeaderLength: Infinity,
|
||||
maxSubjectLength: Infinity,
|
||||
minSubjectLength: 0,
|
||||
scopeOverrides: undefined,
|
||||
defaultBody: "",
|
||||
defaultIssues: "",
|
||||
defaultScope: "",
|
||||
defaultSubject: ""
|
||||
}
|
||||
};
|
||||
|
|
106
index.html
106
index.html
|
@ -1,87 +1,87 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"/>
|
||||
<meta content="webkit" name="renderer"/>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||
name="viewport"
|
||||
/>
|
||||
<title>vue-pure-admin</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title>bunny-admin</title>
|
||||
<link href="/favicon.ico" rel="icon"/>
|
||||
<script>
|
||||
window.process = {};
|
||||
window.process = {};
|
||||
</script>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<style>
|
||||
<body>
|
||||
<div id="app">
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loader,
|
||||
.loader::before,
|
||||
.loader::after {
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
border-radius: 50%;
|
||||
animation: load-animation 1.8s infinite ease-in-out;
|
||||
animation-fill-mode: both;
|
||||
animation: load-animation 1.8s infinite ease-in-out;
|
||||
animation-fill-mode: both;
|
||||
border-radius: 50%;
|
||||
height: 2.5em;
|
||||
width: 2.5em;
|
||||
}
|
||||
|
||||
.loader {
|
||||
position: relative;
|
||||
top: 0;
|
||||
margin: 80px auto;
|
||||
font-size: 10px;
|
||||
color: #406eeb;
|
||||
text-indent: -9999em;
|
||||
transform: translateZ(0);
|
||||
transform: translate(-50%, 0);
|
||||
animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
color: #406eeb;
|
||||
font-size: 10px;
|
||||
margin: 80px auto;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
top: 0;
|
||||
transform: translate(-50%, 0);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.loader::before,
|
||||
.loader::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
content: "";
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.loader::before {
|
||||
left: -3.5em;
|
||||
animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
left: -3.5em;
|
||||
}
|
||||
|
||||
.loader::after {
|
||||
left: 3.5em;
|
||||
left: 3.5em;
|
||||
}
|
||||
|
||||
@keyframes load-animation {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 2.5em 0 -1.3em;
|
||||
}
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 2.5em 0 -1.3em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 2.5em 0 0;
|
||||
}
|
||||
40% {
|
||||
box-shadow: 0 2.5em 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</style>
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
<script src="/src/main.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export default {
|
||||
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
|
||||
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
|
||||
"package.json": ["prettier --write"],
|
||||
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
|
||||
"*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"],
|
||||
"*.md": ["prettier --write"]
|
||||
};
|
|
@ -19,6 +19,7 @@ buttons:
|
|||
pureBackTop: BackTop
|
||||
pureOpenText: Open
|
||||
pureCloseText: Close
|
||||
rest: Rest
|
||||
search:
|
||||
pureTotal: Total
|
||||
pureHistory: History
|
||||
|
@ -180,6 +181,7 @@ menus:
|
|||
pureMindMap: Mind Map
|
||||
pureMenuOverflow: Menu Overflow Show Tooltip Text
|
||||
pureChildMenuOverflow: Child Menu Overflow Show Tooltip Text
|
||||
systemctlTest: Systemctl lTest
|
||||
status:
|
||||
pureLoad: Loading...
|
||||
pureMessage: Message
|
||||
|
@ -229,4 +231,10 @@ login:
|
|||
purePassWordRuleReg: The password format should be any combination of 8-18 digits
|
||||
purePassWordSureReg: Please enter confirm password
|
||||
purePassWordDifferentReg: The two passwords do not match!
|
||||
purePassWordUpdateReg: Password has been updated
|
||||
purePassWordUpdateReg: Password has been updated
|
||||
table:
|
||||
tableNumber: Table Number
|
||||
style:
|
||||
larger: Larger
|
||||
default: Default
|
||||
small: Small
|
||||
|
|
|
@ -19,6 +19,7 @@ buttons:
|
|||
pureBackTop: 回到顶部
|
||||
pureOpenText: 开
|
||||
pureCloseText: 关
|
||||
rest: 重置
|
||||
search:
|
||||
pureTotal: 共
|
||||
pureHistory: 搜索历史
|
||||
|
@ -180,6 +181,7 @@ menus:
|
|||
pureMindMap: 思维导图
|
||||
pureMenuOverflow: 目录超出显示 Tooltip 文字提示
|
||||
pureChildMenuOverflow: 菜单超出显示 Tooltip 文字提示
|
||||
systemctlTest: 系统测试
|
||||
status:
|
||||
pureLoad: 加载中...
|
||||
pureMessage: 消息
|
||||
|
@ -230,3 +232,9 @@ login:
|
|||
purePassWordSureReg: 请输入确认密码
|
||||
purePassWordDifferentReg: 两次密码不一致!
|
||||
purePassWordUpdateReg: 修改密码成功
|
||||
table:
|
||||
tableNumber: 序号
|
||||
style:
|
||||
larger: 宽松
|
||||
default: 默认
|
||||
small: 紧凑
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 模拟后端动态生成路由
|
||||
import { defineFakeRoute } from "vite-plugin-fake-server/client";
|
||||
import { frame, monitor, permission, system, tabs } from "@/router/enums";
|
||||
import { frame, monitor, permission, system } from "@/router/enums";
|
||||
|
||||
/**
|
||||
* roles:页面级别权限,这里模拟二种 "admin"、"common"
|
||||
|
@ -102,6 +102,16 @@ const systemMonitorRouter = {
|
|||
title: "menus.pureSystemLog",
|
||||
roles: ["admin"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/monitor/system-test",
|
||||
component: "monitor/test/index",
|
||||
name: "SystemTest",
|
||||
meta: {
|
||||
icon: "ri:file-search-line",
|
||||
title: "menus.systemctlTest",
|
||||
roles: ["admin"]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -257,66 +267,24 @@ const frameRouter = {
|
|||
]
|
||||
};
|
||||
|
||||
const tabsRouter = {
|
||||
path: "/tabs",
|
||||
const about = {
|
||||
path: "/about",
|
||||
meta: {
|
||||
icon: "ri:bookmark-2-line",
|
||||
title: "menus.pureTabs",
|
||||
rank: tabs
|
||||
title: "menus.pureAbout"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/tabs/index",
|
||||
name: "Tabs",
|
||||
path: "/about/index",
|
||||
name: "About",
|
||||
meta: {
|
||||
title: "menus.pureTabs",
|
||||
roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
// query 传参模式
|
||||
{
|
||||
path: "/tabs/query-detail",
|
||||
name: "TabQueryDetail",
|
||||
meta: {
|
||||
// 不在menu菜单中显示
|
||||
showLink: false,
|
||||
activePath: "/tabs/index",
|
||||
roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
// params 传参模式
|
||||
{
|
||||
path: "/tabs/params-detail/:id",
|
||||
component: "params-detail",
|
||||
name: "TabParamsDetail",
|
||||
meta: {
|
||||
// 不在menu菜单中显示
|
||||
showLink: false,
|
||||
activePath: "/tabs/index",
|
||||
roles: ["admin", "common"]
|
||||
icon: "ri:bookmark-2-line",
|
||||
title: "menus.pureAbout"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const about = {
|
||||
path: "/about",
|
||||
meta: {
|
||||
icon: "ri:bookmark-2-line",
|
||||
title: "menus.pureAbout",
|
||||
rank: tabs
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/about/index",
|
||||
name: "about",
|
||||
meta: {
|
||||
title: "menus.pureAbout",
|
||||
roles: ["admin", "common"]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
url: "/get-async-routes",
|
||||
|
@ -324,7 +292,7 @@ export default defineFakeRoute([
|
|||
response: () => {
|
||||
return {
|
||||
success: true,
|
||||
data: [systemManagementRouter, systemMonitorRouter, permissionRouter, frameRouter, about /*tabsRouter*/]
|
||||
data: [systemManagementRouter, systemMonitorRouter, permissionRouter, frameRouter, about]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ export default defineFakeRoute([
|
|||
return {
|
||||
success: true,
|
||||
data: {
|
||||
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
||||
avatar: "https://pic3.zhimg.com/80/v2-0888fd6c14153bb36fb630230f73a802_720w.webp",
|
||||
username: "admin",
|
||||
nickname: "小铭",
|
||||
// 一个用户可能有多个角色
|
||||
|
@ -24,7 +24,7 @@ export default defineFakeRoute([
|
|||
return {
|
||||
success: true,
|
||||
data: {
|
||||
avatar: "https://avatars.githubusercontent.com/u/52823142",
|
||||
avatar: "https://pic2.zhimg.com/v2-9ee815c09ec2e1f3a007e8d77ec06375_r.jpg",
|
||||
username: "common",
|
||||
nickname: "小林",
|
||||
roles: ["common"],
|
||||
|
|
13
package.json
13
package.json
|
@ -45,7 +45,8 @@
|
|||
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/",
|
||||
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"commit": "git pull && git add -A && git-cz && git push"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
|
@ -135,7 +136,10 @@
|
|||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"boxen": "^7.1.1",
|
||||
"commitizen": "^4.2.4",
|
||||
"commitlint": "^17.0.1",
|
||||
"cssnano": "^7.0.1",
|
||||
"cz-git": "^1.3.2",
|
||||
"dagre": "^0.8.5",
|
||||
"eslint": "^9.2.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
@ -143,7 +147,7 @@
|
|||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.25.0",
|
||||
"gradient-string": "^2.0.2",
|
||||
"husky": "^9.0.11",
|
||||
"husky": "^8.0.1",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-html": "^1.7.0",
|
||||
|
@ -188,5 +192,10 @@
|
|||
"eslint": "9"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1091
pnpm-lock.yaml
1091
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +0,0 @@
|
|||
import vxeTableBar from "./src/bar";
|
||||
import { withInstall } from "@pureadmin/utils";
|
||||
|
||||
/** 配合 `vxe-table` 实现快速便捷的表格操作 */
|
||||
export const VxeTableBar = withInstall(vxeTableBar);
|
|
@ -1,251 +0,0 @@
|
|||
import Sortable from "sortablejs";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||
import { cloneDeep, delay, getKeyList } from "@pureadmin/utils";
|
||||
import { computed, defineComponent, getCurrentInstance, nextTick, type PropType, ref, unref } from "vue";
|
||||
|
||||
import DragIcon from "@/assets/table-bar/drag.svg?component";
|
||||
import ExpandIcon from "@/assets/table-bar/expand.svg?component";
|
||||
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
|
||||
import SettingIcon from "@/assets/table-bar/settings.svg?component";
|
||||
import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
|
||||
|
||||
const props = {
|
||||
/** 头部最左边的标题 */
|
||||
title: {
|
||||
type: String,
|
||||
default: "列表"
|
||||
},
|
||||
vxeTableRef: {
|
||||
type: Object as PropType<any>
|
||||
},
|
||||
/** 需要展示的列 */
|
||||
columns: {
|
||||
type: Array as PropType<any>,
|
||||
default: () => []
|
||||
},
|
||||
/** 是否为树列表 */
|
||||
tree: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isExpandAll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
tableKey: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: "0"
|
||||
}
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "VxeTableBar",
|
||||
props,
|
||||
emits: ["refresh"],
|
||||
setup(props, { emit, slots, attrs }) {
|
||||
const size = ref("small");
|
||||
const loading = ref(false);
|
||||
const checkAll = ref(true);
|
||||
const isIndeterminate = ref(false);
|
||||
const instance = getCurrentInstance()!;
|
||||
const isExpandAll = ref(props.isExpandAll);
|
||||
let checkColumnList = getKeyList(cloneDeep(props?.columns), "title");
|
||||
const checkedColumns = ref(getKeyList(cloneDeep(props?.columns), "title"));
|
||||
const dynamicColumns = ref(cloneDeep(props?.columns));
|
||||
|
||||
const getDropdownItemStyle = computed(() => {
|
||||
return s => {
|
||||
return {
|
||||
background: s === size.value ? useEpThemeStoreHook().epThemeColor : "",
|
||||
color: s === size.value ? "#fff" : "var(--el-text-color-primary)"
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
const iconClass = computed(() => {
|
||||
return ["text-black", "dark:text-white", "duration-100", "hover:!text-primary", "cursor-pointer", "outline-none"];
|
||||
});
|
||||
|
||||
const topClass = computed(() => {
|
||||
return ["flex", "justify-between", "pt-[3px]", "px-[11px]", "border-b-[1px]", "border-solid", "border-[#dcdfe6]", "dark:border-[#303030]"];
|
||||
});
|
||||
|
||||
function onReFresh() {
|
||||
loading.value = true;
|
||||
emit("refresh");
|
||||
delay(500).then(() => (loading.value = false));
|
||||
}
|
||||
|
||||
function onExpand() {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
isExpandAll.value ? props.vxeTableRef.setAllTreeExpand(true) : props.vxeTableRef.clearTreeExpand();
|
||||
props.vxeTableRef.refreshColumn();
|
||||
}
|
||||
|
||||
function reloadColumn() {
|
||||
const curCheckedColumns = cloneDeep(dynamicColumns.value).filter(item => checkedColumns.value.includes(item.title));
|
||||
props.vxeTableRef.reloadColumn(curCheckedColumns);
|
||||
}
|
||||
|
||||
function handleCheckAllChange(val: boolean) {
|
||||
checkedColumns.value = val ? checkColumnList : [];
|
||||
isIndeterminate.value = false;
|
||||
reloadColumn();
|
||||
}
|
||||
|
||||
function handleCheckedColumnsChange(value: string[]) {
|
||||
checkedColumns.value = value;
|
||||
const checkedCount = value.length;
|
||||
checkAll.value = checkedCount === checkColumnList.length;
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < checkColumnList.length;
|
||||
}
|
||||
|
||||
async function onReset() {
|
||||
checkAll.value = true;
|
||||
isIndeterminate.value = false;
|
||||
dynamicColumns.value = cloneDeep(props?.columns);
|
||||
checkColumnList = [];
|
||||
checkColumnList = await getKeyList(cloneDeep(props?.columns), "title");
|
||||
checkedColumns.value = getKeyList(cloneDeep(props?.columns), "title");
|
||||
props.vxeTableRef.refreshColumn();
|
||||
}
|
||||
|
||||
function changeSize(curSize: string) {
|
||||
size.value = curSize;
|
||||
props.vxeTableRef.refreshColumn();
|
||||
}
|
||||
|
||||
const dropdown = {
|
||||
dropdown: () => (
|
||||
<el-dropdown-menu class="translation">
|
||||
<el-dropdown-item style={getDropdownItemStyle.value("medium")} onClick={() => changeSize("medium")}>
|
||||
宽松
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item style={getDropdownItemStyle.value("small")} onClick={() => changeSize("small")}>
|
||||
默认
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item style={getDropdownItemStyle.value("mini")} onClick={() => changeSize("mini")}>
|
||||
紧凑
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
)
|
||||
};
|
||||
|
||||
/** 列展示拖拽排序 */
|
||||
const rowDrop = (event: { preventDefault: () => void }) => {
|
||||
event.preventDefault();
|
||||
nextTick(() => {
|
||||
const wrapper: HTMLElement = (instance?.proxy?.$refs[`VxeGroupRef${unref(props.tableKey)}`] as any).$el.firstElementChild;
|
||||
Sortable.create(wrapper, {
|
||||
animation: 300,
|
||||
handle: ".drag-btn",
|
||||
onEnd: ({ newIndex, oldIndex, item }) => {
|
||||
const targetThElem = item;
|
||||
const wrapperElem = targetThElem.parentNode as HTMLElement;
|
||||
const oldColumn = dynamicColumns.value[oldIndex];
|
||||
const newColumn = dynamicColumns.value[newIndex];
|
||||
if (oldColumn?.fixed || newColumn?.fixed) {
|
||||
// 当前列存在fixed属性 则不可拖拽
|
||||
const oldThElem = wrapperElem.children[oldIndex] as HTMLElement;
|
||||
if (newIndex > oldIndex) {
|
||||
wrapperElem.insertBefore(targetThElem, oldThElem);
|
||||
} else {
|
||||
wrapperElem.insertBefore(targetThElem, oldThElem ? oldThElem.nextElementSibling : oldThElem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0];
|
||||
dynamicColumns.value.splice(newIndex, 0, currentRow);
|
||||
reloadColumn();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const isFixedColumn = (title: string) => {
|
||||
return dynamicColumns.value.filter(item => transformI18n(item.title) === transformI18n(title))[0].fixed ? true : false;
|
||||
};
|
||||
|
||||
const rendTippyProps = (content: string) => {
|
||||
// https://vue-tippy.netlify.app/props
|
||||
return {
|
||||
content,
|
||||
offset: [0, 18],
|
||||
duration: [300, 0],
|
||||
followCursor: true,
|
||||
hideOnClick: "toggle"
|
||||
};
|
||||
};
|
||||
|
||||
const reference = {
|
||||
reference: () => <SettingIcon class={["w-[16px]", iconClass.value]} v-tippy={rendTippyProps("列设置")} />
|
||||
};
|
||||
|
||||
return () => (
|
||||
<>
|
||||
<div {...attrs} class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
|
||||
<div class="flex justify-between w-full h-[60px] p-4">
|
||||
{slots?.title ? slots.title() : <p class="font-bold truncate">{props.title}</p>}
|
||||
<div class="flex items-center justify-around">
|
||||
{slots?.buttons ? <div class="flex mr-4">{slots.buttons()}</div> : null}
|
||||
{props.tree ? (
|
||||
<>
|
||||
<ExpandIcon
|
||||
class={["w-[16px]", iconClass.value]}
|
||||
style={{
|
||||
transform: isExpandAll.value ? "none" : "rotate(-90deg)"
|
||||
}}
|
||||
v-tippy={rendTippyProps(isExpandAll.value ? "折叠" : "展开")}
|
||||
onClick={() => onExpand()}
|
||||
/>
|
||||
<el-divider direction="vertical" />
|
||||
</>
|
||||
) : null}
|
||||
<RefreshIcon class={["w-[16px]", iconClass.value, loading.value ? "animate-spin" : ""]} v-tippy={rendTippyProps("刷新")} onClick={() => onReFresh()} />
|
||||
<el-divider direction="vertical" />
|
||||
<el-dropdown v-slots={dropdown} trigger="click" v-tippy={rendTippyProps("密度")}>
|
||||
<CollapseIcon class={["w-[16px]", iconClass.value]} />
|
||||
</el-dropdown>
|
||||
<el-divider direction="vertical" />
|
||||
|
||||
<el-popover v-slots={reference} placement="bottom-start" popper-style={{ padding: 0 }} width="200" trigger="click">
|
||||
<div class={[topClass.value]}>
|
||||
<el-checkbox class="!-mr-1" label="列展示" v-model={checkAll.value} indeterminate={isIndeterminate.value} onChange={value => handleCheckAllChange(value)} />
|
||||
<el-button type="primary" link onClick={() => onReset()}>
|
||||
重置
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="pt-[6px] pl-[11px]">
|
||||
<el-scrollbar max-height="36vh">
|
||||
<el-checkbox-group ref={`VxeGroupRef${unref(props.tableKey)}`} modelValue={checkedColumns.value} onChange={value => handleCheckedColumnsChange(value)}>
|
||||
<el-space direction="vertical" alignment="flex-start" size={0}>
|
||||
{checkColumnList.map((item, index) => {
|
||||
return (
|
||||
<div class="flex items-center">
|
||||
<DragIcon class={["drag-btn w-[16px] mr-2", isFixedColumn(item) ? "!cursor-no-drop" : "!cursor-grab"]} onMouseenter={(event: { preventDefault: () => void }) => rowDrop(event)} />
|
||||
<el-checkbox key={index} label={item} value={item} onChange={reloadColumn}>
|
||||
<span title={transformI18n(item)} class="inline-block w-[120px] truncate hover:text-text_color_primary">
|
||||
{transformI18n(item)}
|
||||
</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</el-space>
|
||||
</el-checkbox-group>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
{slots.default({
|
||||
size: size.value,
|
||||
dynamicColumns: dynamicColumns.value
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div class="main">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:columns="column"
|
||||
:data="dataList"
|
||||
:header-cell-style="cellHeaderStyle"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
border
|
||||
row-key="id"
|
||||
table-layout="auto"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { cellHeaderStyle } from "@/components/TableBar/utils/tableStyle";
|
||||
import PureTable from "@pureadmin/table";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
// * 传入数据
|
||||
defineProps({
|
||||
// 表格数据
|
||||
dataList: {
|
||||
type: Array<any>,
|
||||
default: []
|
||||
},
|
||||
// 表格列字段
|
||||
column: {
|
||||
type: Array<any>,
|
||||
default: []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<any>,
|
||||
default: "default"
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,303 @@
|
|||
<template>
|
||||
<div class="main">
|
||||
<!-- 表单设置,外加插槽 -->
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<slot name="tableForm" />
|
||||
</el-form>
|
||||
<!-- 表格头部设置 -->
|
||||
<div class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
|
||||
<div class="flex justify-between w-full h-[60px] p-4">
|
||||
<!-- 自定义左边头部内容 -->
|
||||
<slot name="tableTitle">
|
||||
<p class="font-bold truncate">{{ tableTitle ? tableTitle : t(route.meta.title) }}</p>
|
||||
</slot>
|
||||
|
||||
<!-- 自定义表格操作内容 -->
|
||||
<slot name="tableOperation">
|
||||
<div class="flex items-center justify-around">
|
||||
<!-- 插槽内容 -->
|
||||
<div class="mr-4">
|
||||
<slot name="tableButtons" />
|
||||
</div>
|
||||
|
||||
<!-- 表格刷新按钮 -->
|
||||
<RefreshIcon v-tippy="rendTipProps('刷新')" :class="`w-[16px] ${iconClass()}} ${loading ? 'animate-spin' : ''}`" @click="onReFresh" />
|
||||
<el-divider direction="vertical" />
|
||||
|
||||
<!-- 选择表格大小 -->
|
||||
<el-dropdown trigger="click">
|
||||
<CollapseIcon :class="`w-[16px] ${iconClass()}`" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="translation">
|
||||
<el-dropdown-item :style="getDropdownItemStyle(size, 'large')" @click="handleTableSizeClick('large')">
|
||||
{{ $t("style.larger") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :style="getDropdownItemStyle(size, 'default')" @click="handleTableSizeClick('default')"> {{ t("style.default") }} </el-dropdown-item>
|
||||
<el-dropdown-item :style="getDropdownItemStyle(size, 'small')" @click="handleTableSizeClick('small')">
|
||||
{{ t("style.small") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-divider direction="vertical" />
|
||||
|
||||
<!-- 表格列设置 -->
|
||||
<el-popover :popper-style="{ padding: 0 }" placement="bottom-start" trigger="click" width="200">
|
||||
<template #reference>
|
||||
<SettingIcon v-tippy="rendTipProps('列设置')" :class="`w-[16px] ${iconClass()}`" />
|
||||
</template>
|
||||
|
||||
<div :class="topClass()">
|
||||
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" class="!-mr-1" label="列展示" @change="handleCheckAllChange" />
|
||||
<el-button link type="primary" @click="onReset"> {{ t("buttons.rest") }}</el-button>
|
||||
</div>
|
||||
|
||||
<div class="pt-[6px] pl-[11px]">
|
||||
<el-scrollbar max-height="36vh">
|
||||
<el-checkbox-group :ref="`GroupRef${unref(props.tableKey)}`" :modelValue="checkedColumns" @change="handleCheckedColumnsChange">
|
||||
<el-space :alignment="'flex-start'" :size="0" direction="vertical">
|
||||
<div v-for="(item, index) in checkColumnList" :key="index" class="flex items-center">
|
||||
<DragIcon :class="`drag-btn w-[16px] mr-2 ${isFixedColumn(item) ? '!cursor-no-drop' : '!cursor-grab'}`" @mouseenter="rowDrop" />
|
||||
<el-checkbox :key="index" :label="item" :value="item" @change="handleCheckColumnListChange(item)">
|
||||
<span :title="transformI18n(item)" class="inline-block w-[120px] truncate hover:text-text_color_primary"> {{ transformI18n(item) }} </span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</el-space>
|
||||
</el-checkbox-group>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<slot name="tableSelect" />
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:columns="column"
|
||||
:data="dataList"
|
||||
:header-cell-style="cellHeaderStyle"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small'"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
border
|
||||
table-layout="fixed"
|
||||
v-bind="$attrs"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
>
|
||||
<template v-for="(item, index) in column" :key="index" v-slot:[item.slot]="slotProps">
|
||||
<slot :name="item.slot" v-bind="slotProps" />
|
||||
</template>
|
||||
</pure-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getCurrentInstance, nextTick, onMounted, PropType, ref, unref, watch } from "vue";
|
||||
import { rendTipProps } from "@/components/TableBar/utils/tableConfig";
|
||||
import { cellHeaderStyle, getDropdownItemStyle, iconClass, topClass } from "@/components/TableBar/utils/tableStyle";
|
||||
import PureTable from "@pureadmin/table";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
|
||||
import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
|
||||
import SettingIcon from "@/assets/table-bar/settings.svg?component";
|
||||
import { cloneDeep, getKeyList, isBoolean, isFunction } from "@pureadmin/utils";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import DragIcon from "@/assets/table-bar/drag.svg?component";
|
||||
import Sortable from "sortablejs";
|
||||
|
||||
// * 传入数据
|
||||
const props = defineProps({
|
||||
// 表格数据
|
||||
dataList: { type: Array<any>, default: [] },
|
||||
// 表格列字段
|
||||
column: { type: Array<any>, default: [] },
|
||||
// 是否加载
|
||||
loading: { type: Boolean, default: false },
|
||||
// 页面字体大小,small | default | large
|
||||
size: { type: String as PropType<any>, default: "default" },
|
||||
// 分页器参数
|
||||
pagination: { type: Object, default: Object },
|
||||
// 一页大小变化
|
||||
handleSelectionChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 分页大小变化
|
||||
handleSizeChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 当前页变化
|
||||
handleCurrentChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
type: Object as PropType<any>,
|
||||
default: Object
|
||||
},
|
||||
// 刷新时
|
||||
onReFresh: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 表格的key值
|
||||
tableKey: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: "0"
|
||||
},
|
||||
// 表格标题
|
||||
tableTitle: { type: String, default: "" }
|
||||
});
|
||||
|
||||
const emit = defineEmits(["changeColumn"]);
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
// 是否全选
|
||||
const checkAll = ref(true);
|
||||
// 表格样式大小
|
||||
const size = ref(props.size);
|
||||
const isIndeterminate = ref(false);
|
||||
// 动态行
|
||||
const dynamicColumns = ref(props.column);
|
||||
// 过滤是否选中的列
|
||||
const filterColumns = cloneDeep(props.column).filter(column => (isBoolean(column?.hide) ? !column.hide : !(isFunction(column?.hide) && column?.hide())));
|
||||
// 选择当前列
|
||||
const checkedColumns = ref(getKeyList(cloneDeep(filterColumns), "label"));
|
||||
const checkColumnList = ref(getKeyList(cloneDeep(dynamicColumns.value), "label"));
|
||||
const instance = getCurrentInstance()!;
|
||||
|
||||
/**
|
||||
* * 修改表格样式大小
|
||||
* @param value 修改样式大小 larger | default | small
|
||||
*/
|
||||
const handleTableSizeClick = (value: string) => {
|
||||
size.value = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置表格列是否全部显示
|
||||
* @param val 是否全部显示
|
||||
*/
|
||||
const handleCheckAllChange = (val: boolean) => {
|
||||
checkedColumns.value = val ? checkColumnList.value : [];
|
||||
isIndeterminate.value = false;
|
||||
dynamicColumns.value.map(column => (val ? (column.hide = false) : (column.hide = true)));
|
||||
};
|
||||
|
||||
/**
|
||||
* * 选中的表格列---是否显示
|
||||
* @param value
|
||||
*/
|
||||
const handleCheckedColumnsChange = (value: string[]) => {
|
||||
checkedColumns.value = value;
|
||||
const checkedCount = value.length;
|
||||
checkAll.value = checkedCount === checkColumnList.value.length;
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < checkColumnList.value.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* * 点击是否显示列
|
||||
* @param label
|
||||
*/
|
||||
const handleCheckColumnListChange = (label: string) => {
|
||||
dynamicColumns.value.filter(item => transformI18n(item.label) === transformI18n(label))[0].hide = !(event.target as any).checked;
|
||||
};
|
||||
|
||||
/**
|
||||
* * 是否为固定列
|
||||
* @param label
|
||||
*/
|
||||
const isFixedColumn = (label: string) => {
|
||||
return dynamicColumns.value.filter(item => transformI18n(item.label) === transformI18n(label))[0].fixed;
|
||||
};
|
||||
|
||||
/**
|
||||
* * 重置自定义表格列字段
|
||||
*/
|
||||
const onReset = async () => {
|
||||
// 重置列表值
|
||||
const list = [];
|
||||
// 全选按钮设为true
|
||||
checkAll.value = true;
|
||||
isIndeterminate.value = false;
|
||||
// 当前选中的列
|
||||
checkedColumns.value = getKeyList(cloneDeep(filterColumns), "label");
|
||||
// ? 重新赋值,拖拽排序不会改变原有值,响应式数据的特性 直接对 ref 或 reactive 创建的变量进行赋值可能不会触发视图的更新
|
||||
// ? Proxy 来追踪属性的访问和修改,使用异步方式确保正确更新视图
|
||||
checkColumnList.value = [];
|
||||
await nextTick(() => {
|
||||
checkColumnList.value = getKeyList(filterColumns, "label");
|
||||
});
|
||||
|
||||
// checkedColumns 是原有顺序,根据这个顺序重新得到list
|
||||
checkedColumns.value.forEach(item => {
|
||||
dynamicColumns.value.forEach(column => {
|
||||
if (column.label == item) {
|
||||
list.push(column);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
emit("changeColumn", list);
|
||||
};
|
||||
|
||||
/** 列展示拖拽排序 */
|
||||
const rowDrop = (event: any) => {
|
||||
event.preventDefault();
|
||||
nextTick(() => {
|
||||
const wrapper: HTMLElement = (instance?.proxy?.$refs[`GroupRef${unref(props.tableKey)}`] as any).$el.firstElementChild;
|
||||
Sortable.create(wrapper, {
|
||||
animation: 300,
|
||||
handle: ".drag-btn",
|
||||
onEnd: ({ newIndex, oldIndex, item }) => {
|
||||
const targetThElem = item;
|
||||
const wrapperElem = targetThElem.parentNode as HTMLElement;
|
||||
const oldColumn = dynamicColumns.value[oldIndex];
|
||||
const newColumn = dynamicColumns.value[newIndex];
|
||||
if (oldColumn?.fixed || newColumn?.fixed) {
|
||||
// 当前列存在fixed属性 则不可拖拽
|
||||
const oldThElem = wrapperElem.children[oldIndex] as HTMLElement;
|
||||
if (newIndex > oldIndex) {
|
||||
wrapperElem.insertBefore(targetThElem, oldThElem);
|
||||
} else {
|
||||
wrapperElem.insertBefore(targetThElem, oldThElem ? oldThElem.nextElementSibling : oldThElem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0];
|
||||
dynamicColumns.value.splice(newIndex, 0, currentRow);
|
||||
emit("changeColumn", dynamicColumns.value);
|
||||
}
|
||||
});
|
||||
}).then();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
watch([() => props.column], () => {
|
||||
dynamicColumns.value = props.column;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div class="main mt-2 p-2 bg-bg_color">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:columns="column"
|
||||
:data="dataList"
|
||||
:header-cell-style="cellHeaderStyle"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small'"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
border
|
||||
row-key="id"
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { cellHeaderStyle } from "@/components/TableBar/utils/tableStyle";
|
||||
import PureTable from "@pureadmin/table";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
// * 传入数据
|
||||
defineProps({
|
||||
// 表格数据
|
||||
dataList: {
|
||||
type: Array<any>,
|
||||
default: []
|
||||
},
|
||||
// 表格列字段
|
||||
column: {
|
||||
type: Array<any>,
|
||||
default: []
|
||||
},
|
||||
// 是否加载
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 页面字体大小,small | default | large
|
||||
size: {
|
||||
type: String as PropType<any>,
|
||||
default: "default"
|
||||
},
|
||||
// 分页器参数
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: Object
|
||||
},
|
||||
// 一页大小变化
|
||||
handleSelectionChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 分页大小变化
|
||||
handleSizeChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 当前页变化
|
||||
handleCurrentChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<div class="main">
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<slot name="tableForm" />
|
||||
</el-form>
|
||||
|
||||
<div class="mt-2 p-2 bg-bg_color">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:columns="column"
|
||||
:data="dataList"
|
||||
:header-cell-style="cellHeaderStyle"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small'"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
border
|
||||
row-key="id"
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import { cellHeaderStyle } from "@/components/TableBar/utils/tableStyle";
|
||||
import PureTable from "@pureadmin/table";
|
||||
|
||||
// * 传入数据
|
||||
defineProps({
|
||||
// 表格数据
|
||||
dataList: {
|
||||
type: Array<any>,
|
||||
default: []
|
||||
},
|
||||
// 表格列字段
|
||||
column: {
|
||||
type: Array<any>,
|
||||
default: []
|
||||
},
|
||||
// 是否加载
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 页面字体大小,small | default | large
|
||||
size: {
|
||||
type: String as PropType<any>,
|
||||
default: "default"
|
||||
},
|
||||
// 分页器参数
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: Object
|
||||
},
|
||||
// 一页大小变化
|
||||
handleSelectionChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 分页大小变化
|
||||
handleSizeChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 当前页变化
|
||||
handleCurrentChange: {
|
||||
type: Function as PropType<Function>,
|
||||
default: () => {}
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
type: Object as PropType<any>,
|
||||
default: Object
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin: 24px 24px 0 !important;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -3,7 +3,6 @@ import { transformI18n } from "@/plugins/i18n";
|
|||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||
import { computed, defineComponent, getCurrentInstance, nextTick, type PropType, ref, unref } from "vue";
|
||||
import { cloneDeep, delay, getKeyList, isBoolean, isFunction } from "@pureadmin/utils";
|
||||
|
||||
import DragIcon from "@/assets/table-bar/drag.svg?component";
|
||||
import ExpandIcon from "@/assets/table-bar/expand.svg?component";
|
||||
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
|
||||
|
@ -32,6 +31,34 @@ const props = {
|
|||
tableKey: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: "0"
|
||||
},
|
||||
dataList: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<any>,
|
||||
default: "default"
|
||||
},
|
||||
handleSelectionChange: {
|
||||
type: String as PropType<any>,
|
||||
default: () => {}
|
||||
},
|
||||
handleSizeChange: {
|
||||
type: String as PropType<any>,
|
||||
default: () => {}
|
||||
},
|
||||
handleCurrentChange: {
|
||||
type: String as PropType<any>,
|
||||
default: () => {}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -52,7 +79,7 @@ export default defineComponent({
|
|||
const dynamicColumns = ref(cloneDeep(props?.columns));
|
||||
|
||||
const getDropdownItemStyle = computed(() => {
|
||||
return s => {
|
||||
return (s: string) => {
|
||||
return {
|
||||
background: s === size.value ? useEpThemeStoreHook().epThemeColor : "",
|
||||
color: s === size.value ? "#fff" : "var(--el-text-color-primary)"
|
||||
|
@ -110,7 +137,7 @@ export default defineComponent({
|
|||
isIndeterminate.value = false;
|
||||
dynamicColumns.value = cloneDeep(props?.columns);
|
||||
checkColumnList = [];
|
||||
checkColumnList = await getKeyList(cloneDeep(props?.columns), "label");
|
||||
checkColumnList = getKeyList(cloneDeep(props?.columns), "label");
|
||||
checkedColumns.value = getKeyList(cloneDeep(filterColumns), "label");
|
||||
}
|
||||
|
||||
|
@ -157,11 +184,11 @@ export default defineComponent({
|
|||
dynamicColumns.value.splice(newIndex, 0, currentRow);
|
||||
}
|
||||
});
|
||||
});
|
||||
}).then();
|
||||
};
|
||||
|
||||
const isFixedColumn = (label: string) => {
|
||||
return dynamicColumns.value.filter(item => transformI18n(item.label) === transformI18n(label))[0].fixed ? true : false;
|
||||
return dynamicColumns.value.filter(item => transformI18n(item.label) === transformI18n(label))[0].fixed;
|
||||
};
|
||||
|
||||
const rendTippyProps = (content: string) => {
|
||||
|
@ -181,7 +208,7 @@ export default defineComponent({
|
|||
|
||||
return () => (
|
||||
<>
|
||||
<div {...attrs} class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
|
||||
<div {...attrs} size={props.size} class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
|
||||
<div class="flex justify-between w-full h-[60px] p-4">
|
||||
{slots?.title ? slots.title() : <p class="font-bold truncate">{props.title}</p>}
|
||||
<div class="flex items-center justify-around">
|
||||
|
@ -217,11 +244,14 @@ export default defineComponent({
|
|||
<div class="pt-[6px] pl-[11px]">
|
||||
<el-scrollbar max-height="36vh">
|
||||
<el-checkbox-group ref={`GroupRef${unref(props.tableKey)}`} modelValue={checkedColumns.value} onChange={value => handleCheckedColumnsChange(value)}>
|
||||
<el-space direction="vertical" alignment="flex-start" size={0}>
|
||||
<el-space direction="vertical" alignment={"flex-start"} size={0}>
|
||||
{checkColumnList.map((item, index) => {
|
||||
return (
|
||||
<div class="flex items-center">
|
||||
<DragIcon class={["drag-btn w-[16px] mr-2", isFixedColumn(item) ? "!cursor-no-drop" : "!cursor-grab"]} onMouseenter={(event: { preventDefault: () => void }) => rowDrop(event)} />
|
||||
<DragIcon
|
||||
class={["drag-btn w-[16px] mr-2", isFixedColumn(item) ? "!cursor-no-drop" : "!cursor-grab"]}
|
||||
onMouseenter={(event: { preventDefault: () => void }) => rowDrop(event)}
|
||||
/>
|
||||
<el-checkbox key={index} label={item} value={item} onChange={value => handleCheckColumnListChange(value, item)}>
|
||||
<span title={transformI18n(item)} class="inline-block w-[120px] truncate hover:text-text_color_primary">
|
||||
{transformI18n(item)}
|
|
@ -0,0 +1,7 @@
|
|||
export const rendTipProps = (content: string) => ({
|
||||
content,
|
||||
offset: [0, 18],
|
||||
duration: [300, 0],
|
||||
followCursor: true,
|
||||
hideOnClick: "toggle"
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
import { computed } from "vue";
|
||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||
|
||||
/**
|
||||
* * 表格头部样式
|
||||
*/
|
||||
export const cellHeaderStyle = () => ({
|
||||
background: "var(--el-fill-color-light)",
|
||||
color: "var(--el-text-color-primary)"
|
||||
});
|
||||
|
||||
// * icon 样式
|
||||
export const iconClass = () => "text-black dark:text-white duration-100 hover:!text-primary cursor-pointer outline-none";
|
||||
|
||||
// * 顶部样式
|
||||
export const topClass = () => "flex justify-between pt-[3px] px-[11px] border-b-[1px] border-solid border-[#dcdfe6] dark:border-[#303030]";
|
||||
|
||||
/**
|
||||
* * 拖拽列样式
|
||||
*/
|
||||
export const getDropdownItemStyle = computed(() => {
|
||||
return (size: string, s: string) => {
|
||||
return {
|
||||
background: s === size ? useEpThemeStoreHook().epThemeColor : "",
|
||||
color: s === size ? "#fff" : "var(--el-text-color-primary)"
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import dayjs from "dayjs";
|
||||
import { message } from "@/utils/message";
|
||||
import { getOnlineLogsList } from "@/api/system";
|
||||
import { reactive, ref, onMounted, toRaw } from "vue";
|
||||
import { onMounted, reactive, ref, toRaw } from "vue";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
|
||||
export function useRole() {
|
||||
|
@ -51,8 +51,7 @@ export function useRole() {
|
|||
label: "登录时间",
|
||||
prop: "loginTime",
|
||||
minWidth: 180,
|
||||
formatter: ({ loginTime }) =>
|
||||
dayjs(loginTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
formatter: ({ loginTime }) => dayjs(loginTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
},
|
||||
{
|
||||
label: "操作",
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref, computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { noticesData } from "./data";
|
||||
import NoticeList from "./components/NoticeList.vue";
|
||||
import BellIcon from "@iconify-icons/ep/bell";
|
||||
|
@ -12,23 +12,13 @@ const activeKey = ref(noticesData[0]?.key);
|
|||
|
||||
notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
|
||||
const getLabel = computed(
|
||||
() => item =>
|
||||
t(item.name) + (item.list.length > 0 ? `(${item.list.length})` : "")
|
||||
);
|
||||
const getLabel = computed(() => item => t(item.name) + (item.list.length > 0 ? `(${item.list.length})` : ""));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dropdown trigger="click" placement="bottom-end">
|
||||
<span
|
||||
:class="[
|
||||
'dropdown-badge',
|
||||
'navbar-bg-hover',
|
||||
'select-none',
|
||||
Number(noticesNum) !== 0 && 'mr-[10px]'
|
||||
]"
|
||||
>
|
||||
<el-badge :value="Number(noticesNum) === 0 ? '' : noticesNum" :max="99">
|
||||
<el-dropdown placement="bottom-end" trigger="click">
|
||||
<span :class="['dropdown-badge', 'navbar-bg-hover', 'select-none', Number(noticesNum) !== 0 && 'mr-[10px]']">
|
||||
<el-badge :max="99" :value="Number(noticesNum) === 0 ? '' : noticesNum">
|
||||
<span class="header-notice-icon">
|
||||
<IconifyIconOffline :icon="BellIcon" />
|
||||
</span>
|
||||
|
@ -36,23 +26,14 @@ const getLabel = computed(
|
|||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-tabs
|
||||
v-model="activeKey"
|
||||
:stretch="true"
|
||||
class="dropdown-tabs"
|
||||
:style="{ width: notices.length === 0 ? '200px' : '330px' }"
|
||||
>
|
||||
<el-empty
|
||||
v-if="notices.length === 0"
|
||||
:description="t('status.pureNoMessage')"
|
||||
:image-size="60"
|
||||
/>
|
||||
<el-tabs v-model="activeKey" :stretch="true" :style="{ width: notices.length === 0 ? '200px' : '330px' }" class="dropdown-tabs">
|
||||
<el-empty v-if="notices.length === 0" :description="t('status.pureNoMessage')" :image-size="60" />
|
||||
<span v-else>
|
||||
<template v-for="item in notices" :key="item.key">
|
||||
<el-tab-pane :label="getLabel(item)" :name="`${item.key}`">
|
||||
<el-scrollbar max-height="330px">
|
||||
<div class="noticeList-container">
|
||||
<NoticeList :list="item.list" :emptyText="item.emptyText" />
|
||||
<NoticeList :emptyText="item.emptyText" :list="item.list" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
|
|
|
@ -13,7 +13,6 @@ import { injectResponsiveStorage } from "@/utils/responsive";
|
|||
import { FontIcon, IconifyIconOffline, IconifyIconOnline } from "./components/ReIcon";
|
||||
import Table from "@pureadmin/table";
|
||||
import PureDescriptions from "@pureadmin/descriptions";
|
||||
|
||||
// 引入重置样式
|
||||
import "./style/reset.scss";
|
||||
// 导入公共样式
|
||||
|
@ -42,7 +41,6 @@ Object.keys(directives).forEach(key => {
|
|||
app.component("IconifyIconOffline", IconifyIconOffline);
|
||||
app.component("IconifyIconOnline", IconifyIconOnline);
|
||||
app.component("FontIcon", FontIcon);
|
||||
|
||||
app.component("Auth", Auth);
|
||||
|
||||
app.use(VueTippy);
|
||||
|
|
|
@ -18,7 +18,7 @@ const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以
|
|||
tabs = 15,
|
||||
about = 16,
|
||||
editor = 17,
|
||||
flowchart = 18,
|
||||
bills = 18,
|
||||
formdesign = 19,
|
||||
board = 20,
|
||||
ppt = 21,
|
||||
|
@ -26,4 +26,4 @@ const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以
|
|||
guide = 23,
|
||||
menuoverflow = 24;
|
||||
|
||||
export { home, vueflow, ganttastic, components, able, table, form, list, result, error, frame, nested, permission, system, monitor, tabs, about, editor, flowchart, formdesign, board, ppt, mind, guide, menuoverflow };
|
||||
export { home, vueflow, ganttastic, components, able, table, form, list, result, error, frame, nested, permission, system, monitor, tabs, about, editor, bills, formdesign, board, ppt, mind, guide, menuoverflow };
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { $t } from "@/plugins/i18n";
|
||||
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
|
||||
export default [
|
||||
|
@ -32,7 +33,7 @@ export default [
|
|||
{
|
||||
path: "/empty",
|
||||
name: "Empty",
|
||||
component: () => import("@/views/empty/index.vue"),
|
||||
component: () => import("@/components/Empty/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureEmpty"),
|
||||
showLink: false,
|
||||
|
|
|
@ -155,19 +155,19 @@ watch(loginDay, value => {
|
|||
]"
|
||||
prop="username"
|
||||
>
|
||||
<el-input v-model="ruleForm.username" :placeholder="t('login.pureUsername')" :prefix-icon="useRenderIcon(User)" clearable />
|
||||
<el-input v-model="ruleForm.username" :placeholder="t('login.pureUsername')" :prefix-icon="useRenderIcon(User)" clearable @keydown.enter="onLogin(ruleFormRef)" />
|
||||
</el-form-item>
|
||||
</Motion>
|
||||
|
||||
<Motion :delay="150">
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="ruleForm.password" :placeholder="t('login.purePassword')" :prefix-icon="useRenderIcon(Lock)" clearable show-password />
|
||||
<el-input v-model="ruleForm.password" :placeholder="t('login.purePassword')" :prefix-icon="useRenderIcon(Lock)" clearable show-password @keydown.enter="onLogin(ruleFormRef)" />
|
||||
</el-form-item>
|
||||
</Motion>
|
||||
|
||||
<Motion :delay="200">
|
||||
<el-form-item prop="verifyCode">
|
||||
<el-input v-model="ruleForm.verifyCode" :placeholder="t('login.pureVerifyCode')" :prefix-icon="useRenderIcon('ri:shield-keyhole-line')" clearable>
|
||||
<el-input v-model="ruleForm.verifyCode" :placeholder="t('login.pureVerifyCode')" :prefix-icon="useRenderIcon('ri:shield-keyhole-line')" clearable @keydown.enter="onLogin(ruleFormRef)">
|
||||
<template v-slot:append>
|
||||
<ReImageVerify v-model:code="imgCode" />
|
||||
</template>
|
||||
|
|
|
@ -8,11 +8,10 @@ import { useUserStoreHook } from "@/store/modules/user";
|
|||
export const REGEXP_SIX = /^\d{6}$/;
|
||||
|
||||
/** 密码正则(密码格式应为8-18位数字、字母、符号的任意两种组合) */
|
||||
export const REGEXP_PWD =
|
||||
/^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
|
||||
export const REGEXP_PWD = /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
|
||||
|
||||
/** 登录校验 */
|
||||
const loginRules = reactive<FormRules>({
|
||||
export const loginRules = reactive<FormRules>({
|
||||
password: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
|
@ -33,9 +32,7 @@ const loginRules = reactive<FormRules>({
|
|||
if (value === "") {
|
||||
callback(new Error(transformI18n($t("login.pureVerifyCodeReg"))));
|
||||
} else if (useUserStoreHook().verifyCode !== value) {
|
||||
callback(
|
||||
new Error(transformI18n($t("login.pureVerifyCodeCorrectReg")))
|
||||
);
|
||||
callback(new Error(transformI18n($t("login.pureVerifyCodeCorrectReg"))));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
@ -46,7 +43,7 @@ const loginRules = reactive<FormRules>({
|
|||
});
|
||||
|
||||
/** 手机登录校验 */
|
||||
const phoneRules = reactive<FormRules>({
|
||||
export const phoneRules = reactive<FormRules>({
|
||||
phone: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
|
@ -78,7 +75,7 @@ const phoneRules = reactive<FormRules>({
|
|||
});
|
||||
|
||||
/** 忘记密码校验 */
|
||||
const updateRules = reactive<FormRules>({
|
||||
export const updateRules = reactive<FormRules>({
|
||||
phone: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
|
@ -122,5 +119,3 @@ const updateRules = reactive<FormRules>({
|
|||
}
|
||||
]
|
||||
});
|
||||
|
||||
export { loginRules, phoneRules, updateRules };
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
import dayjs from "dayjs";
|
||||
import { message } from "@/utils/message";
|
||||
import { getKeyList } from "@pureadmin/utils";
|
||||
import { getLoginLogsList } from "@/api/system";
|
||||
import { usePublicHooks } from "@/hooks/system/usePublicHooks";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
import { onMounted, reactive, ref, type Ref, toRaw } from "vue";
|
||||
|
||||
export function useRole(tableRef: Ref) {
|
||||
const form = reactive({
|
||||
username: "",
|
||||
status: "",
|
||||
loginTime: ""
|
||||
});
|
||||
const dataList = ref([]);
|
||||
const loading = ref(true);
|
||||
const selectedNum = ref(0);
|
||||
const { tagStyle } = usePublicHooks();
|
||||
|
||||
const pagination = reactive<PaginationProps>({
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
background: true
|
||||
});
|
||||
const columns: TableColumnList = [
|
||||
{
|
||||
label: "勾选列", // 如果需要表格多选,此处label必须设置
|
||||
type: "selection",
|
||||
fixed: "left",
|
||||
reserveSelection: true // 数据刷新后保留选项
|
||||
},
|
||||
{
|
||||
label: "序号",
|
||||
prop: "id",
|
||||
minWidth: 90
|
||||
},
|
||||
{
|
||||
label: "用户名",
|
||||
prop: "username",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "登录 IP",
|
||||
prop: "ip",
|
||||
minWidth: 140
|
||||
},
|
||||
{
|
||||
label: "登录地点",
|
||||
prop: "address",
|
||||
minWidth: 140
|
||||
},
|
||||
{
|
||||
label: "操作系统",
|
||||
prop: "system",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "浏览器类型",
|
||||
prop: "browser",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "登录状态",
|
||||
prop: "status",
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag size={props.size} style={tagStyle.value(row.status)}>
|
||||
{row.status === 1 ? "成功" : "失败"}
|
||||
</el-tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: "登录行为",
|
||||
prop: "behavior",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "登录时间",
|
||||
prop: "loginTime",
|
||||
minWidth: 180,
|
||||
formatter: ({ loginTime }) => dayjs(loginTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
];
|
||||
|
||||
function handleSizeChange(val: number) {
|
||||
console.log(`${val} items per page`);
|
||||
}
|
||||
|
||||
function handleCurrentChange(val: number) {
|
||||
console.log(`current page: ${val}`);
|
||||
}
|
||||
|
||||
/** 当CheckBox选择项发生变化时会触发该事件 */
|
||||
function handleSelectionChange(val) {
|
||||
selectedNum.value = val.length;
|
||||
// 重置表格高度
|
||||
tableRef.value.setAdaptive();
|
||||
}
|
||||
|
||||
/** 取消选择 */
|
||||
function onSelectionCancel() {
|
||||
selectedNum.value = 0;
|
||||
// 用于多选表格,清空用户的选择
|
||||
tableRef.value.getTableRef().clearSelection();
|
||||
}
|
||||
|
||||
/** 批量删除 */
|
||||
function onbatchDel() {
|
||||
// 返回当前选中的行
|
||||
const curSelected = tableRef.value.getTableRef().getSelectionRows();
|
||||
// 接下来根据实际业务,通过选中行的某项数据,比如下面的id,调用接口进行批量删除
|
||||
message(`已删除序号为 ${getKeyList(curSelected, "id")} 的数据`, {
|
||||
type: "success"
|
||||
});
|
||||
tableRef.value.getTableRef().clearSelection();
|
||||
onSearch();
|
||||
}
|
||||
|
||||
/** 清空日志 */
|
||||
function clearAll() {
|
||||
// 根据实际业务,调用接口删除所有日志数据
|
||||
message("已删除所有日志数据", {
|
||||
type: "success"
|
||||
});
|
||||
onSearch();
|
||||
}
|
||||
|
||||
async function onSearch() {
|
||||
loading.value = true;
|
||||
const { data } = await getLoginLogsList(toRaw(form));
|
||||
dataList.value = data.list;
|
||||
pagination.total = data.total;
|
||||
pagination.pageSize = data.pageSize;
|
||||
pagination.currentPage = data.currentPage;
|
||||
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
const resetForm = formEl => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
onSearch();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
|
||||
return {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
selectedNum,
|
||||
onSearch,
|
||||
clearAll,
|
||||
resetForm,
|
||||
onbatchDel,
|
||||
handleSizeChange,
|
||||
onSelectionCancel,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange
|
||||
};
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useRole } from "./hook";
|
||||
import { useRole } from "@/hooks/monitor/useRole";
|
||||
import { getPickerShortcuts } from "../../utils";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import PureTable from "@pureadmin/table";
|
||||
|
||||
defineOptions({
|
||||
name: "LoginLog"
|
||||
|
@ -15,130 +15,68 @@ defineOptions({
|
|||
const formRef = ref();
|
||||
const tableRef = ref();
|
||||
|
||||
const {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
selectedNum,
|
||||
onSearch,
|
||||
clearAll,
|
||||
resetForm,
|
||||
onbatchDel,
|
||||
handleSizeChange,
|
||||
onSelectionCancel,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange
|
||||
} = useRole(tableRef);
|
||||
const { form, loading, columns, dataList, pagination, selectedNum, onSearch, clearAll, resetForm, onbatchDel, handleSizeChange, onSelectionCancel, handleCurrentChange, handleSelectionChange } =
|
||||
useRole(tableRef);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input
|
||||
v-model="form.username"
|
||||
placeholder="请输入用户名"
|
||||
clearable
|
||||
class="!w-[150px]"
|
||||
/>
|
||||
<el-input v-model="form.username" class="!w-[150px]" clearable placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录状态" prop="status">
|
||||
<el-select
|
||||
v-model="form.status"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-[150px]"
|
||||
>
|
||||
<el-select v-model="form.status" class="!w-[150px]" clearable placeholder="请选择">
|
||||
<el-option label="成功" value="1" />
|
||||
<el-option label="失败" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="登录时间" prop="loginTime">
|
||||
<el-date-picker
|
||||
v-model="form.loginTime"
|
||||
:shortcuts="getPickerShortcuts()"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期时间"
|
||||
end-placeholder="结束日期时间"
|
||||
/>
|
||||
<el-date-picker v-model="form.loginTime" :shortcuts="getPickerShortcuts()" end-placeholder="结束日期时间" range-separator="至" start-placeholder="开始日期时间" type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('ri:search-line')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="登录日志(仅演示,操作后不生效)"
|
||||
:columns="columns"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<PureTableBar :columns="columns" title="登录日志(仅演示,操作后不生效)" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll">
|
||||
<template #reference>
|
||||
<el-button type="danger" :icon="useRenderIcon(Delete)">
|
||||
清空日志
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Delete)" type="danger"> 清空日志</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<div
|
||||
v-if="selectedNum > 0"
|
||||
v-motion-fade
|
||||
class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center"
|
||||
>
|
||||
<div v-motion-fade class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center">
|
||||
<div class="flex-auto">
|
||||
<span
|
||||
style="font-size: var(--el-font-size-base)"
|
||||
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
|
||||
>
|
||||
已选 {{ selectedNum }} 项
|
||||
</span>
|
||||
<el-button type="primary" text @click="onSelectionCancel">
|
||||
取消选择
|
||||
</el-button>
|
||||
<span class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]" style="font-size: var(--el-font-size-base)"> 已选 {{ selectedNum }} 项 </span>
|
||||
<el-button text type="primary" @click="onSelectionCancel"> 取消选择</el-button>
|
||||
</div>
|
||||
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
|
||||
<template #reference>
|
||||
<el-button type="danger" text class="mr-1"> 批量删除 </el-button>
|
||||
<el-button class="mr-1" text type="danger"> 批量删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
align-whole="center"
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:data="dataList"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small'"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
|
@ -148,7 +86,7 @@ const {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
import dayjs from "dayjs";
|
||||
import { message } from "@/utils/message";
|
||||
import { getKeyList } from "@pureadmin/utils";
|
||||
import { getOperationLogsList } from "@/api/system";
|
||||
import { usePublicHooks } from "@/views/system/hooks";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
import { type Ref, reactive, ref, onMounted, toRaw } from "vue";
|
||||
|
||||
export function useRole(tableRef: Ref) {
|
||||
const form = reactive({
|
||||
module: "",
|
||||
status: "",
|
||||
operatingTime: ""
|
||||
});
|
||||
const dataList = ref([]);
|
||||
const loading = ref(true);
|
||||
const selectedNum = ref(0);
|
||||
const { tagStyle } = usePublicHooks();
|
||||
|
||||
const pagination = reactive<PaginationProps>({
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
background: true
|
||||
});
|
||||
const columns: TableColumnList = [
|
||||
{
|
||||
label: "勾选列", // 如果需要表格多选,此处label必须设置
|
||||
type: "selection",
|
||||
fixed: "left",
|
||||
reserveSelection: true // 数据刷新后保留选项
|
||||
},
|
||||
{
|
||||
label: "序号",
|
||||
prop: "id",
|
||||
minWidth: 90
|
||||
},
|
||||
{
|
||||
label: "操作人员",
|
||||
prop: "username",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "所属模块",
|
||||
prop: "module",
|
||||
minWidth: 140
|
||||
},
|
||||
{
|
||||
label: "操作概要",
|
||||
prop: "summary",
|
||||
minWidth: 140
|
||||
},
|
||||
{
|
||||
label: "操作 IP",
|
||||
prop: "ip",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "操作地点",
|
||||
prop: "address",
|
||||
minWidth: 140
|
||||
},
|
||||
{
|
||||
label: "操作系统",
|
||||
prop: "system",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "浏览器类型",
|
||||
prop: "browser",
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
label: "操作状态",
|
||||
prop: "status",
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag size={props.size} style={tagStyle.value(row.status)}>
|
||||
{row.status === 1 ? "成功" : "失败"}
|
||||
</el-tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: "操作时间",
|
||||
prop: "operatingTime",
|
||||
minWidth: 180,
|
||||
formatter: ({ operatingTime }) =>
|
||||
dayjs(operatingTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
];
|
||||
|
||||
function handleSizeChange(val: number) {
|
||||
console.log(`${val} items per page`);
|
||||
}
|
||||
|
||||
function handleCurrentChange(val: number) {
|
||||
console.log(`current page: ${val}`);
|
||||
}
|
||||
|
||||
/** 当CheckBox选择项发生变化时会触发该事件 */
|
||||
function handleSelectionChange(val) {
|
||||
selectedNum.value = val.length;
|
||||
// 重置表格高度
|
||||
tableRef.value.setAdaptive();
|
||||
}
|
||||
|
||||
/** 取消选择 */
|
||||
function onSelectionCancel() {
|
||||
selectedNum.value = 0;
|
||||
// 用于多选表格,清空用户的选择
|
||||
tableRef.value.getTableRef().clearSelection();
|
||||
}
|
||||
|
||||
/** 批量删除 */
|
||||
function onbatchDel() {
|
||||
// 返回当前选中的行
|
||||
const curSelected = tableRef.value.getTableRef().getSelectionRows();
|
||||
// 接下来根据实际业务,通过选中行的某项数据,比如下面的id,调用接口进行批量删除
|
||||
message(`已删除序号为 ${getKeyList(curSelected, "id")} 的数据`, {
|
||||
type: "success"
|
||||
});
|
||||
tableRef.value.getTableRef().clearSelection();
|
||||
onSearch();
|
||||
}
|
||||
|
||||
/** 清空日志 */
|
||||
function clearAll() {
|
||||
// 根据实际业务,调用接口删除所有日志数据
|
||||
message("已删除所有日志数据", {
|
||||
type: "success"
|
||||
});
|
||||
onSearch();
|
||||
}
|
||||
|
||||
async function onSearch() {
|
||||
loading.value = true;
|
||||
const { data } = await getOperationLogsList(toRaw(form));
|
||||
dataList.value = data.list;
|
||||
pagination.total = data.total;
|
||||
pagination.pageSize = data.pageSize;
|
||||
pagination.currentPage = data.currentPage;
|
||||
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
const resetForm = formEl => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
onSearch();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
|
||||
return {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
selectedNum,
|
||||
onSearch,
|
||||
clearAll,
|
||||
resetForm,
|
||||
onbatchDel,
|
||||
handleSizeChange,
|
||||
onSelectionCancel,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange
|
||||
};
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useRole } from "./hook";
|
||||
import { useRole } from "@/hooks/monitor/useRole";
|
||||
import { getPickerShortcuts } from "../../utils";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import PureTable from "@pureadmin/table";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
|
||||
|
@ -15,130 +15,68 @@ defineOptions({
|
|||
const formRef = ref();
|
||||
const tableRef = ref();
|
||||
|
||||
const {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
selectedNum,
|
||||
onSearch,
|
||||
clearAll,
|
||||
resetForm,
|
||||
onbatchDel,
|
||||
handleSizeChange,
|
||||
onSelectionCancel,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange
|
||||
} = useRole(tableRef);
|
||||
const { form, loading, columns, dataList, pagination, selectedNum, onSearch, clearAll, resetForm, onbatchDel, handleSizeChange, onSelectionCancel, handleCurrentChange, handleSelectionChange } =
|
||||
useRole(tableRef);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="所属模块" prop="module">
|
||||
<el-input
|
||||
v-model="form.module"
|
||||
placeholder="请输入所属模块"
|
||||
clearable
|
||||
class="!w-[170px]"
|
||||
/>
|
||||
<el-input v-model="form.module" class="!w-[170px]" clearable placeholder="请输入所属模块" />
|
||||
</el-form-item>
|
||||
<el-form-item label="操作状态" prop="status">
|
||||
<el-select
|
||||
v-model="form.status"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-[150px]"
|
||||
>
|
||||
<el-select v-model="form.status" class="!w-[150px]" clearable placeholder="请选择">
|
||||
<el-option label="成功" value="1" />
|
||||
<el-option label="失败" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="操作时间" prop="operatingTime">
|
||||
<el-date-picker
|
||||
v-model="form.operatingTime"
|
||||
:shortcuts="getPickerShortcuts()"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期时间"
|
||||
end-placeholder="结束日期时间"
|
||||
/>
|
||||
<el-date-picker v-model="form.operatingTime" :shortcuts="getPickerShortcuts()" end-placeholder="结束日期时间" range-separator="至" start-placeholder="开始日期时间" type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('ri:search-line')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="操作日志(仅演示,操作后不生效)"
|
||||
:columns="columns"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<PureTableBar :columns="columns" title="操作日志(仅演示,操作后不生效)" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll">
|
||||
<template #reference>
|
||||
<el-button type="danger" :icon="useRenderIcon(Delete)">
|
||||
清空日志
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Delete)" type="danger"> 清空日志</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<div
|
||||
v-if="selectedNum > 0"
|
||||
v-motion-fade
|
||||
class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center"
|
||||
>
|
||||
<div v-if="selectedNum > 0" v-motion-fade class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center">
|
||||
<div class="flex-auto">
|
||||
<span
|
||||
style="font-size: var(--el-font-size-base)"
|
||||
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
|
||||
>
|
||||
已选 {{ selectedNum }} 项
|
||||
</span>
|
||||
<el-button type="primary" text @click="onSelectionCancel">
|
||||
取消选择
|
||||
</el-button>
|
||||
<span class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]" style="font-size: var(--el-font-size-base)"> 已选 {{ selectedNum }} 项 </span>
|
||||
<el-button text type="primary" @click="onSelectionCancel"> 取消选择</el-button>
|
||||
</div>
|
||||
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
|
||||
<template #reference>
|
||||
<el-button type="danger" text class="mr-1"> 批量删除 </el-button>
|
||||
<el-button class="mr-1" text type="danger"> 批量删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
align-whole="center"
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:data="dataList"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
|
@ -148,7 +86,7 @@ const {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script setup lang="tsx">
|
||||
<script lang="tsx" setup>
|
||||
import { ref } from "vue";
|
||||
import "vue-json-pretty/lib/styles.css";
|
||||
import VueJsonPretty from "vue-json-pretty";
|
||||
|
@ -82,15 +82,10 @@ const dataList = ref([
|
|||
<template>
|
||||
<div>
|
||||
<el-scrollbar>
|
||||
<PureDescriptions border :data="data" :columns="columns" :column="5" />
|
||||
<PureDescriptions :column="5" :columns="columns" :data="data" border />
|
||||
</el-scrollbar>
|
||||
<el-tabs :modelValue="'responseBody'" type="border-card" class="mt-4">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in dataList"
|
||||
:key="index"
|
||||
:name="item.name"
|
||||
:label="item.title"
|
||||
>
|
||||
<el-tabs :modelValue="'responseBody'" class="mt-4" type="border-card">
|
||||
<el-tab-pane v-for="(item, index) in dataList" :key="index" :label="item.title" :name="item.name">
|
||||
<el-scrollbar max-height="calc(100vh - 240px)">
|
||||
<vue-json-pretty v-model:data="item.data" />
|
||||
</el-scrollbar>
|
||||
|
|
|
@ -3,9 +3,9 @@ import Detail from "./detail.vue";
|
|||
import { message } from "@/utils/message";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
import { type Ref, reactive, ref, onMounted, toRaw } from "vue";
|
||||
import { onMounted, reactive, ref, type Ref, toRaw } from "vue";
|
||||
import { getKeyList, useCopyToClipboard } from "@pureadmin/utils";
|
||||
import { getSystemLogsList, getSystemLogsDetail } from "@/api/system";
|
||||
import { getSystemLogsDetail, getSystemLogsList } from "@/api/system";
|
||||
import Info from "@iconify-icons/ri/question-line";
|
||||
|
||||
export function useRole(tableRef: Ref) {
|
||||
|
@ -25,20 +25,20 @@ export function useRole(tableRef: Ref) {
|
|||
background: true
|
||||
});
|
||||
|
||||
// const getLevelType = (type, text = false) => {
|
||||
// switch (type) {
|
||||
// case 0:
|
||||
// return text ? "debug" : "primary";
|
||||
// case 1:
|
||||
// return text ? "info" : "success";
|
||||
// case 2:
|
||||
// return text ? "warn" : "info";
|
||||
// case 3:
|
||||
// return text ? "error" : "warning";
|
||||
// case 4:
|
||||
// return text ? "fatal" : "danger";
|
||||
// }
|
||||
// };
|
||||
const getLevelType = (type, text = false) => {
|
||||
switch (type) {
|
||||
case 0:
|
||||
return text ? "debug" : "primary";
|
||||
case 1:
|
||||
return text ? "info" : "success";
|
||||
case 2:
|
||||
return text ? "warn" : "info";
|
||||
case 3:
|
||||
return text ? "error" : "warning";
|
||||
case 4:
|
||||
return text ? "fatal" : "danger";
|
||||
}
|
||||
};
|
||||
|
||||
const columns: TableColumnList = [
|
||||
{
|
||||
|
@ -98,26 +98,22 @@ export function useRole(tableRef: Ref) {
|
|||
prop: "browser",
|
||||
minWidth: 100
|
||||
},
|
||||
// {
|
||||
// label: "级别",
|
||||
// prop: "level",
|
||||
// minWidth: 90,
|
||||
// cellRenderer: ({ row, props }) => (
|
||||
// <el-tag size={props.size} type={getLevelType(row.level)} effect="plain">
|
||||
// {getLevelType(row.level, true)}
|
||||
// </el-tag>
|
||||
// )
|
||||
// },
|
||||
{
|
||||
label: "级别",
|
||||
prop: "level",
|
||||
minWidth: 90,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag size={props.size} type={getLevelType(row.level)} effect="plain">
|
||||
{getLevelType(row.level, true)}
|
||||
</el-tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: "请求耗时",
|
||||
prop: "takesTime",
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag
|
||||
size={props.size}
|
||||
type={row.takesTime < 1000 ? "success" : "warning"}
|
||||
effect="plain"
|
||||
>
|
||||
<el-tag size={props.size} type={row.takesTime < 1000 ? "success" : "warning"} effect="plain">
|
||||
{row.takesTime} ms
|
||||
</el-tag>
|
||||
)
|
||||
|
@ -126,8 +122,7 @@ export function useRole(tableRef: Ref) {
|
|||
label: "请求时间",
|
||||
prop: "requestTime",
|
||||
minWidth: 180,
|
||||
formatter: ({ requestTime }) =>
|
||||
dayjs(requestTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
formatter: ({ requestTime }) => dayjs(requestTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
},
|
||||
{
|
||||
label: "操作",
|
||||
|
@ -162,9 +157,7 @@ export function useRole(tableRef: Ref) {
|
|||
function handleCellDblclick({ url }, { property }) {
|
||||
if (property !== "url") return;
|
||||
update(url);
|
||||
copied.value
|
||||
? message(`${url} 已拷贝`, { type: "success" })
|
||||
: message("拷贝失败", { type: "warning" });
|
||||
copied.value ? message(`${url} 已拷贝`, { type: "success" }) : message("拷贝失败", { type: "warning" });
|
||||
}
|
||||
|
||||
/** 批量删除 */
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useRole } from "./hook";
|
||||
import { getPickerShortcuts } from "../../utils";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import PureTable from "@pureadmin/table";
|
||||
import View from "@iconify-icons/ep/view";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
|
@ -38,115 +38,63 @@ const {
|
|||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="所属模块" prop="module">
|
||||
<el-input
|
||||
v-model="form.module"
|
||||
placeholder="请输入所属模块"
|
||||
clearable
|
||||
class="!w-[170px]"
|
||||
/>
|
||||
<el-input v-model="form.module" class="!w-[170px]" clearable placeholder="请输入所属模块" />
|
||||
</el-form-item>
|
||||
<el-form-item label="请求时间" prop="requestTime">
|
||||
<el-date-picker
|
||||
v-model="form.requestTime"
|
||||
:shortcuts="getPickerShortcuts()"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期时间"
|
||||
end-placeholder="结束日期时间"
|
||||
/>
|
||||
<el-date-picker v-model="form.requestTime" :shortcuts="getPickerShortcuts()" end-placeholder="结束日期时间" range-separator="至" start-placeholder="开始日期时间" type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('ri:search-line')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="系统日志(仅演示,操作后不生效)"
|
||||
:columns="columns"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<PureTableBar :columns="columns" title="系统日志(仅演示,操作后不生效)" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll">
|
||||
<template #reference>
|
||||
<el-button type="danger" :icon="useRenderIcon(Delete)">
|
||||
清空日志
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Delete)" type="danger"> 清空日志</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<div
|
||||
v-if="selectedNum > 0"
|
||||
v-motion-fade
|
||||
class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center"
|
||||
>
|
||||
<div v-if="selectedNum > 0" v-motion-fade class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center">
|
||||
<div class="flex-auto">
|
||||
<span
|
||||
style="font-size: var(--el-font-size-base)"
|
||||
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
|
||||
>
|
||||
已选 {{ selectedNum }} 项
|
||||
</span>
|
||||
<el-button type="primary" text @click="onSelectionCancel">
|
||||
取消选择
|
||||
</el-button>
|
||||
<span class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]" style="font-size: var(--el-font-size-base)"> 已选 {{ selectedNum }} 项 </span>
|
||||
<el-button text type="primary" @click="onSelectionCancel"> 取消选择</el-button>
|
||||
</div>
|
||||
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
|
||||
<template #reference>
|
||||
<el-button type="danger" text class="mr-1"> 批量删除 </el-button>
|
||||
<el-button class="mr-1" text type="danger"> 批量删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
align-whole="center"
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:data="dataList"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small'"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
@cell-dblclick="handleCellDblclick"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
class="reset-margin !outline-none"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(View)"
|
||||
@click="onDetail(row)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(View)" :size="size" class="reset-margin !outline-none" link type="primary" @click="onDetail(row)"> 详情 </el-button>
|
||||
</template>
|
||||
</pure-table>
|
||||
</template>
|
||||
|
@ -154,7 +102,7 @@ const {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useRole } from "./hook";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRole } from "@/hooks/monitor/useRole";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import Plane from "@iconify-icons/ri/plane-line";
|
||||
|
@ -12,93 +12,47 @@ defineOptions({
|
|||
});
|
||||
|
||||
const formRef = ref();
|
||||
const {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
onSearch,
|
||||
resetForm,
|
||||
handleOffline,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange
|
||||
} = useRole();
|
||||
const { form, loading, columns, dataList, pagination, onSearch, resetForm, handleOffline, handleSizeChange, handleCurrentChange, handleSelectionChange } = useRole();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input
|
||||
v-model="form.username"
|
||||
placeholder="请输入用户名"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
<el-input v-model="form.username" class="!w-[180px]" clearable placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('ri:search-line')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="在线用户(仅演示,操作后不生效)"
|
||||
:columns="columns"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<PureTableBar :columns="columns" title="在线用户(仅演示,操作后不生效)" @refresh="onSearch">
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
align-whole="center"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:data="dataList"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small'"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-popconfirm
|
||||
:title="`是否强制下线${row.username}`"
|
||||
@confirm="handleOffline(row)"
|
||||
>
|
||||
<el-popconfirm :title="`是否强制下线${row.username}`" @confirm="handleOffline(row)">
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Plane)"
|
||||
>
|
||||
强退
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Plane)" :size="size" class="reset-margin" link type="primary"> 强退 </el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
|
@ -108,7 +62,7 @@ const {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<TablePlusBar
|
||||
:column="columns"
|
||||
:data-list="dataList"
|
||||
:form="form"
|
||||
:handle-current-change="handleCurrentChange"
|
||||
:handle-selection-change="handleSelectionChange"
|
||||
:handle-size-change="handleSizeChange"
|
||||
:loading="loading"
|
||||
:onReFresh="onReFresh"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
tableTitle="系统测试(仅演示,操作后不生效)"
|
||||
@changeColumn="handleChangeColumn"
|
||||
@cell-dblclick="handleCellDblclick"
|
||||
>
|
||||
<template #tableForm>
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="form.username" class="!w-[150px]" clearable placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录状态" prop="status">
|
||||
<el-select v-model="form.status" class="!w-[150px]" clearable placeholder="请选择">
|
||||
<el-option label="成功" value="1" />
|
||||
<el-option label="失败" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="登录时间" prop="loginTime">
|
||||
<el-date-picker v-model="form.loginTime" :shortcuts="getPickerShortcuts()" end-placeholder="结束日期时间" range-separator="至" start-placeholder="开始日期时间" type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置</el-button>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template #tableButtons>
|
||||
<el-button type="danger">按钮</el-button>
|
||||
</template>
|
||||
|
||||
<template #tableSelect>
|
||||
<div v-if="selectedNum > 0" v-motion-fade class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center">
|
||||
<div class="flex-auto">
|
||||
<span class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]" style="font-size: var(--el-font-size-base)"> 已选 {{ selectedNum }} 项 </span>
|
||||
<el-button text type="primary" @click="onSelectionCancel"> 取消选择</el-button>
|
||||
</div>
|
||||
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
|
||||
<template #reference>
|
||||
<el-button class="mr-1" text type="danger"> 批量删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #operation> 111</template>
|
||||
</TablePlusBar>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { PaginationProps } from "@pureadmin/table";
|
||||
import TablePlusBar from "@/components/TableBar/src/TablePlusBar.vue";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import { getPickerShortcuts } from "@/views/monitor/utils";
|
||||
import { delay } from "@pureadmin/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const loading = ref(true);
|
||||
const form = reactive({});
|
||||
const selectedNum = ref(0);
|
||||
const pagination = reactive<PaginationProps>({
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
background: true
|
||||
});
|
||||
let columns = ref([
|
||||
{ label: t("table.tableNumber"), prop: "id", minWidth: 60 },
|
||||
{ label: "用户名", prop: "username", minWidth: 100 },
|
||||
{ label: "登录 IP", prop: "ip", minWidth: 140 },
|
||||
{ label: "登录地点", prop: "address", minWidth: 140 },
|
||||
{ label: "操作系统", prop: "system", minWidth: 100 },
|
||||
{ label: "浏览器类型", prop: "browser", minWidth: 100 },
|
||||
{ label: "登录时间", prop: "loginTime", minWidth: 180 },
|
||||
{ label: "操作", fixed: "right", slot: "operation" }
|
||||
]);
|
||||
|
||||
const dataList = [
|
||||
{
|
||||
id: 1,
|
||||
username: "admin",
|
||||
ip: "163.108.28.226",
|
||||
address: "中国河南省信阳市",
|
||||
system: "macOS",
|
||||
browser: "Chrome",
|
||||
loginTime: "2024-05-12T07:33:12.080Z"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: "common",
|
||||
ip: "106.62.252.181",
|
||||
address: "中国广东省深圳市",
|
||||
system: "Windows",
|
||||
browser: "Firefox",
|
||||
loginTime: "2024-05-12T07:33:12.080Z"
|
||||
}
|
||||
];
|
||||
|
||||
const onSearch = () => {
|
||||
console.log(form);
|
||||
console.log("搜索。。。");
|
||||
};
|
||||
|
||||
const resetForm = formEl => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
onSearch();
|
||||
};
|
||||
|
||||
function handleSizeChange(val: number) {
|
||||
console.log(`${val} items per page`);
|
||||
}
|
||||
|
||||
function handleCurrentChange(val: number) {
|
||||
console.log(`current page: ${val}`);
|
||||
}
|
||||
|
||||
function handleSelectionChange(val) {
|
||||
console.log("handleSelectionChange", val);
|
||||
}
|
||||
|
||||
function onReFresh() {
|
||||
loading.value = true;
|
||||
delay(500).then(() => (loading.value = false));
|
||||
}
|
||||
|
||||
function handleChangeColumn(value: Object[]) {
|
||||
columns.value = value;
|
||||
}
|
||||
|
||||
const onSelectionCancel = () => {};
|
||||
|
||||
const onbatchDel = () => {};
|
||||
const handleCellDblclick = val => {
|
||||
console.log(val);
|
||||
};
|
||||
onMounted(() => {
|
||||
setTimeout(() => (loading.value = false), 1000);
|
||||
});
|
||||
</script>
|
|
@ -1,9 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import ReCol from "@/components/ReCol";
|
||||
import { formRules } from "./utils/rule";
|
||||
import { FormProps } from "./utils/types";
|
||||
import { usePublicHooks } from "../hooks";
|
||||
import { usePublicHooks } from "@/hooks/system/usePublicHooks";
|
||||
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
formInline: () => ({
|
||||
|
@ -31,18 +31,12 @@ defineExpose({ getRef });
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
:model="newFormInline"
|
||||
:rules="formRules"
|
||||
label-width="82px"
|
||||
>
|
||||
<el-form ref="ruleFormRef" :model="newFormInline" :rules="formRules" label-width="82px">
|
||||
<el-row :gutter="30">
|
||||
<re-col>
|
||||
<el-form-item label="上级部门">
|
||||
<el-cascader
|
||||
v-model="newFormInline.parentId"
|
||||
class="w-full"
|
||||
:options="newFormInline.higherDeptOptions"
|
||||
:props="{
|
||||
value: 'id',
|
||||
|
@ -50,6 +44,7 @@ defineExpose({ getRef });
|
|||
emitPath: false,
|
||||
checkStrictly: true
|
||||
}"
|
||||
class="w-full"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择上级部门"
|
||||
|
@ -62,76 +57,42 @@ defineExpose({ getRef });
|
|||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input
|
||||
v-model="newFormInline.name"
|
||||
clearable
|
||||
placeholder="请输入部门名称"
|
||||
/>
|
||||
<el-input v-model="newFormInline.name" clearable placeholder="请输入部门名称" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="部门负责人">
|
||||
<el-input
|
||||
v-model="newFormInline.principal"
|
||||
clearable
|
||||
placeholder="请输入部门负责人"
|
||||
/>
|
||||
<el-input v-model="newFormInline.principal" clearable placeholder="请输入部门负责人" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input
|
||||
v-model="newFormInline.phone"
|
||||
clearable
|
||||
placeholder="请输入手机号"
|
||||
/>
|
||||
<el-input v-model="newFormInline.phone" clearable placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input
|
||||
v-model="newFormInline.email"
|
||||
clearable
|
||||
placeholder="请输入邮箱"
|
||||
/>
|
||||
<el-input v-model="newFormInline.email" clearable placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="排序">
|
||||
<el-input-number
|
||||
v-model="newFormInline.sort"
|
||||
class="!w-full"
|
||||
:min="0"
|
||||
:max="9999"
|
||||
controls-position="right"
|
||||
/>
|
||||
<el-input-number v-model="newFormInline.sort" :max="9999" :min="0" class="!w-full" controls-position="right" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="部门状态">
|
||||
<el-switch
|
||||
v-model="newFormInline.status"
|
||||
inline-prompt
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="启用"
|
||||
inactive-text="停用"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-switch v-model="newFormInline.status" :active-value="1" :inactive-value="0" :style="switchStyle" active-text="启用" inactive-text="停用" inline-prompt />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="newFormInline.remark"
|
||||
placeholder="请输入备注信息"
|
||||
type="textarea"
|
||||
/>
|
||||
<el-input v-model="newFormInline.remark" placeholder="请输入备注信息" type="textarea" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
</el-row>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useDept } from "./utils/hook";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
|
@ -15,131 +15,57 @@ defineOptions({
|
|||
|
||||
const formRef = ref();
|
||||
const tableRef = ref();
|
||||
const {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
onSearch,
|
||||
resetForm,
|
||||
openDialog,
|
||||
handleDelete,
|
||||
handleSelectionChange
|
||||
} = useDept();
|
||||
const { form, loading, columns, dataList, onSearch, resetForm, openDialog, handleDelete, handleSelectionChange } = useDept();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="部门名称:" prop="name">
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
placeholder="请输入部门名称"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
<el-input v-model="form.name" class="!w-[180px]" clearable placeholder="请输入部门名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态:" prop="status">
|
||||
<el-select
|
||||
v-model="form.status"
|
||||
placeholder="请选择状态"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
>
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="停用" :value="0" />
|
||||
<el-select v-model="form.status" class="!w-[180px]" clearable placeholder="请选择状态">
|
||||
<el-option :value="1" label="启用" />
|
||||
<el-option :value="0" label="停用" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('ri:search-line')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="部门管理(仅演示,操作后不生效)"
|
||||
:columns="columns"
|
||||
:tableRef="tableRef?.getTableRef()"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<PureTableBar :columns="columns" :tableRef="tableRef?.getTableRef()" title="部门管理(仅演示,操作后不生效)" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog()"
|
||||
>
|
||||
新增部门
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="openDialog()"> 新增部门 </el-button>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
default-expand-all
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:data="dataList"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
default-expand-all
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
@click="openDialog('修改', row)"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
:title="`是否确认删除部门名称为${row.name}的这条数据`"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<el-button :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="openDialog('修改', row)"> 修改 </el-button>
|
||||
<el-button :icon="useRenderIcon(AddFill)" :size="size" class="reset-margin" link type="primary" @click="openDialog('新增', { parentId: row.id } as any)"> 新增 </el-button>
|
||||
<el-popconfirm :title="`是否确认删除部门名称为${row.name}的这条数据`" @confirm="handleDelete(row)">
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary"> 删除 </el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import ReCol from "@/components/ReCol";
|
||||
import { formRules } from "./utils/rule";
|
||||
|
@ -7,15 +7,7 @@ import { transformI18n } from "@/plugins/i18n";
|
|||
import { IconSelect } from "@/components/ReIcon";
|
||||
import Segmented from "@/components/ReSegmented";
|
||||
import ReAnimateSelector from "@/components/ReAnimateSelector";
|
||||
import {
|
||||
menuTypeOptions,
|
||||
showLinkOptions,
|
||||
fixedTagOptions,
|
||||
keepAliveOptions,
|
||||
hiddenTagOptions,
|
||||
showParentOptions,
|
||||
frameLoadingOptions
|
||||
} from "./utils/enums";
|
||||
import { fixedTagOptions, frameLoadingOptions, hiddenTagOptions, keepAliveOptions, menuTypeOptions, showLinkOptions, showParentOptions } from "./utils/enums";
|
||||
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
formInline: () => ({
|
||||
|
@ -55,19 +47,11 @@ defineExpose({ getRef });
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
:model="newFormInline"
|
||||
:rules="formRules"
|
||||
label-width="82px"
|
||||
>
|
||||
<el-form ref="ruleFormRef" :model="newFormInline" :rules="formRules" label-width="82px">
|
||||
<el-row :gutter="30">
|
||||
<re-col>
|
||||
<el-form-item label="菜单类型">
|
||||
<Segmented
|
||||
v-model="newFormInline.menuType"
|
||||
:options="menuTypeOptions"
|
||||
/>
|
||||
<Segmented v-model="newFormInline.menuType" :options="menuTypeOptions" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
|
@ -75,7 +59,6 @@ defineExpose({ getRef });
|
|||
<el-form-item label="上级菜单">
|
||||
<el-cascader
|
||||
v-model="newFormInline.parentId"
|
||||
class="w-full"
|
||||
:options="newFormInline.higherMenuOptions"
|
||||
:props="{
|
||||
value: 'id',
|
||||
|
@ -83,6 +66,7 @@ defineExpose({ getRef });
|
|||
emitPath: false,
|
||||
checkStrictly: true
|
||||
}"
|
||||
class="w-full"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择上级菜单"
|
||||
|
@ -95,158 +79,80 @@ defineExpose({ getRef });
|
|||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="菜单名称" prop="title">
|
||||
<el-input
|
||||
v-model="newFormInline.title"
|
||||
clearable
|
||||
placeholder="请输入菜单名称"
|
||||
/>
|
||||
<el-input v-model="newFormInline.title" clearable placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-if="newFormInline.menuType !== 3" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="路由名称" prop="name">
|
||||
<el-input
|
||||
v-model="newFormInline.name"
|
||||
clearable
|
||||
placeholder="请输入路由名称"
|
||||
/>
|
||||
<el-input v-model="newFormInline.name" clearable placeholder="请输入路由名称" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-if="newFormInline.menuType !== 3" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="路由路径" prop="path">
|
||||
<el-input
|
||||
v-model="newFormInline.path"
|
||||
clearable
|
||||
placeholder="请输入路由路径"
|
||||
/>
|
||||
<el-input v-model="newFormInline.path" clearable placeholder="请输入路由路径" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 0"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType === 0" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="组件路径">
|
||||
<el-input
|
||||
v-model="newFormInline.component"
|
||||
clearable
|
||||
placeholder="请输入组件路径"
|
||||
/>
|
||||
<el-input v-model="newFormInline.component" clearable placeholder="请输入组件路径" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<re-col :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="菜单排序">
|
||||
<el-input-number
|
||||
v-model="newFormInline.rank"
|
||||
class="!w-full"
|
||||
:min="1"
|
||||
:max="9999"
|
||||
controls-position="right"
|
||||
/>
|
||||
<el-input-number v-model="newFormInline.rank" :max="9999" :min="1" class="!w-full" controls-position="right" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 0"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType === 0" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="路由重定向">
|
||||
<el-input
|
||||
v-model="newFormInline.redirect"
|
||||
clearable
|
||||
placeholder="请输入默认跳转地址"
|
||||
/>
|
||||
<el-input v-model="newFormInline.redirect" clearable placeholder="请输入默认跳转地址" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType !== 3" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="菜单图标">
|
||||
<IconSelect v-model="newFormInline.icon" class="w-full" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType !== 3" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="右侧图标">
|
||||
<el-input
|
||||
v-model="newFormInline.extraIcon"
|
||||
clearable
|
||||
placeholder="菜单名称右侧的额外图标"
|
||||
/>
|
||||
<el-input v-model="newFormInline.extraIcon" clearable placeholder="菜单名称右侧的额外图标" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-show="newFormInline.menuType < 2" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="进场动画">
|
||||
<ReAnimateSelector
|
||||
v-model="newFormInline.enterTransition"
|
||||
placeholder="请选择页面进场加载动画"
|
||||
/>
|
||||
<ReAnimateSelector v-model="newFormInline.enterTransition" placeholder="请选择页面进场加载动画" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-show="newFormInline.menuType < 2" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="离场动画">
|
||||
<ReAnimateSelector
|
||||
v-model="newFormInline.leaveTransition"
|
||||
placeholder="请选择页面离场加载动画"
|
||||
/>
|
||||
<ReAnimateSelector v-model="newFormInline.leaveTransition" placeholder="请选择页面离场加载动画" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 0"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType === 0" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="菜单激活">
|
||||
<el-input
|
||||
v-model="newFormInline.activePath"
|
||||
clearable
|
||||
placeholder="请输入需要激活的菜单"
|
||||
/>
|
||||
<el-input v-model="newFormInline.activePath" clearable placeholder="请输入需要激活的菜单" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-if="newFormInline.menuType === 3" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-if="newFormInline.menuType === 3" :sm="24" :value="12" :xs="24">
|
||||
<!-- 按钮级别权限设置 -->
|
||||
<el-form-item label="权限标识" prop="auths">
|
||||
<el-input
|
||||
v-model="newFormInline.auths"
|
||||
clearable
|
||||
placeholder="请输入权限标识"
|
||||
/>
|
||||
<el-input v-model="newFormInline.auths" clearable placeholder="请输入权限标识" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 1"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType === 1" :sm="24" :value="12" :xs="24">
|
||||
<!-- iframe -->
|
||||
<el-form-item label="链接地址">
|
||||
<el-input
|
||||
v-model="newFormInline.frameSrc"
|
||||
clearable
|
||||
placeholder="请输入 iframe 链接地址"
|
||||
/>
|
||||
<el-input v-model="newFormInline.frameSrc" clearable placeholder="请输入 iframe 链接地址" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-if="newFormInline.menuType === 1" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-if="newFormInline.menuType === 1" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="加载动画">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.frameLoading ? 0 : 1"
|
||||
|
@ -260,12 +166,7 @@ defineExpose({ getRef });
|
|||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType !== 3" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="菜单">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.showLink ? 0 : 1"
|
||||
|
@ -278,12 +179,7 @@ defineExpose({ getRef });
|
|||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<re-col v-show="newFormInline.menuType !== 3" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="父级菜单">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.showParent ? 0 : 1"
|
||||
|
@ -297,7 +193,7 @@ defineExpose({ getRef });
|
|||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-show="newFormInline.menuType < 2" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="缓存页面">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.keepAlive ? 0 : 1"
|
||||
|
@ -311,7 +207,7 @@ defineExpose({ getRef });
|
|||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-show="newFormInline.menuType < 2" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="标签页">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.hiddenTag ? 1 : 0"
|
||||
|
@ -324,7 +220,7 @@ defineExpose({ getRef });
|
|||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<re-col v-show="newFormInline.menuType < 2" :sm="24" :value="12" :xs="24">
|
||||
<el-form-item label="固定标签页">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.fixedTag ? 0 : 1"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useMenu } from "./utils/hook";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
|
@ -16,105 +16,47 @@ defineOptions({
|
|||
|
||||
const formRef = ref();
|
||||
const tableRef = ref();
|
||||
const {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
onSearch,
|
||||
resetForm,
|
||||
openDialog,
|
||||
handleDelete,
|
||||
handleSelectionChange
|
||||
} = useMenu();
|
||||
const { form, loading, columns, dataList, onSearch, resetForm, openDialog, handleDelete, handleSelectionChange } = useMenu();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="菜单名称:" prop="title">
|
||||
<el-input
|
||||
v-model="form.title"
|
||||
placeholder="请输入菜单名称"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
<el-input v-model="form.title" class="!w-[180px]" clearable placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('ri:search-line')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="菜单管理(仅演示,操作后不生效)"
|
||||
:columns="columns"
|
||||
:isExpandAll="false"
|
||||
:tableRef="tableRef?.getTableRef()"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<PureTableBar :columns="columns" :isExpandAll="false" :tableRef="tableRef?.getTableRef()" title="菜单管理(仅演示,操作后不生效)" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog()"
|
||||
>
|
||||
新增菜单
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="openDialog()"> 新增菜单 </el-button>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:data="dataList"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
@click="openDialog('修改', row)"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="row.menuType !== 3"
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
||||
>
|
||||
<el-button :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="openDialog('修改', row)"> 修改 </el-button>
|
||||
<el-button v-show="row.menuType !== 3" :icon="useRenderIcon(AddFill)" :size="size" class="reset-margin" link type="primary" @click="openDialog('新增', { parentId: row.id } as any)">
|
||||
新增
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
|
@ -122,15 +64,7 @@ const {
|
|||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary"> 删除 </el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { useRole } from "./utils/hook";
|
||||
import { ref, computed, nextTick, onMounted } from "vue";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { computed, nextTick, onMounted, ref } from "vue";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import {
|
||||
delay,
|
||||
subBefore,
|
||||
deviceDetection,
|
||||
useResizeObserver
|
||||
} from "@pureadmin/utils";
|
||||
import { delay, deviceDetection, subBefore, useResizeObserver } from "@pureadmin/utils";
|
||||
|
||||
// import Database from "@iconify-icons/ri/database-2-line";
|
||||
// import More from "@iconify-icons/ep/more-filled";
|
||||
|
@ -82,9 +77,7 @@ onMounted(() => {
|
|||
useResizeObserver(contentRef, async () => {
|
||||
await nextTick();
|
||||
delay(60).then(() => {
|
||||
treeHeight.value = parseFloat(
|
||||
subBefore(tableRef.value.getTableDoms().tableWrapper.style.height, "px")
|
||||
);
|
||||
treeHeight.value = parseFloat(subBefore(tableRef.value.getTableDoms().tableWrapper.style.height, "px"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -92,134 +85,67 @@ onMounted(() => {
|
|||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form ref="formRef" :inline="true" :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<el-form-item label="角色名称:" prop="name">
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
placeholder="请输入角色名称"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
<el-input v-model="form.name" class="!w-[180px]" clearable placeholder="请输入角色名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色标识:" prop="code">
|
||||
<el-input
|
||||
v-model="form.code"
|
||||
placeholder="请输入角色标识"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
<el-input v-model="form.code" class="!w-[180px]" clearable placeholder="请输入角色标识" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态:" prop="status">
|
||||
<el-select
|
||||
v-model="form.status"
|
||||
placeholder="请选择状态"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
>
|
||||
<el-select v-model="form.status" class="!w-[180px]" clearable placeholder="请选择状态">
|
||||
<el-option label="已启用" value="1" />
|
||||
<el-option label="已停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('ri:search-line')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="loading" type="primary" @click="onSearch"> 搜索 </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div
|
||||
ref="contentRef"
|
||||
:class="['flex', deviceDetection() ? 'flex-wrap' : '']"
|
||||
>
|
||||
<div ref="contentRef" :class="['flex', deviceDetection() ? 'flex-wrap' : '']">
|
||||
<PureTableBar
|
||||
:class="[isShow && !deviceDetection() ? '!w-[60vw]' : 'w-full']"
|
||||
:columns="columns"
|
||||
style="transition: width 220ms cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
title="角色管理(仅演示,操作后不生效)"
|
||||
:columns="columns"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<template #buttons>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog()"
|
||||
>
|
||||
新增角色
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="openDialog()"> 新增角色 </el-button>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
align-whole="center"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
:row-style="rowStyle"
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:data="dataList"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:row-style="rowStyle"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
@page-size-change="handleSizeChange"
|
||||
@page-current-change="handleCurrentChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
@click="openDialog('修改', row)"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
:title="`是否确认删除角色名称为${row.name}的这条数据`"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<el-button :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="openDialog('修改', row)"> 修改 </el-button>
|
||||
<el-popconfirm :title="`是否确认删除角色名称为${row.name}的这条数据`" @confirm="handleDelete(row)">
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary"> 删除 </el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Menu)"
|
||||
@click="handleMenu(row)"
|
||||
>
|
||||
权限
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Menu)" :size="size" class="reset-margin" link type="primary" @click="handleMenu(row)"> 权限 </el-button>
|
||||
<!-- <el-dropdown>
|
||||
<el-button
|
||||
class="ml-3 mt-[2px]"
|
||||
|
@ -262,10 +188,7 @@ onMounted(() => {
|
|||
</template>
|
||||
</PureTableBar>
|
||||
|
||||
<div
|
||||
v-if="isShow"
|
||||
class="!min-w-[calc(100vw-60vw-268px)] mt-2 px-2 pb-2 bg-bg_color ml-2 overflow-auto"
|
||||
>
|
||||
<div v-if="isShow" class="!min-w-[calc(100vw-60vw-268px)] mt-2 px-2 pb-2 bg-bg_color ml-2 overflow-auto">
|
||||
<div class="flex justify-between w-full px-3 pt-5 pb-4">
|
||||
<div class="flex">
|
||||
<span :class="iconClass">
|
||||
|
@ -273,10 +196,10 @@ onMounted(() => {
|
|||
v-tippy="{
|
||||
content: '关闭'
|
||||
}"
|
||||
class="dark:text-white"
|
||||
width="18px"
|
||||
height="18px"
|
||||
:icon="Close"
|
||||
class="dark:text-white"
|
||||
height="18px"
|
||||
width="18px"
|
||||
@click="handleMenu"
|
||||
/>
|
||||
</span>
|
||||
|
@ -285,10 +208,10 @@ onMounted(() => {
|
|||
v-tippy="{
|
||||
content: '保存菜单权限'
|
||||
}"
|
||||
class="dark:text-white"
|
||||
width="18px"
|
||||
height="18px"
|
||||
:icon="Check"
|
||||
class="dark:text-white"
|
||||
height="18px"
|
||||
width="18px"
|
||||
@click="handleSave"
|
||||
/>
|
||||
</span>
|
||||
|
@ -298,27 +221,13 @@ onMounted(() => {
|
|||
{{ `${curRow?.name ? `(${curRow.name})` : ""}` }}
|
||||
</p>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="treeSearchValue"
|
||||
placeholder="请输入菜单进行搜索"
|
||||
class="mb-1"
|
||||
clearable
|
||||
@input="onQueryChanged"
|
||||
/>
|
||||
<el-input v-model="treeSearchValue" class="mb-1" clearable placeholder="请输入菜单进行搜索" @input="onQueryChanged" />
|
||||
<div class="flex flex-wrap">
|
||||
<el-checkbox v-model="isExpandAll" label="展开/折叠" />
|
||||
<el-checkbox v-model="isSelectAll" label="全选/全不选" />
|
||||
<el-checkbox v-model="isLinkage" label="父子联动" />
|
||||
</div>
|
||||
<el-tree-v2
|
||||
ref="treeRef"
|
||||
show-checkbox
|
||||
:data="treeData"
|
||||
:props="treeProps"
|
||||
:height="treeHeight"
|
||||
:check-strictly="!isLinkage"
|
||||
:filter-method="filterMethod"
|
||||
>
|
||||
<el-tree-v2 ref="treeRef" :check-strictly="!isLinkage" :data="treeData" :filter-method="filterMethod" :height="treeHeight" :props="treeProps" show-checkbox>
|
||||
<template #default="{ node }">
|
||||
<span>{{ transformI18n(node.label) }}</span>
|
||||
</template>
|
||||
|
@ -328,7 +237,7 @@ onMounted(() => {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { ref } from "vue";
|
||||
import tree from "./tree.vue";
|
||||
import { useUser } from "./utils/hook";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { PureTableBar } from "@/components/TableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import Upload from "@iconify-icons/ri/upload-line";
|
||||
|
|
Loading…
Reference in New Issue