[重大更新] 整体主题样式重写 改为卡片式布局 样式更企业化
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.2",
|
||||
"@highlightjs/vue-plugin": "2.1.2",
|
||||
"@iconify/vue": "^5.0.0",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "14.2.1",
|
||||
"animate.css": "4.1.1",
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
/*-------------Dialog-------------**/
|
||||
.el-overlay {
|
||||
overflow: hidden;
|
||||
background: var(--app-overlay-mask);
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
.el-overlay-dialog {
|
||||
display: flex;
|
||||
@@ -71,21 +73,72 @@
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
|
||||
.el-dialog {
|
||||
margin: 0 auto !important;
|
||||
border-radius: var(--app-radius-lg);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0)) padding-box,
|
||||
var(--app-surface-bg);
|
||||
|
||||
.el-dialog__header {
|
||||
padding: 18px 20px 14px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
||||
margin-right: 0;
|
||||
background: var(--app-elevated-soft-bg);
|
||||
}
|
||||
|
||||
.el-dialog__title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
color: var(--app-text-title);
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
top: 15px;
|
||||
right: 16px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: var(--app-elevated-close-bg);
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease,
|
||||
color 0.2s ease;
|
||||
|
||||
.el-dialog__close {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--app-elevated-close-hover-bg);
|
||||
transform: translateY(-1px);
|
||||
|
||||
.el-dialog__close {
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 15px !important;
|
||||
padding: 18px 20px 12px !important;
|
||||
}
|
||||
.el-dialog__header {
|
||||
padding: 16px 16px 8px 16px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid var(--brder-color);
|
||||
margin-right: 0;
|
||||
|
||||
.el-dialog__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 14px 20px 20px;
|
||||
border-top: 1px solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +150,10 @@
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.el-dialog__footer .el-button {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
// refine element ui upload
|
||||
.upload-container {
|
||||
.el-upload {
|
||||
@@ -111,8 +168,46 @@
|
||||
|
||||
// dropdown
|
||||
.el-dropdown-menu {
|
||||
border-radius: var(--app-radius-md);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
padding: 8px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
|
||||
.el-dropdown-menu__item {
|
||||
min-height: 36px;
|
||||
margin: 2px 0;
|
||||
border-radius: 10px;
|
||||
color: var(--app-text-title);
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: var(--app-accent-soft);
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item.is-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item--divided {
|
||||
margin-top: 8px;
|
||||
position: relative;
|
||||
border-top: none;
|
||||
|
||||
&::before {
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
top: -6px;
|
||||
width: auto;
|
||||
background: rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
@@ -128,7 +223,7 @@
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.el-menu--collapse > div > .el-submenu > .el-submenu__title .el-submenu__icon-arrow {
|
||||
.el-menu--collapse > div > .el-sub-menu > .el-sub-menu__title .el-sub-menu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -152,14 +247,104 @@
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.el-form--inline .el-date-editor {
|
||||
width: 308px;
|
||||
}
|
||||
|
||||
/* 设置 el-message-box 消息弹框内容强制换行 */
|
||||
.el-message-box .el-message-box__message {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.el-message-box {
|
||||
border-radius: var(--app-radius-lg);
|
||||
width: min(440px, calc(100vw - 32px));
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
border-radius: 20px;
|
||||
box-shadow: var(--app-shadow-md);
|
||||
|
||||
.el-message-box__header {
|
||||
padding: 18px 20px 0;
|
||||
}
|
||||
|
||||
.el-message-box__title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-message-box__headerbtn {
|
||||
top: 14px;
|
||||
right: 16px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: var(--app-elevated-close-bg);
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
|
||||
.el-message-box__close {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--app-elevated-close-hover-bg);
|
||||
transform: translateY(-1px);
|
||||
|
||||
.el-message-box__close {
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__content {
|
||||
padding: 16px 20px 18px;
|
||||
}
|
||||
|
||||
.el-message-box__container {
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.el-message-box__status {
|
||||
position: static;
|
||||
margin-top: 1px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px !important;
|
||||
background: var(--app-elevated-soft-bg);
|
||||
}
|
||||
|
||||
.el-message-box__message {
|
||||
color: var(--app-text-title);
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.el-message-box__btns {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 16px 20px 20px;
|
||||
border-top: 1px solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.el-message-box__btns .el-button {
|
||||
margin-left: 0;
|
||||
min-width: 88px;
|
||||
}
|
||||
|
||||
.el-message-box__input {
|
||||
padding-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message,
|
||||
@@ -169,6 +354,281 @@
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
.el-message {
|
||||
min-width: 320px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
|
||||
.el-message__icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.el-message__content {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-message__closeBtn {
|
||||
color: var(--app-text-muted);
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-message--success {
|
||||
border-color: var(--el-color-success-light-7);
|
||||
background: var(--el-color-success-light-9);
|
||||
}
|
||||
|
||||
.el-message--warning {
|
||||
border-color: var(--el-color-warning-light-7);
|
||||
background: var(--el-color-warning-light-9);
|
||||
}
|
||||
|
||||
.el-message--error {
|
||||
border-color: var(--el-color-danger-light-7);
|
||||
background: var(--el-color-danger-light-9);
|
||||
}
|
||||
|
||||
.el-message--info {
|
||||
border-color: var(--el-color-primary-light-7);
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.el-notification {
|
||||
width: 340px;
|
||||
padding: 16px 18px 16px 16px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
|
||||
.el-notification__group {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.el-notification__title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-notification__content {
|
||||
margin-top: 6px;
|
||||
font-size: 13px;
|
||||
line-height: 1.65;
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.el-notification__closeBtn {
|
||||
top: 18px;
|
||||
color: var(--app-text-muted);
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-notification.right,
|
||||
.el-notification.left {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.el-notification--success {
|
||||
border-color: var(--el-color-success-light-7);
|
||||
}
|
||||
|
||||
.el-notification--warning {
|
||||
border-color: var(--el-color-warning-light-7);
|
||||
}
|
||||
|
||||
.el-notification--error {
|
||||
border-color: var(--el-color-danger-light-7);
|
||||
}
|
||||
|
||||
.el-notification--info {
|
||||
border-color: var(--el-color-primary-light-7);
|
||||
}
|
||||
|
||||
.el-select__popper.el-popper,
|
||||
.el-picker__popper.el-popper,
|
||||
.el-autocomplete__popper.el-popper,
|
||||
.el-cascader__dropdown,
|
||||
.el-dropdown__popper.el-popper,
|
||||
.el-popover.el-popper {
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
}
|
||||
|
||||
.el-select__popper.el-popper,
|
||||
.el-autocomplete__popper.el-popper,
|
||||
.el-cascader__dropdown,
|
||||
.el-dropdown__popper.el-popper,
|
||||
.el-popover.el-popper {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.el-select-dropdown__list,
|
||||
.el-autocomplete-suggestion__list,
|
||||
.el-cascader-menu {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item,
|
||||
.el-autocomplete-suggestion li,
|
||||
.el-cascader-node {
|
||||
min-height: 38px;
|
||||
margin: 2px 0;
|
||||
border-radius: 10px;
|
||||
color: var(--app-text-title);
|
||||
line-height: 38px;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.hover,
|
||||
.el-select-dropdown__item:hover,
|
||||
.el-autocomplete-suggestion li:hover,
|
||||
.el-cascader-node:not(.is-disabled):hover,
|
||||
.el-cascader-node.in-active-path {
|
||||
background: var(--app-accent-soft);
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.is-selected,
|
||||
.el-autocomplete-suggestion li.highlighted {
|
||||
background: rgba(64, 158, 255, 0.12);
|
||||
color: var(--app-accent-strong);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.is-disabled,
|
||||
.el-cascader-node.is-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.el-autocomplete-suggestion li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.el-autocomplete-suggestion li div {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.el-cascader-menu {
|
||||
min-width: 180px;
|
||||
border-right-color: rgba(148, 163, 184, 0.12);
|
||||
}
|
||||
|
||||
.el-cascader-node__prefix,
|
||||
.el-cascader-node__postfix {
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.el-popper.is-dark {
|
||||
border-color: rgba(15, 23, 42, 0.72);
|
||||
background: rgba(15, 23, 42, 0.92);
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.el-popper.is-dark .el-popper__arrow::before {
|
||||
background: rgba(15, 23, 42, 0.92);
|
||||
border-color: rgba(15, 23, 42, 0.72);
|
||||
}
|
||||
|
||||
.el-popper.is-light {
|
||||
border-color: var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.el-popper.is-light .el-popper__arrow::before {
|
||||
background: var(--app-surface-bg);
|
||||
border-color: var(--app-surface-border);
|
||||
}
|
||||
|
||||
.el-picker__popper.el-popper {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.el-date-picker,
|
||||
.el-date-range-picker,
|
||||
.el-time-panel,
|
||||
.el-time-range-picker,
|
||||
.el-picker-panel {
|
||||
background: transparent;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-picker-panel__body-wrapper,
|
||||
.el-date-range-picker__content {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.el-picker-panel__icon-btn,
|
||||
.el-date-picker__header-label,
|
||||
.el-date-range-picker__header-label {
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-date-table td,
|
||||
.el-month-table td .cell,
|
||||
.el-year-table td .cell {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.el-date-table td.today .el-date-table-cell__text,
|
||||
.el-month-table td.today .cell,
|
||||
.el-year-table td.today .cell {
|
||||
color: var(--app-accent-strong);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.el-date-table td.available:hover,
|
||||
.el-month-table td .cell:hover,
|
||||
.el-year-table td .cell:hover {
|
||||
color: var(--app-accent-strong);
|
||||
background: var(--app-accent-soft);
|
||||
}
|
||||
|
||||
.el-date-table td.current:not(.disabled) .el-date-table-cell__text,
|
||||
.el-month-table td.current:not(.disabled) .cell,
|
||||
.el-year-table td.current:not(.disabled) .cell {
|
||||
background: var(--app-accent-strong);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-date-editor .el-range-separator {
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.el-time-panel__content::before,
|
||||
.el-time-panel__content::after {
|
||||
border-color: rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.el-scrollbar__thumb {
|
||||
background: rgba(148, 163, 184, 0.42);
|
||||
}
|
||||
|
||||
.el-scrollbar__thumb:hover {
|
||||
background: rgba(100, 116, 139, 0.56);
|
||||
}
|
||||
|
||||
// Modern rounded inputs
|
||||
.el-input__wrapper,
|
||||
.el-textarea__inner,
|
||||
@@ -193,11 +653,10 @@
|
||||
// Buttons
|
||||
.el-button {
|
||||
border-radius: var(--app-radius-md);
|
||||
transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
|
||||
transition: box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.el-button:not(.is-text):not(.is-link):hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
@@ -214,32 +673,200 @@
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
.el-popover {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.el-tooltip__popper {
|
||||
max-width: 320px;
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.el-popconfirm {
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.el-popconfirm__main {
|
||||
gap: 10px;
|
||||
line-height: 1.6;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-popconfirm__action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.el-popconfirm__action .el-button {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.el-drawer {
|
||||
border-radius: var(--app-radius-lg);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
}
|
||||
|
||||
.el-drawer__header {
|
||||
margin-bottom: 0;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
padding: 18px 20px 16px;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
||||
background: var(--app-elevated-soft-bg);
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-drawer__title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-drawer__body {
|
||||
padding: 18px 20px 20px;
|
||||
}
|
||||
|
||||
.el-drawer__close-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: var(--app-elevated-close-bg);
|
||||
color: var(--app-text-muted);
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--app-elevated-close-hover-bg);
|
||||
color: var(--app-accent-strong);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.el-loading-mask {
|
||||
background: rgba(248, 250, 252, 0.76);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.el-loading-spinner .path {
|
||||
stroke: var(--app-accent-strong);
|
||||
}
|
||||
|
||||
.el-descriptions {
|
||||
--el-descriptions-table-border: rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.el-descriptions__body .el-descriptions__table {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-descriptions__label.el-descriptions__cell {
|
||||
background: var(--app-elevated-soft-bg) !important;
|
||||
color: var(--app-text-title);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-descriptions__content.el-descriptions__cell {
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.el-tree {
|
||||
color: var(--app-text-title);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.el-tree-node__content {
|
||||
min-height: 34px;
|
||||
margin: 2px 0;
|
||||
border-radius: 10px;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
}
|
||||
|
||||
.el-tree-node__content:hover,
|
||||
.el-tree-node.is-current > .el-tree-node__content {
|
||||
background: var(--app-accent-soft);
|
||||
}
|
||||
|
||||
.el-radio-button__inner,
|
||||
.el-checkbox-button__inner {
|
||||
border-radius: 10px !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.el-radio-button:first-child .el-radio-button__inner,
|
||||
.el-checkbox-button:first-child .el-checkbox-button__inner,
|
||||
.el-radio-button:last-child .el-radio-button__inner,
|
||||
.el-checkbox-button:last-child .el-checkbox-button__inner {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.el-empty__image {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.el-empty__description p {
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.el-color-dropdown {
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
}
|
||||
|
||||
html.dark {
|
||||
.el-message--success {
|
||||
background: rgba(31, 138, 90, 0.18);
|
||||
}
|
||||
|
||||
.el-message--warning {
|
||||
background: rgba(184, 121, 34, 0.18);
|
||||
}
|
||||
|
||||
.el-message--error {
|
||||
background: rgba(178, 74, 74, 0.18);
|
||||
}
|
||||
|
||||
.el-message--info {
|
||||
background: rgba(43, 107, 211, 0.18);
|
||||
}
|
||||
|
||||
.el-loading-mask {
|
||||
background: rgba(6, 11, 22, 0.7);
|
||||
}
|
||||
|
||||
.el-popper.is-dark {
|
||||
border-color: rgba(30, 41, 59, 0.92);
|
||||
background: rgba(15, 23, 42, 0.96);
|
||||
}
|
||||
|
||||
.el-popper.is-dark .el-popper__arrow::before {
|
||||
background: rgba(15, 23, 42, 0.96);
|
||||
border-color: rgba(30, 41, 59, 0.92);
|
||||
}
|
||||
}
|
||||
|
||||
// Table polish
|
||||
.el-table {
|
||||
border-radius: var(--app-radius-lg);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
}
|
||||
|
||||
.el-table__header-wrapper,
|
||||
.el-table__body-wrapper {
|
||||
background: var(--el-bg-color);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.el-table__row:hover td.el-table__cell {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
background-color: rgba(53, 109, 255, 0.05) !important;
|
||||
}
|
||||
|
||||
// Tabs
|
||||
@@ -264,6 +891,27 @@
|
||||
transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.el-pagination .el-pagination__total,
|
||||
.el-pagination .el-pagination__jump {
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev,
|
||||
.el-pagination .btn-next,
|
||||
.el-pagination .el-pager li,
|
||||
.el-pagination .el-pagination__sizes .el-input__wrapper {
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
background: var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.el-pagination .el-pager li.is-active {
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
background: var(--el-bg-color-page);
|
||||
background: var(--app-shell-bg);
|
||||
font-family:
|
||||
'MiSans',
|
||||
'HarmonyOS Sans SC',
|
||||
@@ -44,6 +44,12 @@ html.dark svg {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
@@ -147,7 +153,7 @@ aside {
|
||||
|
||||
//main-container全局样式
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
padding: 16px;
|
||||
background: var(--app-surface-bg);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
border-radius: var(--app-radius-lg);
|
||||
@@ -159,19 +165,62 @@ aside {
|
||||
.search {
|
||||
margin-bottom: 0.75rem;
|
||||
border-radius: var(--app-radius-lg);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background-color: var(--app-surface-bg);
|
||||
padding: 0.75rem;
|
||||
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
|
||||
transition: box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border-color: var(--el-border-color);
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.el-card,
|
||||
.el-dialog,
|
||||
.el-drawer,
|
||||
.el-popover.el-popper,
|
||||
.el-dropdown__popper .el-dropdown-menu,
|
||||
.el-picker__popper,
|
||||
.el-select__popper.el-popper {
|
||||
border-radius: var(--app-radius-lg);
|
||||
border-color: var(--app-surface-border);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
}
|
||||
|
||||
.el-card,
|
||||
.el-dialog,
|
||||
.el-drawer {
|
||||
background: var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.el-button {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
--el-button-bg-color: var(--app-accent-strong);
|
||||
--el-button-border-color: var(--app-accent-strong);
|
||||
--el-button-hover-bg-color: #4a7cff;
|
||||
--el-button-hover-border-color: #4a7cff;
|
||||
--el-button-active-bg-color: #2e5ce0;
|
||||
--el-button-active-border-color: #2e5ce0;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
--el-table-border-color: var(--app-surface-border);
|
||||
--el-table-header-bg-color: var(--tableHeaderBg);
|
||||
--el-table-tr-bg-color: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.el-input__wrapper,
|
||||
.el-textarea__inner,
|
||||
.el-select__wrapper {
|
||||
box-shadow: 0 0 0 1px transparent inset;
|
||||
}
|
||||
|
||||
.components-container {
|
||||
margin: 30px 50px;
|
||||
position: relative;
|
||||
|
||||
+333
-15
@@ -76,6 +76,41 @@ h6 {
|
||||
.el-form .el-form-item__label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.app-main > .page-shell,
|
||||
.app-main > .page-shell.p-2 {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.p-2 > .el-row,
|
||||
.p-2 > .el-card,
|
||||
.p-2 > .search-wrap,
|
||||
.p-2 > div,
|
||||
.p-2 > section {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.page-shell > .el-row,
|
||||
.page-shell > .el-card,
|
||||
.page-shell > .search-wrap,
|
||||
.page-shell > div,
|
||||
.page-shell > section {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.el-dialog:not(.is-fullscreen) {
|
||||
margin-top: 6vh !important;
|
||||
}
|
||||
@@ -84,7 +119,7 @@ h6 {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 70vh;
|
||||
padding: 10px 20px 0;
|
||||
padding: 14px 22px 4px;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
@@ -118,17 +153,18 @@ h6 {
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
padding: 10px 20px !important;
|
||||
margin-top: 18px;
|
||||
padding: 10px 0 0 !important;
|
||||
}
|
||||
|
||||
/* tree border */
|
||||
.tree-border {
|
||||
margin-top: 5px;
|
||||
border: 1px solid #e5e6e7;
|
||||
background: #ffffff none;
|
||||
border-radius: var(--app-radius-md);
|
||||
margin-top: 8px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
background: rgba(255, 255, 255, 0.42) none;
|
||||
border-radius: 22px;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -188,27 +224,28 @@ h6 {
|
||||
}
|
||||
|
||||
.el-card__header {
|
||||
padding: 14px 15px 7px !important;
|
||||
min-height: 40px;
|
||||
background: var(--el-fill-color-blank);
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
padding: 14px 16px 10px !important;
|
||||
min-height: auto;
|
||||
background: transparent;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.el-card__body {
|
||||
padding: 15px 20px 20px 20px !important;
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
border-radius: var(--app-radius-lg);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border-color: var(--el-border-color-lighter);
|
||||
border-color: var(--app-surface-border);
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.2s ease, transform 0.2s ease;
|
||||
transition: box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
background: var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.el-card:hover {
|
||||
box-shadow: var(--app-shadow-md);
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(64, 158, 255, 0.16);
|
||||
}
|
||||
|
||||
.card-box {
|
||||
@@ -298,6 +335,287 @@ h6 {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.panel-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.panel-heading h3,
|
||||
.table-heading h3 {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.panel-kicker {
|
||||
display: none;
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.toolbar-shell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.table-heading p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 13px;
|
||||
color: var(--app-text-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.toolbar-actions::-webkit-scrollbar {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toolbar-actions > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toolbar-actions .top-right-btn {
|
||||
margin-left: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toolbar-actions .el-button + .el-button,
|
||||
.toolbar-actions .el-dropdown + .el-button,
|
||||
.toolbar-actions .el-button + .el-dropdown,
|
||||
.toolbar-actions .el-dropdown + .el-dropdown,
|
||||
.toolbar-actions .top-right-btn + .el-button,
|
||||
.toolbar-actions .el-button + .top-right-btn,
|
||||
.toolbar-actions .top-right-btn + .el-dropdown,
|
||||
.toolbar-actions .el-dropdown + .top-right-btn {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.table-panel .toolbar-actions .el-button:not(.is-circle):not(.is-link) {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.query-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 14px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.query-form .el-form-item {
|
||||
flex: 0 0 auto;
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.query-form .el-form-item__content {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child {
|
||||
margin-left: auto;
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child .el-form-item__content {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child .el-button {
|
||||
margin-left: 0 !important;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child .el-button + .el-button {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.query-form .el-input__wrapper,
|
||||
.query-form .el-select__wrapper,
|
||||
.query-form .el-textarea__inner,
|
||||
.query-form .el-date-editor,
|
||||
.query-form .el-range-editor,
|
||||
.query-form .el-cascader .el-input__wrapper,
|
||||
.query-form .el-input-number,
|
||||
.query-form .el-input-number__decrease,
|
||||
.query-form .el-input-number__increase {
|
||||
border-radius: 12px !important;
|
||||
}
|
||||
|
||||
.query-form .el-button {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.query-form {
|
||||
gap: 10px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.query-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.query-form .el-form-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.query-form .el-input,
|
||||
.query-form .el-select,
|
||||
.query-form .el-date-editor,
|
||||
.query-form .el-cascader,
|
||||
.query-form .el-input-number {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.data-table .el-button.is-link {
|
||||
width: 28px !important;
|
||||
height: 28px !important;
|
||||
min-width: 28px !important;
|
||||
padding: 0 !important;
|
||||
border-radius: 10px !important;
|
||||
background: rgba(53, 109, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
.data-table .el-button.is-link + .el-button.is-link {
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
|
||||
.search-wrap {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.el-card.search-panel,
|
||||
.el-card.side-panel {
|
||||
border-radius: 16px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-card.table-panel {
|
||||
border-radius: 14px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-card.search-panel .el-card__header,
|
||||
.el-card.side-panel .el-card__header {
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
}
|
||||
|
||||
.el-card.table-panel .el-card__header {
|
||||
border-top-left-radius: 14px;
|
||||
border-top-right-radius: 14px;
|
||||
}
|
||||
|
||||
.el-card.search-panel .el-card__body,
|
||||
.el-card.side-panel .el-card__body {
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
}
|
||||
|
||||
.el-card.table-panel .el-card__body {
|
||||
border-bottom-left-radius: 14px;
|
||||
border-bottom-right-radius: 14px;
|
||||
}
|
||||
|
||||
.search-panel {
|
||||
.el-card__header {
|
||||
display: block;
|
||||
padding: 12px 16px !important;
|
||||
}
|
||||
|
||||
.el-card__body {
|
||||
padding-top: 16px !important;
|
||||
overflow: hidden;
|
||||
max-height: 560px;
|
||||
opacity: 1;
|
||||
transition:
|
||||
max-height 0.32s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
opacity 0.24s ease,
|
||||
padding-top 0.24s ease,
|
||||
padding-bottom 0.24s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.search-panel.is-collapsed {
|
||||
.el-card__body {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.search-panel-toggle {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: color 0.24s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
margin-left: auto;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-right: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
color: var(--app-text-muted);
|
||||
transform: rotate(-135deg);
|
||||
transition:
|
||||
transform 0.24s ease,
|
||||
color 0.24s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.search-panel.is-collapsed .search-panel-toggle::after {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
:global(html.dark) {
|
||||
.tree-border {
|
||||
background: rgba(15, 23, 42, 0.42);
|
||||
border-color: rgba(100, 116, 139, 0.22);
|
||||
}
|
||||
}
|
||||
|
||||
/* horizontal el menu */
|
||||
.el-menu--horizontal .el-menu-item .svg-icon + span,
|
||||
.el-menu--horizontal .el-sub-menu__title .svg-icon + span {
|
||||
|
||||
+177
-58
@@ -4,9 +4,9 @@
|
||||
.main-container {
|
||||
height: 100%;
|
||||
transition: margin-left 0.28s;
|
||||
margin-left: $base-sidebar-width;
|
||||
margin-left: calc(#{$base-sidebar-width} + 12px);
|
||||
position: relative;
|
||||
background: var(--el-bg-color-page);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sidebarHide {
|
||||
@@ -17,17 +17,19 @@
|
||||
-webkit-transition: width 0.28s;
|
||||
transition: width 0.28s;
|
||||
width: $base-sidebar-width !important;
|
||||
background-color: $base-menu-background;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
height: calc(100vh - 24px);
|
||||
position: fixed;
|
||||
font-size: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
-webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
|
||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1);
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
border-right: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
@@ -49,10 +51,24 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.has-logo {
|
||||
.el-scrollbar {
|
||||
height: calc(100% - 50px);
|
||||
.el-scrollbar.theme-light {
|
||||
--side-menu-text: #1f2937;
|
||||
--side-menu-hover-bg: rgba(64, 158, 255, 0.08);
|
||||
--side-menu-hover-text: #111827;
|
||||
--side-menu-active-bg: rgba(64, 158, 255, 0.12);
|
||||
--side-menu-active-text: #409eff;
|
||||
--side-menu-active-border: rgba(64, 158, 255, 0.16);
|
||||
--side-menu-active-line: #409eff;
|
||||
}
|
||||
|
||||
.el-scrollbar.theme-dark {
|
||||
--side-menu-text: #e5edf8;
|
||||
--side-menu-hover-bg: rgba(255, 255, 255, 0.08);
|
||||
--side-menu-hover-text: #ffffff;
|
||||
--side-menu-active-bg: rgba(64, 158, 255, 0.22);
|
||||
--side-menu-active-text: #ffffff;
|
||||
--side-menu-active-border: rgba(96, 165, 250, 0.24);
|
||||
--side-menu-active-line: #60a5fa;
|
||||
}
|
||||
|
||||
.is-horizontal {
|
||||
@@ -73,6 +89,11 @@
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.el-menu--inline {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
@@ -82,89 +103,123 @@
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title,
|
||||
.el-menu-item .svg-icon,
|
||||
.el-sub-menu__title .svg-icon,
|
||||
.el-sub-menu__icon-arrow {
|
||||
color: var(--side-menu-text) !important;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-menu-item .el-menu-tooltip__trigger {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
// menu hover
|
||||
.theme-dark .sub-menu-title-noDropdown,
|
||||
.theme-dark .submenu-title-noDropdown,
|
||||
.theme-dark .el-sub-menu__title {
|
||||
border-radius: var(--app-radius-md);
|
||||
margin: 1px 5px 1px 5px;
|
||||
margin: 3px 8px;
|
||||
&:hover {
|
||||
background-color: $base-sub-menu-title-hover !important;
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
.sub-menu-title-noDropdown,
|
||||
.submenu-title-noDropdown,
|
||||
.el-sub-menu__title {
|
||||
border-radius: var(--app-radius-md);
|
||||
margin: 1px 5px 1px 5px;
|
||||
margin: 3px 8px;
|
||||
color: var(--side-menu-text) !important;
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05) !important;
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .is-active > .el-sub-menu__title {
|
||||
color: $base-menu-color-active !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: calc($base-sidebar-width - 20px) !important;
|
||||
min-width: calc($base-sidebar-width - 16px) !important;
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
&:not(.is-active):hover {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .el-sub-menu .el-menu-item {
|
||||
background-color: $base-sub-menu-background !important;
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff;
|
||||
background: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow:
|
||||
inset 3px 0 0 var(--side-menu-active-line),
|
||||
inset 0 0 0 1px var(--side-menu-active-border);
|
||||
}
|
||||
&:not(.is-active):hover {
|
||||
// you can use $sub-menuHover
|
||||
background-color: $base-sub-menu-hover !important;
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .el-menu-item {
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff;
|
||||
background: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow:
|
||||
inset 3px 0 0 var(--side-menu-active-line),
|
||||
inset 0 0 0 1px var(--side-menu-active-border);
|
||||
}
|
||||
&:not(.is-active):hover {
|
||||
// you can use $sub-menuHover
|
||||
background-color: $base-menu-hover !important;
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-menu-item {
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff;
|
||||
background: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow:
|
||||
inset 3px 0 0 var(--side-menu-active-line),
|
||||
inset 0 0 0 1px var(--side-menu-active-border);
|
||||
}
|
||||
&:not(.is-active):hover {
|
||||
// you can use $sub-menuHover
|
||||
background-color: rgba(0, 0, 0, 0.04) !important;
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,60 +227,124 @@
|
||||
// 收起菜单后的样式
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 54px !important;
|
||||
width: 58px !important;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
margin-left: 54px;
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.sub-menu-title-noDropdown {
|
||||
.sidebar-shell {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
height: 45px;
|
||||
height: 40px;
|
||||
width: 40px !important;
|
||||
min-width: 40px !important;
|
||||
margin: 3px auto !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center !important;
|
||||
box-sizing: border-box;
|
||||
// 选中状态的菜单
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff !important;
|
||||
background-color: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow: inset 3px 0 0 var(--side-menu-active-line);
|
||||
}
|
||||
|
||||
.el-tooltip {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-menu-tooltip__trigger {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
& .el-sub-menu {
|
||||
overflow: hidden;
|
||||
border-radius: var(--app-radius-md);
|
||||
.el-sub-menu__title,
|
||||
.el-sub-menu__title.el-tooltip__trigger {
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 45px;
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center !important;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
// 选中状态的菜单
|
||||
&.is-active .el-sub-menu__title,
|
||||
&.is-active .el-sub-menu__title.el-tooltip__trigger {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
background-color: var(--side-menu-active-bg) !important;
|
||||
box-shadow: inset 3px 0 0 var(--side-menu-active-line);
|
||||
}
|
||||
|
||||
|
||||
& > .el-sub-menu__title {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
> div > .el-sub-menu {
|
||||
width: 40px !important;
|
||||
min-width: 40px !important;
|
||||
margin: 3px auto !important;
|
||||
}
|
||||
|
||||
> div > .el-sub-menu > .el-sub-menu__title,
|
||||
> div > .el-sub-menu > .el-sub-menu__title.el-tooltip__trigger {
|
||||
width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
box-sizing: border-box;
|
||||
transform: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.is-active .svg-icon {
|
||||
fill: #fff;
|
||||
fill: currentColor;
|
||||
}
|
||||
.svg-icon {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
// 这里设置width会跟随sidebar-container的transition 不符合预期
|
||||
|
||||
> div > .el-sub-menu > .el-sub-menu__title .svg-icon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transform: translate(-50%, -50%);
|
||||
display: block;
|
||||
margin: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
.el-sub-menu {
|
||||
& > .el-sub-menu__title {
|
||||
|
||||
> div > .el-sub-menu > .el-sub-menu__title {
|
||||
justify-content: center !important;
|
||||
|
||||
.el-sub-menu__icon-arrow {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
margin: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
& > span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
@@ -243,7 +362,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse .el-menu .el-sub-menu {
|
||||
min-width: $base-sidebar-width !important;
|
||||
@@ -264,7 +382,7 @@
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(-$base-sidebar-width, 0, 0);
|
||||
transform: translate3d(calc(-#{$base-sidebar-width} - 12px), 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,6 +406,7 @@
|
||||
// 收起菜单后悬浮的菜单样式
|
||||
.el-popper.is-pure{
|
||||
border-radius: var(--app-radius-md);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
.el-menu--popup{
|
||||
border-radius: var(--app-radius-md);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// 全局SCSS变量
|
||||
:root {
|
||||
--menuBg: #1f2d3d;
|
||||
--menuColor: #bfcbd9;
|
||||
--menuActiveText: #f4f4f5;
|
||||
--menuHover: #263445;
|
||||
--menuBg: #ffffff;
|
||||
--menuColor: #1f2937;
|
||||
--menuActiveText: #111827;
|
||||
--menuHover: rgba(64, 158, 255, 0.12);
|
||||
|
||||
--subMenuBg: #1f2d3d;
|
||||
--subMenuActiveText: #f4f4f5;
|
||||
--subMenuHover: #001528;
|
||||
--subMenuTitleHover: #293444;
|
||||
--subMenuBg: #ffffff;
|
||||
--subMenuActiveText: #111827;
|
||||
--subMenuHover: rgba(64, 158, 255, 0.12);
|
||||
--subMenuTitleHover: rgba(64, 158, 255, 0.08);
|
||||
|
||||
// 菜单栏缩进
|
||||
--el-menu-base-level-padding: 12px;
|
||||
@@ -16,8 +16,8 @@
|
||||
--el-menu-item-height: 50px;
|
||||
|
||||
--fixedHeaderBg: #ffffff;
|
||||
--tableHeaderBg: #f8f8f9;
|
||||
--tableHeaderTextColor: #515a6e;
|
||||
--tableHeaderBg: #f8fafc;
|
||||
--tableHeaderTextColor: #475569;
|
||||
|
||||
// ele
|
||||
--brder-color: #e8e8e8;
|
||||
@@ -28,13 +28,28 @@
|
||||
|
||||
// Modern rounded style + soft shadows
|
||||
--app-radius-sm: 6px;
|
||||
--app-radius-md: 10px;
|
||||
--app-radius-lg: 14px;
|
||||
--app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.08);
|
||||
--app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.12);
|
||||
--app-shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.16);
|
||||
--app-radius-md: 12px;
|
||||
--app-radius-lg: 18px;
|
||||
--app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
--app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.06);
|
||||
--app-shadow-lg: 0 14px 36px rgba(15, 23, 42, 0.1);
|
||||
--app-surface-bg: #ffffff;
|
||||
--app-surface-border: var(--el-border-color-lighter);
|
||||
--app-surface-border: #e5e7eb;
|
||||
--app-shell-bg: #f5f7fa;
|
||||
--app-shell-spot-1: rgba(64, 158, 255, 0.08);
|
||||
--app-shell-spot-2: rgba(64, 158, 255, 0.04);
|
||||
--app-sidebar-border: #e5e7eb;
|
||||
--app-navbar-bg: #ffffff;
|
||||
--app-navbar-border: #e5e7eb;
|
||||
--app-navbar-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
--app-accent-soft: rgba(64, 158, 255, 0.08);
|
||||
--app-accent-strong: #409eff;
|
||||
--app-text-title: #1f2937;
|
||||
--app-text-muted: #6b7280;
|
||||
--app-overlay-mask: rgba(15, 23, 42, 0.22);
|
||||
--app-elevated-soft-bg: #f8fafc;
|
||||
--app-elevated-close-bg: rgba(148, 163, 184, 0.14);
|
||||
--app-elevated-close-hover-bg: rgba(64, 158, 255, 0.14);
|
||||
|
||||
// Element Plus tokens
|
||||
--el-border-radius-base: var(--app-radius-md);
|
||||
@@ -42,21 +57,21 @@
|
||||
--el-border-radius-round: 999px;
|
||||
--el-box-shadow-light: var(--app-shadow-sm);
|
||||
--el-box-shadow: var(--app-shadow-md);
|
||||
--el-bg-color-page: #f5f7fb;
|
||||
--el-bg-color-page: var(--app-shell-bg);
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--menuBg: #1d1e1f;
|
||||
--menuColor: #bfcbd9;
|
||||
--menuActiveText: #f4f4f5;
|
||||
--menuHover: #171819;
|
||||
--menuBg: #111827;
|
||||
--menuColor: #e5edf8;
|
||||
--menuActiveText: #f8fbff;
|
||||
--menuHover: rgba(85, 129, 255, 0.2);
|
||||
|
||||
--subMenuBg: #1d1e1f;
|
||||
--subMenuActiveText: #1d1e1f;
|
||||
--subMenuHover: #171819;
|
||||
--subMenuTitleHover: #171819;
|
||||
--subMenuBg: #111827;
|
||||
--subMenuActiveText: #f8fbff;
|
||||
--subMenuHover: rgba(85, 129, 255, 0.18);
|
||||
--subMenuTitleHover: rgba(148, 163, 184, 0.12);
|
||||
|
||||
--fixedHeaderBg: #171819;
|
||||
--fixedHeaderBg: #0f172a;
|
||||
--tableHeaderBg: var(--el-bg-color);
|
||||
--tableHeaderTextColor: var(--el-text-color);
|
||||
|
||||
@@ -204,12 +219,27 @@ html.dark {
|
||||
--tags-view-active-border-color: var(--el-color-primary-light-2);
|
||||
|
||||
// Modern rounded style + soft shadows (dark)
|
||||
--app-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.28), 0 8px 18px rgba(0, 0, 0, 0.25);
|
||||
--app-shadow-md: 0 10px 26px rgba(0, 0, 0, 0.35);
|
||||
--app-shadow-lg: 0 14px 34px rgba(0, 0, 0, 0.4);
|
||||
--app-surface-bg: #151922;
|
||||
--app-surface-border: var(--el-border-color);
|
||||
--el-bg-color-page: #0f1115;
|
||||
--app-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.28);
|
||||
--app-shadow-md: 0 12px 28px rgba(0, 0, 0, 0.28);
|
||||
--app-shadow-lg: 0 20px 44px rgba(0, 0, 0, 0.32);
|
||||
--app-surface-bg: #111827;
|
||||
--app-surface-border: rgba(71, 85, 105, 0.5);
|
||||
--app-shell-bg: #060b16;
|
||||
--app-shell-spot-1: rgba(69, 120, 255, 0.18);
|
||||
--app-shell-spot-2: rgba(45, 212, 191, 0.14);
|
||||
--app-sidebar-border: rgba(148, 163, 184, 0.1);
|
||||
--app-navbar-bg: #111827;
|
||||
--app-navbar-border: rgba(71, 85, 105, 0.48);
|
||||
--app-navbar-shadow: 0 1px 2px rgba(0, 0, 0, 0.22);
|
||||
--app-accent-soft: rgba(64, 158, 255, 0.14);
|
||||
--app-accent-strong: #60a5fa;
|
||||
--app-text-title: #f3f7ff;
|
||||
--app-text-muted: #8ea0bd;
|
||||
--app-overlay-mask: rgba(2, 6, 23, 0.58);
|
||||
--app-elevated-soft-bg: rgba(148, 163, 184, 0.08);
|
||||
--app-elevated-close-bg: rgba(148, 163, 184, 0.18);
|
||||
--app-elevated-close-hover-bg: rgba(96, 165, 250, 0.18);
|
||||
--el-bg-color-page: var(--app-shell-bg);
|
||||
// vxe-table 主题
|
||||
--vxe-font-color: #98989e;
|
||||
--vxe-primary-color: #2c7ecf;
|
||||
|
||||
@@ -78,12 +78,18 @@ onMounted(() => {
|
||||
<style lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
margin-left: 4px;
|
||||
color: var(--app-text-muted);
|
||||
|
||||
:deep(.el-breadcrumb__inner) {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
color: var(--app-text-title);
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,19 @@
|
||||
|
||||
<el-input v-model="filterValue" class="p-2" placeholder="搜索图标" clearable @input="filterIcons" />
|
||||
|
||||
<div class="iconify-panel">
|
||||
<div class="iconify-heading">也可以直接输入 Iconify 图标名</div>
|
||||
<div class="iconify-form">
|
||||
<el-input
|
||||
v-model="customIcon"
|
||||
placeholder="例如:mdi:account-circle-outline"
|
||||
clearable
|
||||
@keyup.enter="applyCustomIcon"
|
||||
/>
|
||||
<el-button type="primary" plain @click="applyCustomIcon">使用</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-scrollbar height="w-[200px]">
|
||||
<ul class="icon-list">
|
||||
<el-tooltip v-for="(iconName, index) in iconNames" :key="index" :content="iconName" placement="bottom" effect="light">
|
||||
@@ -44,6 +57,7 @@ const { modelValue, width } = toRefs(props);
|
||||
const iconNames = ref<string[]>(icons);
|
||||
|
||||
const filterValue = ref('');
|
||||
const customIcon = ref('');
|
||||
|
||||
/**
|
||||
* 筛选图标
|
||||
@@ -63,6 +77,21 @@ const selectedIcon = (iconName: string) => {
|
||||
emit('update:modelValue', iconName);
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const applyCustomIcon = () => {
|
||||
const value = customIcon.value.trim();
|
||||
if (!value) return;
|
||||
emit('update:modelValue', value);
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
customIcon.value = value?.includes(':') ? value : '';
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -73,31 +102,59 @@ const selectedIcon = (iconName: string) => {
|
||||
.el-divider--horizontal {
|
||||
margin: 10px auto !important;
|
||||
}
|
||||
|
||||
.iconify-panel {
|
||||
padding: 2px 4px 12px;
|
||||
}
|
||||
|
||||
.iconify-heading {
|
||||
margin-bottom: 8px;
|
||||
color: var(--app-text-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.iconify-form {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.icon-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
|
||||
gap: 8px;
|
||||
padding: 4px;
|
||||
margin-top: 10px;
|
||||
|
||||
.icon-item {
|
||||
cursor: pointer;
|
||||
width: 10%;
|
||||
margin: 0 10px 10px 0;
|
||||
padding: 5px;
|
||||
min-height: 44px;
|
||||
padding: 8px 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
border-radius: 12px;
|
||||
background: var(--app-surface-bg);
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
background: var(--app-accent-soft);
|
||||
color: var(--el-color-primary);
|
||||
transition: all 0.2s;
|
||||
transform: scaleX(1.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
border-color: var(--el-color-primary);
|
||||
background: rgba(64, 158, 255, 0.12);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,15 @@ const handleLanguageChange = (lang: any) => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.lang-select--style {
|
||||
font-size: 18px;
|
||||
line-height: 50px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
:deep(.svg-icon) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -68,8 +68,15 @@ function handleCurrentChange(val: number) {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-top: 18px;
|
||||
margin-top: 18px;
|
||||
border-top: 1px solid rgba(148, 163, 184, 0.14);
|
||||
|
||||
.el-pagination {
|
||||
float: v-bind(float);
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
.pagination-container.hidden {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="top-right-btn" :style="style">
|
||||
<el-row>
|
||||
<el-row class="toolbar-row">
|
||||
<el-tooltip v-if="search" class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top">
|
||||
<el-button circle icon="Search" @click="toggleSearch()" />
|
||||
</el-tooltip>
|
||||
@@ -79,6 +79,27 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-button.is-circle) {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.62);
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
color: var(--app-text-muted);
|
||||
transition:
|
||||
transform 0.25s ease,
|
||||
border-color 0.25s ease,
|
||||
color 0.25s ease,
|
||||
background 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
color: var(--app-accent-strong);
|
||||
background: var(--app-accent-soft);
|
||||
border-color: rgba(53, 109, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-transfer__button) {
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
@@ -91,12 +112,27 @@ onMounted(() => {
|
||||
.my-el-transfer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbar-row {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tree-header {
|
||||
width: 100%;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.show-btn {
|
||||
margin-left: 12px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
:global(html.dark) {
|
||||
.top-right-btn :deep(.el-button.is-circle) {
|
||||
background: rgba(15, 23, 42, 0.56);
|
||||
border-color: rgba(100, 116, 139, 0.22);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog v-model="roleDialog.visible.value" :title="roleDialog.title.value" width="80%" append-to-body>
|
||||
<el-dialog v-model="roleDialog.visible.value" :title="roleDialog.title.value" width="80%" append-to-body class="role-select-dialog">
|
||||
<div class="page-shell role-select-shell">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div v-show="showSearch">
|
||||
<el-card shadow="hover" class="search-panel selector-card">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -21,15 +22,23 @@
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel selector-card">
|
||||
<template #header>
|
||||
<el-tag v-for="role in selectRoleList" :key="role.roleId" closable style="margin: 2px" @close="handleCloseTag(role)">
|
||||
<div class="toolbar-shell selector-header">
|
||||
<div class="table-heading">
|
||||
<h3>角色列表</h3>
|
||||
</div>
|
||||
<div v-if="selectRoleList.length" class="selector-tags">
|
||||
<el-tag v-for="role in selectRoleList" :key="role.roleId" closable @close="handleCloseTag(role)">
|
||||
{{ role.roleName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
class="selector-table"
|
||||
height="400px"
|
||||
border
|
||||
show-overflow
|
||||
@@ -66,6 +75,7 @@
|
||||
@pagination="pageList"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="confirm">确定</el-button>
|
||||
@@ -91,7 +101,6 @@ const prop = withDefaults(defineProps<PropType>(), {
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
|
||||
|
||||
@@ -248,3 +257,66 @@ defineExpose({
|
||||
close: roleDialog.closeDialog
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.role-select-shell {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.selector-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.selector-header {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.selector-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
max-width: min(100%, 520px);
|
||||
}
|
||||
|
||||
.selector-tags :deep(.el-tag) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.role-select-dialog :deep(.el-dialog__body) {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.selector-table {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-table--render-default) {
|
||||
border-radius: 10px;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-header--column) {
|
||||
background: var(--tableHeaderBg);
|
||||
color: var(--tableHeaderTextColor);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-body--column),
|
||||
.selector-table :deep(.vxe-header--column) {
|
||||
border-color: var(--app-surface-border);
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-body--row.row--hover),
|
||||
.selector-table :deep(.vxe-body--row:hover) {
|
||||
background-color: rgba(53, 109, 255, 0.05);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.selector-tags {
|
||||
justify-content: flex-start;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -34,8 +34,15 @@ const handleSetSize = (size: 'large' | 'default' | 'small') => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.size-icon--style {
|
||||
font-size: 18px;
|
||||
line-height: 50px;
|
||||
padding-right: 7px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
:deep(.svg-icon) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
<template>
|
||||
<svg :class="svgClass" aria-hidden="true">
|
||||
<Icon v-if="isIconify" :icon="normalizedIcon" :class="svgClass" :style="iconStyle" aria-hidden="true" />
|
||||
<svg v-else :class="svgClass" aria-hidden="true" :style="iconStyle">
|
||||
<use :xlink:href="iconName" :fill="color" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
const props = defineProps({
|
||||
iconClass: propTypes.string.isRequired,
|
||||
className: propTypes.string.def(''),
|
||||
color: propTypes.string.def('')
|
||||
color: propTypes.string.def(''),
|
||||
size: propTypes.string.def('')
|
||||
});
|
||||
const iconName = computed(() => `#icon-${props.iconClass}`);
|
||||
const normalizedIcon = computed(() => props.iconClass.replace(/^i-/, ''));
|
||||
const isIconify = computed(() => normalizedIcon.value.includes(':'));
|
||||
const iconName = computed(() => `#icon-${normalizedIcon.value}`);
|
||||
const svgClass = computed(() => {
|
||||
if (props.className) {
|
||||
return `svg-icon ${props.className}`;
|
||||
}
|
||||
return 'svg-icon';
|
||||
});
|
||||
const iconStyle = computed(() => ({
|
||||
color: props.color || undefined,
|
||||
fontSize: props.size || undefined
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -36,5 +45,6 @@ const svgClass = computed(() => {
|
||||
position: relative;
|
||||
fill: currentColor;
|
||||
vertical-align: -2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
+209
-35
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<el-menu :default-active="activeMenu" mode="horizontal" :ellipsis="false" @select="handleSelect">
|
||||
<el-menu class="mix-topnav-menu" :default-active="activeMenu" mode="horizontal" :ellipsis="false" @select="handleSelect">
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item v-if="index < visibleNumber" :key="index" :style="{ '--theme': theme }" :index="item.path"
|
||||
<el-menu-item v-if="index < visibleNumber" :key="index" :index="item.path"
|
||||
><svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta ? item.meta.icon : ''" />
|
||||
{{ item.meta?.title }}</el-menu-item
|
||||
>
|
||||
</template>
|
||||
|
||||
<!-- 顶部菜单超出数量折叠 -->
|
||||
<el-sub-menu v-if="topMenus.length > visibleNumber" :style="{ '--theme': theme }" index="more">
|
||||
<el-sub-menu v-if="topMenus.length > visibleNumber" class="el-sub-menu__hide-arrow" popper-class="mix-topnav-popper" index="more">
|
||||
<template #title>更多菜单</template>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item v-if="index >= visibleNumber" :key="index" :index="item.path"
|
||||
@@ -29,8 +29,6 @@ import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
// 顶部栏初始数
|
||||
const visibleNumber = ref<number>(-1);
|
||||
// 当前激活菜单的 index
|
||||
const currentIndex = ref<string>();
|
||||
// 隐藏侧边栏路由
|
||||
const hideList = ['/index', '/user/profile'];
|
||||
|
||||
@@ -104,12 +102,14 @@ const activeMenu = computed(() => {
|
||||
});
|
||||
|
||||
const setVisibleNumber = () => {
|
||||
const width = document.body.getBoundingClientRect().width / 3;
|
||||
visibleNumber.value = parseInt(String(width / 85));
|
||||
let width = document.body.getBoundingClientRect().width;
|
||||
if (width >= 1000) {
|
||||
width -= 420;
|
||||
}
|
||||
visibleNumber.value = Math.max(1, Math.floor(width / 3 / 92) + 2);
|
||||
};
|
||||
|
||||
const handleSelect = (key: string) => {
|
||||
currentIndex.value = key;
|
||||
const route = routers.value.find((item) => item.path === key);
|
||||
if (isHttp(key)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
@@ -161,40 +161,214 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #999093 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
.mix-topnav-menu.el-menu--horizontal {
|
||||
--topbar-pill-bg: rgba(255, 255, 255, 0.7);
|
||||
--topbar-pill-hover-bg: rgba(255, 255, 255, 0.9);
|
||||
--topbar-pill-active-bg: rgba(255, 255, 255, 0.82);
|
||||
--topbar-pill-text: var(--app-text-title);
|
||||
--topbar-pill-muted: var(--app-text-muted);
|
||||
--topbar-pill-border: rgba(148, 163, 184, 0.12);
|
||||
--topbar-pill-active-border: rgba(148, 163, 184, 0.14);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active,
|
||||
.el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
|
||||
border-bottom: 2px solid #{'var(--theme)'} !important;
|
||||
color: #303133;
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-menu-item,
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 7px;
|
||||
height: 36px !important;
|
||||
line-height: 36px !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 18px !important;
|
||||
border: 1px solid transparent !important;
|
||||
border-radius: 13px;
|
||||
color: var(--topbar-pill-text) !important;
|
||||
background: transparent !important;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
/* sub-menu item */
|
||||
.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #999093 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-menu-item:hover,
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title:hover {
|
||||
background: linear-gradient(180deg, var(--topbar-pill-hover-bg), var(--topbar-pill-bg)) !important;
|
||||
border-color: var(--topbar-pill-border) !important;
|
||||
color: v-bind(theme) !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 背景色隐藏 */
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
|
||||
.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title:hover {
|
||||
background-color: #ffffff !important;
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-menu-item.is-active,
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
background: linear-gradient(180deg, var(--topbar-pill-hover-bg), var(--topbar-pill-active-bg)) !important;
|
||||
border-color: var(--topbar-pill-active-border) !important;
|
||||
color: v-bind(theme) !important;
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
/* 图标右间距 */
|
||||
.topmenu-container .svg-icon {
|
||||
margin-right: 4px;
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-menu-item .svg-icon,
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title .svg-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 0 !important;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-menu-item span,
|
||||
#app .mix-topnav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title span {
|
||||
color: inherit !important;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.mix-topnav-menu .el-sub-menu .el-sub-menu__icon-arrow {
|
||||
position: static;
|
||||
margin: 0 0 0 2px;
|
||||
display: block !important;
|
||||
color: inherit;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.mix-topnav-menu.el-menu--horizontal > .el-menu-item::after,
|
||||
.mix-topnav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mix-topnav-menu.el-menu--horizontal > .el-sub-menu.el-sub-menu__hide-arrow > .el-sub-menu__title {
|
||||
color: var(--topbar-pill-muted) !important;
|
||||
padding-right: 16px !important;
|
||||
}
|
||||
|
||||
.mix-topnav-popper.el-popper {
|
||||
border: none !important;
|
||||
border-radius: 18px !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.mix-topnav-popper.el-popper .el-popper__arrow {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup {
|
||||
min-width: 188px;
|
||||
padding: 10px !important;
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 250, 252, 0.94)) !important;
|
||||
box-shadow:
|
||||
0 18px 40px rgba(15, 23, 42, 0.12),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup .el-menu-item,
|
||||
.mix-topnav-popper .el-menu--popup .el-sub-menu__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
padding: 0 14px !important;
|
||||
margin: 0 0 4px !important;
|
||||
border-radius: 12px;
|
||||
color: var(--app-text-title) !important;
|
||||
background: transparent !important;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup .el-menu-item:last-child,
|
||||
.mix-topnav-popper .el-menu--popup .el-sub-menu:last-child > .el-sub-menu__title {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup .el-menu-item:hover,
|
||||
.mix-topnav-popper .el-menu--popup .el-sub-menu__title:hover {
|
||||
background: rgba(64, 158, 255, 0.08) !important;
|
||||
color: v-bind(theme) !important;
|
||||
transform: translateX(1px);
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup .el-menu-item.is-active,
|
||||
.mix-topnav-popper .el-menu--popup .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
background: linear-gradient(180deg, rgba(64, 158, 255, 0.16), rgba(64, 158, 255, 0.1)) !important;
|
||||
color: v-bind(theme) !important;
|
||||
box-shadow: inset 0 0 0 1px rgba(64, 158, 255, 0.12);
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup .svg-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup .el-sub-menu__title > span,
|
||||
.mix-topnav-popper .el-menu--popup .el-menu-item > span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.mix-topnav-popper .el-menu--popup .el-sub-menu__icon-arrow {
|
||||
position: static;
|
||||
margin-left: auto;
|
||||
margin-top: 0;
|
||||
font-size: 11px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
html.dark .mix-topnav-menu.el-menu--horizontal {
|
||||
--topbar-pill-bg: rgba(30, 41, 59, 0.64);
|
||||
--topbar-pill-hover-bg: rgba(51, 65, 85, 0.88);
|
||||
--topbar-pill-active-bg: rgba(51, 65, 85, 0.78);
|
||||
--topbar-pill-border: rgba(71, 85, 105, 0.28);
|
||||
--topbar-pill-active-border: rgba(71, 85, 105, 0.32);
|
||||
}
|
||||
|
||||
html.dark .mix-topnav-popper .el-menu--popup {
|
||||
border-color: rgba(71, 85, 105, 0.34);
|
||||
background: linear-gradient(180deg, rgba(15, 23, 42, 0.98), rgba(30, 41, 59, 0.94)) !important;
|
||||
box-shadow:
|
||||
0 20px 42px rgba(0, 0, 0, 0.34),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
html.dark .mix-topnav-popper .el-menu--popup .el-menu-item,
|
||||
html.dark .mix-topnav-popper .el-menu--popup .el-sub-menu__title {
|
||||
color: #e5edf8 !important;
|
||||
}
|
||||
|
||||
html.dark .mix-topnav-popper .el-menu--popup .el-menu-item:hover,
|
||||
html.dark .mix-topnav-popper .el-menu--popup .el-sub-menu__title:hover {
|
||||
background: rgba(96, 165, 250, 0.14) !important;
|
||||
}
|
||||
|
||||
html.dark .mix-topnav-popper .el-menu--popup .el-menu-item.is-active,
|
||||
html.dark .mix-topnav-popper .el-menu--popup .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
background: linear-gradient(180deg, rgba(37, 99, 235, 0.28), rgba(59, 130, 246, 0.18)) !important;
|
||||
box-shadow: inset 0 0 0 1px rgba(96, 165, 250, 0.16);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog v-model="userDialog.visible.value" :title="userDialog.title.value" width="80%" append-to-body>
|
||||
<el-row :gutter="20">
|
||||
<el-dialog v-model="userDialog.visible.value" :title="userDialog.title.value" width="80%" append-to-body class="user-select-dialog">
|
||||
<div class="page-shell user-select-shell">
|
||||
<el-row :gutter="12" class="selector-layout">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-col :lg="5" :xs="24">
|
||||
<el-card shadow="hover" class="side-panel selector-card selector-side-card">
|
||||
<template #header>
|
||||
<div class="table-heading">
|
||||
<h3>部门结构</h3>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="deptName" class="selector-dept-input" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
class="selector-tree"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' } as any"
|
||||
@@ -20,11 +26,12 @@
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<el-col :lg="19" :xs="24">
|
||||
<div class="page-shell user-select-main">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div v-show="showSearch">
|
||||
<el-card shadow="hover" class="search-panel selector-card">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -40,15 +47,23 @@
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template v-if="prop.multiple" #header>
|
||||
<el-tag v-for="user in selectUserList" :key="user.userId" closable style="margin: 2px" @close="handleCloseTag(user)">
|
||||
<el-card shadow="hover" class="table-panel selector-card">
|
||||
<template #header>
|
||||
<div class="toolbar-shell selector-header">
|
||||
<div class="table-heading">
|
||||
<h3>用户列表</h3>
|
||||
</div>
|
||||
<div v-if="prop.multiple && selectUserList.length" class="selector-tags">
|
||||
<el-tag v-for="user in selectUserList" :key="user.userId" closable @close="handleCloseTag(user)">
|
||||
{{ user.nickName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
class="selector-table"
|
||||
height="400px"
|
||||
border
|
||||
show-overflow
|
||||
@@ -86,8 +101,10 @@
|
||||
@pagination="pageList"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
@@ -309,3 +326,87 @@ defineExpose({
|
||||
close: userDialog.closeDialog
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-select-shell,
|
||||
.user-select-main {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.selector-layout {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.selector-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.selector-side-card {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.selector-dept-input {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.selector-tree {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.selector-header {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.selector-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
max-width: min(100%, 520px);
|
||||
}
|
||||
|
||||
.selector-tags :deep(.el-tag) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-select-dialog :deep(.el-dialog__body) {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.selector-table {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-table--render-default) {
|
||||
border-radius: 10px;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-header--column) {
|
||||
background: var(--tableHeaderBg);
|
||||
color: var(--tableHeaderTextColor);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-body--column),
|
||||
.selector-table :deep(.vxe-header--column) {
|
||||
border-color: var(--app-surface-border);
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-body--row.row--hover),
|
||||
.selector-table :deep(.vxe-body--row:hover) {
|
||||
background-color: rgba(53, 109, 255, 0.05);
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.selector-tree {
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
.selector-tags {
|
||||
justify-content: flex-start;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,11 +22,9 @@ const tagsViewStore = useTagsViewStore();
|
||||
|
||||
// 随机动画集合
|
||||
const animate = ref<string>('');
|
||||
const animationEnable = ref(useSettingsStore().animationEnable);
|
||||
watch(
|
||||
() => useSettingsStore().animationEnable,
|
||||
(val: boolean) => {
|
||||
animationEnable.value = val;
|
||||
if (val) {
|
||||
animate.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string;
|
||||
} else {
|
||||
@@ -36,10 +34,6 @@ watch(
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
addIframe();
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
addIframe();
|
||||
});
|
||||
@@ -53,26 +47,21 @@ function addIframe() {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-main {
|
||||
/* 50= navbar 50 */
|
||||
min-height: calc(100vh - 50px);
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 50px;
|
||||
.app-main.with-fixed-header {
|
||||
padding-top: 76px;
|
||||
min-height: calc(100vh - 76px);
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
.app-main {
|
||||
/* 84 = navbar + tags-view = 50 + 34 */
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 84px;
|
||||
}
|
||||
.app-main.with-fixed-header.with-tags-view {
|
||||
min-height: calc(100vh - 111px);
|
||||
padding-top: 111px;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
<template>
|
||||
<div class="navbar" :class="'nav' + navType">
|
||||
<div class="navbar-left">
|
||||
<div v-if="navType !== NavTypeEnum.TOP" class="hamburger-shell">
|
||||
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggle-click="toggleSideBar" />
|
||||
</div>
|
||||
<router-link v-else-if="showLogo" to="/" class="navtop-logo-shell">
|
||||
<img :src="appLogo" class="navtop-logo-icon" alt="logo" />
|
||||
</router-link>
|
||||
|
||||
<div class="nav-context">
|
||||
<breadcrumb v-if="navType == NavTypeEnum.LEFT" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="navType == NavTypeEnum.MIX" id="topmenu-container" class="topmenu-container" />
|
||||
|
||||
<template v-if="navType == NavTypeEnum.TOP">
|
||||
<logo v-show="showLogo" :collapse="false"></logo>
|
||||
<top-bar id="topbar-container" class="topbar-container" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-menu flex align-center">
|
||||
<template v-if="appStore.device !== 'mobile'">
|
||||
<search-menu ref="searchMenuRef" />
|
||||
@@ -23,7 +31,7 @@
|
||||
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
|
||||
<template #reference>
|
||||
<el-badge :value="newNotice > 0 ? newNotice : ''" :max="99">
|
||||
<div class="right-menu-item hover-effect" style="display: block"><svg-icon icon-class="message" /></div>
|
||||
<div class="right-menu-item hover-effect message-trigger"><svg-icon icon-class="message" /></div>
|
||||
</el-badge>
|
||||
</template>
|
||||
<template #default>
|
||||
@@ -53,10 +61,14 @@
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<div class="avatar-container">
|
||||
<el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
|
||||
<el-dropdown class="avatar-dropdown" trigger="click" @command="handleCommand">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="userStore.avatar" class="user-avatar" />
|
||||
<el-icon><caret-bottom /></el-icon>
|
||||
<div class="avatar-meta">
|
||||
<span class="avatar-name">{{ displayName }}</span>
|
||||
<span class="avatar-role">Workspace</span>
|
||||
</div>
|
||||
<el-icon class="avatar-arrow"><caret-bottom /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
@@ -87,8 +99,9 @@ import notice from './notice/index.vue';
|
||||
import router from '@/router';
|
||||
import type { ElMessageBoxOptions } from 'element-plus';
|
||||
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||
import Logo from "@/layout/components/Sidebar/Logo.vue";
|
||||
import TopBar from './TopBar/index.vue';
|
||||
import { CaretBottom } from '@element-plus/icons-vue';
|
||||
import appLogo from '@/assets/logo/logo.png';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
@@ -98,9 +111,9 @@ const newNotice = ref(<number>0);
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const userId = ref(userStore.userId);
|
||||
const navType = computed(() => settingsStore.navType);
|
||||
const showLogo = computed(() => settingsStore.sidebarLogo);
|
||||
const displayName = computed(() => userStore.nickname || '管理员');
|
||||
|
||||
// 搜索菜单
|
||||
const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
|
||||
@@ -157,8 +170,56 @@ watch(
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar.navtop {
|
||||
.hamburger-container {
|
||||
display: none !important;
|
||||
.nav-context {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.navtop-logo-shell {
|
||||
width: 48px;
|
||||
height: 40px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(248, 250, 252, 0.82));
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.72),
|
||||
0 6px 14px rgba(15, 23, 42, 0.04);
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(64, 158, 255, 0.22);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.76),
|
||||
0 10px 22px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.navtop-logo-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.topbar-container {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 0;
|
||||
padding: 4px 8px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(248, 250, 252, 0.58));
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.72),
|
||||
0 6px 18px rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +228,17 @@ watch(
|
||||
}
|
||||
|
||||
:deep(.el-badge__content.is-fixed) {
|
||||
top: 12px;
|
||||
top: 8px;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
:deep(.el-badge) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-dropdown) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.flex {
|
||||
@@ -179,42 +250,71 @@ watch(
|
||||
}
|
||||
|
||||
.navbar {
|
||||
height: 50px;
|
||||
min-height: 52px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: var(--el-bg-color);
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
box-shadow: none;
|
||||
background: var(--app-navbar-bg);
|
||||
border: 1px solid var(--app-navbar-border);
|
||||
box-shadow: var(--app-navbar-shadow);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// padding: 0 8px;
|
||||
justify-content: space-between;
|
||||
border-radius: 16px;
|
||||
padding: 6px 12px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.navbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hamburger-shell {
|
||||
width: 36px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background: transparent;
|
||||
color: var(--app-accent-strong);
|
||||
flex-shrink: 0;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
}
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
line-height: 32px;
|
||||
height: 100%;
|
||||
//float: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-fill-color-lighter);
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-context {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
//float: left;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.topmenu-container {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
position: static;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.topbar-container {
|
||||
@@ -226,66 +326,159 @@ watch(
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
|
||||
.errLog-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.right-menu {
|
||||
//float: right;
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-left: auto;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.right-menu-item {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
font-size: 18px;
|
||||
color: var(--el-text-color-regular);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 16px;
|
||||
color: var(--app-text-muted);
|
||||
border-radius: 12px;
|
||||
vertical-align: text-bottom;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
flex-shrink: 0;
|
||||
|
||||
:deep(.svg-icon),
|
||||
:deep(svg),
|
||||
:deep(.el-icon) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.hover-effect {
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
transition:
|
||||
background 0.3s,
|
||||
color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-fill-color-lighter);
|
||||
background: var(--app-accent-soft);
|
||||
color: var(--app-accent-strong);
|
||||
border-color: rgba(64, 158, 255, 0.16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-trigger {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
margin-right: 40px;
|
||||
margin-left: 6px;
|
||||
margin-right: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-dropdown {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
margin-top: 5px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 4px 8px 4px 4px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
min-width: 0;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.3s,
|
||||
border-color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: var(--app-accent-soft);
|
||||
border-color: rgba(64, 158, 255, 0.16);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: var(--app-radius-md);
|
||||
margin-top: 10px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 12px;
|
||||
object-fit: cover;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 25px;
|
||||
.avatar-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.avatar-name {
|
||||
max-width: 88px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--app-text-title);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.avatar-role {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.avatar-arrow {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.dark) {
|
||||
.navbar.navtop .navtop-logo-shell {
|
||||
background: linear-gradient(180deg, rgba(15, 23, 42, 0.92), rgba(30, 41, 59, 0.82));
|
||||
border-color: rgba(71, 85, 105, 0.42);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06),
|
||||
0 8px 18px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.navbar.navtop .topbar-container {
|
||||
background: linear-gradient(180deg, rgba(15, 23, 42, 0.82), rgba(15, 23, 42, 0.7));
|
||||
border-color: rgba(71, 85, 105, 0.34);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.05),
|
||||
0 8px 22px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.navbar .right-menu .right-menu-item,
|
||||
.navbar .right-menu .avatar-wrapper {
|
||||
background: var(--app-navbar-bg);
|
||||
border-color: var(--app-navbar-border);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-drawer v-model="showSettings" :with-header="false" direction="rtl" size="300px" close-on-click-modal>
|
||||
<el-drawer v-model="showSettings" class="settings-drawer" :with-header="false" direction="rtl" size="300px" close-on-click-modal>
|
||||
<h3 class="drawer-title">菜单导航设置</h3>
|
||||
<div class="nav-wrap">
|
||||
<el-tooltip content="左侧菜单" placement="bottom">
|
||||
@@ -229,6 +229,15 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-drawer {
|
||||
:deep(.el-drawer__body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 18px 18px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
@@ -248,12 +257,23 @@ defineExpose({
|
||||
.setting-drawer-block-checbox-item {
|
||||
position: relative;
|
||||
margin-right: 16px;
|
||||
border-radius: 2px;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(64, 158, 255, 0.28);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.custom-img {
|
||||
@@ -281,6 +301,8 @@ defineExpose({
|
||||
.drawer-item {
|
||||
padding: 12px 0;
|
||||
font-size: 14px;
|
||||
color: var(--app-text-title);
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.08);
|
||||
|
||||
.comp-style {
|
||||
float: right;
|
||||
@@ -306,9 +328,16 @@ defineExpose({
|
||||
cursor: pointer;
|
||||
width: 56px;
|
||||
height: 48px;
|
||||
border-radius: 4px;
|
||||
background: #f0f2f5;
|
||||
border-radius: 12px;
|
||||
background: var(--app-elevated-soft-bg);
|
||||
border: 2px solid transparent;
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
border-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
@@ -317,6 +346,7 @@ defineExpose({
|
||||
height: 30%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
b:last-child {
|
||||
width: 30%;
|
||||
background: #1b2a47;
|
||||
@@ -326,6 +356,7 @@ defineExpose({
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.mix {
|
||||
b:first-child {
|
||||
border-radius: 4px 4px 0 0;
|
||||
@@ -333,6 +364,7 @@ defineExpose({
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
}
|
||||
|
||||
b:last-child {
|
||||
width: 30%;
|
||||
background: #1b2a47;
|
||||
@@ -341,6 +373,7 @@ defineExpose({
|
||||
border-radius: 0 0 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.top {
|
||||
b:first-child {
|
||||
display: block;
|
||||
@@ -348,6 +381,10 @@ defineExpose({
|
||||
background: #1b2a47;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
b:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import variables from '@/assets/styles/variables.module.scss';
|
||||
import logo from '@/assets/logo/logo.png';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -37,28 +36,11 @@ defineProps({
|
||||
const title = import.meta.env.VITE_APP_LOGO_TITLE;
|
||||
const settingsStore = useSettingsStore();
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
|
||||
// 获取Logo背景色
|
||||
const getLogoBackground = computed(() => {
|
||||
if (settingsStore.isDark) {
|
||||
return 'var(--sidebar-bg)'
|
||||
}
|
||||
if (settingsStore.navType == NavTypeEnum.TOP) {
|
||||
return variables.menuLightBackground
|
||||
}
|
||||
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBackground
|
||||
})
|
||||
|
||||
// 获取Logo文字颜色
|
||||
const getLogoTextColor = computed(() => {
|
||||
if (settingsStore.isDark) {
|
||||
return 'var(--sidebar-text)'
|
||||
}
|
||||
if (settingsStore.navType == NavTypeEnum.TOP) {
|
||||
return variables.logoLightTitleColor
|
||||
}
|
||||
return sideTheme.value === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor
|
||||
})
|
||||
const isTopNav = computed(() => settingsStore.navType === NavTypeEnum.TOP);
|
||||
const isDarkSide = computed(() => !isTopNav.value && sideTheme.value === 'theme-dark');
|
||||
const logoSurface = computed(() => (isDarkSide.value ? 'rgba(255, 255, 255, 0.04)' : '#f8fafc'));
|
||||
const logoBorder = computed(() => (isDarkSide.value ? 'rgba(148, 163, 184, 0.12)' : '#e5e7eb'));
|
||||
const logoTextColor = computed(() => (isDarkSide.value ? '#f8fbff' : 'var(--app-text-title)'));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -73,39 +55,61 @@ const getLogoTextColor = computed(() => {
|
||||
|
||||
.sidebar-logo-container {
|
||||
position: relative;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
background: v-bind(getLogoBackground);
|
||||
flex-shrink: 0;
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
padding: 0 8px;
|
||||
margin-top: 8px;
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0;
|
||||
|
||||
& .sidebar-logo-link {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
border-radius: 14px;
|
||||
background: v-bind(logoSurface);
|
||||
border: 1px solid v-bind(logoBorder);
|
||||
|
||||
& .sidebar-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
vertical-align: middle;
|
||||
margin-right: 12px;
|
||||
margin-left: 12px;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
& .sidebar-title {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
color: v-bind(getLogoTextColor);
|
||||
color: v-bind(logoTextColor);
|
||||
font-weight: 600;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||
line-height: 1;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.02em;
|
||||
font-family:
|
||||
'MiSans',
|
||||
'HarmonyOS Sans SC',
|
||||
'PingFang SC',
|
||||
sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
.sidebar-logo-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
margin-right: 0px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</app-link>
|
||||
</template>
|
||||
|
||||
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
|
||||
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" :popper-offset="isNest ? 4 : 12" :popper-class="popperClass" teleported>
|
||||
<template v-if="item.meta" #title>
|
||||
<svg-icon :icon-class="item.meta ? item.meta.icon : ''" />
|
||||
<span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
|
||||
@@ -22,6 +22,7 @@
|
||||
:key="child.path + index"
|
||||
:is-nest="true"
|
||||
:item="child"
|
||||
:popper-class="popperClass"
|
||||
:base-path="resolvePath(child.path)"
|
||||
class="nest-menu"
|
||||
/>
|
||||
@@ -47,6 +48,10 @@ const props = defineProps({
|
||||
basePath: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
popperClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: bgColor }">
|
||||
<div class="sidebar-shell" :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: bgColor }">
|
||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
|
||||
<transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in">
|
||||
@@ -24,7 +24,6 @@
|
||||
<script setup lang="ts">
|
||||
import Logo from './Logo.vue';
|
||||
import SidebarItem from './SidebarItem.vue';
|
||||
import variables from '@/assets/styles/variables.module.scss';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
@@ -51,6 +50,37 @@ const activeMenu = computed(() => {
|
||||
return path;
|
||||
});
|
||||
|
||||
const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground));
|
||||
const textColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor));
|
||||
const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? '#111827' : '#ffffff'));
|
||||
const textColor = computed(() => (sideTheme.value === 'theme-dark' ? '#e5edf8' : '#1f2937'));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sidebar-shell {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 10px 8px 12px;
|
||||
border: 1px solid var(--app-sidebar-border);
|
||||
border-radius: 22px;
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
background: v-bind(bgColor) !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-scrollbar__view) {
|
||||
min-height: 0;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
:deep(.el-scrollbar) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
<template>
|
||||
<el-menu class="topbar-menu" :ellipsis="false" :default-active="activeMenu" :active-text-color="theme" mode="horizontal">
|
||||
<sidebar-item :key="route.path + index" v-for="(route, index) in topMenus" :item="route" :base-path="route.path" />
|
||||
<sidebar-item :key="route.path + index" v-for="(route, index) in topMenus" :item="route" :base-path="route.path" popper-class="topbar-menu-popper" />
|
||||
|
||||
<el-sub-menu index="more" class="el-sub-menu__hide-arrow" v-if="moreRoutes.length > 0">
|
||||
<el-sub-menu index="more" class="el-sub-menu__hide-arrow" popper-class="topbar-menu-popper" v-if="moreRoutes.length > 0">
|
||||
<template #title>
|
||||
<span>更多菜单</span>
|
||||
</template>
|
||||
<sidebar-item :key="route.path + index" v-for="(route, index) in moreRoutes" :item="route" :base-path="route.path" />
|
||||
<sidebar-item :key="route.path + index" v-for="(route, index) in moreRoutes" :item="route" :base-path="route.path" popper-class="topbar-menu-popper" />
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import SidebarItem from '../Sidebar/SidebarItem'
|
||||
import {useAppStore} from '@/store/modules/app'
|
||||
import {useSettingsStore} from '@/store/modules/settings'
|
||||
import {usePermissionStore} from '@/store/modules/permission'
|
||||
|
||||
const route = useRoute()
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
|
||||
const theme = computed(() => settingsStore.theme)
|
||||
const device = computed(() => appStore.device)
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route
|
||||
if (meta.activeMenu) {
|
||||
@@ -38,14 +34,14 @@ const topMenus = computed(() => {
|
||||
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(0, visibleNumber.value)
|
||||
})
|
||||
const moreRoutes = computed(() => {
|
||||
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(visibleNumber.value, sidebarRouters.value.length - visibleNumber.value)
|
||||
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(visibleNumber.value)
|
||||
})
|
||||
function setVisibleNumber() {
|
||||
let width = document.body.getBoundingClientRect().width
|
||||
if (width >= 1000) {
|
||||
width -= 500
|
||||
width -= 420
|
||||
}
|
||||
visibleNumber.value = parseInt(width / 3 / 85)
|
||||
visibleNumber.value = Math.max(1, Math.floor(width / 3 / 92) + 2)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@@ -61,43 +57,234 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.topbar-menu.el-menu--horizontal {
|
||||
--topbar-pill-bg: rgba(255, 255, 255, 0.7);
|
||||
--topbar-pill-hover-bg: rgba(255, 255, 255, 0.9);
|
||||
--topbar-pill-active-bg: rgba(255, 255, 255, 0.82);
|
||||
--topbar-pill-text: var(--app-text-title);
|
||||
--topbar-pill-muted: var(--app-text-muted);
|
||||
--topbar-pill-border: rgba(148, 163, 184, 0.12);
|
||||
--topbar-pill-active-border: rgba(148, 163, 184, 0.14);
|
||||
|
||||
/* menu item */
|
||||
#app .topbar-menu.el-menu--horizontal .el-sub-menu__title, #app .topbar-menu.el-menu--horizontal .el-menu-item {
|
||||
padding: 0 10px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.topbar-menu.el-menu--horizontal > .el-menu-item {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #303133 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
#app .topbar-menu.el-menu--horizontal > div > a > .el-menu-item,
|
||||
#app .topbar-menu.el-menu--horizontal > div > .el-sub-menu > .el-sub-menu__title,
|
||||
#app .topbar-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 7px;
|
||||
height: 36px !important;
|
||||
line-height: 36px !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 18px !important;
|
||||
border: 1px solid transparent !important;
|
||||
border-radius: 13px;
|
||||
color: var(--topbar-pill-text) !important;
|
||||
background: transparent !important;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.el-sub-menu.is-active .svg-icon, .el-menu-item.is-active .svg-icon + span, .el-sub-menu.is-active .svg-icon + span, .el-sub-menu.is-active .el-sub-menu__title span {
|
||||
color: v-bind(theme);
|
||||
#app .topbar-menu.el-menu--horizontal > div > a > .el-menu-item:hover,
|
||||
#app .topbar-menu.el-menu--horizontal > div > .el-sub-menu > .el-sub-menu__title:hover,
|
||||
#app .topbar-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title:hover {
|
||||
background: linear-gradient(180deg, var(--topbar-pill-hover-bg), var(--topbar-pill-bg)) !important;
|
||||
border-color: var(--topbar-pill-border) !important;
|
||||
color: v-bind(theme) !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* sub-menu item */
|
||||
.topbar-menu.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
|
||||
float: left;
|
||||
line-height: 50px !important;
|
||||
color: #303133 !important;
|
||||
margin: 0 15px -3px!important;
|
||||
#app .topbar-menu.el-menu--horizontal > div > a > .el-menu-item.is-active,
|
||||
#app .topbar-menu.el-menu--horizontal > div > .el-sub-menu.is-active > .el-sub-menu__title,
|
||||
#app .topbar-menu.el-menu--horizontal > .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
background: linear-gradient(180deg, var(--topbar-pill-hover-bg), var(--topbar-pill-active-bg)) !important;
|
||||
border-color: var(--topbar-pill-active-border) !important;
|
||||
color: v-bind(theme) !important;
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
#app .topbar-menu.el-menu--horizontal > div > a > .el-menu-item .svg-icon,
|
||||
#app .topbar-menu.el-menu--horizontal > div > .el-sub-menu > .el-sub-menu__title .svg-icon,
|
||||
#app .topbar-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title .svg-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 0 !important;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#app .topbar-menu.el-menu--horizontal > div > a > .el-menu-item span,
|
||||
#app .topbar-menu.el-menu--horizontal > div > .el-sub-menu > .el-sub-menu__title span,
|
||||
#app .topbar-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title span {
|
||||
color: inherit !important;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.topbar-menu.el-menu--horizontal > div > a > .el-menu-item.is-active span,
|
||||
.topbar-menu.el-menu--horizontal > div > .el-sub-menu.is-active > .el-sub-menu__title span,
|
||||
.topbar-menu.el-menu--horizontal > .el-sub-menu.is-active > .el-sub-menu__title span {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* topbar more arrow */
|
||||
.topbar-menu .el-sub-menu .el-sub-menu__icon-arrow {
|
||||
position: static;
|
||||
margin-left: 8px;
|
||||
margin-top: 0px;
|
||||
margin: 0 0 0 2px;
|
||||
display: block !important;
|
||||
color: inherit;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* menu__title el-menu-item */
|
||||
.topbar-menu.el-menu--horizontal .el-sub-menu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
|
||||
height: 60px;
|
||||
.topbar-menu.el-menu--horizontal > div > a > .el-menu-item::after,
|
||||
.topbar-menu.el-menu--horizontal > div > .el-sub-menu > .el-sub-menu__title::after,
|
||||
.topbar-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.topbar-menu.el-menu--horizontal > .el-sub-menu.el-sub-menu__hide-arrow > .el-sub-menu__title {
|
||||
color: var(--topbar-pill-muted) !important;
|
||||
padding-right: 16px !important;
|
||||
}
|
||||
|
||||
html.dark .topbar-menu.el-menu--horizontal {
|
||||
--topbar-pill-bg: rgba(30, 41, 59, 0.64);
|
||||
--topbar-pill-hover-bg: rgba(51, 65, 85, 0.88);
|
||||
--topbar-pill-active-bg: rgba(51, 65, 85, 0.78);
|
||||
--topbar-pill-border: rgba(71, 85, 105, 0.28);
|
||||
--topbar-pill-active-border: rgba(71, 85, 105, 0.32);
|
||||
}
|
||||
|
||||
html.dark #app .topbar-menu.el-menu--horizontal > div > a > .el-menu-item.is-active,
|
||||
html.dark #app .topbar-menu.el-menu--horizontal > div > .el-sub-menu.is-active > .el-sub-menu__title,
|
||||
html.dark #app .topbar-menu.el-menu--horizontal > .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.topbar-menu-popper.el-popper {
|
||||
border: none !important;
|
||||
border-radius: 18px !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.topbar-menu-popper.el-popper .el-popper__arrow {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup {
|
||||
min-width: 188px;
|
||||
padding: 10px !important;
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 250, 252, 0.94)) !important;
|
||||
box-shadow:
|
||||
0 18px 40px rgba(15, 23, 42, 0.12),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup .el-menu-item,
|
||||
.topbar-menu-popper .el-menu--popup .el-sub-menu__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
padding: 0 14px !important;
|
||||
margin: 0 0 4px !important;
|
||||
border-radius: 12px;
|
||||
color: var(--app-text-title) !important;
|
||||
background: transparent !important;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup .el-menu-item:last-child,
|
||||
.topbar-menu-popper .el-menu--popup .el-sub-menu:last-child > .el-sub-menu__title {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup .el-menu-item:hover,
|
||||
.topbar-menu-popper .el-menu--popup .el-sub-menu__title:hover {
|
||||
background: rgba(64, 158, 255, 0.08) !important;
|
||||
color: v-bind(theme) !important;
|
||||
transform: translateX(1px);
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup .el-menu-item.is-active,
|
||||
.topbar-menu-popper .el-menu--popup .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
background: linear-gradient(180deg, rgba(64, 158, 255, 0.16), rgba(64, 158, 255, 0.1)) !important;
|
||||
color: v-bind(theme) !important;
|
||||
box-shadow: inset 0 0 0 1px rgba(64, 158, 255, 0.12);
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup .svg-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup .menu-title,
|
||||
.topbar-menu-popper .el-menu--popup .el-sub-menu__title > span,
|
||||
.topbar-menu-popper .el-menu--popup .el-menu-item > span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.topbar-menu-popper .el-menu--popup .el-sub-menu__icon-arrow {
|
||||
position: static;
|
||||
margin-left: auto;
|
||||
margin-top: 0;
|
||||
font-size: 11px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
html.dark .topbar-menu-popper .el-menu--popup {
|
||||
border-color: rgba(71, 85, 105, 0.34);
|
||||
background: linear-gradient(180deg, rgba(15, 23, 42, 0.98), rgba(30, 41, 59, 0.94)) !important;
|
||||
box-shadow:
|
||||
0 20px 42px rgba(0, 0, 0, 0.34),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
html.dark .topbar-menu-popper .el-menu--popup .el-menu-item,
|
||||
html.dark .topbar-menu-popper .el-menu--popup .el-sub-menu__title {
|
||||
color: #e5edf8 !important;
|
||||
}
|
||||
|
||||
html.dark .topbar-menu-popper .el-menu--popup .el-menu-item:hover,
|
||||
html.dark .topbar-menu-popper .el-menu--popup .el-sub-menu__title:hover {
|
||||
background: rgba(96, 165, 250, 0.14) !important;
|
||||
}
|
||||
|
||||
html.dark .topbar-menu-popper .el-menu--popup .el-menu-item.is-active,
|
||||
html.dark .topbar-menu-popper .el-menu--popup .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
background: linear-gradient(180deg, rgba(37, 99, 235, 0.28), rgba(59, 130, 246, 0.18)) !important;
|
||||
box-shadow: inset 0 0 0 1px rgba(96, 165, 250, 0.16);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -145,6 +145,9 @@ defineExpose({
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: -53vh;
|
||||
padding: 0;
|
||||
border-top: none;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
:deep(.el-autocomplete) {
|
||||
|
||||
@@ -66,59 +66,101 @@ onMounted(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-navbars-breadcrumb-user-news {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
|
||||
.head-box {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
||||
box-sizing: border-box;
|
||||
color: var(--el-text-color-primary);
|
||||
color: var(--app-text-title);
|
||||
justify-content: space-between;
|
||||
height: 35px;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
padding: 0 2px 10px;
|
||||
|
||||
.head-box-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.head-box-btn {
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-box {
|
||||
height: 300px;
|
||||
overflow: auto;
|
||||
font-size: 13px;
|
||||
padding: 8px 0;
|
||||
|
||||
.content-box-item {
|
||||
padding-top: 12px;
|
||||
display: flex;
|
||||
&:last-of-type {
|
||||
padding-bottom: 12px;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
padding: 12px;
|
||||
margin: 4px 0;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--app-accent-soft);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.content-box-msg {
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.content-box-time {
|
||||
color: var(--el-text-color-secondary);
|
||||
color: var(--app-text-muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.item-conten {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
color: var(--app-text-title);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.read {
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.foot-box {
|
||||
height: 35px;
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
border-top: 1px solid rgba(148, 163, 184, 0.14);
|
||||
margin-top: 4px;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
+19
-18
@@ -1,21 +1,13 @@
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<side-bar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<side-bar v-if="showSidebar" class="sidebar-container" />
|
||||
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
|
||||
<!-- <el-scrollbar>
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<navbar ref="navbarRef" @setLayout="setLayout" />
|
||||
<div :class="{ 'fixed-header': fixedHeader }" class="layout-header">
|
||||
<navbar @set-layout="setLayout" />
|
||||
<tags-view v-if="needTagsView" />
|
||||
</div>
|
||||
<app-main />
|
||||
<settings ref="settingRef" />
|
||||
</el-scrollbar> -->
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<navbar ref="navbarRef" @set-layout="setLayout" />
|
||||
<tags-view v-if="needTagsView" />
|
||||
</div>
|
||||
<app-main />
|
||||
<app-main :class="{ 'with-fixed-header': fixedHeader, 'with-tags-view': needTagsView }" />
|
||||
<settings ref="settingRef" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,7 +58,6 @@ watchEffect(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const navbarRef = ref<InstanceType<typeof Navbar>>();
|
||||
const settingRef = ref<InstanceType<typeof Settings>>();
|
||||
|
||||
onMounted(() => {
|
||||
@@ -96,6 +87,7 @@ const setLayout = () => {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: var(--app-shell-bg);
|
||||
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
@@ -105,7 +97,7 @@ const setLayout = () => {
|
||||
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
opacity: 0.4;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
@@ -113,18 +105,26 @@ const setLayout = () => {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.layout-header {
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
padding: 12px 12px 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$base-sidebar-width});
|
||||
width: calc(100% - #{$base-sidebar-width} - 12px);
|
||||
transition: width 0.28s;
|
||||
background: $fixed-header-bg;
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px);
|
||||
width: calc(100% - 70px);
|
||||
}
|
||||
|
||||
.sidebarHide .fixed-header {
|
||||
@@ -133,5 +133,6 @@ const setLayout = () => {
|
||||
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -71,7 +71,7 @@ export default {
|
||||
loadingInstance = ElLoading.service({
|
||||
lock: true,
|
||||
text: content,
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
background: 'var(--app-overlay-mask)'
|
||||
});
|
||||
},
|
||||
// 关闭遮罩层
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ const setting: DefaultSettings = {
|
||||
/**
|
||||
* 显示页签图标
|
||||
*/
|
||||
tagsIcon: false,
|
||||
tagsIcon: true,
|
||||
|
||||
/**
|
||||
* 是否固定头部
|
||||
@@ -46,7 +46,7 @@ const setting: DefaultSettings = {
|
||||
/**
|
||||
* 是否显示动态标题
|
||||
*/
|
||||
dynamicTitle: false,
|
||||
dynamicTitle: true,
|
||||
|
||||
/**
|
||||
* 是否开启动画 开启随机 关闭渐进渐出
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell demo-demo-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="key键" prop="testKey">
|
||||
<el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -17,30 +21,24 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>测试单列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['demo:demo:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
|
||||
<el-button v-hasPermi="['demo:demo:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="demoList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="demoList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="true" label="主键" align="center" prop="id" />
|
||||
<el-table-column label="部门id" align="center" prop="deptId" />
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell demo-tree-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="树节点名" prop="treeName">
|
||||
<el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -14,23 +18,24 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>测试树列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['demo:tree:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
ref="treeTableRef"
|
||||
v-loading="loading"
|
||||
class="data-table"
|
||||
:data="treeList"
|
||||
row-key="id"
|
||||
border
|
||||
|
||||
+346
-137
@@ -1,100 +1,101 @@
|
||||
<template>
|
||||
<div class="app-container home">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||
<h2>RuoYi-Vue-Plus后台管理系统</h2>
|
||||
<p>
|
||||
RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
|
||||
<br />
|
||||
* 前端开发框架 Vue3、TS、Element Plus<br />
|
||||
* 后端开发框架 Spring Boot<br />
|
||||
* 容器框架 Undertow 基于 Netty 的高性能容器<br />
|
||||
* 权限认证框架 Sa-Token 支持多终端认证系统<br />
|
||||
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
|
||||
* 缓存数据库 Redis 适配 6.X 最低 4.X<br />
|
||||
* 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br />
|
||||
* 数据库框架 p6spy 更强劲的 SQL 分析<br />
|
||||
* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br />
|
||||
* 序列化框架 Jackson 统一使用 jackson 高效可靠<br />
|
||||
* Redis客户端 Redisson 性能强劲、API丰富<br />
|
||||
* 分布式限流 Redisson 全局、请求IP、集群ID 多种限流<br />
|
||||
* 分布式锁 Lock4j 注解锁、工具锁 多种多样<br />
|
||||
* 分布式幂等 Lock4j 基于分布式锁实现<br />
|
||||
* 分布式链路追踪 SkyWalking 支持链路追踪、网格分析、度量聚合、可视化<br />
|
||||
* 分布式任务调度 SnailJob 高性能 高可靠 易扩展<br />
|
||||
* 文件存储 Minio 本地存储<br />
|
||||
* 文件存储 七牛、阿里、腾讯 云存储<br />
|
||||
* 监控框架 SpringBoot-Admin 全方位服务监控<br />
|
||||
* 校验框架 Validation 增强接口安全性 严谨性<br />
|
||||
* Excel框架 FastExcel(原Alibaba EasyExcel) 性能优异 扩展性强<br />
|
||||
* 文档框架 SpringDoc、javadoc 无注解零入侵基于java注释<br />
|
||||
* 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性<br />
|
||||
* 代码生成器 适配MP、SpringDoc规范化代码 一键生成前后端代码<br />
|
||||
* 部署方式 Docker 容器编排 一键部署业务集群<br />
|
||||
* 国际化 SpringMessage Spring标准国际化方案<br />
|
||||
</p>
|
||||
<p><b>当前版本:</b> <span>v5.5.3</span></p>
|
||||
<p>
|
||||
<el-tag type="danger">¥免费开源</el-tag>
|
||||
</p>
|
||||
<p>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
|
||||
>更新日志</el-button
|
||||
>
|
||||
</p>
|
||||
</el-col>
|
||||
<div class="home">
|
||||
<section class="hero-panel">
|
||||
<div class="hero-copy">
|
||||
<h1>RuoYi-Vue-Plus 控制台</h1>
|
||||
<p>企业级后台管理系统 重写 RuoYi-Vue 所有功能 集成 Sa-Token、Mybatis-Plus、WarmFlow、SpringDoc、Hutool、OSS 等组件</p>
|
||||
<div class="hero-actions">
|
||||
<el-button type="primary" @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">查看源码</el-button>
|
||||
<el-button plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')">更新日志</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||
<h2>RuoYi-Cloud-Plus微服务后台管理系统</h2>
|
||||
<p>
|
||||
RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
|
||||
<br />
|
||||
* 前端开发框架 Vue3、TS、Element UI<br />
|
||||
* 后端开发框架 Spring Boot<br />
|
||||
* 微服务开发框架 Spring Cloud、Spring Cloud Alibaba<br />
|
||||
* 容器框架 Undertow 基于 XNIO 的高性能容器<br />
|
||||
* 权限认证框架 Sa-Token、Jwt 支持多终端认证系统<br />
|
||||
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
|
||||
* 关系数据库 Oracle 适配 11g 12c<br />
|
||||
* 关系数据库 PostgreSQL 适配 13 14<br />
|
||||
* 关系数据库 SQLServer 适配 2017 2019<br />
|
||||
* 缓存数据库 Redis 适配 6.X 最低 5.X<br />
|
||||
* 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
|
||||
* 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
|
||||
* 服务网关 Spring Cloud Gateway 响应式高性能网关<br />
|
||||
* 负载均衡 Spring Cloud Loadbalancer 负载均衡处理<br />
|
||||
* RPC远程调用 Apache Dubbo 原生态使用体验、高性能<br />
|
||||
* 分布式限流熔断 Alibaba Sentinel 无侵入、高扩展<br />
|
||||
* 分布式事务 Alibaba Seata 无侵入、高扩展 支持 四种模式<br />
|
||||
* 分布式消息队列 Apache Kafka 高性能高速度<br />
|
||||
* 分布式消息队列 Apache RocketMQ 高可用功能多样<br />
|
||||
* 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性<br />
|
||||
* 分布式搜索引擎 ElasticSearch 业界知名<br />
|
||||
* 分布式链路追踪 Apache SkyWalking 链路追踪、网格分析、度量聚合、可视化<br />
|
||||
* 分布式日志中心 ELK 业界成熟解决方案<br />
|
||||
* 分布式监控 Prometheus、Grafana 全方位性能监控<br />
|
||||
* 其余与 Vue 版本一致<br />
|
||||
</p>
|
||||
<p><b>当前版本:</b> <span>v2.5.3</span></p>
|
||||
<p>
|
||||
<el-tag type="danger">¥免费开源</el-tag>
|
||||
</p>
|
||||
<p>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
|
||||
>更新日志</el-button
|
||||
>
|
||||
</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<div class="content-grid">
|
||||
<section class="section-card">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2>项目矩阵</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-list">
|
||||
<article v-for="product in products" :key="product.name" class="product-card">
|
||||
<div class="product-top">
|
||||
<div>
|
||||
<h3>{{ product.name }}</h3>
|
||||
<p>{{ product.summary }}</p>
|
||||
</div>
|
||||
<span class="product-version">{{ product.version }}</span>
|
||||
</div>
|
||||
<div class="product-tags">
|
||||
<el-tag v-for="tag in product.tags" :key="tag" effect="plain">{{ tag }}</el-tag>
|
||||
</div>
|
||||
<div class="product-actions">
|
||||
<el-button type="primary" plain @click="goTarget(product.primaryUrl)">{{ product.primaryLabel }}</el-button>
|
||||
<el-button plain @click="goTarget(product.secondaryUrl)">{{ product.secondaryLabel }}</el-button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card capability-card">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2>能力地图</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="capability-groups">
|
||||
<article v-for="group in capabilityGroups" :key="group.title" class="capability-group">
|
||||
<h3>{{ group.title }}</h3>
|
||||
<ul>
|
||||
<li v-for="item in group.items" :key="item">{{ item }}</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Index" lang="ts">
|
||||
const products = [
|
||||
{
|
||||
name: 'RuoYi-Vue-Plus',
|
||||
version: 'v5.5.3',
|
||||
summary: '面向分布式集群场景的后台管理系统,保持现有业务接口与权限逻辑,适合先完成前端壳升级。',
|
||||
tags: ['Vue 3', 'Element Plus', 'Spring Boot', 'Sa-Token'],
|
||||
primaryLabel: '访问 GitHub',
|
||||
primaryUrl: 'https://github.com/dromara/RuoYi-Vue-Plus',
|
||||
secondaryLabel: '查看更新日志',
|
||||
secondaryUrl: 'https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog'
|
||||
},
|
||||
{
|
||||
name: 'RuoYi-Cloud-Plus',
|
||||
version: 'v2.5.3',
|
||||
summary: '微服务通用权限管理系统,适合更复杂的服务治理场景,也可以沿用同样的前端升级思路。',
|
||||
tags: ['Spring Cloud', 'Gateway', 'Nacos', 'Dubbo'],
|
||||
primaryLabel: '访问 GitHub',
|
||||
primaryUrl: 'https://github.com/dromara/RuoYi-Cloud-Plus',
|
||||
secondaryLabel: '查看文档',
|
||||
secondaryUrl: 'https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog'
|
||||
}
|
||||
];
|
||||
|
||||
const capabilityGroups = [
|
||||
{
|
||||
title: '后端基建',
|
||||
items: ['Spring Boot / Spring Cloud', 'Sa-Token 认证与权限', 'MySQL / Redis', '代码生成器与国际化']
|
||||
},
|
||||
{
|
||||
title: '平台能力',
|
||||
items: ['动态菜单与按钮权限', '监控、日志、在线用户', '任务调度与工作流', '文件存储与多云适配']
|
||||
},
|
||||
{
|
||||
title: '前端方向',
|
||||
items: ['UI 壳层重构', '主题与布局统一', '通用页面容器规范化', '逐模块迁移业务页']
|
||||
}
|
||||
];
|
||||
|
||||
const goTarget = (url: string) => {
|
||||
window.open(url, '__blank');
|
||||
};
|
||||
@@ -102,64 +103,272 @@ const goTarget = (url: string) => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home {
|
||||
blockquote {
|
||||
padding: 10px 20px;
|
||||
margin: 0 0 20px;
|
||||
font-size: 17.5px;
|
||||
border-left: 5px solid #eee;
|
||||
}
|
||||
hr {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.col-item {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
.hero-panel,
|
||||
.section-card {
|
||||
border-radius: 28px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.hero-panel {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.3fr) minmax(280px, 0.7fr);
|
||||
gap: 18px;
|
||||
padding: 30px;
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(53, 109, 255, 0.16), transparent 30%),
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.68), rgba(255, 255, 255, 0.42));
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #676a6c;
|
||||
overflow-x: hidden;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
font-size: 26px;
|
||||
font-weight: 100;
|
||||
font-size: clamp(30px, 4vw, 46px);
|
||||
line-height: 1.06;
|
||||
letter-spacing: -0.04em;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
margin: 0;
|
||||
max-width: 760px;
|
||||
color: var(--app-text-muted);
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
b {
|
||||
.hero-badge {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(53, 109, 255, 0.12);
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 18px 20px;
|
||||
border-radius: 22px;
|
||||
background: rgba(255, 255, 255, 0.54);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
strong {
|
||||
color: var(--app-text-title);
|
||||
font-size: 24px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.15fr) minmax(300px, 0.85fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
|
||||
h2 {
|
||||
margin: 6px 0 0;
|
||||
color: var(--app-text-title);
|
||||
font-size: 26px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
}
|
||||
|
||||
.section-kicker {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.product-list,
|
||||
.capability-groups {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
padding: 22px;
|
||||
border-radius: 24px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
transition:
|
||||
transform 0.25s ease,
|
||||
box-shadow 0.25s ease,
|
||||
border-color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border-color: rgba(53, 109, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.product-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
color: var(--app-text-title);
|
||||
font-size: 22px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--app-text-muted);
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
.product-version {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(53, 109, 255, 0.12);
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.product-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.update-log {
|
||||
ol {
|
||||
display: block;
|
||||
list-style-type: decimal;
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
padding-inline-start: 40px;
|
||||
.product-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.capability-card {
|
||||
background: radial-gradient(circle at top right, rgba(14, 165, 233, 0.12), transparent 28%), var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.capability-group {
|
||||
padding: 18px 18px 18px 20px;
|
||||
border-radius: 22px;
|
||||
background: rgba(255, 255, 255, 0.46);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
|
||||
h3 {
|
||||
margin: 0 0 12px;
|
||||
color: var(--app-text-title);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
padding-left: 16px;
|
||||
color: var(--app-text-muted);
|
||||
line-height: 1.7;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 10px;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--app-accent-strong);
|
||||
box-shadow: 0 0 0 5px rgba(53, 109, 255, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.hero-panel,
|
||||
.content-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.hero-panel,
|
||||
.section-card {
|
||||
padding: 20px;
|
||||
border-radius: 22px;
|
||||
}
|
||||
|
||||
.product-top {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.dark) {
|
||||
.hero-panel {
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(53, 109, 255, 0.18), transparent 30%),
|
||||
linear-gradient(135deg, rgba(15, 23, 42, 0.76), rgba(15, 23, 42, 0.48));
|
||||
}
|
||||
|
||||
.stat-card,
|
||||
.product-card,
|
||||
.capability-group {
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
border-color: rgba(100, 116, 139, 0.16);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+324
-106
@@ -1,15 +1,40 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<div class="login-shell">
|
||||
<section class="login-brand">
|
||||
<span class="brand-pill">Plus UI Workspace</span>
|
||||
<h1 class="brand-title">企业级后台管理系统</h1>
|
||||
<p class="brand-desc">
|
||||
真正面向企业级的应用框架 组件化 模块化 轻耦合 高扩展 针对企业痛点 业界一流技术栈<br />
|
||||
重写 RuoYi-Vue 所有功能 集成 Sa-Token、Mybatis-Plus、WarmFlow、SpringDoc、Hutool、OSS 定期同步。
|
||||
</p>
|
||||
<div class="brand-highlights">
|
||||
<span v-for="item in highlights" :key="item" class="highlight-chip">{{ item }}</span>
|
||||
</div>
|
||||
<div class="brand-metrics">
|
||||
<article v-for="item in quickStats" :key="item.label" class="metric-card">
|
||||
<strong>{{ item.value }}</strong>
|
||||
<span>{{ item.label }}</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<div class="title-box">
|
||||
<div>
|
||||
<p class="eyebrow">Workspace Sign In</p>
|
||||
<h3 class="title">{{ title }}</h3>
|
||||
<p class="subtitle">使用当前账号体系登录到业务工作台。</p>
|
||||
</div>
|
||||
<lang-select />
|
||||
</div>
|
||||
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" :placeholder="proxy.$t('login.username')">
|
||||
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
@@ -22,23 +47,24 @@
|
||||
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="captchaEnabled" prop="code">
|
||||
<el-input
|
||||
v-model="loginForm.code"
|
||||
size="large"
|
||||
auto-complete="off"
|
||||
:placeholder="proxy.$t('login.code')"
|
||||
style="width: 63%"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
|
||||
<el-form-item v-if="captchaEnabled" prop="code" class="captcha-row">
|
||||
<el-input v-model="loginForm.code" size="large" auto-complete="off" :placeholder="proxy.$t('login.code')" @keyup.enter="handleLogin">
|
||||
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
<div class="login-code">
|
||||
<img :src="codeUrl" class="login-code-img" @click="getCode" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">{{ proxy.$t('login.rememberPassword') }}</el-checkbox>
|
||||
<el-form-item style="float: right">
|
||||
|
||||
<div class="form-meta">
|
||||
<el-checkbox v-model="loginForm.rememberMe">{{ proxy.$t('login.rememberPassword') }}</el-checkbox>
|
||||
<router-link v-if="register" class="link-type" :to="'/register'">{{ proxy.$t('login.switchRegisterPage') }}</router-link>
|
||||
</div>
|
||||
|
||||
<div class="social-panel">
|
||||
<span class="social-label">第三方登录</span>
|
||||
<div class="social-actions">
|
||||
<el-button circle :title="proxy.$t('login.social.wechat')" @click="doSocialLogin('wechat')">
|
||||
<svg-icon icon-class="wechat" />
|
||||
</el-button>
|
||||
@@ -54,20 +80,20 @@
|
||||
<el-button circle :title="proxy.$t('login.social.github')" @click="doSocialLogin('github')">
|
||||
<svg-icon icon-class="github" />
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%">
|
||||
<el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleLogin">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form-item class="submit-row">
|
||||
<el-button :loading="loading" size="large" type="primary" class="submit-button" @click.prevent="handleLogin">
|
||||
<span v-if="!loading">{{ proxy.$t('login.login') }}</span>
|
||||
<span v-else>{{ proxy.$t('login.logging') }}</span>
|
||||
</el-button>
|
||||
<div v-if="register" style="float: right">
|
||||
<router-link class="link-type" :to="'/register'">{{ proxy.$t('login.switchRegisterPage') }}</router-link>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
</div>
|
||||
|
||||
<div class="el-login-footer">
|
||||
<span>Copyright © 2018-2026 疯狂的狮子Li All Rights Reserved.</span>
|
||||
<span>Copyright © 2018-{{ currentYear }} 疯狂的狮子Li All Rights Reserved.</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -84,6 +110,13 @@ import { useI18n } from 'vue-i18n';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const title = import.meta.env.VITE_APP_TITLE;
|
||||
const currentYear = new Date().getFullYear();
|
||||
const quickStats = [
|
||||
{ label: '细粒度权限管理', value: '动态权限控制' },
|
||||
{ label: '主流技术栈', value: '全栈技术集成' },
|
||||
{ label: 'UI样式', value: '卡片式' }
|
||||
];
|
||||
const highlights = ['技术栈全面升级', '动态菜单', '多主题布局', '深浅色主题'];
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
@@ -104,10 +137,8 @@ const loginRules: ElFormRules = {
|
||||
|
||||
const codeUrl = ref('');
|
||||
const loading = ref(false);
|
||||
// 验证码开关
|
||||
const captchaEnabled = ref(true);
|
||||
// 注册开关
|
||||
const register = ref(false);
|
||||
const register = ref(true);
|
||||
const redirect = ref('/');
|
||||
const loginRef = ref<ElFormInstance>();
|
||||
|
||||
@@ -123,18 +154,15 @@ const handleLogin = () => {
|
||||
loginRef.value?.validate(async (valid: boolean, fields: any) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
// 勾选了需要记住密码设置在 localStorage 中设置记住用户名和密码
|
||||
if (loginForm.value.rememberMe) {
|
||||
localStorage.setItem('username', String(loginForm.value.username));
|
||||
localStorage.setItem('password', String(loginForm.value.password));
|
||||
localStorage.setItem('rememberMe', String(loginForm.value.rememberMe));
|
||||
} else {
|
||||
// 否则移除
|
||||
localStorage.removeItem('username');
|
||||
localStorage.removeItem('password');
|
||||
localStorage.removeItem('rememberMe');
|
||||
}
|
||||
// 调用action的登录方法
|
||||
const [err] = await to(userStore.login(loginForm.value));
|
||||
if (!err) {
|
||||
const redirectUrl = redirect.value || '/';
|
||||
@@ -142,7 +170,6 @@ const handleLogin = () => {
|
||||
loading.value = false;
|
||||
} else {
|
||||
loading.value = false;
|
||||
// 重新获取验证码
|
||||
if (captchaEnabled.value) {
|
||||
await getCode();
|
||||
}
|
||||
@@ -153,15 +180,11 @@ const handleLogin = () => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
const getCode = async () => {
|
||||
const res = await getCodeImg();
|
||||
const { data } = res;
|
||||
captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled;
|
||||
if (captchaEnabled.value) {
|
||||
// 刷新验证码时清空输入框
|
||||
loginForm.value.code = '';
|
||||
codeUrl.value = 'data:image/gif;base64,' + data.img;
|
||||
loginForm.value.uuid = data.uuid;
|
||||
@@ -179,14 +202,9 @@ const getLoginData = () => {
|
||||
} as LoginData;
|
||||
};
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
* @param type
|
||||
*/
|
||||
const doSocialLogin = (type: string) => {
|
||||
authRouterUrl(type).then((res: any) => {
|
||||
if (res.code === HttpStatus.SUCCESS) {
|
||||
// 获取授权地址跳转
|
||||
window.location.href = res.data;
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
@@ -202,105 +220,266 @@ onMounted(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-image: url('../assets/images/login-background.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
padding: 40px 24px 88px;
|
||||
background:
|
||||
radial-gradient(circle at 12% 12%, rgba(53, 109, 255, 0.22), transparent 24%),
|
||||
radial-gradient(circle at 88% 18%, rgba(14, 165, 233, 0.18), transparent 24%), linear-gradient(135deg, #071120 0%, #0f1b33 42%, #15345f 100%);
|
||||
}
|
||||
|
||||
.title-box {
|
||||
.login-shell {
|
||||
width: min(1180px, 100%);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.05fr) minmax(360px, 440px);
|
||||
gap: 26px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.login-brand,
|
||||
.login-form {
|
||||
border-radius: 28px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
box-shadow: 0 30px 80px rgba(2, 8, 23, 0.32);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
padding: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.title {
|
||||
margin: 0px auto 26px auto;
|
||||
text-align: center;
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: #eef4ff;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.02)),
|
||||
linear-gradient(135deg, rgba(53, 109, 255, 0.32), rgba(15, 23, 42, 0.24));
|
||||
}
|
||||
|
||||
:deep(.lang-select--style) {
|
||||
line-height: 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
.brand-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
margin: 22px 0 14px;
|
||||
font-size: clamp(34px, 4vw, 52px);
|
||||
line-height: 1.08;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.brand-desc {
|
||||
margin: 0;
|
||||
max-width: 580px;
|
||||
color: rgba(226, 232, 240, 0.88);
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.brand-highlights {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin: 28px 0 34px;
|
||||
}
|
||||
|
||||
.highlight-chip {
|
||||
padding: 9px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
color: #f8fbff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.brand-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
padding: 18px 16px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
strong {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span {
|
||||
color: rgba(226, 232, 240, 0.72);
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form {
|
||||
border-radius: var(--app-radius-lg);
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
width: min(420px, 90vw);
|
||||
padding: 32px 30px 12px 30px;
|
||||
width: 100%;
|
||||
padding: 34px 30px 26px;
|
||||
z-index: 1;
|
||||
box-shadow: var(--app-shadow-lg);
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
.el-input {
|
||||
height: 40px;
|
||||
input {
|
||||
height: 40px;
|
||||
background: rgba(248, 250, 252, 0.92);
|
||||
}
|
||||
|
||||
.title-box {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 28px;
|
||||
|
||||
.eyebrow {
|
||||
margin: 0 0 8px;
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
color: var(--app-text-title);
|
||||
font-weight: 700;
|
||||
font-size: 30px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 8px 0 0;
|
||||
color: var(--app-text-muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
:deep(.lang-select--style) {
|
||||
line-height: 0;
|
||||
color: var(--app-text-muted);
|
||||
padding: 10px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
height: 39px;
|
||||
.login-form .el-input {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.login-form .input-icon {
|
||||
height: 46px;
|
||||
width: 14px;
|
||||
margin-left: 0px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.captcha-row {
|
||||
:deep(.el-form-item__content) {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 122px;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-tip {
|
||||
.form-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin: -2px 0 18px;
|
||||
}
|
||||
|
||||
.social-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 22px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.58);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.social-label {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
color: #bfbfbf;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.social-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.submit-row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 18px 34px rgba(53, 109, 255, 0.22);
|
||||
}
|
||||
|
||||
.login-form :deep(.el-input__wrapper) {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
min-height: 48px;
|
||||
background-color: rgba(255, 255, 255, 0.88);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 0 0 1px rgba(148, 163, 184, 0.12) inset;
|
||||
}
|
||||
|
||||
.login-form :deep(.el-input__wrapper.is-focus) {
|
||||
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(53, 109, 255, 0.24) inset,
|
||||
0 0 0 4px rgba(53, 109, 255, 0.12);
|
||||
}
|
||||
|
||||
.login-form :deep(.el-button--primary) {
|
||||
border-radius: var(--app-radius-md);
|
||||
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.25);
|
||||
.login-form :deep(.el-checkbox__label) {
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.login-form :deep(.el-button.is-circle) {
|
||||
background: rgba(15, 23, 42, 0.04);
|
||||
border: 1px solid rgba(15, 23, 42, 0.08);
|
||||
color: var(--el-text-color-regular);
|
||||
background: rgba(255, 255, 255, 0.82);
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
color: var(--app-text-muted);
|
||||
}
|
||||
|
||||
.login-form :deep(.el-button.is-circle:hover) {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: rgba(59, 130, 246, 0.2);
|
||||
background: rgba(53, 109, 255, 0.12);
|
||||
border-color: rgba(53, 109, 255, 0.2);
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
|
||||
.login-code {
|
||||
width: calc(37% - 10px);
|
||||
height: 40px;
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
height: 48px;
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--app-radius-sm);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
background: rgba(255, 255, 255, 0.88);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
@@ -312,35 +491,74 @@ onMounted(() => {
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
font-family: Arial, serif;
|
||||
color: rgba(226, 232, 240, 0.68);
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.login-code-img {
|
||||
height: 40px;
|
||||
height: 48px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.login {
|
||||
padding: 24px 14px 80px;
|
||||
}
|
||||
|
||||
.login-shell {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
padding: 28px 24px;
|
||||
}
|
||||
|
||||
.brand-metrics {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.login-brand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
padding: 26px 18px 20px;
|
||||
}
|
||||
|
||||
.title-box {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.social-panel {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.social-actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.captcha-row :deep(.el-form-item__content) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.dark) {
|
||||
.login-form {
|
||||
background: rgba(17, 24, 39, 0.9);
|
||||
border-color: rgba(148, 163, 184, 0.2);
|
||||
}
|
||||
|
||||
.login-form :deep(.el-input__wrapper) {
|
||||
background-color: rgba(17, 24, 39, 0.7);
|
||||
background: rgba(12, 18, 30, 0.88);
|
||||
border-color: rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
.login-form :deep(.el-input__wrapper),
|
||||
.login-code,
|
||||
.title-box :deep(.lang-select--style),
|
||||
.social-panel,
|
||||
.login-form :deep(.el-button.is-circle) {
|
||||
background: rgba(148, 163, 184, 0.12);
|
||||
border-color: rgba(148, 163, 184, 0.25);
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.el-login-footer {
|
||||
color: rgba(226, 232, 240, 0.65);
|
||||
background: rgba(15, 23, 42, 0.56);
|
||||
border-color: rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="p-2 page-shell iframe-page">
|
||||
<div class="iframe-page__inner">
|
||||
<i-frame v-model:src="url"></i-frame>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const url = ref(import.meta.env.VITE_APP_MONITOR_ADMIN);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.iframe-page__inner {
|
||||
overflow: hidden;
|
||||
border-radius: 14px;
|
||||
background: var(--app-surface-bg);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
</style>
|
||||
|
||||
Vendored
+49
-16
@@ -1,14 +1,18 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="24" class="card-box">
|
||||
<el-card shadow="hover">
|
||||
<div class="p-2 page-shell monitor-cache-page">
|
||||
<el-row :gutter="12" class="cache-grid">
|
||||
<el-col :span="24">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<Monitor style="width: 1em; height: 1em; vertical-align: middle" />
|
||||
<span style="vertical-align: middle">基本信息</span>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>缓存概览</h3>
|
||||
<p>Redis 运行状态、资源消耗与实时统计。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium cache-table">
|
||||
<table style="width: 100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -97,24 +101,31 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card shadow="hover">
|
||||
<el-col :xs="24" :lg="12">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<PieChart style="width: 1em; height: 1em; vertical-align: middle" />
|
||||
<span style="vertical-align: middle">命令统计</span>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>命令统计</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium cache-chart">
|
||||
<div ref="commandstats" style="height: 420px" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card shadow="hover">
|
||||
<el-col :xs="24" :lg="12">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<Odometer style="width: 1em; height: 1em; vertical-align: middle" /> <span style="vertical-align: middle">内存信息</span>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>内存信息</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium cache-chart">
|
||||
<div ref="usedmemory" style="height: 420px" />
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -190,3 +201,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cache-grid {
|
||||
margin: 0 !important;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
.cache-table {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.cache-table table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.cache-chart {
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell monitor-logininfo-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="登录地址" prop="ipaddr">
|
||||
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -33,35 +40,34 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Access Logs</span>
|
||||
<h3>登录日志</h3>
|
||||
<p>共 {{ total }} 条记录,支持排序、批量清理、导出和账号解锁。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['monitor:logininfo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['monitor:logininfo:remove']" type="danger" plain icon="Delete" @click="handleClean">清空</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['monitor:logininfo:unlock']" type="primary" plain icon="Unlock" :disabled="single" @click="handleUnlock">
|
||||
解锁
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['monitor:logininfo:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="loginInfoTableRef"
|
||||
v-loading="loading"
|
||||
:data="loginInfoList"
|
||||
class="data-table"
|
||||
:default-sort="defaultSort"
|
||||
border
|
||||
@selection-change="handleSelectionChange"
|
||||
@@ -207,3 +213,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<div class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell monitor-online-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel">
|
||||
<template #header>
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="登录地址" prop="ipaddr">
|
||||
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -16,10 +24,20 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Online Sessions</span>
|
||||
<h3>在线用户</h3>
|
||||
<p>共 {{ total }} 条记录,支持按账号或地址检索并执行会话强退。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
border
|
||||
class="data-table"
|
||||
:data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)"
|
||||
style="width: 100%"
|
||||
>
|
||||
@@ -115,3 +133,19 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell monitor-operlog-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="操作地址" prop="operIp">
|
||||
<el-input v-model="queryParams.operIp" placeholder="请输入操作地址" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -41,30 +48,31 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Operation Logs</span>
|
||||
<h3>操作日志</h3>
|
||||
<p>共 {{ total }} 条记录,支持类型过滤、详情查看、批量清空和导出。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="WarnTriangleFilled" @click="handleClean">清空</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['monitor:operlog:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="operLogTableRef"
|
||||
v-loading="loading"
|
||||
:data="operlogList"
|
||||
class="data-table"
|
||||
border
|
||||
:default-sort="defaultSort"
|
||||
@selection-change="handleSelectionChange"
|
||||
@@ -259,3 +267,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="p-2 page-shell iframe-page">
|
||||
<div class="iframe-page__inner">
|
||||
<i-frame v-model:src="url"></i-frame>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const url = ref(import.meta.env.VITE_APP_SNAILJOB_ADMIN);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.iframe-page__inner {
|
||||
overflow: hidden;
|
||||
border-radius: 14px;
|
||||
background: var(--app-surface-bg);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
</style>
|
||||
|
||||
+298
-49
@@ -1,10 +1,34 @@
|
||||
<template>
|
||||
<div class="register">
|
||||
<div class="register-shell">
|
||||
<section class="register-brand">
|
||||
<span class="brand-pill">Plus UI Workspace</span>
|
||||
<h1 class="brand-title">企业级后台管理系统</h1>
|
||||
<p class="brand-desc">
|
||||
真正面向企业级的应用框架 组件化 模块化 轻耦合 高扩展 针对企业痛点 业界一流技术栈<br />
|
||||
重写 RuoYi-Vue 所有功能 集成 Sa-Token、Mybatis-Plus、WarmFlow、SpringDoc、Hutool、OSS 定期同步。
|
||||
</p>
|
||||
<div class="brand-highlights">
|
||||
<span v-for="item in highlights" :key="item" class="highlight-chip">{{ item }}</span>
|
||||
</div>
|
||||
<div class="brand-metrics">
|
||||
<article v-for="item in quickStats" :key="item.label" class="metric-card">
|
||||
<strong>{{ item.value }}</strong>
|
||||
<span>{{ item.label }}</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
|
||||
<div class="title-box">
|
||||
<div>
|
||||
<p class="eyebrow">Workspace Register</p>
|
||||
<h3 class="title">{{ title }}</h3>
|
||||
<p class="subtitle">创建新的业务工作台账号,接入当前系统权限与登录体系。</p>
|
||||
</div>
|
||||
<lang-select />
|
||||
</div>
|
||||
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="registerForm.username" type="text" size="large" auto-complete="off" :placeholder="proxy.$t('register.username')">
|
||||
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
|
||||
@@ -34,34 +58,31 @@
|
||||
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="captchaEnabled" prop="code">
|
||||
<el-input
|
||||
v-model="registerForm.code"
|
||||
size="large"
|
||||
auto-complete="off"
|
||||
:placeholder="proxy.$t('register.code')"
|
||||
style="width: 63%"
|
||||
@keyup.enter="handleRegister"
|
||||
>
|
||||
<el-form-item v-if="captchaEnabled" prop="code" class="captcha-row">
|
||||
<el-input v-model="registerForm.code" size="large" auto-complete="off" :placeholder="proxy.$t('register.code')" @keyup.enter="handleRegister">
|
||||
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
<div class="register-code">
|
||||
<img :src="codeUrl" class="register-code-img" @click="getCode" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%">
|
||||
<el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleRegister">
|
||||
|
||||
<div class="form-meta">
|
||||
<span class="register-tip">注册后将返回登录页继续完成认证</span>
|
||||
<router-link class="link-type" :to="'/login'">{{ proxy.$t('register.switchLoginPage') }}</router-link>
|
||||
</div>
|
||||
|
||||
<el-form-item class="submit-row">
|
||||
<el-button :loading="loading" size="large" type="primary" class="submit-button" @click.prevent="handleRegister">
|
||||
<span v-if="!loading">{{ proxy.$t('register.register') }}</span>
|
||||
<span v-else>{{ proxy.$t('register.registering') }}</span>
|
||||
</el-button>
|
||||
<div style="float: right">
|
||||
<router-link class="link-type" :to="'/login'">{{ proxy.$t('register.switchLoginPage') }}</router-link>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
</div>
|
||||
|
||||
<div class="el-register-footer">
|
||||
<span>Copyright © 2018-2026 疯狂的狮子Li All Rights Reserved.</span>
|
||||
<span>Copyright © 2018-{{ currentYear }} 疯狂的狮子Li All Rights Reserved.</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -75,6 +96,13 @@ import { useI18n } from 'vue-i18n';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const title = import.meta.env.VITE_APP_TITLE;
|
||||
const currentYear = new Date().getFullYear();
|
||||
const quickStats = [
|
||||
{ label: '细粒度权限管理', value: '动态权限控制' },
|
||||
{ label: '主流技术栈', value: '全栈技术集成' },
|
||||
{ label: 'UI样式', value: '卡片式' }
|
||||
];
|
||||
const highlights = ['技术栈全面升级', '动态菜单', '多主题布局', '深浅色主题'];
|
||||
const router = useRouter();
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -157,67 +185,234 @@ onMounted(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.register {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-image: url('../assets/images/login-background.jpg');
|
||||
background-size: cover;
|
||||
justify-content: center;
|
||||
padding: 40px 24px 88px;
|
||||
background:
|
||||
radial-gradient(circle at 12% 12%, rgba(53, 109, 255, 0.22), transparent 24%),
|
||||
radial-gradient(circle at 88% 18%, rgba(14, 165, 233, 0.18), transparent 24%), linear-gradient(135deg, #071120 0%, #0f1b33 42%, #15345f 100%);
|
||||
}
|
||||
|
||||
.title-box {
|
||||
.register-shell {
|
||||
width: min(1180px, 100%);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.05fr) minmax(360px, 440px);
|
||||
gap: 26px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.register-brand,
|
||||
.register-form {
|
||||
border-radius: 28px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
box-shadow: 0 30px 80px rgba(2, 8, 23, 0.32);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.register-brand {
|
||||
padding: 42px;
|
||||
display: flex;
|
||||
|
||||
.title {
|
||||
margin: 0px auto 30px auto;
|
||||
text-align: center;
|
||||
color: #707070;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: #eef4ff;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.02)),
|
||||
linear-gradient(135deg, rgba(53, 109, 255, 0.32), rgba(15, 23, 42, 0.24));
|
||||
}
|
||||
|
||||
:deep(.lang-select--style) {
|
||||
line-height: 0;
|
||||
color: #7483a3;
|
||||
.brand-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
margin: 22px 0 14px;
|
||||
font-size: clamp(34px, 4vw, 52px);
|
||||
line-height: 1.08;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.brand-desc {
|
||||
margin: 0;
|
||||
max-width: 580px;
|
||||
color: rgba(226, 232, 240, 0.88);
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.brand-highlights {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin: 28px 0 34px;
|
||||
}
|
||||
|
||||
.highlight-chip {
|
||||
padding: 9px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
color: #f8fbff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.brand-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
padding: 18px 16px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
strong {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span {
|
||||
color: rgba(226, 232, 240, 0.72);
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.register-form {
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
width: 400px;
|
||||
padding: 25px 25px 5px 25px;
|
||||
width: 100%;
|
||||
padding: 34px 30px 26px;
|
||||
z-index: 1;
|
||||
background: rgba(248, 250, 252, 0.92);
|
||||
}
|
||||
|
||||
.el-input {
|
||||
height: 40px;
|
||||
.title-box {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 28px;
|
||||
|
||||
input {
|
||||
height: 40px;
|
||||
.eyebrow {
|
||||
margin: 0 0 8px;
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
color: var(--app-text-title);
|
||||
font-weight: 700;
|
||||
font-size: 30px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 8px 0 0;
|
||||
color: var(--app-text-muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
:deep(.lang-select--style) {
|
||||
line-height: 0;
|
||||
color: var(--app-text-muted);
|
||||
padding: 10px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
height: 39px;
|
||||
.register-form .el-input {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.register-form .input-icon {
|
||||
height: 46px;
|
||||
width: 14px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.captcha-row {
|
||||
:deep(.el-form-item__content) {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 122px;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin: -2px 0 18px;
|
||||
}
|
||||
|
||||
.register-tip {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.register-code {
|
||||
width: 33%;
|
||||
height: 40px;
|
||||
float: right;
|
||||
height: 48px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: rgba(255, 255, 255, 0.88);
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 18px 34px rgba(53, 109, 255, 0.22);
|
||||
}
|
||||
|
||||
.register-form :deep(.el-input__wrapper) {
|
||||
min-height: 48px;
|
||||
background-color: rgba(255, 255, 255, 0.88);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 0 0 1px rgba(148, 163, 184, 0.12) inset;
|
||||
}
|
||||
|
||||
.register-form :deep(.el-input__wrapper.is-focus) {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(53, 109, 255, 0.24) inset,
|
||||
0 0 0 4px rgba(53, 109, 255, 0.12);
|
||||
}
|
||||
|
||||
.el-register-footer {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
@@ -225,14 +420,68 @@ onMounted(() => {
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-family: Arial, serif;
|
||||
color: rgba(226, 232, 240, 0.68);
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.register-code-img {
|
||||
height: 40px;
|
||||
padding-left: 12px;
|
||||
height: 48px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.register {
|
||||
padding: 24px 14px 80px;
|
||||
}
|
||||
|
||||
.register-shell {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.register-brand {
|
||||
padding: 28px 24px;
|
||||
}
|
||||
|
||||
.brand-metrics {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.register-brand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.register-form {
|
||||
padding: 26px 18px 20px;
|
||||
}
|
||||
|
||||
.title-box {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-meta {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.captcha-row :deep(.el-form-item__content) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.dark) {
|
||||
.register-form {
|
||||
background: rgba(12, 18, 30, 0.88);
|
||||
border-color: rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
.register-form :deep(.el-input__wrapper),
|
||||
.register-code,
|
||||
.title-box :deep(.lang-select--style) {
|
||||
background: rgba(15, 23, 42, 0.56);
|
||||
border-color: rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="search">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="85px">
|
||||
<div class="p-2 page-shell system-client-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="85px" class="query-form">
|
||||
<el-form-item label="客户端key" prop="clientKey">
|
||||
<el-input v-model="queryParams.clientKey" placeholder="请输入客户端key" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -19,33 +24,30 @@
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>客户端列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:client:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:client:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:client:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:client:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="clientList" border @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" :data="clientList" border class="data-table" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="true" label="id" align="center" prop="id" />
|
||||
<el-table-column label="客户端id" align="center" prop="clientId" />
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell system-config-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="参数名称" prop="configName">
|
||||
<el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -33,34 +40,31 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Config Dataset</span>
|
||||
<h3>参数列表</h3>
|
||||
<p>共 {{ total }} 条记录,支持键值维护、导出和缓存刷新。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:config:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:config:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:config:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:config:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:config:remove']" type="danger" plain icon="Refresh" @click="handleRefreshCache">刷新缓存</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="configList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="configList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="参数主键" align="center" prop="configId" />
|
||||
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
|
||||
@@ -259,3 +263,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell system-dept-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="部门名称" prop="deptName">
|
||||
<el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -22,24 +29,27 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Department Dataset</span>
|
||||
<h3>部门列表</h3>
|
||||
<p>支持树形层级维护、负责人绑定和部门状态管理。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:dept:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="deptTableRef"
|
||||
v-loading="loading"
|
||||
class="data-table"
|
||||
:data="deptList"
|
||||
row-key="deptId"
|
||||
border
|
||||
@@ -318,3 +328,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
<template>
|
||||
<div class="p-2 dict-page">
|
||||
<div class="p-2 page-shell dict-page">
|
||||
<el-row :gutter="16" class="dict-grid">
|
||||
<!-- 字典类型 -->
|
||||
<el-col :xs="24" :lg="12">
|
||||
<el-card shadow="hover" class="dict-card">
|
||||
<el-card shadow="hover" class="dict-card table-panel">
|
||||
<template #header>
|
||||
<div class="dict-card__header">
|
||||
<div class="dict-card__title">字典管理</div>
|
||||
<right-toolbar v-model:show-search="showTypeSearch" @query-table="getTypeList" />
|
||||
<div class="toolbar-shell dict-card__header">
|
||||
<div class="table-heading">
|
||||
<div class="panel-heading search-panel-toggle dict-title-toggle" @click.stop="showTypeSearch = !showTypeSearch">
|
||||
<div>
|
||||
<h3>字典管理</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<right-toolbar v-model:show-search="showTypeSearch" :search="false" @query-table="getTypeList" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-show="showTypeSearch" class="dict-form-scroll">
|
||||
<el-form ref="typeQueryFormRef" :model="typeQueryParams" :inline="true">
|
||||
<div class="dict-search" :class="{ 'is-collapsed': !showTypeSearch }">
|
||||
<el-form ref="typeQueryFormRef" :model="typeQueryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="字典名称" prop="dictName">
|
||||
<el-input v-model="typeQueryParams.dictName" placeholder="请输入字典名称" clearable @keyup.enter="handleTypeQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典类型" prop="dictType">
|
||||
<el-input v-model="typeQueryParams.dictType" placeholder="请输入字典类型" clearable @keyup.enter="handleTypeQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleTypeQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="handleTypeResetQuery">重置</el-button>
|
||||
@@ -37,7 +34,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="dict-actions">
|
||||
<div class="toolbar-actions dict-actions">
|
||||
<el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" @click="handleTypeAdd">新增</el-button>
|
||||
<el-button v-hasPermi="['system:dict:edit']" type="success" plain icon="Edit" :disabled="typeSingle" @click="handleTypeUpdate()"
|
||||
>修改</el-button
|
||||
@@ -54,6 +51,7 @@
|
||||
ref="typeTableRef"
|
||||
v-loading="typeLoading"
|
||||
border
|
||||
class="data-table"
|
||||
:data="typeList"
|
||||
highlight-current-row
|
||||
@row-click="handleTypeRowClick"
|
||||
@@ -98,19 +96,25 @@
|
||||
|
||||
<!-- 字典数据 -->
|
||||
<el-col :xs="24" :lg="12">
|
||||
<el-card shadow="hover" class="dict-card">
|
||||
<el-card shadow="hover" class="dict-card table-panel">
|
||||
<template #header>
|
||||
<div class="dict-card__header">
|
||||
<div class="dict-card__title">
|
||||
字典数据
|
||||
<span class="dict-card__subtitle">{{ currentDictLabel }}</span>
|
||||
<div class="toolbar-shell dict-card__header">
|
||||
<div class="table-heading">
|
||||
<div class="panel-heading search-panel-toggle dict-title-toggle" @click.stop="showDataSearch = !showDataSearch">
|
||||
<div>
|
||||
<h3>字典数据</h3>
|
||||
<p v-if="hasCurrentDict" class="dict-card__subtitle">{{ currentDictLabel }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<right-toolbar v-model:show-search="showDataSearch" :search="false" @query-table="getDataList" />
|
||||
</div>
|
||||
<right-toolbar v-model:show-search="showDataSearch" @query-table="getDataList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-show="showDataSearch" class="dict-form-scroll">
|
||||
<el-form ref="dataQueryFormRef" :model="dataQueryParams" :inline="true">
|
||||
<div class="dict-search" :class="{ 'is-collapsed': !showDataSearch }">
|
||||
<el-form ref="dataQueryFormRef" :model="dataQueryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="字典标签" prop="dictLabel">
|
||||
<el-input
|
||||
v-model="dataQueryParams.dictLabel"
|
||||
@@ -127,7 +131,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="dict-actions">
|
||||
<div class="toolbar-actions dict-actions">
|
||||
<el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" :disabled="!hasCurrentDict" @click="handleDataAdd"
|
||||
>新增</el-button
|
||||
>
|
||||
@@ -155,10 +159,10 @@
|
||||
</div>
|
||||
|
||||
<div class="dict-table-wrap">
|
||||
<el-table v-loading="dataLoading" border :data="dataList" @selection-change="handleDataSelectionChange">
|
||||
<el-table v-loading="dataLoading" border class="data-table" :data="dataList" @selection-change="handleDataSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="字典编码" align="center" prop="dictCode" />
|
||||
<el-table-column label="字典标签" align="center" prop="dictLabel" width="80">
|
||||
<el-table-column label="字典标签" align="center" prop="dictLabel" width="100">
|
||||
<template #default="scope">
|
||||
<span
|
||||
v-if="
|
||||
@@ -174,7 +178,7 @@
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字典键值" align="center" prop="dictValue" width="80" />
|
||||
<el-table-column label="字典键值" align="center" prop="dictValue" width="100" />
|
||||
<el-table-column label="字典排序" align="center" prop="dictSort" width="80" />
|
||||
<el-table-column label="备注" align="center" prop="remark" width="100" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
@@ -213,13 +217,15 @@
|
||||
<el-input v-model="typeForm.dictName" placeholder="请输入字典名称" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="dictType">
|
||||
<el-input v-model="typeForm.dictType" placeholder="请输入字典类型" maxlength="100" />
|
||||
<span slot="label">
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="数据存储中的Key值,如:sys_user_sex" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
字典类型
|
||||
</span>
|
||||
</template>
|
||||
<el-input v-model="typeForm.dictType" placeholder="请输入字典类型" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="typeForm.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
@@ -291,7 +297,6 @@ const typeIds = ref<Array<number | string>>([]);
|
||||
const typeSingle = ref(true);
|
||||
const typeMultiple = ref(true);
|
||||
const typeTotal = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
|
||||
const typeFormRef = ref<ElFormInstance>();
|
||||
const typeQueryFormRef = ref<ElFormInstance>();
|
||||
@@ -387,7 +392,7 @@ const { queryParams: dataQueryParams, form: dataForm, rules: dataRules } = toRef
|
||||
|
||||
const getTypeList = () => {
|
||||
typeLoading.value = true;
|
||||
listType(proxy?.addDateRange(typeQueryParams.value, dateRange.value)).then((res) => {
|
||||
listType(typeQueryParams.value).then((res) => {
|
||||
typeList.value = res.rows;
|
||||
typeTotal.value = res.total;
|
||||
typeLoading.value = false;
|
||||
@@ -438,7 +443,6 @@ const handleTypeQuery = () => {
|
||||
};
|
||||
|
||||
const handleTypeResetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
typeQueryFormRef.value?.resetFields();
|
||||
handleTypeQuery();
|
||||
};
|
||||
@@ -614,54 +618,55 @@ onMounted(() => {
|
||||
row-gap: 16px;
|
||||
}
|
||||
|
||||
.dict-card__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
.dict-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dict-card__title {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
.dict-card__header {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dict-title-toggle {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.dict-card__subtitle {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.dict-form-scroll {
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
.dict-search {
|
||||
overflow: hidden;
|
||||
max-height: 240px;
|
||||
opacity: 1;
|
||||
margin-bottom: 12px;
|
||||
padding-right: 6px;
|
||||
padding-bottom: 4px;
|
||||
transition:
|
||||
max-height 0.32s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
opacity 0.24s ease,
|
||||
margin-bottom 0.24s ease;
|
||||
}
|
||||
|
||||
.dict-form-scroll :deep(.el-form) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 12px;
|
||||
row-gap: 10px;
|
||||
.dict-search.is-collapsed {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
margin-bottom: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dict-form-scroll :deep(.el-form-item) {
|
||||
margin: 0;
|
||||
.dict-search :deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dict-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin: 8px 0 12px;
|
||||
margin: 0 0 12px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.dict-actions :deep(.el-button) {
|
||||
height: 32px;
|
||||
padding: 0 14px;
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.dict-actions :deep(.el-button + .el-button) {
|
||||
@@ -671,5 +676,4 @@ onMounted(() => {
|
||||
.dict-table-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell system-menu-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="菜单名称" prop="menuName">
|
||||
<el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -19,24 +26,27 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Menu Dataset</span>
|
||||
<h3>菜单列表</h3>
|
||||
<p>支持树形加载、图标选择、级联删除和目录/菜单/按钮三级维护。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:menu:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:menu:remove']" type="danger" plain icon="Delete" @click="handleCascadeDelete" :loading="deleteLoading">级联删除</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="menuTableRef"
|
||||
v-loading="loading"
|
||||
class="data-table"
|
||||
:data="menuList"
|
||||
row-key="menuId"
|
||||
border
|
||||
@@ -532,6 +542,28 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tree-border {
|
||||
height: 300px;
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell system-notice-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="公告标题" prop="noticeTitle">
|
||||
<el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -22,29 +29,29 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Notice Dataset</span>
|
||||
<h3>公告列表</h3>
|
||||
<p>共 {{ total }} 条记录,支持类型筛选、内容编辑和状态管理。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:notice:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:notice:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:notice:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['system:notice:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="noticeList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="noticeList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="序号" align="center" prop="noticeId" width="100" />
|
||||
<el-table-column label="公告标题" align="center" prop="noticeTitle" :show-overflow-tooltip="true" />
|
||||
@@ -241,3 +248,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell system-oss-config-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="配置key" prop="configKey">
|
||||
<el-input v-model="queryParams.configKey" placeholder="配置key" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -23,29 +30,29 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Storage Config</span>
|
||||
<h3>OSS 配置</h3>
|
||||
<p>共 {{ total }} 条记录,支持默认桶切换、权限策略维护和站点配置。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:ossConfig:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:ossConfig:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:ossConfig:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['system:ossConfig:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="ossConfigList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="ossConfigList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="columns[0].visible" label="主建" align="center" prop="ossConfigId" />
|
||||
<el-table-column v-if="columns[1].visible" label="配置key" align="center" prop="configKey" />
|
||||
@@ -342,3 +349,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell system-oss-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="文件名" prop="fileName">
|
||||
<el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -34,42 +41,40 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Object Storage</span>
|
||||
<h3>文件列表</h3>
|
||||
<p>共 {{ total }} 条记录,支持文件上传、预览切换和 OSS 配置跳转。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:oss:upload']" type="primary" plain icon="Upload" @click="handleFile">上传文件</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:oss:upload']" type="primary" plain icon="Upload" @click="handleImage">上传图片</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:oss:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
v-hasPermi="['system:oss:edit']"
|
||||
:type="previewListResource ? 'danger' : 'warning'"
|
||||
plain
|
||||
@click="handlePreviewListResource(!previewListResource)"
|
||||
>预览开关 : {{ previewListResource ? '禁用' : '启用' }}</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
预览开关 : {{ previewListResource ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['system:ossConfig:list']" type="info" plain icon="Operation" @click="handleOssConfig">配置管理</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
v-if="showTable"
|
||||
v-loading="loading"
|
||||
:data="ossList"
|
||||
class="data-table"
|
||||
border
|
||||
:header-cell-class-name="handleHeaderClass"
|
||||
@selection-change="handleSelectionChange"
|
||||
@@ -307,12 +312,10 @@ const handleDownload = (row: OssVO) => {
|
||||
};
|
||||
/** 预览开关按钮 */
|
||||
const handlePreviewListResource = async (preview: boolean) => {
|
||||
const text = preview ? '启用' : '停用';
|
||||
try {
|
||||
await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?');
|
||||
await proxy?.updateConfigByKey('sys.oss.previewListResource', preview);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess(text + '成功');
|
||||
proxy?.$modal.msgSuccess((preview ? '启用' : '停用') + '成功');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
@@ -331,3 +334,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<div class="p-2 page-shell system-post-page">
|
||||
<el-row :gutter="20" class="content-grid">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-col :lg="5" :xs="24">
|
||||
<el-card shadow="hover" class="side-panel">
|
||||
<template #header>
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<span class="panel-kicker">Department Filter</span>
|
||||
<h3>部门结构</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
class="mt-2 dept-tree"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' } as any"
|
||||
@@ -19,11 +27,18 @@
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-col :lg="19" :xs="24" class="content-main">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="岗位编码" prop="postCode">
|
||||
<el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -61,30 +76,28 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Post Dataset</span>
|
||||
<h3>岗位列表</h3>
|
||||
<p>共 {{ total }} 条记录,支持按部门筛选、岗位维护和导出。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:post:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:post:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:post:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['system:post:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:post:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table v-loading="loading" border :data="postList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="postList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="岗位编号" align="center" prop="postId" />
|
||||
<el-table-column label="岗位编码" align="center" prop="postCode" />
|
||||
@@ -359,3 +372,39 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.content-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dept-tree {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="search">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell role-auth-user-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -14,26 +19,25 @@
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="never">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>已授权用户</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="openSelectUser">添加用户</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:remove']" type="danger" plain icon="CircleClose" :disabled="multiple" @click="cancelAuthUserAll">
|
||||
批量取消授权
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Close" @click="handleClose">关闭</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="true" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table v-loading="loading" border :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="page-shell system-role-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -34,28 +41,26 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Role Dataset</span>
|
||||
<h3>角色列表</h3>
|
||||
<p>共 {{ total }} 条记录,支持菜单权限、数据权限和分配用户等完整配置流程。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:remove']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:remove']" type="danger" plain :disabled="ids.length === 0" icon="Delete" @click="handleDelete()">删除</el-button>
|
||||
<el-button v-hasPermi="['system:role:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table ref="roleTableRef" border v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
||||
<el-table ref="roleTableRef" border class="data-table" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="角色编号" prop="roleId" width="120" />
|
||||
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
|
||||
@@ -501,3 +506,25 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-dialog v-model="visible" title="选择用户" width="800px" top="5vh" append-to-body>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-dialog v-model="visible" title="选择用户" width="800px" top="5vh" append-to-body class="select-user-dialog">
|
||||
<div class="page-shell select-user-shell">
|
||||
<el-card shadow="hover" class="search-panel select-user-card">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -13,8 +15,10 @@
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row>
|
||||
<el-table ref="tableRef" border :data="userList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" class="table-panel select-user-card">
|
||||
<el-table ref="tableRef" border class="data-table" :data="userList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
@@ -32,7 +36,8 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-if="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSelectUser">确 定</el-button>
|
||||
@@ -128,3 +133,17 @@ defineExpose({
|
||||
show
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.select-user-shell {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.select-user-card :deep(.el-card__body) {
|
||||
padding: 14px 16px !important;
|
||||
}
|
||||
|
||||
.select-user-dialog :deep(.el-dialog__body) {
|
||||
padding-top: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<div class="panel">
|
||||
<h4 class="panel-title">基本信息</h4>
|
||||
<el-form :model="form" :inline="true">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="2.5">
|
||||
<div class="p-2 page-shell auth-role-page">
|
||||
<el-card shadow="hover" class="search-panel auth-role-info">
|
||||
<template #header>
|
||||
<div class="panel-heading">
|
||||
<div><h3>基本信息</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="form" :inline="true" class="query-form auth-role-form">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="2.5">
|
||||
<el-form-item label="登录账号" prop="userName">
|
||||
<el-input v-model="form.userName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>角色信息</h3>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h4 class="panel-title">角色信息</h4>
|
||||
<div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" @click="submitForm()">提交</el-button>
|
||||
<el-button @click="close()">返回</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
border
|
||||
class="data-table"
|
||||
:row-key="getRowKey"
|
||||
:data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
|
||||
@row-click="clickRow"
|
||||
@@ -45,13 +54,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total > 0" v-model:page="pageNum" v-model:limit="pageSize" :total="total" />
|
||||
<div style="text-align: center; margin-left: -120px; margin-top: 30px">
|
||||
<el-button type="primary" @click="submitForm()">提交</el-button>
|
||||
<el-button @click="close()">返回</el-button>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -144,3 +147,17 @@ onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.auth-role-form :deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.auth-role-form :deep(.el-input) {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.auth-role-info :deep(.el-card__body) {
|
||||
padding-top: 14px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<div class="page-shell system-user-page">
|
||||
<el-row :gutter="20" class="content-grid">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-col :lg="5" :xs="24">
|
||||
<el-card shadow="hover" class="side-panel">
|
||||
<template #header>
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<span class="panel-kicker">Department Filter</span>
|
||||
<h3>部门结构</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
class="mt-2 dept-tree"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' } as any"
|
||||
@@ -19,11 +27,18 @@
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-col :lg="19" :xs="24" class="content-main">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -57,25 +72,23 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">User Dataset</span>
|
||||
<h3>用户列表</h3>
|
||||
<p>共 {{ total }} 条记录,支持部门筛选、状态切换、导入导出和角色分配。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
|
||||
修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-dropdown class="mt-[1px]">
|
||||
<el-button plain type="info">
|
||||
更多
|
||||
@@ -90,12 +103,12 @@
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :columns="columns" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
|
||||
@@ -677,3 +690,39 @@ async function handleDeptChange(value: number | string) {
|
||||
form.value.postIds = [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.content-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dept-tree {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,54 +1,76 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<div class="p-2 page-shell profile-page">
|
||||
<el-row :gutter="20" class="profile-grid">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card class="box-card">
|
||||
<el-card shadow="hover" class="side-panel profile-summary-card">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>个人信息</span>
|
||||
<div class="panel-heading">
|
||||
<div><h3>个人信息</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<div class="profile-summary">
|
||||
<div class="text-center profile-avatar">
|
||||
<userAvatar />
|
||||
</div>
|
||||
<ul class="list-group list-group-striped">
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="user" />用户名称
|
||||
<div class="pull-right">{{ state.user.userName }}</div>
|
||||
<div class="profile-item-label">
|
||||
<svg-icon icon-class="user" />
|
||||
<span>用户名称</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ state.user.userName || '-' }}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="phone" />手机号码
|
||||
<div class="pull-right">{{ state.user.phonenumber }}</div>
|
||||
<div class="profile-item-label">
|
||||
<svg-icon icon-class="phone" />
|
||||
<span>手机号码</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ state.user.phonenumber || '-' }}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="email" />用户邮箱
|
||||
<div class="pull-right">{{ state.user.email }}</div>
|
||||
<div class="profile-item-label">
|
||||
<svg-icon icon-class="email" />
|
||||
<span>用户邮箱</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ state.user.email || '-' }}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="tree" />所属部门
|
||||
<div v-if="state.user.deptName" class="pull-right">{{ state.user.deptName }} / {{ state.postGroup }}</div>
|
||||
<div class="profile-item-label">
|
||||
<svg-icon icon-class="tree" />
|
||||
<span>所属部门</span>
|
||||
</div>
|
||||
<span v-if="state.user.deptName" class="profile-item-value">{{ state.user.deptName }} / {{ state.postGroup }}</span>
|
||||
<span v-else class="profile-item-value">-</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="peoples" />所属角色
|
||||
<div class="pull-right">{{ state.roleGroup }}</div>
|
||||
<div class="profile-item-label">
|
||||
<svg-icon icon-class="peoples" />
|
||||
<span>所属角色</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ state.roleGroup || '-' }}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="date" />创建日期
|
||||
<div class="pull-right">{{ state.user.createTime }}</div>
|
||||
<div class="profile-item-label">
|
||||
<svg-icon icon-class="date" />
|
||||
<span>创建日期</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ state.user.createTime || '-' }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card>
|
||||
<el-card shadow="hover" class="table-panel profile-main-card">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>基本资料</span>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>个人设置</h3>
|
||||
<p>维护个人资料、密码、第三方应用和在线设备。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tabs v-model="activeTab" class="profile-tabs">
|
||||
<el-tab-pane label="基本资料" name="userinfo">
|
||||
<userInfo :user="userForm" />
|
||||
</el-tab-pane>
|
||||
@@ -120,3 +142,88 @@ onMounted(() => {
|
||||
getOnlines();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-grid {
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
.profile-summary-card,
|
||||
.profile-main-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.profile-summary {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.profile-item-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.profile-item-value {
|
||||
max-width: 58%;
|
||||
text-align: right;
|
||||
color: var(--el-text-color-secondary);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.profile-summary :deep(.list-group-item) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.profile-tabs :deep(.el-tabs__header) {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.profile-tabs :deep(.el-tabs__nav-wrap::after),
|
||||
.profile-tabs :deep(.el-tabs__active-bar) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.profile-tabs :deep(.el-tabs__nav) {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.profile-tabs :deep(.el-tabs__item) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 36px;
|
||||
min-width: 88px;
|
||||
padding: 0 16px;
|
||||
border-radius: 10px;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.profile-tabs :deep(.el-tabs__item:nth-child(2)),
|
||||
.profile-tabs :deep(.el-tabs__item.is-top:first-child),
|
||||
.profile-tabs :deep(.el-tabs__item.is-top:last-child) {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.profile-tabs :deep(.el-tabs__item.is-active) {
|
||||
background: rgba(53, 109, 255, 0.1);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.profile-item-value {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="devices" border style="width: 100%; height: 100%; font-size: 14px">
|
||||
<div class="profile-table-wrap">
|
||||
<el-table :data="devices" border class="data-table profile-device-table">
|
||||
<el-table-column label="设备类型" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_device_type" :value="scope.row.deviceType" />
|
||||
@@ -55,3 +55,9 @@ const handldDelOnline = (row: any) => {
|
||||
.catch(() => {});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-table-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px" class="profile-form">
|
||||
<el-form-item label="旧密码" prop="oldPassword">
|
||||
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
@@ -9,9 +9,9 @@
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-form-item class="profile-form__actions">
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
<el-button @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -71,3 +71,22 @@ const close = () => {
|
||||
proxy?.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-form {
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.profile-form :deep(.el-input__wrapper) {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.profile-form :deep(.el-button) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.profile-form__actions :deep(.el-form-item__content) {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="auths" border style="width: 100%; height: 100%; font-size: 14px">
|
||||
<div class="profile-auth">
|
||||
<el-table :data="auths" border class="data-table profile-auth-table">
|
||||
<el-table-column label="序号" width="50" type="index" />
|
||||
<el-table-column label="绑定账号平台" width="140" align="center" prop="source" show-overflow-tooltip />
|
||||
<el-table-column label="头像" width="120" align="center" prop="avatar">
|
||||
@@ -12,43 +12,48 @@
|
||||
<el-table-column label="绑定时间" width="180" align="center" prop="createTime" />
|
||||
<el-table-column label="操作" width="80" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button size="small" type="text" @click="unlockAuth(scope.row)">解绑</el-button>
|
||||
<el-tooltip content="解绑" placement="top">
|
||||
<el-button link type="primary" icon="CircleClose" @click="unlockAuth(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div id="git-user-binding">
|
||||
<h4 class="provider-desc">你可以绑定以下第三方帐号</h4>
|
||||
<div id="authlist" class="user-bind">
|
||||
<div class="provider-section">
|
||||
<div class="provider-heading">
|
||||
<h4 class="provider-desc">可绑定的第三方应用</h4>
|
||||
<p>点击下方平台完成账号绑定。</p>
|
||||
</div>
|
||||
<div class="user-bind">
|
||||
<a class="third-app" href="#" title="使用 微信 账号授权登录" @click="authUrl('wechat')">
|
||||
<div class="git-other-login-icon">
|
||||
<div class="third-app__icon">
|
||||
<svg-icon icon-class="wechat" />
|
||||
</div>
|
||||
<span class="app-name">WeiXin</span>
|
||||
<span class="app-name">微信</span>
|
||||
</a>
|
||||
<a class="third-app" href="#" title="使用 MaxKey 账号授权登录" @click="authUrl('maxkey')">
|
||||
<div class="git-other-login-icon">
|
||||
<div class="third-app__icon">
|
||||
<svg-icon icon-class="maxkey" />
|
||||
</div>
|
||||
<span class="app-name">MaxKey</span>
|
||||
</a>
|
||||
<a class="third-app" href="#" title="使用 TopIam 账号授权登录" @click="authUrl('topiam')">
|
||||
<div class="git-other-login-icon">
|
||||
<div class="third-app__icon">
|
||||
<svg-icon icon-class="topiam" />
|
||||
</div>
|
||||
<span class="app-name">TopIam</span>
|
||||
</a>
|
||||
<a class="third-app" href="#" title="使用 Gitee 账号授权登录" @click="authUrl('gitee')">
|
||||
<div class="git-other-login-icon">
|
||||
<div class="third-app__icon">
|
||||
<svg-icon icon-class="gitee" />
|
||||
</div>
|
||||
<span class="app-name">Gitee</span>
|
||||
</a>
|
||||
<a class="third-app" href="#" title="使用 GitHub 账号授权登录" @click="authUrl('github')">
|
||||
<div class="git-other-login-icon">
|
||||
<div class="third-app__icon">
|
||||
<svg-icon icon-class="github" />
|
||||
</div>
|
||||
<span class="app-name">Github</span>
|
||||
<span class="app-name">GitHub</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,44 +99,77 @@ const authUrl = (source: string) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-bind .third-app {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
.profile-auth {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.profile-auth-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.provider-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.provider-heading p {
|
||||
margin: 6px 0 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.user-bind .third-app {
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
min-width: 80px;
|
||||
float: left;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-height: 84px;
|
||||
padding: 12px 10px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
border-radius: 12px;
|
||||
background: rgba(248, 250, 252, 0.82);
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.user-bind {
|
||||
font-size: 1rem;
|
||||
text-align: start;
|
||||
height: 50px;
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(96px, 108px));
|
||||
gap: 10px;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.git-other-login-icon > img {
|
||||
height: 32px;
|
||||
.third-app:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(53, 109, 255, 0.3);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
.third-app__icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
color: #005980;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.provider-desc {
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Liberation Sans',
|
||||
'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti', SimHei, SimSun,
|
||||
'WenQuanYi Zen Hei Sharp', sans-serif;
|
||||
font-size: 1.071rem;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
td > img {
|
||||
@@ -141,4 +179,18 @@ td > img {
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:global(html.dark) {
|
||||
.user-bind .third-app {
|
||||
background: rgba(15, 23, 42, 0.64);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -159,7 +159,18 @@ const closeDialog = () => {
|
||||
.user-info-head {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.user-info-head :deep(.img-circle) {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
border: 4px solid rgba(255, 255, 255, 0.92);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
.user-info-head:hover:after {
|
||||
@@ -176,7 +187,21 @@ const closeDialog = () => {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
cursor: pointer;
|
||||
line-height: 110px;
|
||||
line-height: 120px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-upload-preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 18px;
|
||||
background: rgba(248, 250, 252, 0.9);
|
||||
}
|
||||
|
||||
.avatar-upload-preview img {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-form ref="userRef" :model="userForm" :rules="rules" label-width="80px">
|
||||
<el-form ref="userRef" :model="userForm" :rules="rules" label-width="80px" class="profile-form">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="userForm.nickName" maxlength="30" />
|
||||
</el-form-item>
|
||||
@@ -15,9 +15,9 @@
|
||||
<el-radio value="1">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-form-item class="profile-form__actions">
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
<el-button @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -67,3 +67,26 @@ const close = () => {
|
||||
proxy?.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-form {
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.profile-form :deep(.el-input__wrapper) {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.profile-form :deep(.el-radio-group) {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.profile-form :deep(.el-button) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.profile-form__actions :deep(.el-form-item__content) {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell tool-gen-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div>
|
||||
<span class="panel-kicker">Search Filters</span>
|
||||
<h3>筛选条件</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="数据源" prop="dataName">
|
||||
<el-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称">
|
||||
<el-option key="" label="全部" value="" />
|
||||
@@ -33,30 +40,28 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<span class="panel-kicker">Code Generator</span>
|
||||
<h3>数据表列表</h3>
|
||||
<p>共 {{ total }} 条记录,支持导入表结构、同步数据库和代码预览生成。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['tool:gen:code']" type="primary" plain icon="Download" @click="handleGenTable()">生成</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['tool:gen:import']" type="info" plain icon="Upload" @click="openImportTable">导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['tool:gen:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleEditTable()">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['tool:gen:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="tableList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="tableList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" align="center" width="55"></el-table-column>
|
||||
<el-table-column label="序号" type="index" width="50" align="center">
|
||||
<template #default="scope">
|
||||
@@ -250,10 +255,30 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: rgba(53, 109, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.el-tab-pane {
|
||||
background-color: #282c34;
|
||||
.el-link {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="search">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell workflow-category-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="分类名称" prop="categoryName">
|
||||
<el-input v-model="queryParams.categoryName" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -11,24 +16,26 @@
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>流程分类</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['workflow:category:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:showSearch="showSearch" :search="false" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
ref="categoryTableRef"
|
||||
v-loading="loading"
|
||||
class="data-table"
|
||||
:data="categoryList"
|
||||
row-key="categoryId"
|
||||
border
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="search">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell workflow-leave-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="请假天数" prop="startLeaveDays">
|
||||
<el-input v-model="queryParams.startLeaveDays" placeholder="请输入请假天数" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -15,23 +20,24 @@
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>请假列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button v-hasPermi="['workflow:leave:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['workflow:leave:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="leaveList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="leaveList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="主键" align="center" prop="id" />
|
||||
<el-table-column label="请假类型" align="center">
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<div class="p-2 page-shell workflow-process-definition-page">
|
||||
<el-row :gutter="20" class="content-grid">
|
||||
<!-- 流程分类树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-col :lg="4" :xs="24">
|
||||
<el-card shadow="hover" class="side-panel">
|
||||
<template #header>
|
||||
<div class="table-heading">
|
||||
<h3>流程分类</h3>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="categoryTreeRef"
|
||||
@@ -19,11 +24,15 @@
|
||||
></el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
|
||||
<el-col :lg="20" :xs="24" class="content-main">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px" class="query-form">
|
||||
<el-form-item label="流程定义名称" prop="flowName">
|
||||
<el-input v-model="queryParams.flowName" placeholder="请输入流程定义名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -37,32 +46,26 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>流程定义</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd()">添加</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" icon="UploadFilled" @click="uploadDialog.visible = true">部署流程文件</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" icon="Download" :disabled="single" @click="handleExportDef">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="handleQuery"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="已发布" name="0"></el-tab-pane>
|
||||
<el-tab-pane label="未发布" name="1"></el-tab-pane>
|
||||
<el-table v-loading="loading" border :data="processDefinitionList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="processDefinitionList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column align="center" prop="id" label="主键" v-if="false"></el-table-column>
|
||||
<el-table-column align="center" prop="flowName" label="流程定义名称" :show-overflow-tooltip="true"></el-table-column>
|
||||
@@ -88,27 +91,19 @@
|
||||
<el-tag v-else type="danger">失效</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" align="center" width="170" class-name="small-padding fixed-width">
|
||||
<el-table-column fixed="right" label="操作" align="center" width="236" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="process-action-group">
|
||||
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除流程</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button link type="primary" size="small" icon="CopyDocument" @click="handleCopyDef(scope.row)">复制流程</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button link type="primary" v-if="scope.row.isPublish === 0" icon="Pointer" size="small" @click="design(scope.row)"
|
||||
>流程设计</el-button
|
||||
>
|
||||
<el-button link type="primary" v-if="scope.row.isPublish === 0" icon="Pointer" size="small" @click="design(scope.row)">
|
||||
流程设计
|
||||
</el-button>
|
||||
<el-button link type="primary" v-else icon="View" size="small" @click="designView(scope.row)">查看流程</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="scope.row.isPublish !== 1" :span="1.5">
|
||||
<el-button link type="primary" size="small" icon="CircleCheck" @click="handlePublish(scope.row)">发布流程</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-button link type="primary" v-if="scope.row.isPublish !== 1" size="small" icon="CircleCheck" @click="handlePublish(scope.row)">
|
||||
发布流程
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -239,11 +234,6 @@ const uploadDialog = reactive<DialogOption>({
|
||||
title: '部署流程文件'
|
||||
});
|
||||
|
||||
const processDefinitionDialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: '历史版本'
|
||||
});
|
||||
|
||||
const modelDialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
@@ -395,7 +385,6 @@ const handlePublish = async (row?: FlowDefinitionVo) => {
|
||||
);
|
||||
loading.value = true;
|
||||
await publish(row.id).finally(() => (loading.value = false));
|
||||
processDefinitionDialog.visible = false;
|
||||
activeName.value = '0';
|
||||
await handleQuery();
|
||||
proxy?.$modal.msgSuccess('发布成功');
|
||||
@@ -559,3 +548,40 @@ const handleExportDef = () => {
|
||||
proxy?.download(`/workflow/definition/exportDef/${ids.value[0]}`, {}, `${flowCodeList.value[0]}.json`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-grid {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.content-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.process-action-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.process-action-group :deep(.el-button.is-link) {
|
||||
width: auto !important;
|
||||
min-width: 0 !important;
|
||||
height: 30px !important;
|
||||
padding: 0 10px !important;
|
||||
border-radius: 10px !important;
|
||||
background: rgba(53, 109, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
.process-action-group :deep(.el-button.is-link + .el-button.is-link) {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.process-action-group :deep(.el-button .el-icon) {
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<div class="p-2 page-shell workflow-process-instance-page">
|
||||
<el-row :gutter="20" class="content-grid">
|
||||
<!-- 流程分类树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-col :lg="4" :xs="24">
|
||||
<el-card shadow="hover" class="side-panel">
|
||||
<template #header>
|
||||
<div class="table-heading">
|
||||
<h3>流程分类</h3>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="categoryTreeRef"
|
||||
@@ -19,11 +24,15 @@
|
||||
></el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-col :lg="20" :xs="24" class="content-main">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item>
|
||||
<el-badge :value="userSelectCount" :max="10" class="item">
|
||||
<el-button type="primary" @click="openUserSelect">选择申请人</el-button>
|
||||
@@ -45,20 +54,22 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>流程实例</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="handleQuery"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="tab" @tab-click="changeTab">
|
||||
<el-tab-pane name="running" label="运行中"></el-tab-pane>
|
||||
<el-tab-pane name="finish" label="已完成"></el-tab-pane>
|
||||
<el-table v-loading="loading" border :data="processInstanceList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="processInstanceList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="businessCode" align="center" label="业务编码"></el-table-column>
|
||||
@@ -476,3 +487,15 @@ onMounted(() => {
|
||||
getTreeselect();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-grid {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.content-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell workflow-spel-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="组件名称" prop="componentName">
|
||||
<el-input v-model="queryParams.componentName" placeholder="请输入组件名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -17,25 +21,23 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>流程表达式</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:spel:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:spel:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:spel:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<right-toolbar v-model:showSearch="showSearch" :search="false" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="spelList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="spelList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center">
|
||||
<template #default="scope">
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell workflow-all-task-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item>
|
||||
<el-badge :value="userSelectCount" :max="10" class="item">
|
||||
<el-button type="primary" @click="openUserSelect">选择申请人</el-button>
|
||||
@@ -22,25 +26,29 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5" v-if="tab === 'waiting'">
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>全部任务</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<template v-if="tab === 'waiting'">
|
||||
<el-button class="todo-action-btn todo-action-btn--primary" type="primary" plain icon="Edit" :disabled="multiple" @click="handleUserOpen()"
|
||||
>修改办理人
|
||||
</el-button>
|
||||
<el-button class="todo-action-btn todo-action-btn--warning" type="warning" plain icon="Bell" :disabled="multiple" @click="handleUrgeTaskOpen()"
|
||||
>催办
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="handleQuery"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="tab" @tab-click="changeTab">
|
||||
<el-tab-pane name="waiting" label="待办任务"> </el-tab-pane>
|
||||
<el-tab-pane name="finish" label="已办任务"> </el-tab-pane>
|
||||
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="businessCode" align="center" label="业务编码"></el-table-column>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<div class="p-2 page-shell workflow-my-document-page">
|
||||
<el-row :gutter="20" class="content-grid">
|
||||
<!-- 流程分类树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-col :lg="4" :xs="24">
|
||||
<el-card shadow="hover" class="side-panel">
|
||||
<template #header>
|
||||
<div class="table-heading">
|
||||
<h3>流程分类</h3>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="categoryTreeRef"
|
||||
@@ -19,11 +24,15 @@
|
||||
></el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
|
||||
<el-col :lg="20" :xs="24" class="content-main">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px" class="query-form">
|
||||
<el-form-item label="流程定义编码" prop="flowCode">
|
||||
<el-input v-model="queryParams.flowCode" placeholder="请输入流程定义编码" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -34,15 +43,19 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
|
||||
</el-row>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>我的单据</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="handleQuery"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="processInstanceList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="processInstanceList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
|
||||
<el-table-column v-if="false" align="center" prop="id" label="id"></el-table-column>
|
||||
@@ -244,3 +257,15 @@ const handleOpen = async (row, type) => {
|
||||
workflowCommon.routerJump(routerJumpVo, proxy);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-grid {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.content-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell workflow-task-copy-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item label="任务名称" prop="nodeName">
|
||||
<el-input v-model="queryParams.nodeName" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -20,15 +24,19 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
|
||||
</el-row>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>抄送任务</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="handleQuery"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="businessCode" align="center" label="业务编码"></el-table-column>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell workflow-task-finish-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item>
|
||||
<el-badge :value="userSelectCount" :max="10" class="item">
|
||||
<el-button type="primary" @click="openUserSelect">选择申请人</el-button>
|
||||
@@ -25,15 +29,19 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
|
||||
</el-row>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>已办任务</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="handleQuery"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="businessCode" align="center" label="业务编码"></el-table-column>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="p-2 page-shell workflow-task-waiting-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<el-form-item>
|
||||
<el-badge :value="userSelectCount" :max="10" class="item">
|
||||
<el-button type="primary" @click="openUserSelect">选择申请人</el-button>
|
||||
@@ -25,15 +29,19 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="hover">
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
|
||||
</el-row>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>待办任务</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="handleQuery"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" border class="data-table" :data="taskList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="businessCode" align="center" label="业务编码"></el-table-column>
|
||||
|
||||
Reference in New Issue
Block a user