refactor(front): 重构前端组件和 API

- 更新了多个组件的样式和结构
- 重命名了一些 API 文件
- 添加了新的 API 接口
- 优化了部分代码的可读性和性能
This commit is contained in:
2025-07-23 20:58:15 +08:00
parent 091c056cd0
commit 902173a9e9
111 changed files with 1841 additions and 1198 deletions
@@ -2,21 +2,21 @@ import request from '@/utils/request'
export function startPipeline() {
return request({
url: 'api/devops/pipeline/start',
url: 'api/devops/pipelineInstance/start',
method: 'post'
})
}
export function restartPipeline() {
return request({
url: 'api/devops/pipeline/restart',
url: 'api/devops/pipelineInstance/restart',
method: 'post'
})
}
export function queryLastPipelineDetail(instanceId) {
return request({
url: 'api/devops/pipeline/last',
url: 'api/devops/pipelineInstance/last',
method: 'post',
data: {
instanceId
@@ -0,0 +1,9 @@
import request from '@/utils/request'
export function getPipelineTemplate(id) {
return request({
url: 'api/devops/pipelineTemplate/getPipelineTemplate',
method: 'post',
data: { id }
})
}
+1 -1
View File
@@ -1,5 +1,5 @@
import request from '@/utils/request'
import { encrypt } from '@/utils/rsaEncrypt'
import { encrypt } from '@/utils/CsRsaUtil'
export function add(data) {
return request({
@@ -2,7 +2,9 @@
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{
item.meta.title
}}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
@@ -73,6 +75,7 @@ export default {
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
@@ -123,15 +123,21 @@ export default {
props: {
permission: {
type: Object,
default: () => { return {} }
default: () => {
return {}
}
},
hiddenColumns: {
type: Array,
default: () => { return [] }
default: () => {
return []
}
},
ignoreColumns: {
type: Array,
default: () => { return [] }
default: () => {
return []
}
}
},
data() {
@@ -253,16 +259,18 @@ export default {
</script>
<style>
.crud-opts {
padding: 4px 0;
display: -webkit-flex;
display: flex;
align-items: center;
}
.crud-opts .crud-opts-right {
margin-left: auto;
}
.crud-opts .crud-opts-right span {
float: left;
}
.crud-opts {
padding: 4px 0;
display: -webkit-flex;
display: flex;
align-items: center;
}
.crud-opts .crud-opts-right {
margin-left: auto;
}
.crud-opts .crud-opts-right span {
float: left;
}
</style>
@@ -12,6 +12,7 @@
</template>
<script>
import { pagination } from '@crud/crud'
export default {
mixins: [pagination()]
}
@@ -1,12 +1,26 @@
<!--搜索与重置-->
<template>
<span>
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="crud.toQuery">搜索</el-button>
<el-button v-if="crud.optShow.reset" class="filter-item" size="mini" type="warning" icon="el-icon-refresh-left" @click="crud.resetQuery()">重置</el-button>
<el-button
class="filter-item"
size="mini"
type="success"
icon="el-icon-search"
@click="crud.toQuery"
>搜索</el-button>
<el-button
v-if="crud.optShow.reset"
class="filter-item"
size="mini"
type="warning"
icon="el-icon-refresh-left"
@click="crud.resetQuery()"
>重置</el-button>
</span>
</template>
<script>
import { crud } from '@crud/crud'
export default {
mixins: [crud()],
props: {
@@ -1,18 +1,48 @@
<template>
<div>
<el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" @click.stop="crud.toEdit(data)" />
<el-popover v-model="pop" v-permission="permission.del" placement="top" width="180" trigger="manual" @show="onPopoverShow" @hide="onPopoverHide">
<el-button
v-permission="permission.edit"
:loading="crud.status.cu === 2"
:disabled="disabledEdit"
size="mini"
type="primary"
icon="el-icon-edit"
@click.stop="crud.toEdit(data)"
/>
<el-popover
v-model="pop"
v-permission="permission.del"
placement="top"
width="180"
trigger="manual"
@show="onPopoverShow"
@hide="onPopoverHide"
>
<p>{{ msg }}</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="doCancel">取消</el-button>
<el-button :loading="crud.dataStatus[crud.getDataId(data)].delete === 2" type="primary" size="mini" @click="crud.doDelete(data)">确定</el-button>
<el-button
:loading="crud.dataStatus[crud.getDataId(data)].delete === 2"
type="primary"
size="mini"
@click="crud.doDelete(data)"
>确定
</el-button>
</div>
<el-button slot="reference" :disabled="disabledDle" type="danger" icon="el-icon-delete" size="mini" @click.stop="toDelete" />
<el-button
slot="reference"
:disabled="disabledDle"
type="danger"
icon="el-icon-delete"
size="mini"
@click.stop="toDelete"
/>
</el-popover>
</div>
</template>
<script>
import CRUD, { crud } from '@crud/crud'
export default {
mixins: [crud()],
props: {
+17 -11
View File
@@ -1,6 +1,6 @@
import { download, initData } from '@/api/data'
import { downloadFile, parseTime } from '@/utils/index'
import Vue from 'vue'
import { DownloadFile } from '@/utils/CsDomUtil'
/**
* CRUD配置
@@ -30,15 +30,20 @@ function CRUD(options) {
// Form 表单
form: {},
// 重置表单
defaultForm: () => {},
defaultForm: () => {
},
// 等待时间
time: 50,
// CRUD Method
crudMethod: {
add: (form) => {},
del: (id) => {},
edit: (form) => {},
get: (id) => {}
add: (form) => {
},
del: (id) => {
},
edit: (form) => {
},
get: (id) => {
}
},
// 主页操作栏显示哪些按钮
optShow: {
@@ -328,7 +333,7 @@ function CRUD(options) {
doExport() {
crud.downloadLoading = true
download(crud.url + '/download', crud.getQueryParams()).then(result => {
downloadFile(result, crud.title + '数据', 'xlsx')
DownloadFile(result, crud.title + '数据', 'xlsx')
crud.downloadLoading = false
}).catch(() => {
crud.downloadLoading = false
@@ -419,6 +424,7 @@ function CRUD(options) {
*/
resetDataStatus() {
const dataStatus = {}
function resetStatus(datas) {
datas.forEach(e => {
dataStatus[crud.getDataId(e)] = {
@@ -430,6 +436,7 @@ function CRUD(options) {
}
})
}
resetStatus(crud.data)
crud.dataStatus = dataStatus
},
@@ -461,7 +468,9 @@ function CRUD(options) {
*/
selectChange(selection, row) {
// 如果selection中存在row代表是选中,否则是取消选中
if (selection.find(val => { return crud.getDataId(val) === crud.getDataId(row) })) {
if (selection.find(val => {
return crud.getDataId(val) === crud.getDataId(row)
})) {
if (row.children) {
row.children.forEach(val => {
crud.getTable().toggleRowSelection(val, true)
@@ -674,9 +683,6 @@ function presenter(crud) {
})
this.crud = this.$crud['defalut'] || cruds[0]
},
methods: {
parseTime
},
created() {
for (const k in this.$crud) {
if (this.$crud[k].queryOnPresenterCreated) {
@@ -4,8 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
const animationDuration = 6000
@@ -31,7 +32,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,8 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -29,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,8 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
const animationDuration = 3000
@@ -31,7 +32,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -4,9 +4,9 @@
<script>
import echarts from 'echarts'
import { DeBounce } from '@/utils/CsUtil'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
@@ -30,7 +30,7 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHandler = debounce(() => {
this.__resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
@@ -1,5 +1,10 @@
<template>
<a href="https://github.com/odboy-tianjun/cutejava" target="_blank" class="github-corner" aria-label="View source on Github">
<a
href="https://github.com/odboy-tianjun/cutejava"
target="_blank"
class="github-corner"
aria-label="View source on Github"
>
<svg
width="80"
height="80"
@@ -43,10 +48,11 @@
}
}
@media (max-width:500px) {
@media (max-width: 500px) {
.github-corner:hover .octo-arm {
animation: none
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
@@ -8,7 +8,9 @@
width="64"
height="64"
>
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
/>
</svg>
</div>
</template>
@@ -106,7 +106,9 @@ export default {
for (const router of routes) {
// skip hidden router
if (router.hidden) { continue }
if (router.hidden) {
continue
}
const data = {
path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path,
@@ -167,7 +169,7 @@ export default {
display: inline-block;
vertical-align: middle;
::v-deep .el-input__inner {
::v-deep .el-input__inner {
border-radius: 0;
border: 0;
padding-left: 0;
@@ -1,6 +1,13 @@
<template>
<div class="icon-body">
<el-input v-model="name" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons" @input.native="filterIcons">
<el-input
v-model="name"
style="position: relative;"
clearable
placeholder="请输入图标名称"
@clear="filterIcons"
@input.native="filterIcons"
>
<i slot="suffix" class="el-icon-search el-input__icon" />
</el-input>
<div class="icon-list">
@@ -14,6 +21,7 @@
<script>
import icons from './requireIcons'
export default {
name: 'IconSelect',
data() {
@@ -42,26 +50,29 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.icon-body {
width: 100%;
padding: 10px;
.icon-list {
height: 200px;
overflow-y: scroll;
div {
height: 30px;
line-height: 30px;
margin-bottom: -5px;
cursor: pointer;
width: 33%;
float: left;
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.icon-body {
width: 100%;
padding: 10px;
.icon-list {
height: 200px;
overflow-y: scroll;
div {
height: 30px;
line-height: 30px;
margin-bottom: -5px;
cursor: pointer;
width: 33%;
float: left;
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
}
}
</style>
@@ -1,4 +1,3 @@
const req = require.context('../../assets/icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()
@@ -95,6 +95,7 @@ export default {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
@@ -10,7 +10,7 @@
</template>
<script>
import { addClass, removeClass } from '@/utils'
import { AddClass, RemoveClass } from '@/utils/CsDomUtil'
export default {
name: 'RightPanel',
@@ -46,9 +46,9 @@ export default {
this.addEventClick()
}
if (value) {
addClass(document.body, 'showRightPanel')
AddClass(document.body, 'showRightPanel')
} else {
removeClass(document.body, 'showRightPanel')
RemoveClass(document.body, 'showRightPanel')
}
}
},
@@ -81,69 +81,70 @@ export default {
</script>
<style>
.showRightPanel {
overflow: hidden;
position: relative;
width: calc(100% - 15px);
}
.showRightPanel {
overflow: hidden;
position: relative;
width: calc(100% - 15px);
}
</style>
<style lang="scss" scoped>
.rightPanel-background {
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
background: rgba(0, 0, 0, .2);
z-index: -1;
}
.rightPanel {
width: 100%;
max-width: 260px;
height: 100vh;
position: fixed;
top: 0;
right: 0;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
transition: all .25s cubic-bezier(.7, .3, .1, 1);
transform: translate(100%);
background: #fff;
z-index: 40000;
}
.show {
transition: all .3s cubic-bezier(.7, .3, .1, 1);
.rightPanel-background {
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
background: rgba(0, 0, 0, .2);
z-index: -1;
z-index: 20000;
opacity: 1;
width: 100%;
height: 100%;
}
.rightPanel {
width: 100%;
max-width: 260px;
height: 100vh;
position: fixed;
top: 0;
right: 0;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
transition: all .25s cubic-bezier(.7, .3, .1, 1);
transform: translate(100%);
background: #fff;
z-index: 40000;
transform: translate(0);
}
}
.show {
transition: all .3s cubic-bezier(.7, .3, .1, 1);
.handle-button {
width: 48px;
height: 48px;
position: absolute;
left: -48px;
text-align: center;
font-size: 24px;
border-radius: 6px 0 0 6px !important;
z-index: 0;
pointer-events: auto;
cursor: pointer;
color: #fff;
line-height: 48px;
.rightPanel-background {
z-index: 20000;
opacity: 1;
width: 100%;
height: 100%;
}
.rightPanel {
transform: translate(0);
}
}
.handle-button {
width: 48px;
height: 48px;
position: absolute;
left: -48px;
text-align: center;
i {
font-size: 24px;
border-radius: 6px 0 0 6px !important;
z-index: 0;
pointer-events: auto;
cursor: pointer;
color: #fff;
line-height: 48px;
i {
font-size: 24px;
line-height: 48px;
}
}
}
</style>
@@ -4,9 +4,15 @@
<svg-icon class-name="size-icon" icon-class="size" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
<el-dropdown-item
v-for="item of sizeOptions"
:key="item.value"
:disabled="size===item.value"
:command="item.value"
>
{{
item.label }}
item.label
}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@@ -7,7 +7,7 @@
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
import { IsExternal } from '@/utils/CsValidateUtil'
export default {
name: 'SvgIcon',
@@ -23,7 +23,7 @@ export default {
},
computed: {
isExternal() {
return isExternal(this.iconClass)
return IsExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
@@ -56,7 +56,7 @@ export default {
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
mask-size: cover !important;
display: inline-block;
}
</style>
@@ -11,6 +11,7 @@
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
import Cookies from 'js-cookie'
export default {
data() {
return {
@@ -7,7 +7,7 @@
</template>
<script>
import { isExternal } from '@/utils/validate'
import { IsExternal } from '@/utils/CsValidateUtil'
export default {
props: {
@@ -18,7 +18,7 @@ export default {
},
methods: {
linkProps(url) {
if (isExternal(url)) {
if (IsExternal(url)) {
return {
is: 'a',
href: url,
@@ -26,7 +26,7 @@
<script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import { IsExternal } from '@/utils/CsValidateUtil'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
@@ -82,10 +82,10 @@ export default {
return false
},
resolvePath(routePath) {
if (isExternal(routePath)) {
if (IsExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
if (IsExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
+3 -3
View File
@@ -10,7 +10,7 @@ import Element from 'element-ui'
import dict from './components/Dict'
// 防止二次提交
import { preventReClick } from '@/utils/commands'
import { preventReClick } from '@/plugins/PreventReClickDirective'
// 一键复制插件
import VueClipboard from 'vue-clipboard2'
@@ -19,7 +19,7 @@ import VueClipboard from 'vue-clipboard2'
import SplitPane from 'vue-splitpane'
// 权限指令
import checkPer from '@/utils/permission'
import checkPer from '@/plugins/CheckPermPlugin'
import permission from './components/Permission'
import './assets/styles/element-variables.scss'
@@ -35,8 +35,8 @@ import './router/index' // permission control
Vue.use(checkPer)
Vue.use(permission)
Vue.directive(preventReClick)
Vue.use(dict)
Vue.use(preventReClick)
Vue.use(VueClipboard)
Vue.use(Element, {
size: Cookies.get('size') || 'small' // set element-ui default size
+2 -6
View File
@@ -2,10 +2,7 @@ import {
initData,
download
} from '@/api/data'
import {
parseTime,
downloadFile
} from '@/utils'
import { DownloadFile } from '@/utils/CsDomUtil'
export default {
data() {
@@ -46,8 +43,7 @@ export default {
}
},
methods: {
parseTime,
downloadFile,
DownloadFile,
async init() {
if (!await this.beforeInit()) {
return
@@ -0,0 +1,21 @@
import Vue from 'vue'
/**
* 防止重复提交指令,需要在main.js中全局注册 Vue.directive('preventReClick', preventReClick)
* 使用默认的 3000 毫秒防重复点击时间
* 例子: <el-button v-prevent-re-click >提交</el-button>
* 自定义防重复点击时间为 5000 毫秒
* 例子: <el-button v-prevent-re-click >提交</el-button>
*/
export const preventReClick = Vue.directive('preventReClick', {
inserted(el, binding) {
el.addEventListener('click', () => {
if (!el.disabled) {
el.disabled = true
setTimeout(() => {
el.disabled = false
}, binding.value || 3000)
}
})
}
})
+1 -1
View File
@@ -34,7 +34,7 @@ const user = {
setToken(res.token, rememberMe)
commit('SET_TOKEN', res.token)
setUserInfo(res.user, commit)
// 第一次加载菜单时用到, 具体见 src 目录下的 permission.js
// 第一次加载菜单时用到, 具体见 src/plugins/CheckPermPlugin.js
commit('SET_LOAD_MENUS', true)
resolve()
}).catch(error => {
+88
View File
@@ -0,0 +1,88 @@
import { FormatDateTimeShortStr } from '@/utils/CsUtil'
import { getToken } from '@/utils/auth'
import axios from 'axios'
/**
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function HasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
/**
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function AddClass(ele, cls) {
if (!HasClass(ele, cls)) ele.className += ' ' + cls
}
/**
* Remove class from element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function RemoveClass(ele, cls) {
if (HasClass(ele, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
ele.className = ele.className.replace(reg, ' ')
}
}
/**
* @param {HTMLElement} element
* @param {string} className
*/
export function ToggleClass(element, className) {
if (!element || !className) {
return
}
let classString = element.className
const nameIndex = classString.indexOf(className)
if (nameIndex === -1) {
classString += '' + className
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length)
}
element.className = classString
}
/**
* 下载文件
* @param obj 后端返回的数据
* @param name 文件名
* @param suffix 文件后缀
* @constructor
*/
export function DownloadFile(obj, name, suffix) {
const url = window.URL.createObjectURL(new Blob([obj]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
const fileName = FormatDateTimeShortStr(new Date()) + '-' + name + '.' + suffix
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
/**
* 上传文件
* @param api 上传地址
* @param file 上传的文件
* @returns {Promise<axios.AxiosResponse<any>>}
*/
export function UploadFile(api, file) {
const data = new FormData()
data.append('file', file)
const config = {
headers: { 'Authorization': getToken() }
}
return axios.post(api, data, config)
}
+330
View File
@@ -0,0 +1,330 @@
import store from '@/store'
import dayjs from 'dayjs'
/**
* 权限校验
* @param value
* @returns {boolean|*}
*/
export function CheckPermissions(value) {
if (value && value instanceof Array && value.length > 0) {
// 拥有的权限
const roles = store.getters && store.getters.roles
// 需要的权限
const permissionRoles = value
return roles.some(role => {
return permissionRoles.includes(role)
})
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}
/**
* 字符串转数字
* @param value
* @returns {Number|*}
*/
export function StringToNumber(value) {
try {
return value ? Number(value) : value
} catch (e) {
return value
}
}
/**
* 保留几位小数
* @param num Number
* @param accuracy 精度
* @returns {string|number}
*/
export function NumberToFixed(num, accuracy) {
return num instanceof Number ? num.toFixed(accuracy) : num
}
/**
* 浅拷贝
* @param source 源对象
* @param target 目标对象
* @returns {*}
*/
export function CopyObject(source, target) {
return Object.assign(target, source)
}
/**
* 深拷贝对象
* @param obj
*/
export function DeepCopyObject(obj) {
return JSON.parse(JSON.stringify(obj))
}
/**
* 统计对象属性等于targetValue的数量
* @param arr 对象数组
* @param propKey 属性名称
* @param targetValue 目标值
* @returns {*|number}
*/
export function CountArraysObjectByPropKey(arr, propKey, targetValue) {
if (!Array.isArray(arr)) return 0
return arr.filter(item => item[propKey] === targetValue).length
}
/**
* 根据value获取label
* @param options /
* @param value /
* @returns {*}
*/
export function GetOptionLabelByValue(options, value) {
if (options instanceof Array) {
const find = options.find(f => f.value === value)
if (find) {
return find.label
}
return ''
}
}
/**
* 格式化字节数字符串
* @param value
* @returns {string}
* @constructor
*/
export function FormatBytesStr(value) {
if (typeof value !== 'number' || isNaN(value) || value <= 0) {
return '0 Bytes'
}
const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const index = Math.floor(Math.log(value) / Math.log(1024))
const unit = units[Math.min(index, units.length - 1)]
const result = (value / Math.pow(1024, index)).toFixed(2)
return `${result} ${unit}`
}
/**
* 格式化日期字符串
* @param originVal
* @returns {string}
* @constructor
*/
export function FormatDateStr(originVal) {
if (originVal === undefined) {
return ''
}
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
return `${y}-${m}-${d}`
}
/**
* 格式化日期时间字符串
* @param originVal
* @returns {string}
* @constructor
*/
export function FormatDateTimeStr(originVal) {
if (originVal) {
return dayjs(originVal).format('YYYY-MM-DD HH:mm:ss')
}
return ''
}
/**
* 格式化日期时间字符串,不带分割符号
* @param originVal
* @returns {string}
* @constructor
*/
export function FormatDateTimeShortStr(originVal) {
if (originVal) {
return dayjs(originVal).format('YYYYMMDDHHmmss')
}
return ''
}
/**
* 格式化表格行列中的日期时间字符串
* @param row
* @param column
* @param cellValue
* @param index
* @returns {string}
* @constructor
*/
export function FormatRowDateTimeStr(row, column, cellValue, index) {
if (cellValue === undefined) {
return ''
}
const dt = new Date(cellValue)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
/**
* 获取变量类型
* @param value
* @returns {string}
* @constructor
*/
export function GetValueType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
}
/**
* 数组去重
* @param arr
* @returns {any[]}
* @constructor
*/
export function UniqueArrays(arr) {
return [...new Set(arr)]
}
/**
* 手机号掩码
* @param mobile
* @returns {*|string}
* @constructor
*/
export function MaskMobile(mobile) {
if (mobile.length > 7) {
return mobile.substr(0, 3) + '****' + mobile.substr(7)
}
return mobile
}
/**
* 邮箱掩码
* @param email
* @returns {string}
* @constructor
*/
export function MaskEmail(email) {
if (String(email).indexOf('@') > 0) {
const str = email.split('@')
let _s = ''
if (str[0].length > 3) {
for (let i = 0; i < str[0].length - 3; i++) {
_s += '*'
}
}
return str[0].substr(0, 3) + _s + '@' + str[1]
}
return email
}
/**
* 获取Url中的查询参数
* @param {string} url
* @returns {Object}
*/
export function GetUrlQueryArgs(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
/**
* 将Url中的查询参数转成对象
* @param {string} url
* @returns {Object}
*/
export function ParseUrlQueryArgsToObject(url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, ' ') +
'"}'
)
}
/**
* 合并两个属性不一致的对象
* @param {Object} target
* @param {(Object|Array)} source
* @returns {Object}
*/
export function MergeObject(target, source) {
if (typeof target !== 'object') {
target = {}
}
if (Array.isArray(source)) {
return source.slice()
}
Object.keys(source).forEach(property => {
const sourceProperty = source[property]
if (typeof sourceProperty === 'object') {
target[property] = MergeObject(target[property], sourceProperty)
} else {
target[property] = sourceProperty
}
})
return target
}
/**
* 防抖动
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function DeBounce(func, wait, immediate = false) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
@@ -1,90 +1,98 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
/**
* 是否链接
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
export function IsExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
// /**
// * 是否有效用户名
// * @param {string} str
// * @returns {Boolean}
// */
// export function IsValidUsername(str) {
// const validMap = ['admin', 'editor']
// return validMap.indexOf(str.trim()) >= 0
// }
/**
* 是否有效链接
* @param {string} url
* @returns {Boolean}
*/
export function validURL(url) {
export function IsValidURL(url) {
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/**
* 是否小写字母
* @param {string} str
* @returns {Boolean}
*/
export function validLowerCase(str) {
export function IsValidLowerCaseLetter(str) {
const reg = /^[a-z]+$/
return reg.test(str)
}
/**
* 是否大写字母
* @param {string} str
* @returns {Boolean}
*/
export function validUpperCase(str) {
export function IsValidUpperCase(str) {
const reg = /^[A-Z]+$/
return reg.test(str)
}
/**
* 是否包含大、小写字母
* @param {string} str
* @returns {Boolean}
*/
export function validAlphabets(str) {
export function IsValidLetter(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
/**
* 是否邮箱
* @param {string} email
* @returns {Boolean}
*/
export function validEmail(email) {
export function IsValidEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return reg.test(email)
}
export function isvalidPhone(phone) {
const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
return reg.test(phone)
}
// /**
// * 是否手机号
// * @param phone
// * @returns {boolean}
// * @constructor
// */
// export function IsValidPhone(phone) {
// const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
// return reg.test(phone)
// }
/**
* 是否是字符串
* @param {string} str
* @returns {Boolean}
*/
export function isString(str) {
if (typeof str === 'string' || str instanceof String) {
return true
}
return false
export function IsString(str) {
return typeof str === 'string' || str instanceof String
}
/**
* 是否数组
* @param {Array} arg
* @returns {Boolean}
*/
export function isArray(arg) {
export function IsArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'
}
@@ -97,7 +105,7 @@ export function isArray(arg) {
* @param value
* @param callback
*/
export function validateIP(rule, value, callback) {
export function IsValidIP(rule, value, callback) {
if (value === '' || value === undefined || value == null) {
callback()
} else {
@@ -111,7 +119,7 @@ export function validateIP(rule, value, callback) {
}
/* 是否手机号码或者固话*/
export function validatePhoneTwo(rule, value, callback) {
export function IsValidPhoneTwo(rule, value, callback) {
const reg = /^((0\d{2,3}-\d{7,8})|(1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}))$/
if (value === '' || value === undefined || value == null) {
callback()
@@ -125,7 +133,7 @@ export function validatePhoneTwo(rule, value, callback) {
}
/* 是否固话*/
export function validateTelephone(rule, value, callback) {
export function IsValidTelephone(rule, value, callback) {
const reg = /0\d{2}-\d{7,8}/
if (value === '' || value === undefined || value == null) {
callback()
@@ -139,7 +147,7 @@ export function validateTelephone(rule, value, callback) {
}
/* 是否手机号码*/
export function validatePhone(rule, value, callback) {
export function IsValidPhone(rule, value, callback) {
const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
if (value === '' || value === undefined || value == null) {
callback()
@@ -153,7 +161,7 @@ export function validatePhone(rule, value, callback) {
}
/* 是否身份证号码*/
export function validateIdNo(rule, value, callback) {
export function IsValidIdNumber(rule, value, callback) {
const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
if (value === '' || value === undefined || value == null) {
callback()
+42
View File
@@ -0,0 +1,42 @@
import store from '@/store'
import Vue from 'vue'
/**
* WebSocket客户端
*/
export default function() {
const user = store.getters && store.getters.user
const webSocketApi = store.getters && store.getters.websocketApi
this.context = Vue.prototype
this.wsUri = webSocketApi.replace('{sid}', user.username)
/**
* 初始化WebSocket连接
* @param handleError -> function (e) { e是异常本身 }
* @param handleMessage -> function (e) { e.data是message }
*/
this.connect = function(handleError, handleMessage) {
this.wsClient = new WebSocket(this.wsUri)
// 连接发生错误
this.wsClient.onerror = handleError
// 收到消息
this.wsClient.onmessage = handleMessage
return this
}
/**
* 发送客户端数据
* @param data 客户端数据
*/
this.sendData = function(data) {
this.wsClient.send(JSON.stringify(data))
}
/**
* 用于关闭ws连接
*/
this.close = function() {
console.log('关闭连接')
if (this.wsClient.readyState === 1) {
this.sendData([{}])
this.wsClient.close()
}
}
}
-20
View File
@@ -1,20 +0,0 @@
import Vue from 'vue'
/**
* 防止重复提交指令<br/>
* 例子: <el-button v-prevent-re-click >提交</el-button>
*/
const preventReClick = Vue.directive('preventReClick', {
inserted(el, binding) {
el.addEventListener('click', () => {
if (!el.disabled) {
el.disabled = true
setTimeout(() => {
el.disabled = false
}, binding.value || 3000)
}
})
}
})
export { preventReClick }
-385
View File
@@ -1,385 +0,0 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
import dayjs from 'dayjs'
/**
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'undefined' || time === null || time === 'null') {
return ''
} else if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
/**
* @param {number} time
* @param {string} option
* @returns {string}
*/
export function formatTime(time, option) {
if (('' + time).length === 10) {
time = parseInt(time) * 1000
} else {
time = +time
}
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
// less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time, option)
} else {
return (
d.getMonth() +
1 +
'月' +
d.getDate() +
'日' +
d.getHours() +
'时' +
d.getMinutes() +
'分'
)
}
}
/**
* @param {string} url
* @returns {Object}
*/
export function getQueryObject(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
/**
* @param {string} input value
* @returns {number} output value
*/
export function byteLength(str) {
// returns the byte length of an utf8 string
let s = str.length
for (var i = str.length - 1; i >= 0; i--) {
const code = str.charCodeAt(i)
if (code > 0x7f && code <= 0x7ff) s++
else if (code > 0x7ff && code <= 0xffff) s += 2
if (code >= 0xDC00 && code <= 0xDFFF) i--
}
return s
}
/**
* @param {Array} actual
* @returns {Array}
*/
export function cleanArray(actual) {
const newArray = []
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i])
}
}
return newArray
}
/**
* @param {Object} json
* @returns {Array}
*/
export function param(json) {
if (!json) return ''
return cleanArray(
Object.keys(json).map(key => {
if (json[key] === undefined) return ''
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
})
).join('&')
}
/**
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, ' ') +
'"}'
)
}
/**
* @param {string} val
* @returns {string}
*/
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
/**
* Merges two objects, giving the last one precedence
* @param {Object} target
* @param {(Object|Array)} source
* @returns {Object}
*/
export function objectMerge(target, source) {
if (typeof target !== 'object') {
target = {}
}
if (Array.isArray(source)) {
return source.slice()
}
Object.keys(source).forEach(property => {
const sourceProperty = source[property]
if (typeof sourceProperty === 'object') {
target[property] = objectMerge(target[property], sourceProperty)
} else {
target[property] = sourceProperty
}
})
return target
}
/**
* @param {HTMLElement} element
* @param {string} className
*/
export function toggleClass(element, className) {
if (!element || !className) {
return
}
let classString = element.className
const nameIndex = classString.indexOf(className)
if (nameIndex === -1) {
classString += '' + className
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length)
}
element.className = classString
}
/**
* @param {string} type
* @returns {Date}
*/
export function getTime(type) {
if (type === 'start') {
return new Date().getTime() - 3600 * 1000 * 24 * 90
} else {
return new Date(new Date().toDateString())
}
}
/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
/**
* @param {Array} arr
* @returns {Array}
*/
export function uniqueArr(arr) {
return Array.from(new Set(arr))
}
/**
* @returns {string}
*/
export function createUniqueString() {
const timestamp = +new Date() + ''
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
return (+(randomNum + timestamp)).toString(32)
}
/**
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function hasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
/**
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += ' ' + cls
}
/**
* Remove class from element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
ele.className = ele.className.replace(reg, ' ')
}
}
// 替换邮箱字符
export function regEmail(email) {
if (String(email).indexOf('@') > 0) {
const str = email.split('@')
let _s = ''
if (str[0].length > 3) {
for (var i = 0; i < str[0].length - 3; i++) {
_s += '*'
}
}
var new_email = str[0].substr(0, 3) + _s + '@' + str[1]
}
return new_email
}
// 替换手机字符
export function regMobile(mobile) {
if (mobile.length > 7) {
var new_mobile = mobile.substr(0, 3) + '****' + mobile.substr(7)
}
return new_mobile
}
// 下载文件
export function downloadFile(obj, name, suffix) {
const url = window.URL.createObjectURL(new Blob([obj]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
const fileName = parseTime(new Date()) + '-' + name + '.' + suffix
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// 数组去重
export const uniqueArrays = (arr) => [...new Set(arr)]
// 获取变量类型
export const type = (value) => Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
// 深拷贝对象
export const deepClone = (obj) => JSON.parse(JSON.stringify(obj))
// 格式化日期时间字符串
export const formatDateTimeStr = (date) => {
if (date) {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
}
return ''
}
-20
View File
@@ -1,20 +0,0 @@
import Vue from 'vue'
/**
* 防止重复提交指令<br/>
* 例子: <el-button v-prevent-re-click >提交</el-button>
*/
const preventReClick = Vue.directive('preventReClick', {
inserted(el, binding) {
el.addEventListener('click', () => {
if (!el.disabled) {
el.disabled = true
setTimeout(() => {
el.disabled = false
}, binding.value || 3000)
}
})
}
})
export { preventReClick }
-137
View File
@@ -1,137 +0,0 @@
import store from '@/store'
const PermissionUtil = {
/**
* 权限校验
* @param value
* @returns {boolean|*}
*/
validate: function(value) {
if (value && value instanceof Array && value.length > 0) {
// 拥有的权限
const roles = store.getters && store.getters.roles
// 需要的权限
const permissionRoles = value
return roles.some(role => {
return permissionRoles.includes(role)
})
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}
}
const StringUtil = {
/**
* 字符串转数字
* @param value
* @returns {Number|*}
*/
toNumber: function(value) {
if (value) {
return Number(value)
}
return 0
},
/**
* 保留几位小数
* @param num Number
* @param accuracy 精度
* @returns {string|number}
*/
retailNum: function(num, accuracy) {
return num instanceof Number ? num.toFixed(accuracy) : 0
}
}
const ObjectUtil = {
/**
* 浅拷贝
* @param source 源对象
* @param target 目标对象
* @returns {*}
*/
copy: function(source, target) {
return Object.assign(target, source)
}
}
const LabelUtil = {
/**
* 根据value获取label
* @param options /
* @param value /
* @returns {*}
*/
getLabel: function(options, value) {
if (options instanceof Array) {
const find = options.find(f => f.value === value)
if (find) {
return find.label
}
return ''
}
}
}
const SizeUtil = {
formatBytes: function(value) {
if (typeof value !== 'number' || isNaN(value) || value <= 0) {
return '0 Bytes'
}
const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const index = Math.floor(Math.log(value) / Math.log(1024))
const unit = units[Math.min(index, units.length - 1)]
const result = (value / Math.pow(1024, index)).toFixed(2)
return `${result} ${unit}`
}
}
const DateUtil = {
formatDate: function(originVal) {
if (originVal === undefined) {
return ''
}
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
return `${y}-${m}-${d}`
},
formatDateTime: function(originVal) {
if (originVal === undefined) {
return ''
}
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
},
formatRowDateTime: function(row, column, cellValue, index) {
if (cellValue === undefined) {
return ''
}
const dt = new Date(cellValue)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
}
export {
PermissionUtil,
StringUtil,
ObjectUtil,
LabelUtil,
DateUtil,
SizeUtil
}
+1 -1
View File
@@ -1,4 +1,4 @@
import Date from './datetime.js'
import Date from '../plugins/DateExtendPlugin.js'
export const calendarBaseShortcuts = [{
text: '今天',
-11
View File
@@ -1,11 +0,0 @@
import axios from 'axios'
import { getToken } from '@/utils/auth'
export function upload(api, file) {
var data = new FormData()
data.append('file', file)
const config = {
headers: { 'Authorization': getToken() }
}
return axios.post(api, data, config)
}
-42
View File
@@ -1,42 +0,0 @@
/**
* @param context 上下文,例如 this
* @param wsUri websocket地址
* @constructor
*/
function EasyWebSocket(context, wsUri) {
this.context = context
this.wsUri = wsUri
this.webSocket = null
/**
* 初始化WebSocket连接
* @param handleError -> function (e) { e是异常本身 }
* @param handleMessage -> function (e) { e.data是message }
*/
this.connect = function(handleError, handleMessage) {
this.webSocket = new WebSocket(this.wsUri)
// 连接发生错误
this.webSocket.onerror = handleError
// 收到消息
this.webSocket.onmessage = handleMessage
return this
}
/**
* 发送客户端数据
* @param data 客户端数据
*/
this.sendData = function(data) {
this.webSocket.send(JSON.stringify(data))
}
/**
* 用于关闭ws连接
*/
this.close = function() {
console.info('关闭连接')
if (this.webSocket.readyState === 1) {
this.sendData([{}])
this.webSocket.close()
}
}
}
export default EasyWebSocket
@@ -98,13 +98,14 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {
padding: 18px 22px 22px 22px;
background-color: rgb(240, 242, 245);
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
.dashboard-editor-container {
padding: 18px 22px 22px 22px;
background-color: rgb(240, 242, 245);
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
</style>
@@ -45,23 +45,26 @@
export default {
name: 'SplitPaneDemo',
methods: {
resize() {}
resize() {
}
}
}
</script>
<style lang="scss" scoped>
.wrap{
height:300px;
.paneL{
background-color:red;
height:100%;
width:100%;
.wrap {
height: 300px;
.paneL {
background-color: red;
height: 100%;
width: 100%;
}
.paneR{
.paneR {
background-color: blue;
height:100%;
width:100%;
height: 100%;
width: 100%;
}
}
</style>
@@ -11,8 +11,7 @@
</el-card>
</template>
<script>
import MyWebSocket from '@/utils/websocket'
import { mapGetters } from 'vuex'
import CsWsClient from '@/utils/CsWsClient'
export default {
data() {
@@ -22,12 +21,6 @@ export default {
responseTxt: ''
}
},
computed: {
...mapGetters([
'websocketApi',
'user'
])
},
// mounted() {},
destroyed() {
if (this.client) {
@@ -39,7 +32,7 @@ export default {
// 获取mapGetters中的值
// console.error('userInfo', this.user)
const that = this
that.client = new MyWebSocket(that, that.websocketApi.replaceAll('{sid}', that.user.username))
that.client = new CsWsClient(that)
that.client.connect(function(event) {
console.info('=============== MyWebSocket:event', event)
}, function(msgEvent) {
@@ -20,6 +20,7 @@
import { Message } from 'element-ui'
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
export default {
name: 'CuteFileUpload',
props: {
@@ -18,6 +18,7 @@
import { Message } from 'element-ui'
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
export default {
name: 'CuteFileUpload',
props: {
@@ -27,6 +27,7 @@ import 'codemirror/mode/sql/sql'
import 'codemirror/mode/vue/vue'
import 'codemirror/mode/xml/xml'
import { Message } from 'element-ui'
const SupportModeList = ['yaml', 'java', 'go', 'swift', 'dockerfile', 'groovy', 'lua', 'perl', 'python', 'ruby', 'sql', 'xml', 'vue']
export default {
name: 'CuteCodeEditor',
@@ -14,8 +14,20 @@
@selection-change="onTableSelectionChange"
>
<el-table-column v-if="mode === 'multi'" type="selection" width="55" />
<el-table-column v-for="(column, index) in crud.columns" :key="index" :label="column.label" :prop="column.prop" :formatter="column.formatter ? column.formatter : null" />
<el-table-column v-if="crud.operateColumn" :width="crud.operateColumn.width" label="操作" :formatter="crud.operateColumn.formatter" :fixed="crud.operateColumn.fixed" />
<el-table-column
v-for="(column, index) in crud.columns"
:key="index"
:label="column.label"
:prop="column.prop"
:formatter="column.formatter ? column.formatter : null"
/>
<el-table-column
v-if="crud.operateColumn"
:width="crud.operateColumn.width"
label="操作"
:formatter="crud.operateColumn.formatter"
:fixed="crud.operateColumn.fixed"
/>
</el-table>
<el-pagination
:current-page="pageProps.current"
@@ -31,6 +43,7 @@
<script>
import Sortable from 'sortablejs'
export default {
name: 'CuteDragTable',
props: {
@@ -13,6 +13,7 @@ import 'codemirror/lib/codemirror.css'
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
import 'codemirror/theme/darcula.css'
import 'codemirror/mode/javascript/javascript'
export default {
name: 'CuteJavaEditor',
props: {
@@ -15,10 +15,11 @@
</template>
<script>
import { upload } from '@/utils/upload'
import { mapGetters } from 'vuex'
import { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
import { UploadFile } from '@/utils/CsDomUtil'
const dynamicHeight = 160
export default {
name: 'CuteMarkdown',
@@ -52,7 +53,7 @@ export default {
methods: {
imgAdd(pos, $file) {
const that = this
upload(that.imagesUploadApi, $file).then(res => {
UploadFile(that.imagesUploadApi, $file).then(res => {
const data = res.data
const url = that.baseApi + '/file/' + data.type + '/' + data.realName
that.$refs.md.$img2Url(pos, url)
@@ -61,7 +61,9 @@
<el-row>
<el-col :span="12" style="text-align: left">
<!-- 流水线状态为fail,且节点支持重试 -->
<div v-if="pipelineData.currentNodeStatus === 'fail' && pipelineData.retry === true">
<div
v-if="pipelineData.currentNodeStatus && pipelineData.currentNodeStatus === 'fail' && pipelineData.retry === true"
>
<div class="box-buttons">
<el-button
type="text"
@@ -69,7 +71,7 @@
:style="{
padding: 0,
margin: '0 10px 0 10px',
color: statusColorConst.success.color
color: statusColorConst.running.color
}"
@click="onNodeRetryClick"
>
@@ -79,7 +81,7 @@
</div>
<!-- 流水线状态为success,且有需要满足条件的操作按钮 -->
<div
v-else-if="pipelineData.currentNodeStatus === statusColorConst.success.code && pipelineData.buttons && pipelineData.buttons.length > 0"
v-else-if="pipelineData.currentNodeStatus && pipelineData.currentNodeStatus === statusColorConst.success.code && pipelineData.buttons && pipelineData.buttons.length > 0"
>
<div class="box-buttons">
<el-button
@@ -90,7 +92,7 @@
:style="{
padding: 0,
margin: '0 10px 0 10px',
color: ['apply','agree','ok','success'].includes(buttonItem.code) ? 'green' : 'red',
color: ['apply','agree','ok','success'].includes(buttonItem.code) ? statusColorConst.running.color : statusColorConst.fail.color,
display: ['success', 'fail'].includes(buttonItem.code) && pipelineData.status === buttonItem.code ? '' : 'none'
}"
@click="onNodeOperationClick(buttonItem)"
@@ -101,7 +103,7 @@
</div>
<!-- 流水线状态非pending,且有操作按钮 -->
<div
v-else-if="pipelineData.currentNodeStatus !== statusColorConst.pending.code && pipelineData.buttons && pipelineData.buttons.length > 0"
v-else-if="pipelineData.currentNodeStatus && pipelineData.currentNodeStatus !== statusColorConst.pending.code && pipelineData.buttons && pipelineData.buttons.length > 0"
>
<div class="box-buttons">
<el-button
@@ -112,7 +114,7 @@
:style="{
padding: 0,
margin: '0 10px 0 10px',
color: ['apply','agree','ok','success'].includes(buttonItem.code) ? 'green' : 'red'
color: ['apply','agree','ok','success'].includes(buttonItem.code) ? statusColorConst.running.color : statusColorConst.fail.color
}"
@click="onNodeOperationClick(buttonItem)"
>
@@ -160,7 +162,7 @@ export default {
return {
statusColorConst: {
pending: { code: 'pending', label: '未开始', color: '#C0C4CC' },
running: { code: 'running', label: '运行中', color: '#67C23A' },
running: { code: 'running', label: '运行中', color: '#409EFF' },
success: { code: 'success', label: '执行成功', color: '#67C23A' },
fail: { code: 'fail', label: '执行失败', color: '#F56C6C' }
},
@@ -265,8 +267,8 @@ export default {
</script>
<style scoped>
.box-card {
width: 240px; /* 卡片宽度 */
max-width: 240px; /* 卡片宽度 */
width: 260px; /* 卡片宽度 */
max-width: 260px; /* 卡片宽度 */
padding: 10px;
background-color: #fff; /* 背景颜色 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
@@ -28,8 +28,9 @@
import { mapGetters } from 'vuex'
import '@wangeditor/editor/dist/css/style.css'
import { Message } from 'element-ui'
import { upload } from '@/utils/upload'
import { Toolbar, Editor } from '@wangeditor/editor-for-vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { UploadFile } from '@/utils/CsDomUtil'
export default {
name: 'CuteRichTextEditor',
components: { Toolbar, Editor },
@@ -62,7 +63,7 @@ export default {
allowedFileTypes: ['image/*'],
// 自定义上传
async customUpload(file, insertFn) { // JS 语法
upload(that.imagesUploadApi, file).then(res => {
UploadFile(that.imagesUploadApi, file).then(res => {
const data = res.data
const url = that.baseApi + '/file/' + data.dateGroup + '/' + data.realName
// 最后插入图片
@@ -14,8 +14,20 @@
@selection-change="onTableSelectionChange"
>
<el-table-column v-if="mode === 'multi'" type="selection" width="55" />
<el-table-column v-for="(column, index) in crud.columns" :key="index" :label="column.label" :prop="column.prop" :formatter="column.formatter ? column.formatter : null" />
<el-table-column v-if="crud.operateColumn" :width="crud.operateColumn.width" label="操作" :formatter="crud.operateColumn.formatter" :fixed="crud.operateColumn.fixed" />
<el-table-column
v-for="(column, index) in crud.columns"
:key="index"
:label="column.label"
:prop="column.prop"
:formatter="column.formatter ? column.formatter : null"
/>
<el-table-column
v-if="crud.operateColumn"
:width="crud.operateColumn.width"
label="操作"
:formatter="crud.operateColumn.formatter"
:fixed="crud.operateColumn.fixed"
/>
</el-table>
<el-pagination
:current-page="pageProps.current"
@@ -13,6 +13,7 @@ import 'codemirror/lib/codemirror.css'
// 主题预览地址 https://blog.csdn.net/qq_41694291/article/details/106429772
import 'codemirror/theme/darcula.css'
import 'codemirror/mode/yaml/yaml'
export default {
name: 'CuteYamlEditor',
props: {
@@ -40,9 +40,10 @@
</template>
<script>
import clipboard from '@/utils/clipboard'
import clipboard from '@/plugins/ClipboardPlugin'
import svgIcons from './svg-icons'
import elementIcons from './element-icons'
export default {
name: 'Icons',
data() {
@@ -75,6 +76,7 @@ export default {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.icon-item {
margin: 20px;
height: 85px;
@@ -85,11 +87,13 @@ export default {
color: #24292e;
cursor: pointer;
}
span {
display: block;
font-size: 16px;
margin-top: 10px;
}
.disabled {
pointer-events: none;
}
@@ -42,9 +42,9 @@
</template>
<script>
import { DateUtil } from '@/utils/myUtil'
import DictService from '@/api/system/dict'
import CuteDragTable from '@/views/components/dev/CuteDragTable.vue'
import { FormatRowDateTimeStr } from '@/utils/CsUtil'
export default {
name: 'CuteDragTableDemo',
@@ -74,7 +74,7 @@ export default {
columns: [
{ prop: 'name', label: '名称' },
{ prop: 'description', label: '描述' },
{ prop: 'createTime', label: '创建时间', formatter: DateUtil.formatRowDateTime },
{ prop: 'createTime', label: '创建时间', formatter: FormatRowDateTimeStr },
{ prop: 'createBy', label: '创建人' }
],
operateColumn: {
@@ -11,6 +11,7 @@
import Clipboard from 'clipboard'
import { Message } from 'element-ui'
import CuteOneKeyCopy from '@/views/components/dev/CuteOneKeyCopy'
export default {
components: { CuteOneKeyCopy },
data() {
@@ -54,6 +55,7 @@ export default {
color: #333;
cursor: pointer;
}
#btn:active {
background-color: #ddd;
color: #666;
@@ -1,19 +1,28 @@
<template>
<div>
<el-divider>演示:动态流水线 <el-button type="primary" @click="startPipelineTest">启动流水线</el-button></el-divider>
<el-divider>演示:动态流水线
<el-button
v-prevent-re-click
type="primary"
:loading="dynamicStartButtonLoading"
:disabled="dynamicStartButtonLoading"
@click="startPipelineTest"
>{{ dynamicStartupStatusMap[dynamicStartupStatus].label }}
</el-button>
</el-divider>
<div class="box-pipeline">
<div v-if="dynamicList && dynamicList.length > 0" class="box-pipeline-content">
<div v-if="dynamicInstance.nodes && dynamicInstance.nodes.length > 0" class="box-pipeline-content">
<cute-pipeline-node
v-for="(template, index) in dynamicList"
v-for="(template, index) in dynamicInstance.nodes"
:key="template.code"
:template-data.sync="dynamicList[index]"
:template-data.sync="dynamicInstance.nodes[index]"
/>
</div>
<div v-else class="box-pipeline-content">
<cute-pipeline-node
v-for="(template, index) in templateList"
v-for="(template, index) in dynamicTemplate"
:key="template.code"
:template-data.sync="templateList[index]"
:template-data.sync="dynamicTemplate[index]"
/>
</div>
</div>
@@ -28,9 +37,9 @@
</div>
<div v-else class="box-pipeline-content">
<cute-pipeline-node
v-for="(template, index) in templateList"
v-for="(template, index) in defaultTemplate"
:key="template.code"
:template-data.sync="templateList[index]"
:template-data.sync="defaultTemplate[index]"
/>
</div>
</div>
@@ -45,9 +54,9 @@
</div>
<div v-else class="box-pipeline-content">
<cute-pipeline-node
v-for="(template, index) in templateList"
v-for="(template, index) in defaultTemplate"
:key="template.code"
:template-data.sync="templateList[index]"
:template-data.sync="defaultTemplate[index]"
/>
</div>
</div>
@@ -57,15 +66,18 @@
<script>
import CutePipelineNode from '@/views/components/dev/CutePipelineNode'
import { formatDateTimeStr } from '@/utils'
import { startPipeline, queryLastPipelineDetail } from '@/api/devops/pipeline'
import { getPipelineTemplate } from '@/api/devops/pipelineTemplate'
import { queryLastPipelineDetail, restartPipeline, startPipeline } from '@/api/devops/pipelineInstance'
import { CountArraysObjectByPropKey, FormatDateTimeStr } from '@/utils/CsUtil'
import CsMessage from '@/utils/elementui/CsMessage'
export default {
name: 'CutePipelineNodeDemo',
components: { CutePipelineNode },
data() {
return {
templateList: [
// 静态模板
defaultTemplate: [
{
code: 'node_init',
type: 'service',
@@ -438,46 +450,96 @@ export default {
}
],
// 动态流水线
dynamicStartupStatus: 'start',
dynamicStartupStatusMap: {
start: { label: '启动', code: 'start' },
restart: { label: '重新启动', code: 'restart' }
},
dynamicStartButtonLoading: false,
dynamicTemplate: [],
dynamicHook: null,
dynamicList: [],
dynamicInstanceId: ''
dynamicInstance: {}
}
},
mounted() {
this.refreshData()
this.initPipelineTemplate(4)
const pipelineInstanceId = sessionStorage.getItem('pipelineInstanceId')
if (pipelineInstanceId) {
this.fetchLastDetail(pipelineInstanceId)
}
},
methods: {
refreshData() {
// 成功
for (const datum of this.dataList2) {
if (datum.status === 'running') {
datum.updateTime = formatDateTimeStr(new Date())
datum.updateTime = FormatDateTimeStr(new Date())
}
}
this.dataList2 = [...this.dataList2]
// 失败或重试
for (const datum of this.dataList3) {
if (datum.status === 'running') {
datum.updateTime = formatDateTimeStr(new Date())
datum.updateTime = FormatDateTimeStr(new Date())
}
}
this.dataList3 = [...this.dataList3]
},
startPipelineTest() {
async initPipelineTemplate(templateId) {
const pipelineTemplate = await getPipelineTemplate(templateId)
if (pipelineTemplate && pipelineTemplate.template) {
try {
this.dynamicTemplate = JSON.parse(pipelineTemplate.template)
} catch (e) {
// ignore
}
}
},
async fetchLastDetail(instanceId) {
const that = this
if (that.dynamicHook) {
clearInterval(that.dynamicHook)
const intervalTime = 2000
setTimeout(() => {
that.dynamicHook = setInterval(() => {
queryLastPipelineDetail(instanceId).then((data) => {
that.dynamicInstance = data
// 判断流水线是否结束
const successCount = CountArraysObjectByPropKey(data.nodes, 'status', 'success')
if (that.dynamicTemplate && that.dynamicTemplate.length === successCount) {
clearInterval(that.dynamicHook)
that.dynamicHook = null
that.dynamicStartupStatus = that.dynamicStartupStatusMap.start.code
} else {
that.dynamicStartupStatus = that.dynamicStartupStatusMap.restart.code
}
})
}, intervalTime)
that.dynamicStartButtonLoading = false
}, 2000)
},
async startPipelineTest() {
const that = this
try {
if (that.dynamicHook) {
clearInterval(that.dynamicHook)
}
that.dynamicStartButtonLoading = true
// 这里的 dynamicStartupStatus状态,需要后期与上下文关联后,初始化的时候带入最近一个流水线实例
// let result
// if (that.dynamicStartupStatus === that.dynamicStartupStatusMap.start.code) {
// result = await restartPipeline()
// CsMessage.Success('流水线重新启动成功')
// } else {
// result = await startPipeline()
// CsMessage.Success('流水线启动成功')
// }
const result = await startPipeline()
CsMessage.Success('流水线启动成功')
await that.fetchLastDetail(result.instanceId)
sessionStorage.setItem('pipelineInstanceId', result.instanceId)
} catch (e) {
that.dynamicStartButtonLoading = false
}
startPipeline().then((instanceId) => {
that.dynamicInstanceId = instanceId
setTimeout(() => {
that.dynamicHook = setInterval(() => {
queryLastPipelineDetail(that.dynamicInstanceId).then((data) => {
that.dynamicList = data
})
}, 1000)
}, 2000)
})
}
}
}
@@ -489,15 +551,19 @@ export default {
white-space: nowrap; /* 防止内容换行,使所有内容在一行显示 */
padding: 30px 20px 30px 20px;
}
.box-pipeline::-webkit-scrollbar {
height: 5px; /* 设置水平滚动条高度 */
}
.box-pipeline::-webkit-scrollbar-track {
background: #f1f1f1; /* 滚动条底色 */
}
.box-pipeline::-webkit-scrollbar-thumb {
background: #DCDFE6; /* 滚动条颜色 */
}
.box-pipeline-content {
display: inline-block; /* 使内容水平排列 */
white-space: nowrap; /* 防止内容换行 */
@@ -68,10 +68,10 @@
<script>
import CuteSimpleTable from '@/views/components/dev/CuteSimpleTable'
import { DateUtil } from '@/utils/myUtil'
import DictService from '@/api/system/dict'
import CuteFormDialog from '@/views/components/dev/CuteFormDialog'
import CuteFormDrawer from '@/views/components/dev/CuteFormDrawer'
import { FormatRowDateTimeStr } from '@/utils/CsUtil'
export default {
name: 'CuteSimpleTableDemo',
@@ -101,7 +101,7 @@ export default {
columns: [
{ prop: 'name', label: '名称' },
{ prop: 'description', label: '描述' },
{ prop: 'createTime', label: '创建时间', formatter: DateUtil.formatRowDateTime },
{ prop: 'createTime', label: '创建时间', formatter: FormatRowDateTimeStr },
{ prop: 'createBy', label: '创建人' }
],
operateColumn: {
@@ -4,9 +4,10 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
require('echarts/theme/macarons') // echarts theme
export default {
mixins: [resize],
props: {
@@ -160,7 +160,7 @@ export default {
}
}
@media (max-width:550px) {
@media (max-width: 550px) {
.card-panel-description {
display: none;
}
@@ -1,4 +1,4 @@
import { debounce } from '@/utils'
import { DeBounce } from '@/utils/CsUtil'
export default {
data() {
@@ -8,7 +8,7 @@ export default {
}
},
mounted() {
this.$_resizeHandler = debounce(() => {
this.$_resizeHandler = DeBounce(() => {
if (this.chart) {
this.chart.resize()
}
+41 -33
View File
@@ -49,41 +49,49 @@ export default {
</script>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none!important;
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none !important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>
+16 -2
View File
@@ -30,27 +30,32 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
.wscn-http404-container {
transform: translate(-50%, -50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
@@ -62,6 +67,7 @@ export default {
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
@@ -73,6 +79,7 @@ export default {
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
@@ -84,6 +91,7 @@ export default {
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
@@ -152,12 +160,14 @@ export default {
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
@@ -169,6 +179,7 @@ export default {
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
@@ -181,6 +192,7 @@ export default {
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
@@ -192,6 +204,7 @@ export default {
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
@@ -210,6 +223,7 @@ export default {
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
+35 -20
View File
@@ -36,6 +36,7 @@ import LineChart from './dashboard/LineChart'
import RadarChart from '@/components/Echarts/RadarChart'
import PieChart from '@/components/Echarts/PieChart'
import BarChart from '@/components/Echarts/BarChart'
import CsMessage from '@/utils/elementui/CsMessage'
const lineChartData = {
newVisitis: {
@@ -71,6 +72,20 @@ export default {
lineChartData: lineChartData.newVisitis
}
},
mounted() {
// setTimeout(() => {
// CsMessage.Info('CsMessage Info')
// }, 1000)
// setTimeout(() => {
// CsMessage.Warning('CsMessage Warning')
// }, 2000)
// setTimeout(() => {
// CsMessage.Error('CsMessage Error')
// }, 3000)
// setTimeout(() => {
// CsMessage.Success('CsMessage Success')
// }, 4000)
},
methods: {
handleSetLineChartData(type) {
this.lineChartData = lineChartData[type]
@@ -80,28 +95,28 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
position: relative;
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
position: relative;
.github-corner {
position: absolute;
top: 0;
border: 0;
right: 0;
}
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
.github-corner {
position: absolute;
top: 0;
border: 0;
right: 0;
}
@media (max-width:1024px) {
.chart-wrapper {
padding: 8px;
}
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
@media (max-width: 1024px) {
.chart-wrapper {
padding: 8px;
}
}
</style>
+31 -5
View File
@@ -1,6 +1,13 @@
<template>
<div class="login" :style="'background-image:url('+ Background +');'">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-position="left" label-width="0px" class="login-form">
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
label-position="left"
label-width="0px"
class="login-form"
>
<h3 class="title">
CuteJava 后台管理系统
</h3>
@@ -10,12 +17,24 @@
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码" @keyup.enter.native="handleLogin">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="密码"
@keyup.enter.native="handleLogin"
>
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
</el-input>
</el-form-item>
<el-form-item prop="code">
<el-input v-model="loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter.native="handleLogin">
<el-input
v-model="loginForm.code"
auto-complete="off"
placeholder="验证码"
style="width: 63%"
@keyup.enter.native="handleLogin"
>
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
</el-input>
<div class="login-code">
@@ -26,7 +45,13 @@
记住我
</el-checkbox>
<el-form-item style="width:100%;">
<el-button :loading="loading" size="medium" type="primary" style="width:100%;" @click.native.prevent="handleLogin">
<el-button
:loading="loading"
size="medium"
type="primary"
style="width:100%;"
@click.native.prevent="handleLogin"
>
<span v-if="!loading">登 录</span>
<span v-else>登 录 中...</span>
</el-button>
@@ -42,12 +67,13 @@
</template>
<script>
import { encrypt } from '@/utils/rsaEncrypt'
import { encrypt } from '@/utils/CsRsaUtil'
import Config from '@/settings'
import { getCodeImg } from '@/api/login'
import Cookies from 'js-cookie'
import qs from 'qs'
import Background from '@/assets/images/background.jpeg'
export default {
name: 'Login',
data() {
@@ -27,7 +27,7 @@ export default {
}
</script>
<style scoped>
.my-code a{
color:#009688;
}
.my-code a {
color: #009688;
}
</style>
+45 -15
View File
@@ -4,9 +4,25 @@
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<el-input
v-model="query.name"
clearable
size="small"
placeholder="输入部门名称搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
<el-select
v-model="query.enabled"
clearable
size="small"
placeholder="状态"
class="filter-item"
style="width: 90px"
@change="crud.toQuery"
>
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<rrOperation />
@@ -14,7 +30,14 @@
<crudOperation :permission="permission" />
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0"
:title="crud.status.title"
width="500px"
>
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="部门名称" prop="name">
<el-input v-model="form.name" style="width: 370px;" />
@@ -44,7 +67,9 @@
/>
</el-form-item>
<el-form-item label="状态" prop="enabled">
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">
{{ item.label }}
</el-radio>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -80,7 +105,13 @@
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
<el-table-column
v-if="checkPer(['admin','dept:edit','dept:del'])"
label="操作"
width="130px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
:data="scope.row"
@@ -96,10 +127,9 @@
<script>
import crudDept from '@/api/system/dept'
import Treeselect from '@riophae/vue-treeselect'
import Treeselect, { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import CRUD, { crud, form, header, presenter } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
@@ -254,13 +284,13 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
height: 30px;
line-height: 30px;
}
::v-deep .vue-treeselect__control, ::v-deep .vue-treeselect__placeholder, ::v-deep .vue-treeselect__single-value {
height: 30px;
line-height: 30px;
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>
@@ -8,12 +8,27 @@
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.label" clearable size="small" placeholder="输入字典标签查询" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
<el-input
v-model="query.label"
clearable
size="small"
placeholder="输入字典标签查询"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="toQuery"
/>
<rrOperation />
</div>
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible="crud.status.cu > 0"
:title="crud.status.title"
width="500px"
>
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="字典标签" prop="label">
<el-input v-model="form.label" style="width: 370px;" />
@@ -22,7 +37,13 @@
<el-input v-model="form.value" style="width: 370px;" />
</el-form-item>
<el-form-item label="排序" prop="dictSort">
<el-input-number v-model.number="form.dictSort" :min="0" :max="999" controls-position="right" style="width: 370px;" />
<el-input-number
v-model.number="form.dictSort"
:min="0"
:max="999"
controls-position="right"
style="width: 370px;"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -31,14 +52,27 @@
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
highlight-current-row
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column label="所属字典">
{{ query.dictName }}
</el-table-column>
<el-table-column prop="label" label="字典标签" />
<el-table-column prop="value" label="字典值" />
<el-table-column prop="dictSort" label="排序" />
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
<el-table-column
v-if="checkPer(['admin','dict:edit','dict:del'])"
label="操作"
width="130px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
:data="scope.row"
@@ -55,7 +89,7 @@
<script>
import crudDictDetail from '@/api/system/dictDetail'
import CRUD, { presenter, header, form } from '@crud/crud'
import CRUD, { form, header, presenter } from '@crud/crud'
import pagination from '@crud/Pagination'
import rrOperation from '@crud/RR.operation'
import udOperation from '@crud/UD.operation'
@@ -66,7 +100,8 @@ export default {
components: { pagination, rrOperation, udOperation },
cruds() {
return [
CRUD({ title: '字典详情', url: 'api/dictDetail', query: { dictName: '' },
CRUD({
title: '字典详情', url: 'api/dictDetail', query: { dictName: '' },
crudMethod: { ...crudDictDetail },
optShow: {
add: true,
@@ -109,7 +144,7 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>
+36 -6
View File
@@ -1,7 +1,14 @@
<template>
<div class="app-container">
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible="crud.status.cu > 0"
:title="crud.status.title"
width="500px"
>
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="字典名称" prop="name">
<el-input v-model="form.name" style="width: 370px;" />
@@ -23,17 +30,39 @@
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable size="small" placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<el-input
v-model="query.blurry"
clearable
size="small"
placeholder="输入名称或者描述搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
highlight-current-row
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
@current-change="handleCurrentChange"
>
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="name" label="名称" />
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
<el-table-column
v-if="checkPer(['admin','dict:edit','dict:del'])"
label="操作"
width="130px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
:data="scope.row"
@@ -59,7 +88,8 @@
type="primary"
icon="el-icon-plus"
@click="$refs.dictDetail && $refs.dictDetail.crud.toAdd()"
>新增</el-button>
>新增
</el-button>
</div>
<dictDetail ref="dictDetail" :permission="permission" />
</el-card>
@@ -71,7 +101,7 @@
<script>
import dictDetail from './dictDetail'
import crudDict from '@/api/system/dict'
import CRUD, { presenter, header, form } from '@crud/crud'
import CRUD, { form, header, presenter } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import rrOperation from '@crud/RR.operation'
@@ -4,6 +4,7 @@
<script>
import { mapGetters } from 'vuex'
import elFrame from '@/components/Iframe/index'
export default {
name: 'DruidSqlConsole',
components: { elFrame },
@@ -28,6 +28,7 @@
<script>
import { getLastEmailConfig, modifyEmailConfig } from '@/api/system/email'
export default {
name: 'Config',
data() {
@@ -17,7 +17,11 @@
# 密码(注意,某些邮箱需要为SMTP服务单独设置密码,如QQ和163等等)
# 是否开启ssl,默认开启</pre>
<blockquote class="my-blockquote">更多帮助</blockquote>
<pre class="my-code">更多帮助请查看文档:<a style="color:#009688" href="http://hutool.mydoc.io/#text_319499" target="_black">hutool工具包</a></pre>
<pre class="my-code">更多帮助请查看文档:<a
style="color:#009688"
href="http://hutool.mydoc.io/#text_319499"
target="_black"
>hutool工具包</a></pre>
</div>
</el-tab-pane>
</el-tabs>
@@ -26,6 +30,7 @@
<script>
import Config from './config.vue'
import Send from './send.vue'
export default {
name: 'Email',
components: { Config, Send },
+20 -11
View File
@@ -8,16 +8,24 @@
<el-input v-model="form.tos" style="width: 646px" placeholder="请输入收件地址,多个地址英文逗号,隔开" />
</el-form-item>
<div ref="editor" class="editor" />
<el-button :loading="loading" style="margin-left:1.6%;margin-bottom: 30px" size="medium" type="primary" @click="doSubmit">发送邮件</el-button>
<el-button
:loading="loading"
style="margin-left:1.6%;margin-bottom: 30px"
size="medium"
type="primary"
@click="doSubmit"
>发送邮件
</el-button>
</el-form>
</div>
</template>
<script>
import { sendEmail } from '@/api/system/email'
import { upload } from '@/utils/upload'
import { mapGetters } from 'vuex'
import E from 'wangeditor'
import { UploadFile } from '@/utils/CsDomUtil'
export default {
name: 'Index',
data() {
@@ -49,7 +57,7 @@ export default {
// files 是 input 中选中的文件列表
// insert 是获取图片 url 后,插入到编辑器的方法
files.forEach(image => {
upload(_this.imagesUploadApi, image).then(res => {
UploadFile(_this.imagesUploadApi, image).then(res => {
const data = res.data
const url = _this.baseApi + '/file/' + data.type + '/' + data.realName
insert(url)
@@ -87,12 +95,13 @@ export default {
</script>
<style scoped>
.editor{
text-align:left;
margin: 20px;
width: 730px;
}
::v-deep .w-e-text-container {
height: 360px !important;
}
.editor {
text-align: left;
margin: 20px;
width: 730px;
}
::v-deep .w-e-text-container {
height: 360px !important;
}
</style>
+11 -4
View File
@@ -6,7 +6,13 @@
<crudOperation :permission="permission" />
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="jobSort" label="排序">
@@ -56,6 +62,7 @@ import CRUD, { presenter } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import udOperation from '@crud/UD.operation'
export default {
name: 'Job',
components: { eHeader, eForm, crudOperation, pagination, udOperation },
@@ -103,7 +110,7 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>
@@ -104,7 +104,7 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>
@@ -2,9 +2,25 @@
<div
v-if="crud.props.searchToggle"
>
<el-input v-model="query.name" clearable size="small" placeholder="输入岗位名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<el-input
v-model="query.name"
clearable
size="small"
placeholder="输入岗位名称搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
<el-select
v-model="query.enabled"
clearable
size="small"
placeholder="状态"
class="filter-item"
style="width: 90px"
@change="crud.toQuery"
>
<el-option v-for="item in dict.dict.job_status" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<rrOperation />
@@ -15,6 +31,7 @@
import { header } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { rrOperation, DateRangePicker },
mixins: [header()],
+85 -20
View File
@@ -4,14 +4,29 @@
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<el-input
v-model="query.blurry"
clearable
size="small"
placeholder="模糊搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!--表单渲染-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="580px">
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0"
:title="crud.status.title"
width="580px"
>
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="form.type" size="mini" style="width: 178px">
@@ -29,7 +44,13 @@
>
<IconSelect ref="iconSelect" @selected="selected" />
<el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly>
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px;width: 16px;" />
<svg-icon
v-if="form.icon"
slot="prefix"
:icon-class="form.icon"
class="el-input__icon"
style="height: 32px;width: 16px;"
/>
<i v-else slot="prefix" class="el-icon-search el-input__icon" />
</el-input>
</el-popover>
@@ -53,24 +74,47 @@
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.type.toString() !== '2'" label="菜单标题" prop="title">
<el-input v-model="form.title" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" />
<el-input
v-model="form.title"
:style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'"
placeholder="菜单标题"
/>
</el-form-item>
<el-form-item v-if="form.type.toString() === '2'" label="按钮名称" prop="title">
<el-input v-model="form.title" placeholder="按钮名称" style="width: 178px;" />
</el-form-item>
<el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission">
<el-input v-model="form.permission" :disabled="form.iFrame.toString() === 'true'" placeholder="权限标识" style="width: 178px;" />
<el-input
v-model="form.permission"
:disabled="form.iFrame.toString() === 'true'"
placeholder="权限标识"
style="width: 178px;"
/>
</el-form-item>
<el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path">
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" />
</el-form-item>
<el-form-item label="菜单排序" prop="menuSort">
<el-input-number v-model.number="form.menuSort" :min="0" :max="999" controls-position="right" style="width: 178px;" />
<el-input-number
v-model.number="form.menuSort"
:min="0"
:max="999"
controls-position="right"
style="width: 178px;"
/>
</el-form-item>
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件名称" prop="componentName">
<el-form-item
v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'"
label="组件名称"
prop="componentName"
>
<el-input v-model="form.componentName" style="width: 450px;" placeholder="匹配组件内Name字段" />
</el-form-item>
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件路径" prop="component">
<el-form-item
v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'"
label="组件路径"
prop="component"
>
<el-input v-model="form.component" style="width: 450px;" placeholder="组件路径" />
</el-form-item>
<el-form-item label="上级类目" prop="pid">
@@ -134,7 +178,13 @@
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建日期" width="135px" />
<el-table-column v-if="checkPer(['admin','menu:edit','menu:del'])" label="操作" width="130px" align="center" fixed="right">
<el-table-column
v-if="checkPer(['admin','menu:edit','menu:del'])"
label="操作"
width="130px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
:data="scope.row"
@@ -150,17 +200,31 @@
<script>
import crudMenu from '@/api/system/menu'
import IconSelect from '@/components/IconSelect'
import Treeselect from '@riophae/vue-treeselect'
import Treeselect, { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import CRUD, { crud, form, header, presenter } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import DateRangePicker from '@/components/DateRangePicker'
// crud交由presenter持有
const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iFrame: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
const defaultForm = {
id: null,
title: null,
menuSort: 999,
path: null,
component: null,
componentName: null,
iFrame: false,
roles: [],
pid: 0,
icon: null,
cache: false,
hidden: false,
type: 0,
permission: null
}
export default {
name: 'Menu',
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker },
@@ -246,11 +310,12 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
height: 30px;
line-height: 30px;
}
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .vue-treeselect__control, ::v-deep .vue-treeselect__placeholder, ::v-deep .vue-treeselect__single-value {
height: 30px;
line-height: 30px;
}
</style>
@@ -2,7 +2,15 @@
<div class="app-container">
<div class="head-container">
<div v-if="crud.props.searchToggle">
<el-input v-model="query.userName" clearable size="small" placeholder="输入用户名称查询" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<el-input
v-model="query.userName"
clearable
size="small"
placeholder="输入用户名称查询"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<rrOperation />
</div>
<crudOperation>
@@ -21,7 +29,13 @@
</crudOperation>
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="uid" label="会话编号" />
<el-table-column prop="userName" label="用户名" />
@@ -42,7 +56,13 @@
<p>确定强制退出该用户吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs[scope.$index].doClose()">取消</el-button>
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.key, scope.$index)">确定</el-button>
<el-button
:loading="delLoading"
type="primary"
size="mini"
@click="delMethod(scope.row.key, scope.$index)"
>确定
</el-button>
</div>
<el-button slot="reference" size="mini" type="text">强退</el-button>
</el-popover>
@@ -56,7 +76,7 @@
<script>
import { kickOutUser } from '@/api/system/online'
import CRUD, { presenter, header, crud } from '@crud/crud'
import CRUD, { crud, header, presenter } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
@@ -91,7 +111,8 @@ export default {
type: 'warning'
}).then(() => {
this.delMethod(datas)
}).catch(() => {})
}).catch(() => {
})
},
// 踢出用户
delMethod(key, index) {
@@ -4,7 +4,15 @@
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
<el-input
v-model="query.jobName"
clearable
size="small"
placeholder="输入任务名称搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="toQuery"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
@@ -17,12 +25,20 @@
type="info"
icon="el-icon-tickets"
@click="doLog"
>日志</el-button>
>日志
</el-button>
</crudOperation>
<Log ref="log" />
</div>
<!--Form表单-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="730px">
<el-dialog
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0"
:title="crud.status.title"
append-to-body
width="730px"
>
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="任务名称" prop="jobName">
<el-input v-model="form.jobName" style="width: 220px;" />
@@ -70,7 +86,13 @@
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="id" label="任务ID" />
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
@@ -80,16 +102,45 @@
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
<el-table-column :show-overflow-tooltip="true" prop="isPause" width="90px" label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{ scope.row.isPause ? '已暂停' : '运行中' }}</el-tag>
<el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{
scope.row.isPause ? '已暂停' : '运行中'
}}
</el-tag>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="description" width="150px" label="描述" />
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="136px" label="创建日期" />
<el-table-column v-if="checkPer(['admin','quartzJob:edit','quartzJob:del'])" label="操作" width="170px" align="center" fixed="right">
<el-table-column
v-if="checkPer(['admin','quartzJob:edit','quartzJob:del'])"
label="操作"
width="170px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<el-button v-permission="['admin','quartzJob:edit']" size="mini" style="margin-right: 3px;" type="text" @click="crud.toEdit(scope.row)">编辑</el-button>
<el-button v-permission="['admin','quartzJob:edit']" style="margin-left: -2px" type="text" size="mini" @click="execute(scope.row.id)">执行</el-button>
<el-button v-permission="['admin','quartzJob:edit']" style="margin-left: 3px" type="text" size="mini" @click="updateStatus(scope.row.id,scope.row.isPause ? '恢复' : '暂停')">
<el-button
v-permission="['admin','quartzJob:edit']"
size="mini"
style="margin-right: 3px;"
type="text"
@click="crud.toEdit(scope.row)"
>编辑
</el-button>
<el-button
v-permission="['admin','quartzJob:edit']"
style="margin-left: -2px"
type="text"
size="mini"
@click="execute(scope.row.id)"
>执行
</el-button>
<el-button
v-permission="['admin','quartzJob:edit']"
style="margin-left: 3px"
type="text"
size="mini"
@click="updateStatus(scope.row.id,scope.row.isPause ? '恢复' : '暂停')"
>
{{ scope.row.isPause ? '恢复' : '暂停' }}
</el-button>
<el-popover
@@ -101,7 +152,8 @@
<p>确定停止并删除该任务吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定
</el-button>
</div>
<el-button slot="reference" type="text" size="mini">删除</el-button>
</el-popover>
@@ -116,13 +168,26 @@
<script>
import crudJob from '@/api/system/quartzJob'
import Log from './log'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import CRUD, { crud, form, header, presenter } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, jobName: null, subTask: null, beanName: null, methodName: null, params: null, cronExpression: null, pauseAfterFailure: true, isPause: false, personInCharge: null, email: null, description: null }
const defaultForm = {
id: null,
jobName: null,
subTask: null,
beanName: null,
methodName: null,
params: null,
cronExpression: null,
pauseAfterFailure: true,
isPause: false,
personInCharge: null,
email: null,
description: null
}
export default {
name: 'QuartzJob',
components: { Log, pagination, crudOperation, rrOperation, DateRangePicker },
@@ -2,9 +2,25 @@
<el-dialog :visible.sync="dialog" append-to-body title="执行日志" width="88%">
<!-- 搜索 -->
<div class="head-container">
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
<el-input
v-model="query.jobName"
clearable
size="small"
placeholder="输入任务名称搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="toQuery"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<el-select v-model="query.isSuccess" placeholder="日志状态" clearable size="small" class="filter-item" style="width: 110px" @change="toQuery">
<el-select
v-model="query.isSuccess"
placeholder="日志状态"
clearable
size="small"
class="filter-item"
style="width: 110px"
@change="toQuery"
>
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.displayName" :value="item.key" />
</el-select>
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
@@ -17,7 +33,8 @@
type="warning"
icon="el-icon-download"
@click="downloadMethod"
>导出</el-button>
>导出
</el-button>
</div>
</div>
<!--表格渲染-->
@@ -29,13 +46,22 @@
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
<el-table-column prop="createTime" label="异常详情" width="110px">
<template slot-scope="scope">
<el-button v-show="scope.row.exceptionDetail" size="mini" type="text" @click="info(scope.row.exceptionDetail)">查看详情</el-button>
<el-button
v-show="scope.row.exceptionDetail"
size="mini"
type="text"
@click="info(scope.row.exceptionDetail)"
>查看详情
</el-button>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" prop="time" width="100px" label="耗时(毫秒)" />
<el-table-column align="center" prop="isSuccess" width="80px" label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.isSuccess ? 'success' : 'danger'">{{ scope.row.isSuccess ? '成功' : '失败' }}</el-tag>
<el-tag :type="scope.row.isSuccess ? 'success' : 'danger'">{{
scope.row.isSuccess ? '成功' : '失败'
}}
</el-tag>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期" />
@@ -59,6 +85,7 @@
<script>
import crud from '@/mixins/crud'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { DateRangePicker },
// Mixins 是一种包含多个组件选项的对象。可以将 mixin 作为组件的一个选项引入,从而在该组件中“混入”这个对象的选项。通过这种方式,mixins 可以帮助你实现功能复用,减少重复代码。
@@ -96,11 +123,12 @@ export default {
</script>
<style scoped>
.java.hljs{
color: #444;
background: #ffffff !important;
}
::v-deep .el-dialog__body{
padding: 0 20px 10px 20px !important;
}
.java.hljs {
color: #444;
background: #ffffff !important;
}
::v-deep .el-dialog__body {
padding: 0 20px 10px 20px !important;
}
</style>
+55 -23
View File
@@ -4,14 +4,29 @@
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<el-input
v-model="query.blurry"
size="small"
clearable
placeholder="输入名称或者描述搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!-- 表单渲染 -->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="520px">
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0"
:title="crud.status.title"
width="520px"
>
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="角色名称" prop="name">
<el-input v-model="form.name" style="width: 380px;" />
@@ -58,7 +73,15 @@
<div slot="header" class="clearfix">
<span class="role-span">角色列表</span>
</div>
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
<el-table
ref="table"
v-loading="crud.loading"
highlight-current-row
style="width: 100%;"
:data="crud.data"
@selection-change="crud.selectionChangeHandler"
@current-change="handleCurrentChange"
>
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="dataScope" label="数据权限" />
@@ -69,7 +92,13 @@
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','roles:edit','roles:del'])" label="操作" width="130px" align="center" fixed="right">
<el-table-column
v-if="checkPer(['admin','roles:edit','roles:del'])"
label="操作"
width="130px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
v-if="scope.row.level >= level"
@@ -99,7 +128,8 @@
style="float: right; padding: 6px 9px"
type="primary"
@click="saveMenu"
>保存</el-button>
>保存
</el-button>
</div>
<el-tree
ref="menu"
@@ -123,15 +153,14 @@
<script>
import crudRoles from '@/api/system/role'
import { queryDeptList, queryDeptSuperiorTree } from '@/api/system/dept'
import { queryMenuListByPid, queryChildMenuSet } from '@/api/system/menu'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import { queryChildMenuSet, queryMenuListByPid } from '@/api/system/menu'
import CRUD, { crud, form, header, presenter } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import Treeselect from '@riophae/vue-treeselect'
import Treeselect, { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 }
@@ -352,21 +381,24 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss">
.role-span {
font-weight: bold;color: #303133;
font-size: 15px;
}
.role-span {
font-weight: bold;
color: #303133;
font-size: 15px;
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .vue-treeselect__multi-value{
margin-bottom: 0;
}
::v-deep .vue-treeselect__multi-value-item{
border: 0;
padding: 0;
}
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .vue-treeselect__multi-value {
margin-bottom: 0;
}
::v-deep .vue-treeselect__multi-value-item {
border: 0;
padding: 0;
}
</style>
@@ -1,5 +1,10 @@
<template>
<div v-loading="!show" element-loading-text="数据加载中..." :style="!show ? 'height: 500px' : 'height: 100%'" class="app-container">
<div
v-loading="!show"
element-loading-text="数据加载中..."
:style="!show ? 'height: 500px' : 'height: 100%'"
class="app-container"
>
<div v-if="show">
<el-card class="box-card">
<div style="color: #666;font-size: 13px;">
@@ -139,6 +144,7 @@ import ECharts from 'vue-echarts'
import 'echarts/lib/chart/line'
import 'echarts/lib/component/polar'
import { initDataByPost } from '@/api/data'
export default {
name: 'ServerMonitor',
components: {
@@ -251,41 +257,47 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .box-card {
margin-bottom: 5px;
span {
margin-right: 28px;
}
.el-icon-refresh {
margin-right: 10px;
float: right;
cursor:pointer;
}
::v-deep .box-card {
margin-bottom: 5px;
span {
margin-right: 28px;
}
.cpu, .memory, .swap, .disk {
width: 20%;
float: left;
padding-bottom: 20px;
margin-right: 5%;
}
.title {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-bottom: 16px;
}
.footer {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-top: -5px;
margin-bottom: 10px;
}
.content {
text-align: center;
margin-top: 5px;
margin-bottom: 5px;
.el-icon-refresh {
margin-right: 10px;
float: right;
cursor: pointer;
}
}
.cpu, .memory, .swap, .disk {
width: 20%;
float: left;
padding-bottom: 20px;
margin-right: 5%;
}
.title {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-bottom: 16px;
}
.footer {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-top: -5px;
margin-bottom: 10px;
}
.content {
text-align: center;
margin-top: 5px;
margin-bottom: 5px;
}
</style>
@@ -12,6 +12,7 @@
<script>
import Local from './local/index.vue'
import Oss from './oss/index.vue'
export default {
name: 'Storage',
components: { Local, Oss },
@@ -4,7 +4,15 @@
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable size="small" placeholder="输入内容模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<el-input
v-model="query.blurry"
clearable
size="small"
placeholder="输入内容模糊搜索"
style="width: 200px;"
class="filter-item"
@keyup.enter.native="crud.toQuery"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
@@ -23,7 +31,14 @@
</crudOperation>
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.add ? '文件上传' : '编辑文件'" width="500px">
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0"
:title="crud.status.add ? '文件上传' : '编辑文件'"
width="500px"
>
<el-form ref="form" :model="form" size="small" label-width="80px">
<el-form-item label="文件名">
<el-input v-model="form.name" style="width: 370px;" />
@@ -52,7 +67,13 @@
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
style="width: 100%;"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="文件名">
<template slot-scope="scope">
@@ -105,7 +126,7 @@
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
import crudFile from '@/api/system/localStorage'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import CRUD, { crud, form, header, presenter } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
@@ -175,10 +196,11 @@ export default {
</script>
<style scoped>
::v-deep .el-image__error, .el-image__placeholder{
background: none;
}
::v-deep .el-image-viewer__wrapper{
top: 55px;
}
::v-deep .el-image__error, .el-image__placeholder {
background: none;
}
::v-deep .el-image-viewer__wrapper {
top: 55px;
}
</style>
@@ -95,7 +95,7 @@
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
import crudFile from '@/api/system/ossStorage'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import CRUD, { crud, form, header, presenter } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
+76 -30
View File
@@ -9,7 +9,12 @@
<div>
<div style="text-align: center">
<div class="el-upload">
<img :src="user.avatarName ? baseApi + '/avatar/' + user.avatarName : Avatar" title="点击上传头像" class="avatar" @click="toggleShow">
<img
:src="user.avatarName ? baseApi + '/avatar/' + user.avatarName : Avatar"
title="点击上传头像"
class="avatar"
@click="toggleShow"
>
<myUpload
v-model="show"
:headers="headers"
@@ -19,13 +24,36 @@
</div>
</div>
<ul class="user-info">
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
<li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
<li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
<li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
<li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
<li>
<svg-icon icon-class="anq" /> 安全设置
<div style="height: 100%">
<svg-icon icon-class="login" />
登录账号
<div class="user-right">{{ user.username }}</div>
</div>
</li>
<li>
<svg-icon icon-class="user1" />
用户昵称
<div class="user-right">{{ user.nickName }}</div>
</li>
<li>
<svg-icon icon-class="dept" />
所属部门
<div class="user-right"> {{ user.dept.name }}</div>
</li>
<li>
<svg-icon icon-class="phone" />
手机号码
<div class="user-right">{{ user.phone }}</div>
</li>
<li>
<svg-icon icon-class="email" />
用户邮箱
<div class="user-right">{{ user.email }}</div>
</li>
<li>
<svg-icon icon-class="anq" />
安全设置
<div class="user-right">
<a @click="$refs.pass.dialog = true">修改密码</a>
<a @click="$refs.email.dialog = true">修改邮箱</a>
@@ -40,7 +68,14 @@
<el-card class="box-card">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="用户资料" name="first">
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px">
<el-form
ref="form"
:model="form"
:rules="rules"
style="margin-top: 10px;"
size="small"
label-width="65px"
>
<el-form-item label="昵称" prop="nickName">
<el-input v-model="form.nickName" style="width: 35%" />
<span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span>
@@ -78,7 +113,10 @@
align="right"
>
<template slot="header">
<div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i class="el-icon-refresh" style="margin-left: 40px" /></div>
<div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i
class="el-icon-refresh"
style="margin-left: 40px"
/></div>
</template>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
@@ -111,10 +149,11 @@ import updatePass from './center/updatePass'
import updateEmail from './center/updateEmail'
import { getToken } from '@/utils/auth'
import store from '@/store'
import { isvalidPhone } from '@/utils/validate'
import { isvalidPhone } from '@/utils/CsValidateUtil'
import crud from '@/mixins/crud'
import { editUser } from '@/api/system/user'
import Avatar from '@/assets/images/avatar.png'
export default {
name: 'Center',
components: { updatePass, updateEmail, myUpload },
@@ -159,7 +198,8 @@ export default {
},
created() {
this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
store.dispatch('GetInfo').then(() => {})
store.dispatch('GetInfo').then(() => {
})
},
methods: {
toggleShow() {
@@ -175,7 +215,8 @@ export default {
return true
},
cropUploadSuccess(jsonData, field) {
store.dispatch('GetInfo').then(() => {})
store.dispatch('GetInfo').then(() => {
})
},
doSubmit() {
if (this.$refs['form']) {
@@ -184,7 +225,8 @@ export default {
this.saveLoading = true
editUser(this.form).then(() => {
this.editSuccessNotify()
store.dispatch('GetInfo').then(() => {})
store.dispatch('GetInfo').then(() => {
})
this.saveLoading = false
}).catch(() => {
this.saveLoading = false
@@ -198,24 +240,28 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss">
.avatar {
width: 120px;
height: 120px;
border-radius: 50%;
.avatar {
width: 120px;
height: 120px;
border-radius: 50%;
}
.user-info {
padding-left: 0;
list-style: none;
li {
border-bottom: 1px solid #F0F3F4;
padding: 11px 0;
font-size: 13px;
}
.user-info {
padding-left: 0;
list-style: none;
li{
border-bottom: 1px solid #F0F3F4;
padding: 11px 0;
font-size: 13px;
}
.user-right {
float: right;
a{
color: #317EF3;
}
.user-right {
float: right;
a {
color: #317EF3;
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More