Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
E
evm-store
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wanli
evm-store
Commits
0587447b
Commit
0587447b
authored
Jul 24, 2021
by
wanli
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
🐞
fix(资源监视模块):
资源监视模块增加更多信息展示、前端数据库缓存、后端接口超时优化
parent
5237c65e
Changes
75
Show whitespace changes
Inline
Side-by-side
Showing
75 changed files
with
7643 additions
and
581 deletions
+7643
-581
backend/controller/monitor.py
backend/controller/monitor.py
+7
-11
backend/view/monitor.py
backend/view/monitor.py
+5
-5
frontend/babel.config.js
frontend/babel.config.js
+9
-1
frontend/src/utils/indexedDB.js
frontend/src/utils/indexedDB.js
+347
-0
frontend/src/views/system/components/EvmChart.vue
frontend/src/views/system/components/EvmChart.vue
+15
-10
frontend/src/views/system/components/LvglChart.vue
frontend/src/views/system/components/LvglChart.vue
+15
-10
frontend/src/views/system/components/SystemChart.vue
frontend/src/views/system/components/SystemChart.vue
+19
-16
frontend/src/views/system/monitor.vue
frontend/src/views/system/monitor.vue
+132
-62
frontend/src/views/system/style.css
frontend/src/views/system/style.css
+1
-1
tools/build_out/tests/http_interval.py
tools/build_out/tests/http_interval.py
+110
-38
tools/build_out/views/monitor.py
tools/build_out/views/monitor.py
+5
-2
tools/frontend/babel.config.js
tools/frontend/babel.config.js
+15
-5
tools/frontend/package.json
tools/frontend/package.json
+3
-0
tools/frontend/src/api/system/menu.js
tools/frontend/src/api/system/menu.js
+45
-0
tools/frontend/src/router/index.js
tools/frontend/src/router/index.js
+21
-14
tools/frontend/src/styles/variables.scss
tools/frontend/src/styles/variables.scss
+12
-0
tools/frontend/src/templates/Template.1.vue
tools/frontend/src/templates/Template.1.vue
+369
-0
tools/frontend/src/templates/Template.2.vue
tools/frontend/src/templates/Template.2.vue
+48
-0
tools/frontend/src/views/Application/Monitor.vue
tools/frontend/src/views/Application/Monitor.vue
+4
-0
tools/frontend/src/views/System/Menu.vue
tools/frontend/src/views/System/Menu.vue
+229
-37
tools/frontend/src/views/System/Role.vue
tools/frontend/src/views/System/Role.vue
+95
-360
tools/frontend/src/views/System/components/TableLayout.vue
tools/frontend/src/views/System/components/TableLayout.vue
+167
-0
tools/frontend/src/views/System/components/base/BaseOpera.vue
...s/frontend/src/views/System/components/base/BaseOpera.vue
+106
-0
tools/frontend/src/views/System/components/base/BasePage.vue
tools/frontend/src/views/System/components/base/BasePage.vue
+57
-0
tools/frontend/src/views/System/components/base/BaseTable.vue
...s/frontend/src/views/System/components/base/BaseTable.vue
+215
-0
tools/frontend/src/views/System/components/common/ColumnDetail.vue
...ntend/src/views/System/components/common/ColumnDetail.vue
+116
-0
tools/frontend/src/views/System/components/common/DepartmentSelect.vue
...d/src/views/System/components/common/DepartmentSelect.vue
+89
-0
tools/frontend/src/views/System/components/common/GlobalWindow.vue
...ntend/src/views/System/components/common/GlobalWindow.vue
+133
-0
tools/frontend/src/views/System/components/common/Header.vue
tools/frontend/src/views/System/components/common/Header.vue
+200
-0
tools/frontend/src/views/System/components/common/Light.vue
tools/frontend/src/views/System/components/common/Light.vue
+122
-0
tools/frontend/src/views/System/components/common/LocationSelect.vue
...end/src/views/System/components/common/LocationSelect.vue
+113
-0
tools/frontend/src/views/System/components/common/Menu.vue
tools/frontend/src/views/System/components/common/Menu.vue
+183
-0
tools/frontend/src/views/System/components/common/MenuItems.vue
...frontend/src/views/System/components/common/MenuItems.vue
+25
-0
tools/frontend/src/views/System/components/common/MenuSelect.vue
...rontend/src/views/System/components/common/MenuSelect.vue
+84
-0
tools/frontend/src/views/System/components/common/NotAllow.vue
.../frontend/src/views/System/components/common/NotAllow.vue
+40
-0
tools/frontend/src/views/System/components/common/Pagination.vue
...rontend/src/views/System/components/common/Pagination.vue
+40
-0
tools/frontend/src/views/System/components/common/PositionSelect.vue
...end/src/views/System/components/common/PositionSelect.vue
+103
-0
tools/frontend/src/views/System/components/common/Profile.vue
...s/frontend/src/views/System/components/common/Profile.vue
+34
-0
tools/frontend/src/views/System/components/common/Scrollbar.vue
...frontend/src/views/System/components/common/Scrollbar.vue
+22
-0
tools/frontend/src/views/System/components/common/SearchFormCollapse.vue
...src/views/System/components/common/SearchFormCollapse.vue
+41
-0
tools/frontend/src/views/System/components/common/TreeSelect.vue
...rontend/src/views/System/components/common/TreeSelect.vue
+67
-0
tools/frontend/src/views/System/components/common/Value.vue
tools/frontend/src/views/System/components/common/Value.vue
+59
-0
tools/frontend/src/views/System/components/system/datapermission/CustomSelect.vue
.../System/components/system/datapermission/CustomSelect.vue
+50
-0
tools/frontend/src/views/System/components/system/datapermission/DataPermModuleSelect.vue
...components/system/datapermission/DataPermModuleSelect.vue
+51
-0
tools/frontend/src/views/System/components/system/datapermission/DataPermTypeSelect.vue
...m/components/system/datapermission/DataPermTypeSelect.vue
+67
-0
tools/frontend/src/views/System/components/system/datapermission/OperaDataPermissionWindow.vue
...nents/system/datapermission/OperaDataPermissionWindow.vue
+121
-0
tools/frontend/src/views/System/components/system/department/DepartmentUserWindow.vue
...tem/components/system/department/DepartmentUserWindow.vue
+191
-0
tools/frontend/src/views/System/components/system/department/OperaDepartmentWindow.vue
...em/components/system/department/OperaDepartmentWindow.vue
+102
-0
tools/frontend/src/views/System/components/system/dict/DictDataManagerWindow.vue
...s/System/components/system/dict/DictDataManagerWindow.vue
+106
-0
tools/frontend/src/views/System/components/system/dict/OperaDictDataWindow.vue
...ews/System/components/system/dict/OperaDictDataWindow.vue
+94
-0
tools/frontend/src/views/System/components/system/dict/OperaDictWindow.vue
...c/views/System/components/system/dict/OperaDictWindow.vue
+55
-0
tools/frontend/src/views/System/components/system/location/OperaLocationWindow.vue
...System/components/system/location/OperaLocationWindow.vue
+118
-0
tools/frontend/src/views/System/components/system/menu/OperaMenuWindow.vue
...c/views/System/components/system/menu/OperaMenuWindow.vue
+173
-0
tools/frontend/src/views/System/components/system/permission/OperaPermissionWindow.vue
...em/components/system/permission/OperaPermissionWindow.vue
+96
-0
tools/frontend/src/views/System/components/system/position/OperaPositionWindow.vue
...System/components/system/position/OperaPositionWindow.vue
+87
-0
tools/frontend/src/views/System/components/system/position/PositionUserWindow.vue
.../System/components/system/position/PositionUserWindow.vue
+152
-0
tools/frontend/src/views/System/components/system/role/MenuConfigWindow.vue
.../views/System/components/system/role/MenuConfigWindow.vue
+114
-0
tools/frontend/src/views/System/components/system/role/OperaRoleWindow.vue
...c/views/System/components/system/role/OperaRoleWindow.vue
+96
-0
tools/frontend/src/views/System/components/system/role/PermissionConfigWindow.vue
.../System/components/system/role/PermissionConfigWindow.vue
+124
-0
tools/frontend/src/views/System/components/system/role/RoleSelect.vue
...nd/src/views/System/components/system/role/RoleSelect.vue
+51
-0
tools/frontend/src/views/System/components/system/user/OperaUserWindow.vue
...c/views/System/components/system/user/OperaUserWindow.vue
+171
-0
tools/frontend/src/views/System/components/system/user/ResetPwdWindow.vue
...rc/views/System/components/system/user/ResetPwdWindow.vue
+91
-0
tools/frontend/src/views/System/components/system/user/RoleConfigWindow.vue
.../views/System/components/system/user/RoleConfigWindow.vue
+127
-0
tools/frontend/src/views/System/data-permission.vue
tools/frontend/src/views/System/data-permission.vue
+210
-0
tools/frontend/src/views/System/department.vue
tools/frontend/src/views/System/department.vue
+104
-0
tools/frontend/src/views/System/dict.vue
tools/frontend/src/views/System/dict.vue
+99
-0
tools/frontend/src/views/System/location.vue
tools/frontend/src/views/System/location.vue
+160
-0
tools/frontend/src/views/System/loginLog.vue
tools/frontend/src/views/System/loginLog.vue
+130
-0
tools/frontend/src/views/System/monitor.vue
tools/frontend/src/views/System/monitor.vue
+308
-0
tools/frontend/src/views/System/permission.vue
tools/frontend/src/views/System/permission.vue
+96
-0
tools/frontend/src/views/System/position.vue
tools/frontend/src/views/System/position.vue
+93
-0
tools/frontend/src/views/System/table.vue
tools/frontend/src/views/System/table.vue
+45
-0
tools/frontend/src/views/System/traceLog.vue
tools/frontend/src/views/System/traceLog.vue
+254
-0
tools/frontend/src/views/System/user.vue
tools/frontend/src/views/System/user.vue
+159
-0
tools/frontend/yarn.lock
tools/frontend/yarn.lock
+226
-9
No files found.
backend/controller/monitor.py
View file @
0587447b
'''
Author: your name
Date: 2021-06-29 19:24:32
LastEditTime: 2021-07-2
1 11:43:14
LastEditTime: 2021-07-2
3 22:26:56
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath:
\
evm-store
\b
ackend
\
controller
\
monitor.py
'''
import
logging
from
model.monitor
import
session
,
System
,
Lvgl
,
Evm
,
Image
,
Device
,
Request
,
User
logger
=
logging
.
getLogger
(
__name__
)
class
SystemResource
(
object
):
def
get
(
self
):
result
=
session
.
query
(
System
)
.
all
()
...
...
@@ -117,16 +121,8 @@ def insert_data(msg):
if
result
:
watch_id
=
result
.
id
else
:
user
=
session
.
query
(
User
)
.
filter
(
User
.
account
==
"evm"
)
.
first
()
if
user
:
result
=
Device
(
imei
=
msg
.
get
(
"imei"
),
name
=
"watch_{}"
.
format
(
msg
.
get
(
"imei"
)),
type
=
"watch"
,
create_by
=
user
.
id
,
update_by
=
user
.
id
)
session
.
add
(
result
)
session
.
flush
()
session
.
commit
()
result
=
session
.
query
(
Device
)
.
filter_by
(
imei
=
msg
.
get
(
"imei"
))
.
first
()
if
result
:
watch_id
=
result
.
id
logger
.
info
(
"设备不存在"
)
return
None
if
msg
.
get
(
"request"
):
msg
.
get
(
"request"
)
.
update
({
"watch"
:
watch_id
})
...
...
backend/view/monitor.py
View file @
0587447b
'''
Author: your name
Date: 2021-06-29 19:33:41
LastEditTime: 2021-07-2
1 12:04:3
4
LastEditTime: 2021-07-2
4 01:18:5
4
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath:
\
evm-store
\b
ackend
\v
iew
\
monitor.py
...
...
@@ -216,7 +216,7 @@ class NotifyHandler(BaseWebsocket):
def
on_heartbeat
(
self
):
# 心跳定时器,固定间隔扫描连接列表,当连接超时,主动剔除该连接
for
i
in
range
(
len
(
self
.
_clients
)
-
1
,
-
1
,
-
1
):
if
int
(
time
.
time
())
-
self
.
_clients
[
i
]
.
get
(
"ts"
)
>
5
:
if
int
(
time
.
time
())
-
self
.
_clients
[
i
]
.
get
(
"ts"
)
>
30
:
# self._clients.pop(i)
del
self
.
_clients
[
i
]
className
=
self
.
__class__
.
__name__
...
...
@@ -327,16 +327,16 @@ class DeviceMessageHandler(BaseHandler):
logger
.
info
(
data
)
data
.
update
({
'request'
:
{
data
.
get
(
"system"
,
{})
.
update
(
{
'host'
:
self
.
request
.
remote_ip
,
'path'
:
self
.
request
.
path
,
'protocol'
:
self
.
request
.
protocol
}
}
)
})
insert_data
(
data
)
data
[
'type'
]
=
'report'
data
[
'
request
'
]
.
update
({
'timestamp'
:
datetime
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
)
})
data
[
'
system
'
]
.
update
({
'timestamp'
:
datetime
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
)
})
NotifyHandler
.
broadcastMessage
(
data
)
self
.
write
(
json
.
dumps
({
'code'
:
100
,
'message'
:
'success'
}))
except
Exception
as
e
:
...
...
frontend/babel.config.js
View file @
0587447b
/*
* @Author: your name
* @Date: 2021-07-15 09:33:39
* @LastEditTime: 2021-07-23 17:39:47
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \evm-store\frontend\babel.config.js
*/
module
.
exports
=
{
presets
:
[
'
@vue/cli-plugin-babel/preset
'
'
@vue/cli-plugin-babel/preset
'
,
]
}
frontend/src/utils/indexedDB.js
0 → 100644
View file @
0587447b
export
default
function
Database
()
{
/**
* 数据库对象
* @param {[type]} option [description]
*/
// var option = {
// dbName: '', // 数据库名
// version: 1, // 数据库版本号
// primaryKey: 'id' // 需要保存的数据字段
// keyNames: [{ // 需要保存的数据字段
// key: '', // 字段名
// unique: // 当前这条数据是否能重复 (最常用) 默认false
// }]
// };
var
IndexedDB
=
function
IndexedDB
(
option
)
{
// 设置unique默认值为false
var
list
;
if
(
option
.
keyNames
)
{
list
=
option
.
keyNames
;
for
(
var
i
=
0
;
i
<
list
.
length
;
i
++
)
{
if
(
!
list
[
i
].
unique
)
{
list
[
i
].
unique
=
false
;
}
}
}
this
.
dbName
=
option
.
dbName
;
this
.
version
=
option
.
version
;
this
.
primaryKey
=
option
.
primaryKey
||
"
id
"
;
this
.
keyNames
=
option
.
keyNames
;
this
.
db
=
null
;
// 存储数据库对象
this
.
allData
=
[];
// 存储返回的所有数据
};
IndexedDB
.
prototype
=
{
/**
* 初始化数据库
* @return {[type]} [description]
*/
init
:
function
()
{
const
_this
=
this
;
return
new
Promise
((
resolve
,
reject
)
=>
{
var
dbName
=
_this
.
dbName
,
version
=
_this
.
version
,
primaryKey
=
_this
.
primaryKey
,
keyNames
=
_this
.
keyNames
;
var
indexedDB
=
window
.
indexedDB
||
window
.
mozIndexedDB
||
window
.
webkitIndexedDB
||
window
.
msIndexedDB
;
// 判断浏览器是否支持indexedDB
if
(
!
indexedDB
)
{
reject
(
new
Error
(
"
您的浏览器不支持当前内容
"
));
return
false
;
}
// 判断数据库名称是否为空
if
(
!
dbName
)
{
reject
(
new
Error
(
null
,
"
数据库名称不能为空
"
));
return
false
;
}
// 打开数据库
var
DBOpenRequest
=
window
.
indexedDB
.
open
(
dbName
,
version
);
// 数据库打开成功
DBOpenRequest
.
onsuccess
=
function
(
event
)
{
_this
.
db
=
DBOpenRequest
.
result
;
resolve
(
event
);
};
// 数据库打开失败
DBOpenRequest
.
onerror
=
function
(
event
)
{
reject
(
event
);
};
// 首次创建或者版本变更(更高版本)
DBOpenRequest
.
onupgradeneeded
=
function
(
event
)
{
_this
.
db
=
event
.
target
.
result
;
_this
.
db
.
onerror
=
function
(
event
)
{
console
.
log
(
event
);
reject
(
new
Error
(
"
数据库打开失败
"
));
};
// 创建一个数据库存储对象
/*
// createIndex的语法
objectStore.createIndex(indexName, keyPath, {
unique: false, // true
multiEntry: false, // 如果为true,同时keyPath是数组元素,则所以会加到所有数组元素上
locale: 'auto' // 目前仅Firefox支持,可以是字符串,类似en-US, 或 zh,也可以是auto或者null, undefined
})
*/
if
(
!
_this
.
db
.
objectStoreNames
.
contains
(
dbName
))
{
var
objectStore
=
_this
.
db
.
createObjectStore
(
dbName
,
{
keyPath
:
primaryKey
,
autoIncrement
:
true
,
});
}
keyNames
.
forEach
(
function
(
currentValue
,
index
,
array
)
{
/**
* 创建索引
* @string indexName 设置当前 index 的名字
* @string property 从存储数据中,指明 index 所指的属性。
* @string property 从存储数据中,指明 index 所指的属性。
* @object options options 有三个选项:
* unique: 当前这条数据是否能重复 (最常用)
* multiEntry: 设置当前的 property 为数组时,会给数组里面每个元素都设置一个 index 值。
* locale:目前仅支持Firefox(43+),这允许您为索引指定区域设置。
*/
console
.
log
(
index
,
array
);
// objectStore.createIndex('indexName', 'property', options);
objectStore
.
createIndex
(
currentValue
.
key
,
currentValue
.
key
,
{
unique
:
currentValue
.
unique
||
false
,
});
});
};
});
},
/**
* 添加数据
* @param {Array || Object} option 存入数据库的数据
*/
set
:
function
(
option
)
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
// version = _this.version,
// primaryKey = _this.primaryKey,
// keyNames = _this.keyNames;
var
transaction
=
this
.
db
.
transaction
([
dbName
],
"
readwrite
"
);
var
objectStore
=
transaction
.
objectStore
(
dbName
);
if
(
Array
.
isArray
(
option
))
{
for
(
var
i
=
0
;
i
<
option
.
length
;
i
++
)
{
objectStore
.
add
(
option
[
i
]).
onsuccess
=
function
(
event
)
{
console
.
log
(
"
数据写入成功
"
,
event
);
};
transaction
.
onerror
=
function
(
event
)
{
console
.
log
(
"
数据库中已有该数据
"
,
event
);
};
}
}
else
{
objectStore
.
add
(
option
).
onsuccess
=
function
(
event
)
{
console
.
log
(
"
数据写入成功
"
,
event
);
};
transaction
.
onerror
=
function
(
event
)
{
console
.
log
(
"
数据库中已有该数据
"
,
event
);
};
}
},
/**
* 更新单条或多条数据
* @param {Array || object} option 更新的数据
* @return {[type]} [description]
*/
updateData
:
function
(
option
)
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
var
transaction
=
_this
.
db
.
transaction
([
dbName
],
"
readwrite
"
);
var
objectStore
=
transaction
.
objectStore
(
dbName
);
if
(
Array
.
isArray
(
option
))
{
for
(
var
i
=
0
;
i
<
option
.
length
;
i
++
)
{
objectStore
.
put
(
option
[
i
]).
onsuccess
=
function
(
event
)
{
console
.
log
(
"
数据更新成功
"
,
event
);
};
transaction
.
onerror
=
function
(
event
)
{
console
.
log
(
"
数据更新成功
"
,
event
);
};
}
}
else
{
objectStore
.
put
(
option
).
onsuccess
=
function
(
event
)
{
console
.
log
(
"
数据更新成功
"
,
event
);
};
transaction
.
onerror
=
function
(
event
)
{
console
.
log
(
"
数据更新成功
"
,
event
);
};
}
},
/**
* 遍历数据表里所有的数据
* @return {Array || Obiect} 返回数据表里所有的数据
*/
getAllData
:
function
(
callback
)
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
return
new
Promise
((
resolve
,
reject
)
=>
{
_this
.
allData
=
[];
var
objectStore
=
_this
.
db
.
transaction
(
dbName
).
objectStore
(
dbName
);
objectStore
.
openCursor
().
onsuccess
=
function
(
event
)
{
var
cursor
=
event
.
target
.
result
;
if
(
cursor
)
{
if
(
callback
&&
callback
(
cursor
.
value
))
{
_this
.
allData
.
push
(
cursor
.
value
);
}
cursor
.
continue
();
}
else
{
resolve
(
_this
.
allData
);
}
};
objectStore
.
openCursor
().
onerror
=
function
(
event
)
{
reject
(
new
Error
(
event
));
};
});
},
/**
* 获取数据表里单条数据
* @param {String || Number} value 数据字段的值(按主键搜索)
* @return {[type]} [description]
*/
getData
:
function
(
value
)
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
return
new
Promise
((
resolve
,
reject
)
=>
{
var
objectStore
=
_this
.
db
.
transaction
(
dbName
).
objectStore
(
dbName
);
var
request
=
objectStore
.
get
(
value
);
request
.
onerror
=
function
(
event
)
{
console
.
log
(
"
获取失败
"
,
event
);
reject
(
new
Error
(
event
));
};
request
.
onsuccess
=
function
(
event
)
{
if
(
request
.
result
)
{
console
.
log
(
request
.
result
);
resolve
(
request
.
result
);
}
else
{
console
.
log
(
"
未获得数据记录
"
,
event
);
reject
(
new
Error
(
event
));
}
};
});
},
/**
* 删除数据库
* @return {[type]} [description]
*/
deleteDB
:
function
()
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
window
.
indexedDB
.
deleteDatabase
(
dbName
);
console
.
log
(
dbName
+
"
数据库已删除
"
);
},
/**
* 关闭数据库
* @return {[type]} [description]
*/
closeDB
:
function
()
{
var
_this
=
this
;
_this
.
db
.
close
();
console
.
log
(
"
数据库已关闭
"
);
},
/**
* 删除某一条记录
* @param {String || Number} value 主键的值
* @return {[type]} [description]
*/
deleteData
:
function
(
value
)
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
return
new
Promise
((
resolve
,
reject
)
=>
{
var
request
=
_this
.
db
.
transaction
(
dbName
,
"
readwrite
"
)
.
objectStore
(
dbName
)
.
delete
(
value
);
request
.
onsuccess
=
function
(
event
)
{
console
.
log
(
"
删除成功
"
,
event
);
resolve
(
event
);
};
request
.
onerror
=
function
(
event
)
{
console
.
log
(
"
删除成功
"
,
event
);
reject
(
new
Error
(
event
));
};
});
},
/**
* 删除存储空间全部记录
* @return {[type]} [description]
*/
clearData
:
function
()
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
var
store
=
_this
.
db
.
transaction
(
dbName
,
"
readwrite
"
).
objectStore
(
dbName
);
store
.
clear
();
console
.
log
(
"
已删除存储空间
"
+
dbName
+
"
全部记录
"
);
},
/**
* 数据查询
* @param {String} key 数据字段名
* @param {String || Number} value 按字段查询的值
* @return {[type]} [description]
*/
searchData
:
function
(
key
,
value
)
{
var
_this
=
this
;
var
dbName
=
_this
.
dbName
;
return
new
Promise
((
resolve
,
reject
)
=>
{
var
store
=
_this
.
db
.
transaction
(
dbName
,
"
readonly
"
)
.
objectStore
(
dbName
);
var
index
=
store
.
index
(
key
);
var
request
=
index
.
get
(
value
);
request
.
onerror
=
function
(
event
)
{
console
.
error
(
"
搜索出错
"
);
reject
(
new
Error
(
event
));
};
request
.
onsuccess
=
function
(
e
)
{
var
result
=
e
.
target
.
result
;
if
(
result
)
{
resolve
(
result
);
}
else
{
console
.
log
(
"
您搜索的数据不存在
"
);
resolve
(
null
);
}
};
});
},
};
return
IndexedDB
;
}
frontend/src/views/system/components/EvmChart.vue
View file @
0587447b
...
...
@@ -31,6 +31,8 @@ import { wsNotify } from "@/utils/eventBus.js";
// dataList.push(randomData());
// }
let
chart
=
null
const
seriesData
=
{
heap_total_size
:
[],
heap_used_size
:
[],
...
...
@@ -65,7 +67,6 @@ export default {
data
()
{
return
{
loading
:
null
,
chart
:
null
,
timer
:
null
,
series
:
[
{
...
...
@@ -131,15 +132,19 @@ export default {
});
wsNotify
.
eventBus
.
$on
(
"
resize
"
,
()
=>
{
if
(
this
.
chart
)
this
.
chart
.
resize
()
if
(
chart
)
chart
.
resize
()
});
wsNotify
.
eventBus
.
$on
(
"
clear-evm-chart
"
,
()
=>
{
this
.
setOptions
()
});
},
beforeDestroy
()
{
if
(
!
this
.
chart
)
{
if
(
!
chart
)
{
return
;
}
this
.
chart
.
dispose
();
this
.
chart
=
null
;
chart
.
dispose
();
chart
=
null
;
},
methods
:
{
cleanData
()
{
...
...
@@ -151,7 +156,7 @@ export default {
this
.
series
.
forEach
((
item
)
=>
{
item
.
data
=
[];
});
this
.
chart
.
setOption
({
series
:
this
.
series
});
chart
.
setOption
({
series
:
this
.
series
});
},
handleMessage
(
data
)
{
if
(
!
data
||
data
.
length
==
0
)
this
.
cleanData
()
...
...
@@ -169,18 +174,18 @@ export default {
});
this
.
$nextTick
(()
=>
{
this
.
chart
&&
this
.
chart
.
setOption
({
chart
&&
chart
.
setOption
({
series
:
this
.
series
,
});
});
},
initChart
()
{
this
.
chart
=
echarts
.
init
(
this
.
$el
,
"
macarons
"
);
chart
=
echarts
.
init
(
this
.
$el
,
"
macarons
"
);
this
.
setOptions
();
},
setOptions
()
{
this
.
chart
.
setOption
({
chart
.
setOption
({
title
:
{
text
:
"
EVM
"
,
},
...
...
frontend/src/views/system/components/LvglChart.vue
View file @
0587447b
...
...
@@ -9,6 +9,8 @@ import resize from "./mixins/resize";
import
{
getDateTimeString
}
from
"
@/utils/utils
"
;
import
{
wsNotify
}
from
"
@/utils/eventBus.js
"
;
let
chart
=
null
const
seriesData
=
{
frag_pct
:
[],
free_biggest_size
:
[],
...
...
@@ -50,7 +52,6 @@ export default {
},
data
()
{
return
{
chart
:
null
,
series
:
[
{
name
:
"
frag_pct
"
,
...
...
@@ -155,15 +156,19 @@ export default {
});
wsNotify
.
eventBus
.
$on
(
"
resize
"
,
()
=>
{
if
(
this
.
chart
)
this
.
chart
.
resize
()
if
(
chart
)
chart
.
resize
()
});
wsNotify
.
eventBus
.
$on
(
"
clear-lvgl-chart
"
,
()
=>
{
this
.
setOptions
()
});
},
beforeDestroy
()
{
if
(
!
this
.
chart
)
{
if
(
!
chart
)
{
return
;
}
this
.
chart
.
dispose
();
this
.
chart
=
null
;
chart
.
dispose
();
chart
=
null
;
},
methods
:
{
handleData
(
data
)
{
...
...
@@ -173,7 +178,7 @@ export default {
this
.
series
.
forEach
(
item
=>
{
item
.
data
=
[]
});
this
.
chart
.
setOption
({
series
:
this
.
series
});
chart
.
setOption
({
series
:
this
.
series
});
data
.
forEach
((
item
)
=>
{
this
.
handleMessage
(
item
);
...
...
@@ -191,18 +196,18 @@ export default {
});
this
.
$nextTick
(()
=>
{
this
.
chart
&&
this
.
chart
.
setOption
({
chart
&&
chart
.
setOption
({
series
:
this
.
series
,
});
});
},
initChart
()
{
this
.
chart
=
echarts
.
init
(
this
.
$el
,
"
macarons
"
);
chart
=
echarts
.
init
(
this
.
$el
,
"
macarons
"
);
this
.
setOptions
();
},
setOptions
()
{
this
.
chart
.
setOption
({
chart
.
setOption
({
title
:
{
text
:
"
LVGL
"
,
},
...
...
frontend/src/views/system/components/SystemChart.vue
View file @
0587447b
...
...
@@ -15,6 +15,8 @@ const seriesData = {
used_space_size
:
[],
};
let
chart
=
null
export
default
{
mixins
:
[
resize
],
props
:
{
...
...
@@ -46,7 +48,6 @@ export default {
},
data
()
{
return
{
chart
:
null
,
series
:
[
{
name
:
"
free_size
"
,
...
...
@@ -80,11 +81,7 @@ export default {
data
:
seriesData
.
used_space_size
,
},
],
legendData
:
[
"
free_size
"
,
"
free_space_size
"
,
"
used_space_size
"
],
legendData
:
Object
.
keys
(
seriesData
),
};
},
watch
:
{
...
...
@@ -107,15 +104,19 @@ export default {
});
wsNotify
.
eventBus
.
$on
(
"
resize
"
,
()
=>
{
if
(
this
.
chart
)
this
.
chart
.
resize
()
if
(
chart
)
chart
.
resize
()
});
wsNotify
.
eventBus
.
$on
(
"
clear-system-chart
"
,
()
=>
{
this
.
setOptions
()
})
},
beforeDestroy
()
{
if
(
!
this
.
chart
)
{
if
(
!
chart
)
{
return
;
}
this
.
chart
.
dispose
();
this
.
chart
=
null
;
chart
.
dispose
();
chart
=
null
;
},
methods
:
{
handleData
(
data
)
{
...
...
@@ -125,7 +126,8 @@ export default {
this
.
series
.
forEach
(
item
=>
{
item
.
data
=
[]
});
this
.
chart
.
setOption
({
series
:
this
.
series
});
// chart.dispose();
chart
.
setOption
({
series
:
this
.
series
});
data
.
forEach
((
item
)
=>
{
this
.
handleMessage
(
item
);
...
...
@@ -135,26 +137,27 @@ export default {
Object
.
keys
(
data
).
forEach
((
k
)
=>
{
var
t
=
getDateTimeString
(
new
Date
());
if
(
k
==
"
timestamp
"
)
t
=
data
[
k
];
if
(
this
.
legendData
.
includes
(
k
))
if
(
this
.
legendData
.
includes
(
k
))
{
seriesData
[
k
].
push
({
name
:
k
,
value
:
[
t
,
data
[
k
]],
});
}
});
this
.
$nextTick
(()
=>
{
this
.
chart
&&
this
.
chart
.
setOption
({
chart
&&
chart
.
setOption
({
series
:
this
.
series
,
});
});
},
initChart
()
{
this
.
chart
=
echarts
.
init
(
this
.
$el
,
"
macarons
"
);
chart
=
echarts
.
init
(
this
.
$el
,
"
macarons
"
);
this
.
setOptions
();
},
setOptions
()
{
this
.
chart
.
setOption
({
chart
.
setOption
({
title
:
{
text
:
"
SYSTEM
"
,
},
...
...
frontend/src/views/system/monitor.vue
View file @
0587447b
...
...
@@ -236,7 +236,7 @@
<grid-item
:x=
"0"
:y=
"10"
:w=
"
12
"
:w=
"
8
"
:h=
"10"
i=
"5"
@
resize=
"resizeEvent"
...
...
@@ -245,7 +245,7 @@
@
container-resized=
"containerResizedEvent"
@
moved=
"movedEvent"
>
<div
style=
"width: 100%; height: 100%;
overflow-y: auto
"
>
<div
style=
"width: 100%; height: 100%;
overflow-y: auto;
"
>
<p
class=
"item-title"
>
APP
</p>
<el-table
element-loading-text=
"Loading"
...
...
@@ -297,19 +297,43 @@
</el-table>
</div>
</grid-item>
<grid-item
:x=
"8"
:y=
"10"
:w=
"4"
:h=
"10"
i=
"6"
@
resize=
"resizeEvent"
@
move=
"moveEvent"
@
resized=
"resizedEvent"
@
container-resized=
"containerResizedEvent"
@
moved=
"movedEvent"
>
<div
style=
"width: 100%; height: 100%;overflow-y: auto;"
>
<el-table
:data=
"pngList"
style=
"width: 100%"
>
<el-table-column
prop=
"uri"
label=
"uri"
min-width=
"180"
></el-table-column>
<el-table-column
prop=
"filesize"
label=
"file size"
width=
"100"
></el-table-column>
<el-table-column
prop=
"uncompressed_size"
label=
"origin size"
width=
"100"
></el-table-column>
<el-table-column
prop=
"ratio"
label=
"ratio"
width=
"100"
>
</el-table-column>
</el-table>
</div>
</grid-item>
<grid-item
:x=
"0"
:y=
"20"
:w=
"12"
:h=
"7"
i=
"
6
"
i=
"
7
"
@
resize=
"resizeEvent"
@
move=
"moveEvent"
@
resized=
"resizedEvent"
@
container-resized=
"containerResizedEvent"
@
moved=
"movedEvent"
>
<SystemChart
:chartData=
"system"
></SystemChart>
<SystemChart
:chartData=
"system"
:dataList=
"systemHistory"
></SystemChart>
</grid-item>
<grid-item
:x=
"0"
...
...
@@ -323,7 +347,7 @@
@
container-resized=
"containerResizedEvent"
@
moved=
"movedEvent"
>
<EvmChart
:chartData=
"evm"
></EvmChart>
<EvmChart
:chartData=
"evm"
:dataList=
"evmHistory"
></EvmChart>
</grid-item>
<grid-item
:x=
"0"
...
...
@@ -337,7 +361,7 @@
@
container-resized=
"containerResizedEvent"
@
moved=
"movedEvent"
>
<LvglChart
:chartData=
"lvgl"
></LvglChart>
<LvglChart
:chartData=
"lvgl"
:dataList=
"lvglHistory"
></LvglChart>
</grid-item>
</grid-layout>
</div>
...
...
@@ -350,17 +374,43 @@ import LvglChart from "./components/LvglChart";
import
SystemChart
from
"
./components/SystemChart
"
;
import
{
GridLayout
,
GridItem
}
from
"
vue-grid-layout
"
;
import
{
wsNotify
}
from
"
@/utils/eventBus.js
"
;
import
Database
from
"
@/utils/indexedDB
"
;
const
dbObject
=
{
dbName
:
"
evmiot
"
,
// 数据库名
version
:
1
,
// 版本号
primaryKey
:
"
id
"
,
// 主键
keyNames
:
[
{
// 需要存储的数据字段对象
key
:
"
system.timestamp
"
,
// 字段名
unique
:
false
,
// 当前这条数据是否能重复 (最常用) 默认false
},
{
key
:
"
imei
"
,
unique
:
false
,
},
],
};
const
indexedDb
=
Database
();
let
monitor
=
new
indexedDb
(
dbObject
);
export
default
{
name
:
"
Monitor
"
,
data
()
{
return
{
watchs
:
[],
pngList
:
[],
globalData
:
null
,
device
:
null
,
devices
:
{},
deviceList
:
null
,
system
:
{},
systemList
:
[],
systemHistory
:
[],
evmHistory
:
[],
lvglHistory
:
[],
evm
:
{},
evmList
:
[],
lvgl
:
{},
...
...
@@ -387,10 +437,11 @@ export default {
{
x
:
6
,
y
:
0
,
w
:
6
,
h
:
5
,
i
:
"
2
"
,
static
:
true
},
{
x
:
0
,
y
:
5
,
w
:
6
,
h
:
5
,
i
:
"
3
"
,
static
:
false
},
{
x
:
6
,
y
:
5
,
w
:
6
,
h
:
5
,
i
:
"
4
"
,
static
:
false
},
{
x
:
0
,
y
:
10
,
w
:
12
,
h
:
10
,
i
:
"
5
"
,
static
:
false
},
{
x
:
0
,
y
:
20
,
w
:
12
,
h
:
7
,
i
:
"
6
"
,
static
:
false
},
{
x
:
0
,
y
:
27
,
w
:
12
,
h
:
7
,
i
:
"
7
"
,
static
:
false
},
{
x
:
0
,
y
:
34
,
w
:
12
,
h
:
7
,
i
:
"
8
"
,
static
:
false
},
{
x
:
0
,
y
:
10
,
w
:
8
,
h
:
10
,
i
:
"
5
"
,
static
:
false
},
{
x
:
8
,
y
:
10
,
w
:
4
,
h
:
10
,
i
:
"
6
"
,
static
:
false
},
{
x
:
0
,
y
:
20
,
w
:
12
,
h
:
7
,
i
:
"
7
"
,
static
:
false
},
{
x
:
0
,
y
:
27
,
w
:
12
,
h
:
7
,
i
:
"
8
"
,
static
:
false
},
{
x
:
0
,
y
:
34
,
w
:
12
,
h
:
7
,
i
:
"
9
"
,
static
:
false
},
],
draggable
:
true
,
resizable
:
true
,
...
...
@@ -408,54 +459,19 @@ export default {
return
row
.
highlight
?
"
success-row
"
:
""
;
},
moveEvent
(
i
,
newX
,
newY
)
{
const
msg
=
"
MOVE i=
"
+
i
+
"
, X=
"
+
newX
+
"
, Y=
"
+
newY
;
console
.
log
(
msg
);
console
.
log
(
i
,
newX
,
newY
);
},
movedEvent
(
i
,
newX
,
newY
)
{
const
msg
=
"
MOVED i=
"
+
i
+
"
, X=
"
+
newX
+
"
, Y=
"
+
newY
;
console
.
log
(
msg
);
console
.
log
(
i
,
newX
,
newY
);
},
resizeEvent
(
i
,
newH
,
newW
,
newHPx
,
newWPx
)
{
const
msg
=
"
RESIZE i=
"
+
i
+
"
, H=
"
+
newH
+
"
, W=
"
+
newW
+
"
, H(px)=
"
+
newHPx
+
"
, W(px)=
"
+
newWPx
;
console
.
log
(
msg
);
console
.
log
(
i
,
newH
,
newW
,
newHPx
,
newWPx
);
},
resizedEvent
(
i
,
newX
,
newY
,
newHPx
,
newWPx
)
{
const
msg
=
"
RESIZED i=
"
+
i
+
"
, X=
"
+
newX
+
"
, Y=
"
+
newY
+
"
, H(px)=
"
+
newHPx
+
"
, W(px)=
"
+
newWPx
;
console
.
log
(
msg
);
console
.
log
(
i
,
newX
,
newY
,
newHPx
,
newWPx
);
},
containerResizedEvent
(
i
,
newH
,
newW
,
newHPx
,
newWPx
)
{
const
msg
=
"
CONTAINER RESIZED i=
"
+
i
+
"
, H=
"
+
newH
+
"
, W=
"
+
newW
+
"
, H(px)=
"
+
newHPx
+
"
, W(px)=
"
+
newWPx
;
console
.
log
(
msg
);
console
.
log
(
i
,
newH
,
newW
,
newHPx
,
newWPx
);
},
layoutCreatedEvent
(
newLayout
)
{
console
.
log
(
"
Created layout:
"
,
newLayout
);
...
...
@@ -527,9 +543,10 @@ export default {
},
handleMessage
(
msg
)
{
if
(
msg
.
type
!==
"
report
"
||
!
msg
.
imei
)
return
null
;
// 如果接收到的数据不是当前选中的设备,那么则直接丢弃
// 将设备发送过来的消息存储到浏览器中
// 这里可以优化,将所有数据,保存到indexed datebase中
if
(
this
.
device
&&
msg
.
imei
!=
this
.
device
)
return
null
;
if
(
monitor
.
db
)
monitor
.
set
(
msg
)
;
if
(
!
this
.
deviceList
)
{
this
.
deviceList
=
[];
...
...
@@ -543,9 +560,13 @@ export default {
else
this
.
device
=
msg
.
imei
;
}
// 如果接收到的数据不是当前选中的设备,那么则直接丢弃
if
(
msg
.
imei
!=
this
.
device
)
{
return
null
;
}
// 处理单位
this
.
processData
(
msg
);
// this.devices[msg.imei] = msg;
this
.
globalData
=
msg
;
this
.
resetData
();
},
...
...
@@ -569,9 +590,35 @@ export default {
},
onSelectChange
(
res
)
{
this
.
device
=
res
;
monitor
.
getAllData
((
params
)
=>
{
return
params
.
imei
&&
params
.
imei
==
res
;
})
.
then
((
res
)
=>
{
console
.
log
(
res
);
const
systemList
=
[],
evmList
=
[],
lvglList
=
[];
res
.
forEach
((
item
)
=>
{
systemList
.
push
(
item
.
system
);
evmList
.
push
(
item
.
evm
);
lvglList
.
push
(
item
.
lvgl
);
});
wsNotify
.
eventBus
.
$emit
(
"
clear-system-chart
"
);
wsNotify
.
eventBus
.
$emit
(
"
clear-evm-chart
"
);
wsNotify
.
eventBus
.
$emit
(
"
clear-lvgl-chart
"
);
// this.systemHistory = systemList;
// this.evmHistory = evmList;
// this.lvglHistory = lvglList;
// 清空图标数据
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
});
// this.processData(this.devices[this.device]);
// this.resetData();
console
.
log
(
res
);
},
resetData
()
{
wsNotify
.
eventBus
.
$emit
(
"
resize
"
);
...
...
@@ -581,11 +628,12 @@ export default {
this
.
systemList
=
[
{
imei
:
this
.
globalData
.
imei
,
...
this
.
globalData
.
system
,
...
this
.
globalData
.
request
,
...
this
.
globalData
.
system
},
];
console
.
log
(
this
.
globalData
)
// 这里需要特殊处理下,先判断uri是否存在,不存在则添加,存在则更新
let
uris
=
[];
this
.
imageList
.
forEach
((
item
)
=>
{
...
...
@@ -600,6 +648,8 @@ export default {
item
.
highlight
=
false
;
}
if
(
item
.
png_detail
&&
item
.
png_detail
.
length
)
this
.
pngList
=
item
.
png_detail
const
target
=
this
.
imageList
.
find
((
img
)
=>
img
.
uri
===
item
.
uri
);
if
(
target
)
{
target
.
length
=
item
.
length
;
...
...
@@ -608,8 +658,7 @@ export default {
if
(
item
.
png_uncompressed_size
&&
item
.
png_uncompressed_size
!==
target
.
png_uncompressed_size
)
{
)
{
target
.
highlight
=
true
;
target
.
png_uncompressed_size
=
item
.
png_uncompressed_size
;
}
...
...
@@ -618,7 +667,6 @@ export default {
item
.
png_file_size
!==
target
.
png_file_size
)
target
.
png_file_size
=
item
.
png_file_size
;
}
else
{
this
.
imageList
.
push
(
item
);
}
...
...
@@ -633,7 +681,29 @@ export default {
},
},
mounted
()
{},
destroyed
()
{
// 页面关闭则销毁该数据库
// monitor.deleteDB()
},
created
()
{
monitor
.
init
()
.
then
((
res
)
=>
{
console
.
log
(
res
);
if
(
monitor
.
db
)
{
monitor
.
set
({
system
:
{
free_size
:
0
,
free_space_size
:
0
,
used_space_size
:
0
,
},
});
}
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
});
this
.
socket
=
wsNotify
;
wsNotify
.
eventBus
.
$on
(
"
open
"
,
(
message
)
=>
{
this
.
sendMsg
();
...
...
frontend/src/views/system/style.css
View file @
0587447b
...
...
@@ -3,5 +3,5 @@
}
.el-table
.success-row
{
background
:
greenyellow
;
background
:
#87e8de
;
}
tools/build_out/tests/http_interval.py
View file @
0587447b
'''
Author: your name
Date: 2021-07-22 19:01:41
LastEditTime: 2021-07-2
2 21:46:18
LastEditTime: 2021-07-2
4 00:27:21
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath:
\
evm-store
\t
ools
\b
uild_out
\t
ests
\
http_interval.py
'''
import
json
import
time
import
random
import
requests
from
threading
import
Timer
from
threading
import
Timer
,
Thread
def
send_request
():
def
send_request
(
imei
):
payload
=
{
"system"
:{
"free_size"
:
1769792
,
"free_space_size"
:
5156864
,
"used_space_size"
:
1134592
},
"lvgl"
:{
"total_size"
:
0
,
"free_cnt"
:
0
,
"free_size"
:
0
,
"free_biggest_size"
:
0
,
"used_cnt"
:
0
,
"used_pct"
:
0
,
"frag_pct"
:
0
},
"evm"
:{
"heap_total_size"
:
2097152
,
"heap_used_size"
:
575072
,
"heap_map_size"
:
8192
,
"stack_total_size"
:
102400
,
"stack_used_size"
:
1312
},
"image"
:[
{
"uri"
:
"evue_launcher"
,
"length"
:
13515
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_startup"
,
"length"
:
3666
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_index"
,
"length"
:
5482
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_story"
,
"length"
:
5509
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_storyList"
,
"length"
:
9196
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_storyPlay"
,
"length"
:
25791
,
"png_total_count"
:
6
,
"png_uncompressed_size"
:
319376
,
"png_file_size"
:
10770
}
"system"
:
{
"free_size"
:
1769792
,
"free_space_size"
:
5156864
,
"used_space_size"
:
1134592
},
"lvgl"
:
{
"total_size"
:
0
,
"free_cnt"
:
0
,
"free_size"
:
0
,
"free_biggest_size"
:
0
,
"used_cnt"
:
0
,
"used_pct"
:
0
,
"frag_pct"
:
0
},
"evm"
:
{
"heap_total_size"
:
2097152
,
"heap_used_size"
:
575072
,
"heap_map_size"
:
8192
,
"stack_total_size"
:
102400
,
"stack_used_size"
:
1312
},
"image"
:
[
{
"uri"
:
"evue_launcher"
,
"length"
:
13515
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_startup"
,
"length"
:
3666
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_index"
,
"length"
:
5482
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_story"
,
"length"
:
5509
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_storyList"
,
"length"
:
9196
,
"png_total_count"
:
0
,
"png_uncompressed_size"
:
0
,
"png_file_size"
:
0
},
{
"uri"
:
"kdgs_1_storyPlay"
,
"length"
:
25791
,
"png_total_count"
:
6
,
"png_uncompressed_size"
:
319376
,
"png_file_size"
:
10770
,
"png_detail"
:
[
{
"uri"
:
"C:/../../test/watch_appstore/kdgs_1_playBackground.png"
,
"filesize"
:
7774
,
"uncompressed_size"
:
259200
,
"ratio"
:
33.341908
},
{
"uri"
:
"C:/../../test/watch_appstore/kdgs_1_playLb.png"
,
"filesize"
:
482
,
"uncompressed_size"
:
12544
,
"ratio"
:
26.024897
},
{
"uri"
:
"C:/../../test/watch_appstore/kdgs_1_playNLike.png"
,
"filesize"
:
1094
,
"uncompressed_size"
:
12544
,
"ratio"
:
11.466179
},
{
"uri"
:
"C:/../../test/watch_appstore/kdgs_1_playYl.png"
,
"filesize"
:
745
,
"uncompressed_size"
:
12544
,
"ratio"
:
16.837584
},
{
"uri"
:
"C:/../../test/watch_appstore/kdgs_1_playNext.png"
,
"filesize"
:
484
,
"uncompressed_size"
:
12544
,
"ratio"
:
25.917355
},
{
"uri"
:
"C:/../../test/watch_appstore/kdgs_1_play_bs.png"
,
"filesize"
:
191
,
"uncompressed_size"
:
10000
,
"ratio"
:
52.356022
}
]}
],
"imei"
:
"352099001761481"
,
"datetime"
:{
"second"
:
55
,
"minute"
:
48
,
"hour"
:
15
,
"day"
:
21
,
"month"
:
7
,
"year"
:
2021
,
"weekday"
:
3
}
"imei"
:
imei
,
"datetime"
:
{
"second"
:
55
,
"minute"
:
48
,
"hour"
:
15
,
"day"
:
21
,
"month"
:
7
,
"year"
:
2021
,
"weekday"
:
3
}
}
while
True
:
for
item
in
payload
.
get
(
"image"
):
item
.
update
({
'length'
:
0
,
...
...
@@ -49,13 +94,40 @@ def send_request():
'png_file_size'
:
random
.
randint
(
0
,
10000
)
})
r
=
requests
.
post
(
"http://localhost:3000/api/v1/evm_store/monitor"
,
data
=
json
.
dumps
(
payload
))
r
=
requests
.
post
(
"http://localhost:3000/api/v1/evm_store/monitor"
,
data
=
json
.
dumps
(
payload
))
print
(
r
.
status_code
)
print
(
r
.
json
())
t
=
Timer
(
3
,
send_request
)
t
.
run
()
time
.
sleep
(
3
)
class
myThread
(
Thread
):
def
__init__
(
self
,
threadID
,
name
,
counter
,
imei
):
Thread
.
__init__
(
self
)
self
.
threadID
=
threadID
self
.
name
=
name
self
.
counter
=
counter
self
.
imei
=
imei
def
run
(
self
):
print
(
"开始线程:"
+
self
.
name
)
print
(
self
.
counter
,
self
.
imei
)
send_request
(
self
.
imei
)
print
(
"退出线程:"
+
self
.
name
)
if
__name__
==
"__main__"
:
send_request
()
# send_request()
# 创建新线程
thread1
=
myThread
(
1
,
"Thread-1"
,
1
,
"352099001761481"
)
thread2
=
myThread
(
2
,
"Thread-2"
,
2
,
"866866040000447"
)
# 开启新线程
thread1
.
start
()
thread2
.
start
()
thread1
.
join
()
thread2
.
join
()
print
(
"退出主线程"
)
tools/build_out/views/monitor.py
View file @
0587447b
'''
Author: your name
Date: 2021-06-29 19:33:41
LastEditTime: 2021-07-2
2 19:30:38
LastEditTime: 2021-07-2
4 01:15:03
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath:
\
evm-store
\b
ackend
\v
iew
\
monitor.py
...
...
@@ -154,6 +154,7 @@ class NotifyHandler(BaseWebsocket):
try
:
className
=
self
.
__class__
.
__name__
message
=
json
.
loads
(
message
)
logger
.
info
(
message
)
# 判断消息类型
if
message
.
get
(
"type"
)
and
message
.
get
(
"token"
):
# 获取token值,检验正确与否,获取uuid
...
...
@@ -189,6 +190,7 @@ class NotifyHandler(BaseWebsocket):
# self.close()
elif
message
.
get
(
"type"
)
==
"heartbeat"
:
# 心跳包
# 收到心跳包消息,更新接收数据时间
logger
.
info
(
"////////////////////////"
)
for
c
in
self
.
_clients
:
if
c
.
get
(
"uuid"
)
==
payload
.
get
(
"sub"
)
.
get
(
"uuid"
):
c
[
"ts"
]
=
int
(
time
.
time
())
...
...
@@ -209,7 +211,8 @@ class NotifyHandler(BaseWebsocket):
def
on_heartbeat
(
self
):
# 心跳定时器,固定间隔扫描连接列表,当连接超时,主动剔除该连接
for
i
in
range
(
len
(
self
.
_clients
)
-
1
,
-
1
,
-
1
):
if
int
(
time
.
time
())
-
self
.
_clients
[
i
]
.
get
(
"ts"
)
>
5
:
if
int
(
time
.
time
())
-
self
.
_clients
[
i
]
.
get
(
"ts"
)
>
30
:
logger
.
info
(
"################################################"
)
# self._clients.pop(i)
del
self
.
_clients
[
i
]
className
=
self
.
__class__
.
__name__
...
...
tools/frontend/babel.config.js
View file @
0587447b
module
.
exports
=
{
"
presets
"
:
[
[
"
@vue/app
"
,
{
useBuiltIns
:
"
entry
"
}
]
],
"
plugins
"
:
[
[
"
import
"
,
{
"
libraryName
"
:
"
ant-design-vue
"
,
"
libraryDirectory
"
:
"
es
"
,
"
style
"
:
true
}]
]
}
presets
:
[[
"
@vue/app
"
,
{
useBuiltIns
:
"
entry
"
}]],
plugins
:
[
[
"
import
"
,
{
libraryName
:
"
ant-design-vue
"
,
libraryDirectory
:
"
es
"
,
style
:
true
},
],
[
"
component
"
,
{
libraryName
:
"
element-ui
"
,
styleLibraryName
:
"
theme-chalk
"
,
},
],
],
};
tools/frontend/package.json
View file @
0587447b
...
...
@@ -16,6 +16,7 @@
"core-js"
:
"^3.9.0"
,
"cropperjs"
:
"^1.5.11"
,
"echarts"
:
"^5.1.2"
,
"element-ui"
:
"^2.15.3"
,
"js-cookie"
:
"^2.2.1"
,
"npm-check-updates"
:
"^11.7.1"
,
"numeral"
:
"^2.0.6"
,
...
...
@@ -26,6 +27,7 @@
"vue-grid-layout"
:
"^2.3.12"
,
"vue-i18n"
:
"^8.1.0"
,
"vue-router"
:
"^3.0.1"
,
"vue-treeselect"
:
"^1.0.7"
,
"vuex"
:
"^3.0.1"
,
"vuex-router-sync"
:
"^5.0.0"
},
...
...
@@ -34,6 +36,7 @@
"@vue/cli-plugin-eslint"
:
"^3.3.0"
,
"@vue/cli-service"
:
"^3.3.0"
,
"babel-eslint"
:
"^10.1.0"
,
"babel-plugin-component"
:
"^1.1.1"
,
"babel-plugin-import"
:
"^1.9.1"
,
"eslint"
:
"^7.30.0"
,
"eslint-plugin-vue"
:
"^7.13.0"
,
...
...
tools/frontend/src/api/system/menu.js
0 → 100644
View file @
0587447b
import
request
from
'
@/utils/request
'
// 查询
export
function
fetchTree
(
data
)
{
return
request
.
post
(
'
/system/menu/treeList
'
,
data
)
}
// 新建
export
function
create
(
data
)
{
return
request
.
post
(
'
/system/menu/create
'
,
data
)
}
// 修改
export
function
updateById
(
data
)
{
return
request
.
post
(
'
/system/menu/updateById
'
,
data
)
}
// 修改状态
export
function
updateStatus
(
data
)
{
return
request
.
post
(
'
/system/menu/updateStatus
'
,
data
)
}
// 删除
export
function
deleteById
(
id
)
{
return
request
.
get
(
`/system/menu/delete/
${
id
}
`
)
}
// 批量删除
export
function
deleteByIdInBatch
(
ids
)
{
return
request
.
get
(
'
/system/menu/delete/batch
'
,
{
params
:
{
ids
}
})
}
// 查询菜单树
export
function
fetchMenuTree
()
{
return
request
.
get
(
'
/system/menu/treeNodes
'
)
}
// 排序
export
function
sort
(
data
)
{
return
request
.
post
(
'
/system/menu/updateSort
'
,
data
)
}
tools/frontend/src/router/index.js
View file @
0587447b
...
...
@@ -128,27 +128,27 @@ const router = new Router({
{
path
:
"
/system/setting/menu
"
,
name
:
"
menu
"
,
component
:
()
=>
import
(
"
@/views/System/
M
enu
"
),
component
:
()
=>
import
(
"
@/views/System/
m
enu
"
),
},
{
path
:
"
/system/setting/
module
"
,
path
:
"
/system/setting/
perssion
"
,
name
:
"
module
"
,
component
:
()
=>
import
(
"
@/views/System/
Role
"
),
component
:
()
=>
import
(
"
@/views/System/
perssion
"
),
},
{
path
:
"
/system/setting/
config
"
,
path
:
"
/system/setting/
data-permission
"
,
name
:
"
config
"
,
component
:
()
=>
import
(
"
@/views/System/
R
ole
"
),
component
:
()
=>
import
(
"
@/views/System/
r
ole
"
),
},
{
path
:
"
/system/setting/dict
"
,
name
:
"
dict
"
,
component
:
()
=>
import
(
"
@/views/System/
Role
"
),
component
:
()
=>
import
(
"
@/views/System/
dict
"
),
},
{
path
:
"
/system/setting/
area
"
,
path
:
"
/system/setting/
location
"
,
name
:
"
area
"
,
component
:
()
=>
import
(
"
@/views/System/
Role
"
),
component
:
()
=>
import
(
"
@/views/System/
location
"
),
},
{
path
:
"
/system/setting/file-manager
"
,
...
...
@@ -158,14 +158,14 @@ const router = new Router({
],
},
{
path
:
"
/system/
role
"
,
name
:
"
rol
e
"
,
component
:
()
=>
import
(
"
@/views/System/
Role
"
),
path
:
"
/system/
trace-log
"
,
name
:
"
trac
e
"
,
component
:
()
=>
import
(
"
@/views/System/
traceLog
"
),
},
{
path
:
"
/system/
admin
"
,
name
:
"
admin
"
,
component
:
()
=>
import
(
"
@/views/System/
Role
"
),
path
:
"
/system/
user
"
,
name
:
"
user
"
,
component
:
()
=>
import
(
"
@/views/System/
user
"
),
},
],
},
...
...
@@ -236,6 +236,13 @@ const router = new Router({
},
],
},
{
path
:
"
/table
"
,
component
:
BasicLayout
,
children
:
[
{
path
:
"
/table
"
,
component
:
()
=>
import
(
"
@/views/System/table
"
)
},
],
},
],
});
...
...
tools/frontend/src/styles/variables.scss
0 → 100644
View file @
0587447b
// 主色调
$primary-color
:
#2E68EC
;
// 头部高度
$header-height
:
60px
;
// 菜单宽度
$menu-width
:
208px
;
// 页面最小宽度
$page-min-width
:
1000px
;
// 字体
$font-color
:
#282828
;
// 颜色
$font-size
:
12px
;
// 大小
tools/frontend/src/templates/Template.1.vue
0 → 100644
View file @
0587447b
<
template
>
<a-page-header-wrapper
title=
"查询表格"
>
<a-card
:bordered=
"false"
>
<div
class=
"tableList"
>
<div
class=
"tableListForm"
>
<a-form
v-show=
"!expandForm"
layout=
"inline"
>
<a-row
:gutter=
"
{ md: 8, lg: 24, xl: 48 }">
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"用户"
v-decorator=
"['username']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"性别"
v-decorator=
"['gender']"
>
<a-select
placeholder=
"请选择"
style=
"width: 100%"
>
<a-option
value=
"male"
>
male
</a-option>
<a-option
value=
"female"
>
female
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<span
class=
"submitButtons"
>
<a-button
type=
"primary"
htmlType=
"submit"
>
查询
</a-button>
<a-button
:style=
"
{ marginLeft: '8px' }"> 重置
</a-button>
<a
:style=
"
{ marginLeft: '8px' }" @click="toggleForm">
展开
<a-icon
type=
"down"
/>
</a>
</span>
</a-col>
</a-row>
</a-form>
<a-form
v-show=
"expandForm"
layout=
"inline"
>
<a-row
:gutter=
"
{ md: 8, lg: 24, xl: 48 }">
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"用户"
v-decorator=
"['username']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"性别"
v-decorator=
"['gender']"
>
<a-select
placeholder=
"请选择"
style=
"width: 100%"
>
<a-option
value=
"male"
>
male
</a-option>
<a-option
value=
"female"
>
female
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"姓名"
v-decorator=
"['name']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
</a-row>
<a-row
:gutter=
"
{ md: 8, lg: 24, xl: 48 }">
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"时间"
v-decorator=
"['registered']"
>
<a-range-picker
style=
"width: 100%"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"邮箱"
v-decorator=
"['email']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"国籍"
v-decorator=
"['nat']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
</a-row>
<div
style=
"overflow: hidden"
>
<div
:style=
"
{ float: 'right', marginBottom: '24px' }">
<a-button
type=
"primary"
htmlType=
"submit"
>
查询
</a-button>
<a-button
:style=
"
{ marginLeft: '8px' }"> 重置
</a-button>
<a
:style=
"
{ marginLeft: '8px' }" @click="toggleForm">
收起
<a-icon
type=
"up"
/>
</a>
</div>
</div>
</a-form>
</div>
<div
class=
"tableListOperator"
>
<a-button
icon=
"plus"
type=
"primary"
>
新建
</a-button>
<span
v-show=
"selectedRowKeys.length > 0"
>
<a-button>
批量操作
</a-button>
<!--
<a-dropdown
overlay=
{menu}>
<a-button>
更多操作
<icon
type=
"down"
/>
</a-button>
</a-dropdown>
-->
</span>
</div>
<!--
<StandardTable
selectedRows=
{selectedRows}
loading={loading}
data={data}
columns={this.columns}
onSelectRow={this.handleSelectRows}
onChange={this.handleStandardTableChange}
/> -->
<a-table
:columns=
"columns"
:rowKey=
"(record) => record.login.uuid"
:dataSource=
"users.data"
:pagination=
"users.pagination"
:loading=
"loading"
@
change=
"handleTableChange"
>
<!-- :rowSelection="
{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" -->
<template
slot=
"login"
slot-scope=
"login"
>
{{
login
.
username
}}
</
template
>
<
template
slot=
"name"
slot-scope=
"name"
>
{{
name
.
first
}}
{{
name
.
last
}}
</
template
>
<
template
slot=
"registered"
slot-scope=
"registered"
>
{{
registered
.
date
}}
(
{{
registered
.
age
}}
)
</
template
>
<
template
slot=
"expandedRowRender"
slot-scope=
"record"
style=
"margin: 0"
>
<p
:style=
"[sya, syb]"
>
<a-avatar
:src=
"record.picture.large"
shape=
"square"
:size=
"128"
/>
</p>
<p
:style=
"[sya]"
>
Personal
</p>
<a-row>
<a-col
:span=
"6"
>
<a-description-item
title=
"Name"
:content=
"record.name.first + ' ' + record.name.last"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Account"
:content=
"record.login.username"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"City"
:content=
"record.location.city"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Postcode"
:content=
"record.location.postcode"
/>
</a-col>
</a-row>
<a-row>
<a-col
:span=
"6"
>
<a-description-item
title=
"Country"
:content=
"record.nat"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Birthday"
:content=
"record.dob.date + ' (' + record.dob.age + ')'"
/>
</a-col>
<a-col
:span=
"12"
>
<a-description-item
title=
"Timezone"
:content=
"record.location.timezone.description"
/>
</a-col>
</a-row>
<a-row>
<a-col
:span=
"12"
>
</a-col>
<a-col
:span=
"12"
>
</a-col>
</a-row>
<a-divider
/>
<p
:style=
"[sya]"
>
Contacts
</p>
<a-row>
<a-col
:span=
"6"
>
<a-description-item
title=
"Email"
:content=
"record.email"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Cell"
:content=
"record.cell"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Phone"
:content=
"record.phone"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Coordinates"
:content=
"
record.location.coordinates.latitude +
' ' +
record.location.coordinates.longitude
"
/>
</a-col>
</a-row>
<a-row>
<a-col
:span=
"12"
>
<a-description-item
title=
"Registered"
:content=
"
record.registered.date + ' (' + record.registered.age + ')'
"
/>
</a-col>
</a-row>
</
template
>
<
template
slot=
"action"
slot-scope=
"text, record"
>
<a
href=
"javascript:;"
>
查看
</a>
<a-divider
type=
"vertical"
/>
<a
href=
"javascript:;"
>
配置
</a>
</
template
>
</a-table>
</div>
</a-card>
</a-page-header-wrapper>
</template>
<
script
>
import
{
Avatar
,
Row
,
Col
,
Card
,
List
,
Button
,
Form
,
Icon
,
Table
,
Divider
,
Dropdown
,
Input
,
Select
,
DatePicker
,
}
from
"
ant-design-vue
"
;
import
PageHeaderWrapper
from
"
@/components/PageHeaderWrapper
"
;
import
DescriptionItem
from
"
@/components/DescriptionItem
"
;
const
columns
=
[
{
title
:
"
用户名
"
,
dataIndex
:
"
login
"
,
sorter
:
true
,
width
:
"
12%
"
,
scopedSlots
:
{
customRender
:
"
login
"
},
},
{
title
:
"
姓名
"
,
dataIndex
:
"
name
"
,
sorter
:
true
,
width
:
"
15%
"
,
scopedSlots
:
{
customRender
:
"
name
"
},
},
{
title
:
"
性别
"
,
dataIndex
:
"
gender
"
,
filters
:
[
{
text
:
"
Male
"
,
value
:
"
male
"
},
{
text
:
"
Female
"
,
value
:
"
female
"
},
],
},
{
title
:
"
邮箱
"
,
dataIndex
:
"
email
"
,
},
{
title
:
"
国籍
"
,
dataIndex
:
"
nat
"
,
},
{
title
:
"
Registered
"
,
dataIndex
:
"
registered
"
,
scopedSlots
:
{
customRender
:
"
registered
"
},
},
{
title
:
"
Action
"
,
key
:
"
action
"
,
scopedSlots
:
{
customRender
:
"
action
"
},
},
];
import
{
mapGetters
}
from
"
vuex
"
;
export
default
{
data
:
()
=>
({
expandForm
:
false
,
selectedRowKeys
:
[],
columns
,
sya
:
{
fontSize
:
"
16px
"
,
color
:
"
rgba(0,0,0,0.85)
"
,
lineHeight
:
"
24px
"
,
display
:
"
block
"
,
marginBottom
:
"
16px
"
,
},
syb
:
{
marginBottom
:
"
24px
"
,
},
}),
async
asyncData
({
store
,
route
},
config
=
{
results
:
15
})
{
await
store
.
dispatch
(
"
frontend/openapi/getUsers
"
,
{
...
config
,
path
:
route
.
path
,
});
},
computed
:
{
...
mapGetters
({
loading
:
"
frontend/openapi/loading
"
,
users
:
"
frontend/openapi/getUsers
"
,
}),
},
components
:
{
APageHeaderWrapper
:
PageHeaderWrapper
,
AAvatar
:
Avatar
,
ARow
:
Row
,
ACol
:
Col
,
ACard
:
Card
,
ACardGrid
:
Card
.
Grid
,
ACardMeta
:
Card
.
Meta
,
AList
:
List
,
AButton
:
Button
,
AForm
:
Form
,
AFormItem
:
Form
.
Item
,
AIcon
:
Icon
,
ATable
:
Table
,
ADescriptionItem
:
DescriptionItem
,
ADivider
:
Divider
,
ADropdown
:
Dropdown
,
AInput
:
Input
,
ASelect
:
Select
,
AOption
:
Select
.
Option
,
ARangePicker
:
DatePicker
.
RangePicker
,
},
methods
:
{
toggleForm
()
{
this
.
expandForm
=
!
this
.
expandForm
;
},
onSelectChange
(
selectedRowKeys
)
{
window
.
console
.
log
(
"
selectedRowKeys changed:
"
,
selectedRowKeys
);
this
.
selectedRowKeys
=
selectedRowKeys
;
},
handleTableChange
(
pagination
,
filters
,
sorter
)
{
const
pager
=
{
...
this
.
users
.
pagination
};
pager
.
current
=
pagination
.
current
;
this
.
users
.
pagination
=
pager
;
this
.
$options
.
asyncData
(
{
store
:
this
.
$store
,
route
:
this
.
$route
},
{
results
:
pagination
.
pageSize
,
page
:
pagination
.
current
,
sortField
:
sorter
.
field
,
sortOrder
:
sorter
.
order
,
...
filters
,
}
);
},
},
mounted
()
{
this
.
$options
.
asyncData
(
{
store
:
this
.
$store
,
route
:
this
.
$route
},
{
results
:
15
}
);
},
};
</
script
>
<
style
lang=
"less"
>
@import url("styles/tableList.less");
</
style
>
\ No newline at end of file
tools/frontend/src/templates/Template.2.vue
0 → 100644
View file @
0587447b
<
template
>
<a-page-header-wrapper
:loading=
"false"
:tabList=
"tabList"
tabActiveKey=
"articles"
:tabChange=
"tabChange"
>
menu
</a-page-header-wrapper>
</
template
>
<
script
>
import
{
Avatar
,
Row
,
Col
,
Card
,
List
}
from
"
ant-design-vue
"
;
import
PageHeaderWrapper
from
"
@/components/PageHeaderWrapper
"
;
export
default
{
data
:
()
=>
({
activitiesLoading
:
true
,
projectLoading
:
false
,
tabList
:
[
{
key
:
"
articles
"
,
tab
:
"
菜单列表
"
,
},
{
key
:
"
application
"
,
tab
:
"
应用列表
"
,
},
],
}),
components
:
{
APageHeaderWrapper
:
PageHeaderWrapper
,
AAvatar
:
Avatar
,
ARow
:
Row
,
ACol
:
Col
,
ACard
:
Card
,
ACardGrid
:
Card
.
Grid
,
ACardMeta
:
Card
.
Meta
,
AList
:
List
,
},
methods
:
{
tabChange
(
e
)
{
window
.
console
.
log
(
e
);
},
},
};
</
script
>
<
style
lang=
"less"
>
</
style
>
\ No newline at end of file
tools/frontend/src/views/Application/Monitor.vue
View file @
0587447b
...
...
@@ -629,6 +629,10 @@ export default {
flex-direction
:
row
;
&
>
.grid-node
{
flex
:
1
;
&
>
h3
{
font-weight
:
bold
;
font-size
:
17px
;
}
&
>
h3
,
p
{
display
:
flex
;
justify-content
:
center
;
...
...
tools/frontend/src/views/System/Menu.vue
View file @
0587447b
<
template
>
<a-page-header-wrapper
:loading=
"false"
:tabList=
"tabList"
tabActiveKey=
"articles"
:tabChange=
"tabChange"
<TableLayout
class=
"menu-layout"
:permissions=
"['system:menu:query']"
>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:menu:create', 'system:menu:delete', 'system:menu:sort']"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaMenuWindow.open('新建一级菜单')"
icon=
"el-icon-plus"
v-permissions=
"['system:menu:create']"
>
新建
</el-button></li>
<li><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
v-permissions=
"['system:menu:delete']"
>
删除
</el-button></li>
<li><el-button
@
click=
"sort('top')"
:loading=
"isWorking.sort"
icon=
"el-icon-sort-up"
v-permissions=
"['system:menu:sort']"
>
上移
</el-button></li>
<li><el-button
@
click=
"sort('bottom')"
:loading=
"isWorking.sort"
icon=
"el-icon-sort-down"
v-permissions=
"['system:menu:sort']"
>
下移
</el-button></li>
</ul>
<el-table
ref=
"table"
v-loading=
"isWorking.search"
:data=
"tableData.list"
:tree-props=
"
{children: 'children', hasChildren: 'hasChildren'}"
row-key="id"
stripe
default-expand-all
@selection-change="handleSelectionChange"
>
menu
</a-page-header-wrapper>
<el-table-column
type=
"selection"
width=
"55"
fixed=
"left"
></el-table-column>
<el-table-column
prop=
"name"
label=
"菜单名称"
fixed=
"left"
min-width=
"160px"
></el-table-column>
<el-table-column
prop=
"icon"
label=
"图标"
min-width=
"80px"
class-name=
"table-column-icon"
>
<template
slot-scope=
"
{row}">
<i
v-if=
"row.icon != null && row.icon !== ''"
:class=
"
{[row.icon]: true}">
</i>
<template
v-else
>
未设置
</
template
>
</template>
</el-table-column>
<el-table-column
prop=
"path"
label=
"访问路径"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"remark"
label=
"备注"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"disabled"
label=
"是否启用"
min-width=
"80px"
>
<
template
slot-scope=
"{row}"
>
<el-switch
v-model=
"row.disabled"
:active-value=
"false"
:inactive-value=
"true"
@
change=
"switchDisabled(row)"
/>
</
template
>
</el-table-column>
<el-table-column
v-if=
"containPermissions(['system:menu:update', 'system:menu:create', 'system:menu:delete'])"
label=
"操作"
min-width=
"220"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
type=
"text"
icon=
"el-icon-edit"
@
click=
"$refs.operaMenuWindow.open('编辑菜单', row)"
v-permissions=
"['system:menu:update']"
>
编辑
</el-button>
<el-button
type=
"text"
icon=
"el-icon-plus"
@
click=
"$refs.operaMenuWindow.open('新建子菜单', null, row)"
v-permissions=
"['system:menu:create']"
>
新建子菜单
</el-button>
<el-button
v-if=
"!row.fixed"
type=
"text"
icon=
"el-icon-delete"
@
click=
"deleteById(row)"
v-permissions=
"['system:menu:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</template>
<!-- 新建/修改 -->
<OperaMenuWindow
ref=
"operaMenuWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
</TableLayout>
</template>
<
script
>
import
{
Avatar
,
Row
,
Col
,
Card
,
List
}
from
"
ant-design-vue
"
;
import
PageHeaderWrapper
from
"
@/components/PageHeaderWrapper
"
;
import
{
Table
,
TableColumn
,
Button
}
from
"
element-ui
"
import
'
element-ui/lib/theme-chalk/index.css
'
import
TableLayout
from
'
./components/TableLayout
'
import
BaseTable
from
'
./components/base/BaseTable
'
import
OperaMenuWindow
from
'
./components/system/menu/OperaMenuWindow
'
import
{
fetchTree
,
updateStatus
,
sort
}
from
'
@/api/system/menu
'
export
default
{
data
:
()
=>
({
activitiesLoading
:
true
,
projectLoading
:
false
,
tabList
:
[
{
key
:
"
articles
"
,
tab
:
"
菜单列表
"
,
},
{
key
:
"
application
"
,
tab
:
"
应用列表
"
,
},
],
}),
name
:
'
SystemMenu
'
,
extends
:
BaseTable
,
components
:
{
APageHeaderWrapper
:
PageHeaderWrapper
,
AAvatar
:
Avatar
,
ARow
:
Row
,
ACol
:
Col
,
ACard
:
Card
,
ACardGrid
:
Card
.
Grid
,
ACardMeta
:
Card
.
Meta
,
AList
:
List
,
"
el-table
"
:
Table
,
"
el-table-column
"
:
TableColumn
,
"
el-button
"
:
Button
,
OperaMenuWindow
,
TableLayout
},
data
()
{
return
{
// 是否正在处理中
isWorking
:
{
sort
:
false
}
}
},
methods
:
{
tabChange
(
e
)
{
window
.
console
.
log
(
e
);
// 查询数据
handlePageChange
()
{
this
.
isWorking
.
search
=
true
fetchTree
()
.
then
(
records
=>
{
this
.
tableData
.
list
=
records
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
search
=
false
})
},
// 排序
sort
(
direction
)
{
if
(
this
.
isWorking
.
sort
)
{
return
}
if
(
this
.
tableData
.
selectedRows
.
length
===
0
)
{
this
.
$tip
.
warning
(
'
请选择一条数据
'
)
return
}
if
(
this
.
tableData
.
selectedRows
.
length
>
1
)
{
this
.
$tip
.
warning
(
'
排序时仅允许选择一条数据
'
)
return
}
const
menuId
=
this
.
tableData
.
selectedRows
[
0
].
id
// 找到菜单范围
let
menuPool
for
(
const
rootMenu
of
this
.
tableData
.
list
)
{
const
parent
=
this
.
__findParent
(
menuId
,
rootMenu
)
if
(
parent
!=
null
)
{
menuPool
=
parent
.
children
}
}
menuPool
=
menuPool
||
this
.
tableData
.
list
const
menuIndex
=
menuPool
.
findIndex
(
menu
=>
menu
.
id
===
menuId
)
// 上移校验
if
(
direction
===
'
top
'
&&
menuIndex
===
0
)
{
this
.
$tip
.
warning
(
'
菜单已到顶部
'
)
return
}
// 下移校验
if
(
direction
===
'
bottom
'
&&
menuIndex
===
menuPool
.
length
-
1
)
{
this
.
$tip
.
warning
(
'
菜单已到底部
'
)
return
}
this
.
isWorking
.
sort
=
true
sort
({
id
:
this
.
tableData
.
selectedRows
[
0
].
id
,
direction
})
.
then
(()
=>
{
if
(
direction
===
'
top
'
)
{
menuPool
.
splice
(
menuIndex
,
0
,
menuPool
.
splice
(
menuIndex
-
1
,
1
)[
0
])
}
else
{
menuPool
.
splice
(
menuIndex
,
0
,
menuPool
.
splice
(
menuIndex
+
1
,
1
)[
0
])
}
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
sort
=
false
})
},
};
// 启用/禁用菜单
switchDisabled
(
row
)
{
if
(
!
row
.
disabled
)
{
this
.
__updateMenuStatus
(
row
)
return
}
this
.
$dialog
.
disableConfirm
(
`确认禁用
${
row
.
name
}
菜单吗?`
)
.
then
(()
=>
{
this
.
__updateMenuStatus
(
row
)
}).
catch
(()
=>
{
row
.
disabled
=
!
row
.
disabled
})
},
// 查询父节点
__findParent
(
id
,
parent
)
{
if
(
parent
.
children
===
0
)
{
return
}
for
(
const
menu
of
parent
.
children
)
{
if
(
menu
.
id
===
id
)
{
return
parent
}
if
(
menu
.
children
.
length
>
0
)
{
const
m
=
this
.
__findParent
(
id
,
menu
)
if
(
m
!=
null
)
{
return
m
}
}
}
return
null
},
// 修改菜单状态
__updateMenuStatus
(
row
)
{
updateStatus
({
id
:
row
.
id
,
parentId
:
row
.
parentId
,
disabled
:
row
.
disabled
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
修改成功
'
)
})
.
catch
(
e
=>
{
row
.
disabled
=
!
row
.
disabled
this
.
$tip
.
apiFailed
(
e
)
})
}
},
created
()
{
this
.
config
({
module
:
'
菜单
'
,
api
:
'
/system/menu
'
})
this
.
search
()
}
}
</
script
>
<
style
lang=
"less"
>
<
style
lang=
"scss"
scoped
>
@import
"@/styles/variables.scss"
;
.menu-layout
{
/
deep
/
.table-content
{
margin-top
:
0
;
}
}
// 图标列
.table-column-icon
{
// element-ui图标
i
{
background-color
:
$primary-color
;
opacity
:
0
.72
;
font-size
:
20px
;
color
:
#fff
;
padding
:
4px
;
border-radius
:
50%
;
}
// 自定义图标
[
class
^=
"eva-icon-"
]
{
width
:
20px
;
height
:
20px
;
background-size
:
16px
;
vertical-align
:
middle
;
}
}
</
style
>
tools/frontend/src/views/System/Role.vue
View file @
0587447b
<
template
>
<a-page-header-wrapper
title=
"查询表格"
>
<a-card
:bordered=
"false"
>
<div
class=
"tableList"
>
<div
class=
"tableListForm"
>
<a-form
v-show=
"!expandForm"
layout=
"inline"
>
<a-row
:gutter=
"
{ md: 8, lg: 24, xl: 48 }">
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"用户"
v-decorator=
"['username']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"性别"
v-decorator=
"['gender']"
>
<a-select
placeholder=
"请选择"
style=
"width: 100%"
>
<a-option
value=
"male"
>
male
</a-option>
<a-option
value=
"female"
>
female
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<span
class=
"submitButtons"
>
<a-button
type=
"primary"
htmlType=
"submit"
>
查询
</a-button>
<a-button
:style=
"
{ marginLeft: '8px' }"> 重置
</a-button>
<a
:style=
"
{ marginLeft: '8px' }" @click="toggleForm">
展开
<a-icon
type=
"down"
/>
</a>
</span>
</a-col>
</a-row>
</a-form>
<a-form
v-show=
"expandForm"
layout=
"inline"
>
<a-row
:gutter=
"
{ md: 8, lg: 24, xl: 48 }">
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"用户"
v-decorator=
"['username']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"性别"
v-decorator=
"['gender']"
>
<a-select
placeholder=
"请选择"
style=
"width: 100%"
>
<a-option
value=
"male"
>
male
</a-option>
<a-option
value=
"female"
>
female
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"姓名"
v-decorator=
"['name']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
</a-row>
<a-row
:gutter=
"
{ md: 8, lg: 24, xl: 48 }">
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"时间"
v-decorator=
"['registered']"
>
<a-range-picker
style=
"width: 100%"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"邮箱"
v-decorator=
"['email']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
<a-col
:md=
"8"
:sm=
"24"
>
<a-form-item
label=
"国籍"
v-decorator=
"['nat']"
>
<a-input
placeholder=
"请输入"
/>
</a-form-item>
</a-col>
</a-row>
<div
style=
"overflow: hidden"
>
<div
:style=
"
{ float: 'right', marginBottom: '24px' }">
<a-button
type=
"primary"
htmlType=
"submit"
>
查询
</a-button>
<a-button
:style=
"
{ marginLeft: '8px' }"> 重置
</a-button>
<a
:style=
"
{ marginLeft: '8px' }" @click="toggleForm">
收起
<a-icon
type=
"up"
/>
</a>
</div>
</div>
</a-form>
</div>
<div
class=
"tableListOperator"
>
<a-button
icon=
"plus"
type=
"primary"
>
新建
</a-button>
<span
v-show=
"selectedRowKeys.length > 0"
>
<a-button>
批量操作
</a-button>
<!--
<a-dropdown
overlay=
{menu}>
<a-button>
更多操作
<icon
type=
"down"
/>
</a-button>
</a-dropdown>
-->
</span>
</div>
<!--
<StandardTable
selectedRows=
{selectedRows}
loading={loading}
data={data}
columns={this.columns}
onSelectRow={this.handleSelectRows}
onChange={this.handleStandardTableChange}
/> -->
<a-table
:columns=
"columns"
:rowKey=
"(record) => record.login.uuid"
:dataSource=
"users.data"
:pagination=
"users.pagination"
:loading=
"loading"
@
change=
"handleTableChange"
<TableLayout
:permissions=
"['system:role:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"100px"
inline
>
<el-form-item
label=
"角色编码"
prop=
"code"
>
<el-input
v-model=
"searchForm.code"
v-trim
placeholder=
"请输入角色编码"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"角色名称"
prop=
"name"
>
<el-input
v-model=
"searchForm.name"
v-trim
placeholder=
"请输入角色名称"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<section>
<el-button
type=
"primary"
icon=
"el-icon-search"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:role:create', 'system:role:delete']"
>
<li
v-permissions=
"['system:role:create']"
><el-button
type=
"primary"
@
click=
"$refs.operaRoleWindow.open('新建角色')"
icon=
"el-icon-plus"
>
新建
</el-button></li>
<li
v-permissions=
"['system:role:delete']"
><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
:default-sort =
"
{prop: 'createTime', order: 'descending'}"
stripe
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>
<!-- :rowSelection="
{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" -->
<template
slot=
"login"
slot-scope=
"login"
>
{{
login
.
username
}}
</
template
>
<
template
slot=
"name"
slot-scope=
"name"
>
{{
name
.
first
}}
{{
name
.
last
}}
</
template
>
<
template
slot=
"registered"
slot-scope=
"registered"
>
{{
registered
.
date
}}
(
{{
registered
.
age
}}
)
</
template
>
<
template
slot=
"expandedRowRender"
slot-scope=
"record"
style=
"margin: 0"
<el-table-column
type=
"selection"
fixed=
"left"
width=
"55"
></el-table-column>
<el-table-column
prop=
"code"
label=
"角色编码"
fixed=
"left"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"name"
label=
"角色名称"
fixed=
"left"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"remark"
label=
"角色备注"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"140px"
sortable=
"custom"
sort-by=
"role.CREATE_TIME"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
min-width=
"140px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:role:update', 'system:role:createRolePermission', 'system:role:createRoleMenu', 'system:role:delete'])"
label=
"操作"
min-width=
"270"
fixed=
"right"
>
<p
:style=
"[sya, syb]"
>
<a-avatar
:src=
"record.picture.large"
shape=
"square"
:size=
"128"
/>
</p>
<p
:style=
"[sya]"
>
Personal
</p>
<a-row>
<a-col
:span=
"6"
>
<a-description-item
title=
"Name"
:content=
"record.name.first + ' ' + record.name.last"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Account"
:content=
"record.login.username"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"City"
:content=
"record.location.city"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Postcode"
:content=
"record.location.postcode"
/>
</a-col>
</a-row>
<a-row>
<a-col
:span=
"6"
>
<a-description-item
title=
"Country"
:content=
"record.nat"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Birthday"
:content=
"record.dob.date + ' (' + record.dob.age + ')'"
/>
</a-col>
<a-col
:span=
"12"
>
<a-description-item
title=
"Timezone"
:content=
"record.location.timezone.description"
/>
</a-col>
</a-row>
<a-row>
<a-col
:span=
"12"
>
</a-col>
<a-col
:span=
"12"
>
</a-col>
</a-row>
<a-divider
/>
<p
:style=
"[sya]"
>
Contacts
</p>
<a-row>
<a-col
:span=
"6"
>
<a-description-item
title=
"Email"
:content=
"record.email"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Cell"
:content=
"record.cell"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Phone"
:content=
"record.phone"
/>
</a-col>
<a-col
:span=
"6"
>
<a-description-item
title=
"Coordinates"
:content=
"
record.location.coordinates.latitude +
' ' +
record.location.coordinates.longitude
"
/>
</a-col>
</a-row>
<a-row>
<a-col
:span=
"12"
>
<a-description-item
title=
"Registered"
:content=
"
record.registered.date + ' (' + record.registered.age + ')'
"
/>
</a-col>
</a-row>
<
template
v-if=
"isAdmin || (row.code !== adminCode && userInfo.roles.findIndex(code => code === row.code) === -1)"
slot-scope=
"{row}"
>
<el-button
type=
"text"
@
click=
"$refs.operaRoleWindow.open('编辑角色', row)"
icon=
"el-icon-edit"
v-permissions=
"['system:role:update']"
>
编辑
</el-button>
<el-button
type=
"text"
@
click=
"$refs.permissionConfigWindow.open(row)"
v-permissions=
"['system:role:createRolePermission']"
>
配置权限
</el-button>
<el-button
type=
"text"
@
click=
"$refs.menuConfigWindow.open(row)"
icon=
"el-icon-menu"
v-permissions=
"['system:role:createRoleMenu']"
>
授权菜单
</el-button>
<el-button
v-if=
"!row.fixed"
type=
"text"
@
click=
"deleteById(row)"
icon=
"el-icon-delete"
v-permissions=
"['system:role:delete']"
>
删除
</el-button>
</
template
>
<
template
slot=
"action"
slot-scope=
"text, record"
>
<a
href=
"javascript:;"
>
查看
</a>
<a-divider
type=
"vertical"
/>
<a
href=
"javascript:;"
>
配置
</a>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
</a-table>
</div>
</a-card>
</a-page-header-wrapper>
<!-- 新建/修改 -->
<OperaRoleWindow
ref=
"operaRoleWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
<!-- 配置权限 -->
<PermissionConfigWindow
ref=
"permissionConfigWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
<!-- 授权菜单 -->
<MenuConfigWindow
ref=
"menuConfigWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
</TableLayout>
</template>
<
script
>
import
{
Avatar
,
Row
,
Col
,
Card
,
List
,
Button
,
Form
,
Icon
,
Table
,
Divider
,
Dropdown
,
Input
,
Select
,
DatePicker
,
}
from
"
ant-design-vue
"
;
import
PageHeaderWrapper
from
"
@/components/PageHeaderWrapper
"
;
import
DescriptionItem
from
"
@/components/DescriptionItem
"
;
const
columns
=
[
{
title
:
"
用户名
"
,
dataIndex
:
"
login
"
,
sorter
:
true
,
width
:
"
12%
"
,
scopedSlots
:
{
customRender
:
"
login
"
},
},
{
title
:
"
姓名
"
,
dataIndex
:
"
name
"
,
sorter
:
true
,
width
:
"
15%
"
,
scopedSlots
:
{
customRender
:
"
name
"
},
},
{
title
:
"
性别
"
,
dataIndex
:
"
gender
"
,
filters
:
[
{
text
:
"
Male
"
,
value
:
"
male
"
},
{
text
:
"
Female
"
,
value
:
"
female
"
},
],
},
{
title
:
"
邮箱
"
,
dataIndex
:
"
email
"
,
},
{
title
:
"
国籍
"
,
dataIndex
:
"
nat
"
,
},
{
title
:
"
Registered
"
,
dataIndex
:
"
registered
"
,
scopedSlots
:
{
customRender
:
"
registered
"
},
},
{
title
:
"
Action
"
,
key
:
"
action
"
,
scopedSlots
:
{
customRender
:
"
action
"
},
},
];
import
{
mapGetters
}
from
"
vuex
"
;
import
Pagination
from
'
@/components/common/Pagination
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
OperaRoleWindow
from
'
@/components/system/role/OperaRoleWindow
'
import
PermissionConfigWindow
from
'
@/components/system/role/PermissionConfigWindow
'
import
MenuConfigWindow
from
'
@/components/system/role/MenuConfigWindow
'
export
default
{
data
:
()
=>
({
expandForm
:
false
,
selectedRowKeys
:
[],
columns
,
sya
:
{
fontSize
:
"
16px
"
,
color
:
"
rgba(0,0,0,0.85)
"
,
lineHeight
:
"
24px
"
,
display
:
"
block
"
,
marginBottom
:
"
16px
"
,
},
syb
:
{
marginBottom
:
"
24px
"
,
},
}),
async
asyncData
({
store
,
route
},
config
=
{
results
:
15
})
{
await
store
.
dispatch
(
"
frontend/openapi/getUsers
"
,
{
...
config
,
path
:
route
.
path
,
});
},
computed
:
{
...
mapGetters
({
loading
:
"
frontend/openapi/loading
"
,
users
:
"
frontend/openapi/getUsers
"
,
}),
},
components
:
{
APageHeaderWrapper
:
PageHeaderWrapper
,
AAvatar
:
Avatar
,
ARow
:
Row
,
ACol
:
Col
,
ACard
:
Card
,
ACardGrid
:
Card
.
Grid
,
ACardMeta
:
Card
.
Meta
,
AList
:
List
,
AButton
:
Button
,
AForm
:
Form
,
AFormItem
:
Form
.
Item
,
AIcon
:
Icon
,
ATable
:
Table
,
ADescriptionItem
:
DescriptionItem
,
ADivider
:
Divider
,
ADropdown
:
Dropdown
,
AInput
:
Input
,
ASelect
:
Select
,
AOption
:
Select
.
Option
,
ARangePicker
:
DatePicker
.
RangePicker
,
},
methods
:
{
toggleForm
()
{
this
.
expandForm
=
!
this
.
expandForm
;
},
onSelectChange
(
selectedRowKeys
)
{
window
.
console
.
log
(
"
selectedRowKeys changed:
"
,
selectedRowKeys
);
this
.
selectedRowKeys
=
selectedRowKeys
;
},
handleTableChange
(
pagination
,
filters
,
sorter
)
{
const
pager
=
{
...
this
.
users
.
pagination
};
pager
.
current
=
pagination
.
current
;
this
.
users
.
pagination
=
pager
;
this
.
$options
.
asyncData
(
{
store
:
this
.
$store
,
route
:
this
.
$route
},
{
results
:
pagination
.
pageSize
,
page
:
pagination
.
current
,
sortField
:
sorter
.
field
,
sortOrder
:
sorter
.
order
,
...
filters
,
name
:
'
SystemRole
'
,
extends
:
BaseTable
,
components
:
{
MenuConfigWindow
,
PermissionConfigWindow
,
OperaRoleWindow
,
TableLayout
,
Pagination
},
data
()
{
return
{
// 搜索
searchForm
:
{
code
:
''
,
name
:
''
,
remark
:
''
}
}
);
},
},
mounted
()
{
this
.
$options
.
asyncData
(
{
store
:
this
.
$store
,
route
:
this
.
$route
},
{
results
:
15
}
);
},
};
created
()
{
this
.
config
({
module
:
'
角色
'
,
api
:
'
/system/role
'
,
sorts
:
[{
property
:
'
role.CREATE_TIME
'
,
direction
:
'
DESC
'
}]
})
this
.
search
()
}
}
</
script
>
<
style
lang=
"less"
>
@import url("styles/tableList.less");
</
style
>
\ No newline at end of file
tools/frontend/src/views/System/components/TableLayout.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"table-layout"
>
<!-- 头部 -->
<div
v-if=
"withBreadcrumb"
class=
"table-header"
>
<el-breadcrumb
separator=
"/"
>
<el-breadcrumb-item
v-for=
"path in paths"
:key=
"path"
>
{{
path
}}
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<Profile
:roles=
"roles"
:permissions=
"permissions"
>
<!-- 搜索表单部分 -->
<div
class=
"table-search-form"
>
<div
class=
"form-wrap"
>
<slot
name=
"search-form"
></slot>
</div>
</div>
<slot
name=
"space"
></slot>
<!-- 列表和分页部分 -->
<div
class=
"table-content"
>
<div
class=
"table-wrap"
>
<slot
name=
"table-wrap"
></slot>
</div>
</div>
<slot></slot>
</Profile>
</div>
</
template
>
<
script
>
import
{
Breadcrumb
,
BreadcrumbItem
}
from
"
element-ui
"
import
'
element-ui/lib/theme-chalk/index.css
'
import
Profile
from
'
./common/Profile
'
export
default
{
name
:
'
TableLayout
'
,
components
:
{
"
el-breadcrumb
"
:
Breadcrumb
,
"
el-breadcrumb-item
"
:
BreadcrumbItem
,
Profile
},
props
:
{
// 角色
roles
:
{
type
:
Array
},
// 权限
permissions
:
{
type
:
Array
},
// 是否展示头部面包屑
withBreadcrumb
:
{
type
:
Boolean
,
default
:
true
}
},
computed
:
{
paths
()
{
return
this
.
$route
.
meta
.
paths
}
}
}
</
script
>
<
style
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
.table-layout
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
.not-allow-wrap
{
padding-top
:
0
;
}
}
// 头部
.table-header
{
overflow
:
hidden
;
padding
:
12px
16px
;
flex-shrink
:
0
;
// 页面路径
.el-breadcrumb
{
.el-breadcrumb__item
{
.el-breadcrumb__inner
{
color
:
#ABB2BE
;
font-size
:
12px
;
}
&
:last-of-type
.el-breadcrumb__inner
{
color
:
#606263
;
font-size
:
14px
;
}
}
}
}
// 搜索
.table-search-form
{
display
:
flex
;
flex-wrap
:
wrap
;
padding
:
0
16px
;
.form-wrap
{
padding
:
16px
16px
0
16px
;
width
:
100%
;
background
:
#fff
;
&
:empty
{
padding
:
0
;
}
}
section
{
display
:
inline-block
;
margin-left
:
16px
;
margin-bottom
:
18px
;
}
}
// 列表和分页
.table-content
{
margin-top
:
10px
;
padding
:
0
16px
;
.table-wrap
{
padding
:
16px
16px
0
16px
;
background
:
#fff
;
// 工具栏
.toolbar
{
border-bottom
:
1px
solid
#eee
;
padding-bottom
:
10px
;
li
{
display
:
inline-block
;
margin-right
:
6px
;
}
}
// 表格
.el-table
{
th
{
.cell
{
color
:
#666
;
}
}
// 复选框列
.el-table-column--selection
{
.cell
{
text-align
:
center
!
important
;
}
}
// 多值字段
.table-column-strings
{
ul
{
li
{
display
:
inline-block
;
background
:
#eee
;
border-radius
:
3px
;
padding
:
0
3px
;
margin-right
:
3px
;
margin-bottom
:
3px
;
}
}
}
// 树视觉调整
[
class
*=
el-table__row--level
]
.el-table__expand-icon
{
position
:
relative
;
left
:
-6px
;
margin-right
:
0
;
}
}
// 分页
.table-pagination
{
padding
:
16px
0
;
text-align
:
left
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/base/BaseOpera.vue
0 → 100644
View file @
0587447b
<
script
>
export
default
{
name
:
'
BaseOpera
'
,
data
()
{
return
{
title
:
''
,
visible
:
false
,
isWorking
:
false
,
// 接口
api
:
null
,
// 配置数据
configData
:
{
'
field.id
'
:
'
id
'
}
}
},
methods
:
{
// 配置
config
(
extParams
=
{})
{
if
(
extParams
==
null
)
{
throw
new
Error
(
'
Parameter can not be null of method
\'
config
\'
.
'
)
}
if
(
extParams
.
api
==
null
)
{
throw
new
Error
(
'
Missing config option
\'
api
\'
.
'
)
}
this
.
api
=
require
(
'
@/api
'
+
extParams
.
api
)
extParams
[
'
field.id
'
]
&&
(
this
.
configData
[
'
field.id
'
]
=
extParams
[
'
field.id
'
])
},
/**
* 打开窗口
* @title 窗口标题
* @target 编辑的对象
*/
open
(
title
,
target
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
[
this
.
configData
[
'
field.id
'
]]
=
null
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
},
// 确认新建/修改
confirm
()
{
if
(
this
.
form
.
id
==
null
||
this
.
form
.
id
===
''
)
{
this
.
__confirmCreate
()
return
}
this
.
__confirmEdit
()
},
// 确认新建
__confirmCreate
()
{
this
.
$refs
.
form
.
validate
((
valid
)
=>
{
if
(
!
valid
)
{
return
}
// 调用新建接口
this
.
isWorking
=
true
this
.
api
.
create
(
this
.
form
)
.
then
(()
=>
{
this
.
visible
=
false
this
.
$tip
.
apiSuccess
(
'
新建成功
'
)
this
.
$emit
(
'
success
'
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
=
false
})
})
},
// 确认修改
__confirmEdit
()
{
this
.
$refs
.
form
.
validate
((
valid
)
=>
{
if
(
!
valid
)
{
return
}
// 调用新建接口
this
.
isWorking
=
true
this
.
api
.
updateById
(
this
.
form
)
.
then
(()
=>
{
this
.
visible
=
false
this
.
$tip
.
apiSuccess
(
'
修改成功
'
)
this
.
$emit
(
'
success
'
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
=
false
})
})
}
}
}
</
script
>
tools/frontend/src/views/System/components/base/BasePage.vue
0 → 100644
View file @
0587447b
<
script
>
import
{
mapState
}
from
'
vuex
'
export
default
{
name
:
'
BasePage
'
,
data
()
{
return
{
// 超级管理员角色code
adminCode
:
'
admin
'
}
},
computed
:
{
...
mapState
([
'
userInfo
'
]),
// 是否为超级管理员
isAdmin
()
{
return
this
.
userInfo
.
roles
.
findIndex
(
code
=>
code
===
this
.
adminCode
)
>
-
1
}
},
methods
:
{
// 是否包含指定角色
containRoles
(
roles
)
{
if
(
roles
==
null
)
{
return
true
}
if
(
this
.
userInfo
==
null
)
{
return
false
}
if
(
this
.
userInfo
.
roles
==
null
||
this
.
userInfo
.
roles
.
length
===
0
)
{
return
false
}
for
(
const
code
of
roles
)
{
if
(
this
.
userInfo
.
roles
.
findIndex
(
r
=>
r
===
code
)
>
-
1
)
{
return
true
}
}
return
false
},
// 是否包含指定权限
containPermissions
(
permissions
)
{
if
(
permissions
==
null
)
{
return
true
}
if
(
this
.
userInfo
==
null
)
{
return
false
}
if
(
this
.
userInfo
.
permissions
==
null
||
this
.
userInfo
.
permissions
.
length
===
0
)
{
return
false
}
for
(
const
code
of
permissions
)
{
if
(
this
.
userInfo
.
permissions
.
findIndex
(
p
=>
p
===
code
)
>
-
1
)
{
return
true
}
}
return
false
}
}
}
</
script
>
tools/frontend/src/views/System/components/base/BaseTable.vue
0 → 100644
View file @
0587447b
<
script
>
import
BasePage
from
'
./BasePage
'
export
default
{
name
:
'
BaseTable
'
,
extends
:
BasePage
,
data
()
{
return
{
// 接口
api
:
null
,
// 模块名称
module
:
'
数据
'
,
// 配置数据
configData
:
{
// id字段
'
field.id
'
:
'
id
'
,
// 主字段
'
field.main
'
:
'
name
'
},
// 是否正在执行
isWorking
:
{
// 搜索中
search
:
false
,
// 删除中
delete
:
false
,
// 导出中
export
:
false
},
// 表格数据
tableData
:
{
// 已选中的数据
selectedRows
:
[],
// 排序的字段
sorts
:
[],
// 当前页数据
list
:
[],
// 分页
pagination
:
{
pageIndex
:
1
,
pageSize
:
10
,
total
:
0
}
}
}
},
methods
:
{
// 配置
config
(
extParams
)
{
if
(
extParams
==
null
)
{
throw
new
Error
(
'
Parameter can not be null of method
\'
config
\'
.
'
)
}
if
(
extParams
.
api
==
null
)
{
throw
new
Error
(
'
Missing config option
\'
api
\'
.
'
)
}
this
.
api
=
require
(
'
@/api
'
+
extParams
.
api
)
extParams
.
module
&&
(
this
.
module
=
extParams
.
module
)
extParams
[
'
field.id
'
]
&&
(
this
.
configData
[
'
field.id
'
]
=
extParams
[
'
field.id
'
])
extParams
[
'
field.main
'
]
&&
(
this
.
configData
[
'
field.main
'
]
=
extParams
[
'
field.main
'
])
this
.
tableData
.
sorts
=
extParams
.
sorts
},
// 搜索
search
()
{
this
.
handlePageChange
(
1
)
},
// 导出Excel
exportExcel
()
{
this
.
__checkApi
()
this
.
$dialog
.
exportConfirm
(
'
确认导出吗?
'
)
.
then
(()
=>
{
this
.
isWorking
.
export
=
true
this
.
api
.
exportExcel
({
page
:
this
.
tableData
.
pagination
.
pageIndex
,
capacity
:
1000000
,
model
:
this
.
searchForm
,
sorts
:
this
.
tableData
.
sorts
})
.
then
(
response
=>
{
this
.
download
(
response
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
export
=
false
})
})
.
catch
(()
=>
{})
},
// 搜索框重置
reset
()
{
this
.
$refs
.
searchForm
.
resetFields
()
this
.
search
()
},
// 每页显示数量变更处理
handleSizeChange
(
pageSize
)
{
this
.
tableData
.
pagination
.
pageSize
=
pageSize
this
.
search
()
},
// 行选中处理
handleSelectionChange
(
selectedRows
)
{
this
.
tableData
.
selectedRows
=
selectedRows
},
// 排序
handleSortChange
(
sortData
)
{
this
.
tableData
.
sorts
=
[]
if
(
sortData
.
order
!=
null
)
{
this
.
tableData
.
sorts
.
push
({
property
:
sortData
.
column
.
sortBy
,
direction
:
sortData
.
order
===
'
descending
'
?
'
DESC
'
:
'
ASC
'
})
}
this
.
handlePageChange
()
},
// 页码变更处理
handlePageChange
(
pageIndex
)
{
this
.
__checkApi
()
this
.
tableData
.
pagination
.
pageIndex
=
pageIndex
||
this
.
tableData
.
pagination
.
pageIndex
this
.
isWorking
.
search
=
true
this
.
api
.
fetchList
({
page
:
this
.
tableData
.
pagination
.
pageIndex
,
capacity
:
this
.
tableData
.
pagination
.
pageSize
,
model
:
this
.
searchForm
,
sorts
:
this
.
tableData
.
sorts
})
.
then
(
data
=>
{
this
.
tableData
.
list
=
data
.
records
this
.
tableData
.
pagination
.
total
=
data
.
total
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
search
=
false
})
},
// 删除
deleteById
(
row
,
childConfirm
=
true
)
{
this
.
__checkApi
()
let
message
=
`确认删除
${
this
.
module
}
【
${
row
[
this
.
configData
[
'
field.main
'
]]}
】吗?`
if
(
childConfirm
&&
row
.
children
!=
null
&&
row
.
children
.
length
>
0
)
{
message
=
`确认删除
${
this
.
module
}
【
${
row
[
this
.
configData
[
'
field.main
'
]]}
】及其子
${
this
.
module
}
吗?`
}
this
.
$dialog
.
deleteConfirm
(
message
)
.
then
(()
=>
{
this
.
isWorking
.
delete
=
true
this
.
api
.
deleteById
(
row
[
this
.
configData
[
'
field.id
'
]])
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
删除成功
'
)
this
.
__afterDelete
()
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
delete
=
false
})
})
.
catch
(()
=>
{})
},
/**
* 批量删除
* @treeMode 是否添加子节点删除确认
*/
deleteByIdInBatch
(
childConfirm
=
true
)
{
this
.
__checkApi
()
if
(
this
.
tableData
.
selectedRows
.
length
===
0
)
{
this
.
$tip
.
warning
(
'
请至少选择一条数据
'
)
return
}
let
message
=
`确认删除已选中的
${
this
.
tableData
.
selectedRows
.
length
}
条
${
this
.
module
}
记录吗?`
if
(
childConfirm
)
{
const
containChildrenRows
=
[]
for
(
const
row
of
this
.
tableData
.
selectedRows
)
{
if
(
row
.
children
!=
null
&&
row
.
children
.
length
>
0
)
{
containChildrenRows
.
push
(
row
[
this
.
configData
[
'
field.main
'
]])
}
}
if
(
containChildrenRows
.
length
>
0
)
{
message
=
`本次将删除
${
this
.
module
}
【
${
containChildrenRows
.
join
(
'
、
'
)}
】及其子
${
this
.
module
}
记录,确认删除吗?`
}
}
this
.
$dialog
.
deleteConfirm
(
message
)
.
then
(()
=>
{
this
.
isWorking
.
delete
=
true
this
.
api
.
deleteByIdInBatch
(
this
.
tableData
.
selectedRows
.
map
(
row
=>
row
.
id
).
join
(
'
,
'
))
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
删除成功
'
)
this
.
__afterDelete
(
this
.
tableData
.
selectedRows
.
length
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
delete
=
false
})
})
.
catch
(()
=>
{})
},
// 删除处理
__afterDelete
(
deleteCount
=
1
)
{
// 删除当前页最后一条记录时查询上一页数据
if
(
this
.
tableData
.
list
.
length
-
deleteCount
===
0
)
{
this
.
handlePageChange
(
this
.
tableData
.
pagination
.
pageIndex
-
1
===
0
?
1
:
this
.
tableData
.
pagination
.
pageIndex
-
1
)
}
else
{
this
.
handlePageChange
(
this
.
tableData
.
pagination
.
pageIndex
)
}
},
// 检查接口是否配置
__checkApi
()
{
if
(
this
.
api
==
null
)
{
throw
new
Error
(
'
The page is not initialized, you can use method
\'
this.config
\'
to initialize this page.
'
)
}
}
}
}
</
script
>
tools/frontend/src/views/System/components/common/ColumnDetail.vue
0 → 100644
View file @
0587447b
<
template
>
<span
v-if=
"content.length
<
=
limit
"
>
{{
content
}}
</span>
<el-popover
v-else
v-model=
"visible"
popper-class=
"eva-column-detail-popover"
trigger=
"click"
>
<div
class=
"eva-column-detail"
>
<pre
class=
"eva-column-detail__main"
>
{{
formattedContent
}}
</pre>
<div
class=
"eva-column-detail__action"
>
<el-button
size=
"mini"
@
click=
"cancel"
>
关闭
</el-button>
<el-button
size=
"mini"
type=
"primary"
v-clipboard:copy=
"formattedContent"
v-clipboard:success=
"copySuccess"
v-clipboard:error=
"copyFailed"
@
click=
"confirm"
>
{{
confirmButtonText
}}
</el-button>
</div>
</div>
<el-button
slot=
"reference"
:type=
"buttonType"
>
查看
</el-button>
</el-popover>
</
template
>
<
script
>
export
default
{
name
:
'
ColumnDetail
'
,
props
:
{
// 按钮类型
buttonType
:
{
type
:
String
},
// 内容
content
:
{
type
:
String
,
default
:
''
},
// 限制,大于限制时展示查看按钮
limit
:
{
type
:
Number
,
default
:
12
},
// 自动识别数据类型并格式化
analyse
:
{
type
:
Boolean
,
default
:
true
},
// 是否允许复制
allowCopy
:
{
type
:
Boolean
,
default
:
true
}
},
data
()
{
return
{
visible
:
false
}
},
computed
:
{
// 确认按钮文案
confirmButtonText
()
{
return
this
.
allowCopy
?
'
复制
'
:
'
确定
'
},
// 格式化后的内容
formattedContent
()
{
let
content
=
this
.
content
if
(
this
.
analyse
)
{
try
{
content
=
JSON
.
stringify
(
JSON
.
parse
(
this
.
content
),
null
,
2
)
}
catch
(
e
)
{
}
}
return
content
}
},
methods
:
{
// 点击确认
confirm
()
{
this
.
visible
=
false
this
.
$emit
(
'
confirm
'
)
},
// 点击取消
cancel
()
{
this
.
visible
=
false
this
.
$emit
(
'
cancel
'
)
},
// 复制成功
copySuccess
()
{
this
.
$tip
.
success
(
'
复制成功
'
)
},
// 复制失败
copyFailed
()
{
this
.
$tip
.
error
(
'
复制失败
'
)
}
}
}
</
script
>
<
style
lang=
"scss"
>
.eva-column-detail-popover
{
max-width
:
80%
;
}
</
style
>
<
style
scoped
lang=
"scss"
>
.eva-column-detail
{
.eva-column-detail__main
{
max-height
:
500px
;
overflow
:
auto
;
}
.eva-column-detail__action
{
text-align
:
right
;
}
}
</
style
>
tools/frontend/src/views/System/components/common/DepartmentSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<TreeSelect
:placeholder=
"placeholder"
:value=
"value"
:data=
"data"
:clearable=
"clearable"
:append-to-body=
"appendToBody"
:inline=
"inline"
:multiple=
"multiple"
:flat=
"multiple"
@
input=
"$emit('input', $event)"
/>
</
template
>
<
script
>
import
TreeSelect
from
'
./TreeSelect
'
import
{
fetchTree
}
from
'
@/api/system/department
'
export
default
{
name
:
'
DepartmentSelect
'
,
components
:
{
TreeSelect
},
props
:
{
value
:
{},
inline
:
{
default
:
true
},
multiple
:
{
default
:
false
},
placeholder
:
{
default
:
'
请选择部门
'
},
// 是否可清空
clearable
:
{
default
:
false
},
appendToBody
:
{
default
:
false
},
// 需被排除的部门ID
excludeId
:
{}
},
data
()
{
return
{
data
:
[]
}
},
watch
:
{
excludeId
()
{
this
.
fetchData
()
}
},
methods
:
{
// 获取所有部门
fetchData
()
{
fetchTree
()
.
then
(
records
=>
{
this
.
data
=
[]
this
.
__fillData
(
this
.
data
,
records
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
},
// 填充部门树
__fillData
(
list
,
pool
)
{
for
(
const
dept
of
pool
)
{
if
(
dept
.
id
===
this
.
excludeId
)
{
continue
}
const
deptNode
=
{
id
:
dept
.
id
,
label
:
dept
.
name
}
list
.
push
(
deptNode
)
if
(
dept
.
children
!=
null
&&
dept
.
children
.
length
>
0
)
{
deptNode
.
children
=
[]
this
.
__fillData
(
deptNode
.
children
,
dept
.
children
)
if
(
deptNode
.
children
.
length
===
0
)
{
deptNode
.
children
=
undefined
}
}
}
}
},
created
()
{
this
.
fetchData
()
}
}
</
script
>
tools/frontend/src/views/System/components/common/GlobalWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<el-drawer
class=
"global-window"
title=
"title"
:visible=
"visible"
:with-header=
"true"
:size=
"width"
:close-on-press-escape=
"false"
:wrapper-closable=
"false"
:append-to-body=
"true"
@
close=
"close"
>
<div
slot=
"title"
class=
"window__header"
>
<span
class=
"header__btn-back"
@
click=
"close"
><i
class=
"el-icon-arrow-left"
></i></span>
{{
title
}}
</div>
<div
class=
"window__body"
>
<slot></slot>
</div>
<div
v-if=
"withFooter"
class=
"window__footer"
>
<slot
name=
"footer"
>
<el-button
@
click=
"confirm"
:loading=
"confirmWorking"
type=
"primary"
>
确定
</el-button>
<el-button
@
click=
"close"
>
取消
</el-button>
</slot>
</div>
</el-drawer>
</
template
>
<
script
>
import
{
Drawer
,
Button
}
from
"
element-ui
"
import
'
element-ui/lib/theme-chalk/index.css
'
export
default
{
name
:
'
GlobalWindow
'
,
props
:
{
width
:
{
type
:
String
,
default
:
'
36%
'
},
// 是否包含底部操作
withFooter
:
{
type
:
Boolean
,
default
:
true
},
// 确认按钮loading状态
confirmWorking
:
{
type
:
Boolean
,
default
:
false
},
// 标题
title
:
{
type
:
String
,
default
:
''
},
// 是否展示
visible
:
{
type
:
Boolean
,
required
:
true
}
},
components
:
{
"
el-drawer
"
:
Drawer
,
"
el-button
"
:
Button
},
methods
:
{
confirm
()
{
this
.
$emit
(
'
confirm
'
)
},
close
()
{
this
.
$emit
(
'
update:visible
'
,
false
)
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/styles/variables.scss"
;
// 输入框高度
$input-height
:
32px
;
.global-window
{
// 头部标题
/
deep
/
.el-drawer__header
{
padding
:
0
10px
0
0
;
line-height
:
40px
;
border-bottom
:
1px
solid
#eee
;
// 返回按钮
.header__btn-back
{
display
:
inline-block
;
width
:
30px
;
background
:
$primary-color
;
color
:
#fff
;
text-align
:
center
;
margin-right
:
12px
;
border-right
:
1px
solid
#eee
;
}
.el-drawer__close-btn
:focus
{
outline
:
none
;
}
}
// 主体
/
deep
/
.el-drawer__body
{
display
:
flex
;
flex-direction
:
column
;
position
:
absolute
;
top
:
40px
;
bottom
:
0
;
width
:
100%
;
overflow
:
hidden
;
// 内容
.window__body
{
height
:
100%
;
overflow-y
:
auto
;
padding
:
12px
16px
;
// 标签
.el-form-item__label
{
float
:
none
;
}
// 元素宽度为100%
.el-form-item__content
>
*
{
width
:
100%
;
}
}
// 尾部
.window__footer
{
user-select
:
none
;
border-top
:
1px
solid
#eee
;
height
:
60px
;
line-height
:
60px
;
text-align
:
center
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/common/Header.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"main-header"
>
<div
class=
"header"
>
<h2>
<i
class=
"el-icon-s-unfold"
v-if=
"menuData.collapse"
@
click=
"switchCollapseMenu(null)"
></i>
<i
class=
"el-icon-s-fold"
v-else
@
click=
"switchCollapseMenu(null)"
></i>
{{
title
}}
</h2>
<div
class=
"user"
>
<el-dropdown
trigger=
"click"
>
<span
class=
"el-dropdown-link"
>
<img
v-if=
"userInfo != null"
:src=
"userInfo.avatar == null ? '@/assets/images/avatar/man.png' : userInfo.avatar"
alt=
""
>
{{
userInfo
|
displayName
}}
<i
class=
"el-icon-arrow-down el-icon--right"
></i>
</span>
<el-dropdown-menu
slot=
"dropdown"
>
<el-dropdown-item
@
click.native=
"changePwd"
>
修改密码
</el-dropdown-item>
<el-dropdown-item
@
click.native=
"logout"
>
退出登录
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
<!-- 修改密码 -->
<GlobalWindow
title=
"修改密码"
:visible.sync=
"visible.changePwd"
@
confirm=
"confirmChangePwd"
@
close=
"visible.changePwd = false"
>
<el-form
:model=
"changePwdData.form"
ref=
"changePwdDataForm"
:rules=
"changePwdData.rules"
>
<el-form-item
label=
"原始密码"
prop=
"oldPwd"
required
>
<el-input
v-model=
"changePwdData.form.oldPwd"
type=
"password"
placeholder=
"请输入原始密码"
maxlength=
"30"
show-password
></el-input>
</el-form-item>
<el-form-item
label=
"新密码"
prop=
"newPwd"
required
>
<el-input
v-model=
"changePwdData.form.newPwd"
type=
"password"
placeholder=
"请输入新密码"
maxlength=
"30"
show-password
></el-input>
</el-form-item>
<el-form-item
label=
"确认新密码"
prop=
"confirmPwd"
required
>
<el-input
v-model=
"changePwdData.form.confirmPwd"
type=
"password"
placeholder=
"请再次输入新密码"
maxlength=
"30"
show-password
></el-input>
</el-form-item>
</el-form>
</GlobalWindow>
</div>
</
template
>
<
script
>
import
{
mapState
,
mapMutations
}
from
'
vuex
'
import
GlobalWindow
from
'
./GlobalWindow
'
import
{
logout
,
updatePwd
}
from
'
@/api/system/common
'
export
default
{
name
:
'
Header
'
,
components
:
{
GlobalWindow
},
data
()
{
return
{
visible
:
{
// 修改密码
changePwd
:
false
},
isWorking
:
{
// 修改密码
changePwd
:
false
},
username
:
'
bob
'
,
// 用户名
// 修改密码弹框
changePwdData
:
{
form
:
{
oldPwd
:
''
,
newPwd
:
''
,
confirmPwd
:
''
},
rules
:
{
oldPwd
:
[
{
required
:
true
,
message
:
'
请输入原始密码
'
}
],
newPwd
:
[
{
required
:
true
,
message
:
'
请输入新密码
'
}
],
confirmPwd
:
[
{
required
:
true
,
message
:
'
请再次输入新密码
'
}
]
}
}
}
},
computed
:
{
...
mapState
([
'
menuData
'
,
'
userInfo
'
]),
title
()
{
return
this
.
$route
.
meta
.
title
}
},
filters
:
{
// 展示名称
displayName
(
userInfo
)
{
if
(
userInfo
==
null
)
{
return
''
}
if
(
userInfo
.
realname
!=
null
&&
userInfo
.
realname
.
trim
().
length
>
0
)
{
return
userInfo
.
realname
}
return
userInfo
.
username
}
},
methods
:
{
...
mapMutations
([
'
setUserInfo
'
,
'
switchCollapseMenu
'
]),
// 修改密码
changePwd
()
{
this
.
visible
.
changePwd
=
true
this
.
$nextTick
(()
=>
{
this
.
$refs
.
changePwdDataForm
.
resetFields
()
})
},
// 确定修改密码
confirmChangePwd
()
{
if
(
this
.
isWorking
.
changePwd
)
{
return
}
this
.
$refs
.
changePwdDataForm
.
validate
((
valid
)
=>
{
if
(
!
valid
)
{
return
}
// 验证两次密码输入是否一致
if
(
this
.
changePwdData
.
form
.
newPwd
!==
this
.
changePwdData
.
form
.
confirmPwd
)
{
this
.
$tip
.
warning
(
'
两次密码输入不一致
'
)
return
}
// 执行修改
this
.
isWorking
.
changePwd
=
true
updatePwd
({
oldPwd
:
this
.
changePwdData
.
form
.
oldPwd
,
newPwd
:
this
.
changePwdData
.
form
.
newPwd
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
修改成功
'
)
this
.
visible
.
changePwd
=
false
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
changePwd
=
false
})
})
},
// 退出登录
logout
()
{
logout
()
.
then
(()
=>
{
this
.
$router
.
push
({
name
:
'
login
'
})
this
.
setUserInfo
(
null
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
.header
{
overflow
:
hidden
;
padding
:
0
25px
;
background
:
#fff
;
height
:
100%
;
display
:
flex
;
h2
{
width
:
50%
;
flex-shrink
:
0
;
line-height
:
$header-height
;
font-size
:
19px
;
font-weight
:
600
;
color
:
#606263
;
display
:
inline
;
&
>
i
{
font-size
:
20px
;
margin-right
:
12px
;
}
}
.user
{
width
:
50%
;
flex-shrink
:
0
;
text-align
:
right
;
.el-dropdown
{
top
:
2px
;
}
img
{
width
:
32px
;
position
:
relative
;
top
:
10px
;
margin-right
:
10px
;
}
}
}
// 下拉菜单框
.el-dropdown-menu
{
width
:
140px
;
.el-dropdown-menu__item
:hover
{
background
:
#E3EDFB
;
color
:
$primary-color
;
}
}
</
style
>
tools/frontend/src/views/System/components/common/Light.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"light"
:class=
"
{normal: !warn
&&
!danger, warn: !danger
&&
warn, danger, mini: mini}">
<em><i></i></em>
</div>
</
template
>
<
script
>
export
default
{
name
:
'
Light
'
,
props
:
{
warn
:
{
type
:
Boolean
,
default
:
false
},
danger
:
{
type
:
Boolean
,
default
:
false
},
mini
:
{
type
:
Boolean
,
default
:
false
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
$cycle-size01
:
16px
;
$cycle-size02
:
6px
;
$normal-color
:
#00CC99
;
$warn-color
:
#FFCC33
;
$danger-color
:
#FF3300
;
@mixin
light-status
(
$cycle-bg
)
{
em
{
background
:
$cycle-bg
;
i
{
background
:
$cycle-bg
-
30
;
}
}
}
.light
{
display
:
inline-block
;
border-radius
:
50%
;
em
{
width
:
$cycle-size01
;
height
:
$cycle-size01
;
border-radius
:
50%
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
i
{
display
:
block
;
width
:
$cycle-size02
;
height
:
$cycle-size02
;
border-radius
:
50%
;
}
}
&
.mini
{
em
{
width
:
12px
;
height
:
12px
;
}
}
// 正常
&
.normal
{
@include
light-status
(
$normal-color
);
animation
:
shine-normal
infinite
1s
;
}
// 警告
&
.warn
{
@include
light-status
(
$warn-color
);
animation
:
shine-warn
infinite
.8s
;
}
// 危险
&
.danger
{
@include
light-status
(
$danger-color
);
animation
:
shine-danger
infinite
.5s
;
}
}
@keyframes
shine-normal
{
0
%
{
box-shadow
:
0
0
5px
$normal-color
+
10
;
}
25
%
{
box-shadow
:
0
0
10px
$normal-color
+
10
;
}
50
%
{
box-shadow
:
0
0
15px
$normal-color
+
10
;
}
100
%
{
box-shadow
:
0
0
20px
$normal-color
+
10
;
}
}
@keyframes
shine-warn
{
0
%
{
box-shadow
:
0
0
5px
$warn-color
-
50
;
}
25
%
{
box-shadow
:
0
0
10px
$warn-color
-
50
;
}
50
%
{
box-shadow
:
0
0
15px
$warn-color
-
50
;
}
100
%
{
box-shadow
:
0
0
20px
$warn-color
-
50
;
}
}
@keyframes
shine-danger
{
0
%
{
box-shadow
:
0
0
5px
$danger-color
+
10
;
}
25
%
{
box-shadow
:
0
0
10px
$danger-color
+
10
;
}
50
%
{
box-shadow
:
0
0
15px
$danger-color
+
10
;
}
100
%
{
box-shadow
:
0
0
20px
$danger-color
+
10
;
}
}
</
style
>
tools/frontend/src/views/System/components/common/LocationSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<el-cascader
v-if=
"visible"
:props=
"props"
:placeholder=
"placeholder"
v-model=
"value"
:clearable=
"clearable"
@
change=
"$emit('change')"
@
input=
"handleInput"
></el-cascader>
</
template
>
<
script
>
import
{
fetchByParentId
}
from
'
@/api/system/location
'
export
default
{
name
:
'
LocationSelect
'
,
props
:
{
placeholder
:
{
default
:
'
请选择地区
'
},
level
:
{
default
:
3
},
clearable
:
{
default
:
false
},
// 省
provinceId
:
{},
// 市
cityId
:
{},
// 区
areaId
:
{}
},
data
()
{
const
vm
=
this
return
{
// 是否展示,用于重新初始化cascader
visible
:
true
,
// 已选值
value
:
[],
// 组件配置
props
:
{
lazy
:
true
,
lazyLoad
(
node
,
resolve
)
{
const
{
level
}
=
node
fetchByParentId
(
level
===
0
?
-
1
:
node
.
value
)
.
cache
()
.
then
(
data
=>
{
resolve
(
data
.
map
(
item
=>
{
return
{
label
:
item
.
name
,
value
:
item
.
id
,
leaf
:
level
>=
vm
.
level
-
1
}
}))
})
.
catch
(
e
=>
{
vm
.
$tip
.
apiFailed
(
e
)
})
}
}
}
},
watch
:
{
provinceId
(
newValue
)
{
this
.
value
[
0
]
=
newValue
if
(
this
.
level
===
1
)
{
if
(
newValue
==
null
)
{
this
.
value
=
[]
}
this
.
__rebuild
()
}
},
cityId
(
newValue
)
{
if
(
this
.
level
>=
2
)
{
this
.
value
[
1
]
=
newValue
}
if
(
this
.
level
===
2
)
{
if
(
newValue
==
null
)
{
this
.
value
=
[]
}
this
.
__rebuild
()
}
},
areaId
(
newValue
)
{
if
(
this
.
level
>=
3
)
{
this
.
value
[
2
]
=
newValue
}
if
(
this
.
level
===
3
)
{
if
(
newValue
==
null
)
{
this
.
value
=
[]
}
this
.
__rebuild
()
}
}
},
methods
:
{
handleInput
(
values
)
{
this
.
$emit
(
'
update:province-id
'
,
values
[
0
])
this
.
$emit
(
'
update:city-id
'
,
values
[
1
])
this
.
$emit
(
'
update:area-id
'
,
values
[
2
])
},
// 重新初始化cascader
__rebuild
()
{
this
.
visible
=
false
this
.
$nextTick
(()
=>
{
this
.
visible
=
true
})
}
}
}
</
script
>
tools/frontend/src/views/System/components/common/Menu.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"menu"
:class=
"
{collapse: menuData.collapse}">
<div
class=
"logo"
>
<div><img
src=
"/logo.png"
></div>
<h1
:class=
"
{hidden: menuData.collapse}">eva
</h1>
</div>
<scrollbar>
<el-menu
ref=
"menu"
:default-active=
"activeIndex"
text-color=
"#fff"
active-text-color=
"#fff"
:collapse=
"menuData.collapse"
:default-openeds=
"defaultOpeneds"
:collapse-transition=
"false"
@
select=
"handleSelect"
>
<MenuItems
v-for=
"menu in menuData.list"
:key=
"menu.index"
:menu=
"menu"
:is-root-menu=
"true"
/>
</el-menu>
</scrollbar>
</div>
</
template
>
<
script
>
import
{
mapState
}
from
'
vuex
'
import
MenuItems
from
'
./MenuItems
'
import
Scrollbar
from
'
./Scrollbar
'
export
default
{
name
:
'
Menu
'
,
components
:
{
Scrollbar
,
MenuItems
},
computed
:
{
...
mapState
([
'
menuData
'
]),
// 选中的菜单index
activeIndex
()
{
let
path
=
this
.
$route
.
path
if
(
path
.
endsWith
(
'
/
'
))
{
path
=
path
.
substring
(
0
,
path
.
length
-
1
)
}
const
menuConfig
=
this
.
__getMenuConfig
(
path
,
'
url
'
,
this
.
menuData
.
list
)
if
(
menuConfig
==
null
)
{
return
null
}
return
menuConfig
.
index
},
// 默认展开的菜单index
defaultOpeneds
()
{
return
this
.
menuData
.
list
.
map
(
menu
=>
menu
.
index
)
}
},
methods
:
{
// 处理菜单选中
handleSelect
(
menuIndex
)
{
const
menuConfig
=
this
.
__getMenuConfig
(
menuIndex
,
'
index
'
,
this
.
menuData
.
list
)
// 找不到页面
try
{
require
(
'
@/views
'
+
menuConfig
.
url
)
}
catch
(
e
)
{
this
.
$tip
.
error
(
'
未找到页面文件@/views
'
+
menuConfig
.
url
+
'
.vue,请检查菜单路径是否正确
'
)
}
// 点击当前菜单不做处理
if
(
menuConfig
.
url
===
this
.
$route
.
path
)
{
return
}
if
(
menuConfig
.
url
==
null
||
menuConfig
.
url
.
trim
().
length
===
0
)
{
return
}
this
.
$router
.
push
(
menuConfig
.
url
)
},
// 获取菜单配置
__getMenuConfig
(
value
,
key
,
menus
)
{
for
(
const
menu
of
menus
)
{
if
(
menu
[
key
]
===
value
)
{
return
menu
}
if
(
menu
.
children
!=
null
&&
menu
.
children
.
length
>
0
)
{
const
menuConfig
=
this
.
__getMenuConfig
(
value
,
key
,
menu
.
children
)
if
(
menuConfig
!=
null
)
{
return
menuConfig
}
}
}
return
null
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
@import
"@/assets/style/variables.scss"
;
.menu
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
// LOGO
.logo
{
height
:
60px
;
flex-shrink
:
0
;
line-height
:
60px
;
overflow
:
hidden
;
display
:
flex
;
background
:
$primary-color
-
20
;
padding
:
0
16px
;
&
>
div
{
width
:
32px
;
flex-shrink
:
0
;
margin-right
:
12px
;
img
{
width
:
100%
;
flex-shrink
:
0
;
vertical-align
:
middle
;
position
:
relative
;
top
:
-2px
;
}
}
h1
{
font-size
:
16px
;
font-weight
:
500
;
transition
:
opacity
ease
.3s
;
overflow
:
hidden
;
&
.hidden
{
opacity
:
0
;
}
}
}
}
</
style
>
<
style
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
// 菜单样式
.el-menu
{
border-right
:
0
!
important
;
user-select
:
none
;
background
:
$primary-color
!
important
;
.el-menu-item
{
background
:
$primary-color
;
// 选中状态
&
.is-active
{
background
:
$primary-color
-
40
!
important
;
}
// 悬浮
&
:hover
{
background-color
:
$primary-color
-
12
;
}
&
:focus
{
background
:
$primary-color
;
}
}
// 子菜单
.el-submenu
{
.el-submenu__title
{
background-color
:
$primary-color
;
}
&
.is-active
{
.el-submenu__title
{
background-color
:
$primary-color
-
20
;
}
.el-menu
.el-menu-item
{
background-color
:
$primary-color
-
20
;
// 悬浮
&
:hover
{
background-color
:
$primary-color
-
30
;
}
}
}
// 菜单上下箭头
.el-submenu__title
i
{
color
:
#f7f7f7
;
}
}
// 菜单图标
i
:not
(
.el-submenu__icon-arrow
)
{
color
:
#f7f7f7
!
important
;
position
:
relative
;
top
:
-1px
;
// 自定义图标
&
[
class
^=
"eva-icon-"
]
{
width
:
24px
;
margin-right
:
5px
;
background-size
:
15px
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/common/MenuItems.vue
0 → 100644
View file @
0587447b
<
template
>
<el-menu-item
v-if=
"menu.children == null || menu.children.length == 0"
:key=
"menu.index"
:index=
"menu.index"
>
<i
:class=
"menu.icon"
></i>
<span
slot=
"title"
>
{{
menu
.
label
}}
</span>
</el-menu-item>
<el-submenu
v-else
:index=
"menu.index"
>
<template
slot=
"title"
>
<i
:class=
"menu.icon"
></i>
<span
slot=
"title"
>
{{
menu
.
label
}}
</span>
</
template
>
<MenuItems
v-for=
"child in menu.children"
:menu=
"child"
:key=
"child.index"
/>
</el-submenu>
</template>
<
script
>
export
default
{
name
:
'
MenuItems
'
,
props
:
{
menu
:
{
type
:
Object
,
required
:
true
}
}
}
</
script
>
tools/frontend/src/views/System/components/common/MenuSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<TreeSelect
:placeholder=
"placeholder"
:value=
"value"
:data=
"data"
:append-to-body=
"appendToBody"
:clearable=
"clearable"
:inline=
"inline"
@
input=
"$emit('input', $event)"
/>
</
template
>
<
script
>
import
TreeSelect
from
'
./TreeSelect
'
import
{
fetchTree
}
from
'
@/api/system/menu
'
export
default
{
name
:
'
MenuSelect
'
,
components
:
{
TreeSelect
},
props
:
{
value
:
{},
inline
:
{
default
:
true
},
placeholder
:
{
default
:
'
请选择菜单
'
},
// 是否可清空
clearable
:
{
default
:
false
},
appendToBody
:
{
default
:
false
},
// 需被排除的部门ID
excludeId
:
{}
},
data
()
{
return
{
data
:
[]
}
},
watch
:
{
excludeId
()
{
this
.
fetchData
()
}
},
methods
:
{
// 获取所有菜单
fetchData
()
{
fetchTree
()
.
then
(
records
=>
{
this
.
data
=
[]
this
.
__fillData
(
this
.
data
,
records
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
},
// 填充菜单树
__fillData
(
list
,
pool
)
{
for
(
const
menu
of
pool
)
{
if
(
menu
.
id
===
this
.
excludeId
)
{
continue
}
const
menuNode
=
{
id
:
menu
.
id
,
label
:
menu
.
name
}
list
.
push
(
menuNode
)
if
(
menu
.
children
!=
null
&&
menu
.
children
.
length
>
0
)
{
menuNode
.
children
=
[]
this
.
__fillData
(
menuNode
.
children
,
menu
.
children
)
if
(
menuNode
.
children
.
length
===
0
)
{
menuNode
.
children
=
undefined
}
}
}
}
},
created
()
{
this
.
fetchData
()
}
}
</
script
>
tools/frontend/src/views/System/components/common/NotAllow.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"not-allow"
>
<slot>
<div
class=
"content"
>
<img
src=
"../../assets/images/not-allow.png"
>
<h2>
无权访问
</h2>
<p>
如您需要访问该页面,请联系系统管理员
</p>
</div>
</slot>
</div>
</
template
>
<
script
>
export
default
{
name
:
'
NotAllow
'
}
</
script
>
<
style
scoped
lang=
"scss"
>
.not-allow
{
height
:
100%
;
background
:
#fff
;
box-sizing
:
border-box
;
padding-top
:
160px
;
.content
{
height
:
200px
;
text-align
:
center
;
h2
{
font-size
:
18px
;
font-weight
:
normal
;
margin-top
:
8px
;
}
p
{
font-size
:
13px
;
color
:
#999
;
margin
:
6px
0
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/common/Pagination.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"table-pagination"
>
<el-pagination
:current-page=
"pagination.pageIndex"
:page-sizes=
"[10, 20, 30, 40]"
:page-size=
"pagination.pageSize"
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"pagination.total"
@
size-change=
"sizeChange"
@
current-change=
"currentChange"
background
>
</el-pagination>
</div>
</
template
>
<
script
>
export
default
{
name
:
'
Pagination
'
,
props
:
{
pagination
:
{
type
:
Object
,
default
:
function
()
{
return
{}
}
}
},
data
()
{
return
{
}
},
methods
:
{
sizeChange
(
value
)
{
this
.
$emit
(
'
size-change
'
,
value
)
},
currentChange
(
value
)
{
this
.
$emit
(
'
current-change
'
,
value
)
}
}
}
</
script
>
tools/frontend/src/views/System/components/common/PositionSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<TreeSelect
:placeholder=
"placeholder"
:value=
"value"
:data=
"data"
:clearable=
"clearable"
:append-to-body=
"appendToBody"
:inline=
"inline"
:multiple=
"multiple"
:flat=
"multiple"
@
input=
"$emit('input', $event)"
/>
</
template
>
<
script
>
import
TreeSelect
from
'
./TreeSelect
'
import
{
fetchTree
}
from
'
@/api/system/position
'
export
default
{
name
:
'
PositionSelect
'
,
components
:
{
TreeSelect
},
props
:
{
value
:
{},
inline
:
{
default
:
true
},
multiple
:
{
default
:
false
},
placeholder
:
{
default
:
'
请选择岗位
'
},
// 是否可清空
clearable
:
{
default
:
false
},
appendToBody
:
{
default
:
false
},
// 需被排除的部门ID
excludeId
:
{}
},
data
()
{
return
{
data
:
[]
}
},
watch
:
{
excludeId
()
{
this
.
fetchData
()
}
},
methods
:
{
// 获取所有岗位
fetchData
()
{
fetchTree
()
.
then
(
records
=>
{
this
.
data
=
[]
this
.
__fillData
(
this
.
data
,
records
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
},
// 填充岗位树
__fillData
(
list
,
pool
)
{
for
(
const
dept
of
pool
)
{
if
(
dept
.
id
===
this
.
excludeId
)
{
continue
}
const
deptNode
=
{
id
:
dept
.
id
,
label
:
dept
.
name
}
list
.
push
(
deptNode
)
if
(
dept
.
children
!=
null
&&
dept
.
children
.
length
>
0
)
{
deptNode
.
children
=
[]
this
.
__fillData
(
deptNode
.
children
,
dept
.
children
)
if
(
deptNode
.
children
.
length
===
0
)
{
deptNode
.
children
=
undefined
}
}
}
}
},
created
()
{
this
.
fetchData
()
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.inline
{
width
:
178px
;
}
.vue-treeselect
{
line-height
:
30px
;
/
deep
/
.vue-treeselect__control
{
height
:
32px
;
.vue-treeselect__single-value
{
line-height
:
30px
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/common/Profile.vue
0 → 100644
View file @
0587447b
<
template
>
<div
v-if=
"containRoles(roles) && containPermissions(permissions)"
>
<slot></slot>
</div>
<div
v-else
class=
"not-allow-wrap"
>
<slot
name=
"not-allow"
><NotAllow/></slot>
</div>
</
template
>
<
script
>
import
BasePage
from
'
../base/BasePage
'
import
NotAllow
from
'
./NotAllow
'
export
default
{
name
:
'
Profile
'
,
components
:
{
NotAllow
},
extends
:
BasePage
,
props
:
{
permissions
:
{
type
:
Array
},
roles
:
{
type
:
Array
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.not-allow-wrap
{
height
:
100%
;
padding
:
10px
16px
;
box-sizing
:
border-box
;
}
</
style
>
tools/frontend/src/views/System/components/common/Scrollbar.vue
0 → 100644
View file @
0587447b
<
template
>
<vue-scroll
:ops=
"options"
>
<slot></slot>
</vue-scroll>
</
template
>
<
script
>
import
VueScroll
from
'
vuescroll
'
export
default
{
name
:
'
Scrollbar
'
,
components
:
{
VueScroll
},
data
()
{
return
{
options
:
{
bar
:
{
background
:
'
rgba(20,20,20,.3)
'
}
}
}
}
}
</
script
>
tools/frontend/src/views/System/components/common/SearchFormCollapse.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"search-form-collapse"
:class=
"
{'collapse__hidden': !showMore}">
<slot></slot>
<el-button
v-if=
"!showMore"
class=
"collapse__switch"
@
click=
"showMore = true"
>
更多查询...
</el-button>
<el-button
v-else
class=
"collapse__switch"
@
click=
"showMore = false"
>
收起
</el-button>
</div>
</
template
>
<
script
>
export
default
{
name
:
'
SearchFormCollapse
'
,
data
()
{
return
{
showMore
:
false
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.search-form-collapse
{
position
:
relative
;
padding-right
:
75px
;
height
:
auto
;
.collapse__switch
{
position
:
absolute
;
top
:
0
;
right
:
0
;
}
&
.collapse__hidden
{
height
:
50px
;
overflow
:
hidden
;
padding-right
:
250px
;
/
deep
/
section
{
position
:
absolute
;
top
:
0
;
right
:
100px
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/common/TreeSelect.vue
0 → 100644
View file @
0587447b
<!-- 组件详情请参阅官方文档:https://www.vue-treeselect.cn/ -->
<
template
>
<vue-tree-select
:class=
"
{inline}"
:placeholder="placeholder"
:value="value"
:options="data"
:clearable="clearable"
:flat="flat"
:append-to-body="appendToBody"
:multiple="multiple"
no-children-text="无记录"
no-options-text="无记录"
no-results-text="未匹配到数据"
@input="$emit('input', $event)"
/>
</
template
>
<
script
>
import
VueTreeSelect
from
'
@riophae/vue-treeselect
'
import
'
@riophae/vue-treeselect/dist/vue-treeselect.css
'
export
default
{
name
:
'
TreeSelect
'
,
props
:
{
inline
:
{
default
:
false
},
multiple
:
{
default
:
false
},
flat
:
{
default
:
false
},
value
:
{},
placeholder
:
{
default
:
'
请选择
'
},
// 是否可清空
clearable
:
{
default
:
false
},
data
:
{
type
:
Array
,
required
:
true
},
appendToBody
:
{
default
:
false
}
},
components
:
{
VueTreeSelect
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.inline
{
width
:
178px
;
}
.vue-treeselect
{
line-height
:
30px
;
/
deep
/
.vue-treeselect__control
{
height
:
32px
;
.vue-treeselect__single-value
{
line-height
:
30px
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/common/Value.vue
0 → 100644
View file @
0587447b
<
template
>
<div
class=
"value"
>
<i
class=
"el-icon-loading"
v-if=
"data == null"
></i>
<slot
v-else
>
{{
getValue
()
}}{{
suffix
}}
</slot>
</div>
</
template
>
<
script
>
export
default
{
name
:
'
Value
'
,
props
:
{
data
:
{
type
:
Object
},
prop
:
{
type
:
String
},
suffix
:
{
type
:
String
},
handler
:
{
type
:
Function
}
},
methods
:
{
getValue
()
{
if
(
this
.
data
==
null
)
{
return
''
}
if
(
this
.
prop
==
null
)
{
return
this
.
data
}
const
props
=
this
.
prop
.
split
(
'
.
'
)
let
i
=
0
let
value
=
this
.
data
while
(
i
<
props
.
length
)
{
value
=
value
[
props
[
i
]]
i
++
}
if
(
this
.
handler
==
null
)
{
return
value
}
return
this
.
handler
(
value
)
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.value
{
word-break
:
break-all
;
.el-icon-loading
{
font-size
:
16px
;
color
:
#999
;
position
:
relative
;
top
:
1px
;
}
}
</
style
>
tools/frontend/src/views/System/components/system/datapermission/CustomSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<component
:is=
"component"
:value=
"values"
:inline=
"false"
@
input=
"handleInput"
multiple
/>
</
template
>
<
script
>
export
default
{
name
:
'
CustomSelect
'
,
props
:
{
value
:
{},
businessCode
:
{
type
:
String
,
required
:
true
}
},
computed
:
{
// vuetreeselect值类型匹配(解决编辑时无法删除已有值的BUG)
values
()
{
if
(
this
.
businessCode
===
'
DEPARTMENT
'
||
this
.
businessCode
===
'
POSITION
'
)
{
const
values
=
[]
for
(
const
id
of
this
.
value
)
{
values
.
push
(
parseInt
(
id
))
}
return
values
}
return
this
.
value
},
component
()
{
// 部门选择器
if
(
this
.
businessCode
===
'
DEPARTMENT
'
)
{
return
()
=>
import
(
'
@/components/common/DepartmentSelect
'
)
}
// 岗位选择器
if
(
this
.
businessCode
===
'
POSITION
'
)
{
return
()
=>
import
(
'
@/components/common/PositionSelect
'
)
}
return
null
}
},
methods
:
{
handleInput
(
value
)
{
this
.
$emit
(
'
input
'
,
value
)
this
.
$emit
(
'
change
'
,
value
)
}
}
}
</
script
>
<
style
scoped
>
</
style
>
tools/frontend/src/views/System/components/system/datapermission/DataPermModuleSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<el-select
class=
"data-perm-module-select"
:class=
"
{select__block: !inline}"
:value="value"
:placeholder="placeholder"
:clearable="clearable"
:disabled="disabled"
@change="$emit('change', $event)"
@input="$emit('input', $event)"
>
<el-option
v-for=
"module in modules"
:key=
"module.businessCode"
:value=
"module.businessCode"
:label=
"module.moduleName"
/>
</el-select>
</
template
>
<
script
>
import
{
fetchModules
}
from
'
@/api/system/dataPermission
'
export
default
{
name
:
'
DataPermModuleSelect
'
,
props
:
{
value
:
{},
placeholder
:
{
default
:
'
请选择权限模块
'
},
inline
:
{
default
:
true
},
disabled
:
{},
clearable
:
{
default
:
false
}
},
data
()
{
return
{
modules
:
[]
}
},
created
()
{
fetchModules
()
.
cache
()
.
then
(
data
=>
{
this
.
modules
=
data
})
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.select__block
{
display
:
block
;
}
</
style
>
tools/frontend/src/views/System/components/system/datapermission/DataPermTypeSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<el-select
class=
"data-perm-type-select"
:class=
"
{select__block: !inline}"
:value="value"
:placeholder="placeholder"
:clearable="clearable"
:disabled="disabled"
@change="$emit('change', $event)"
@input="$emit('input', $event)"
>
<el-option
v-for=
"type in filterTypes"
:key=
"type.code"
:value=
"type.code"
:label=
"type.remark"
/>
</el-select>
</
template
>
<
script
>
import
{
fetchTypes
}
from
'
@/api/system/dataPermission
'
export
default
{
name
:
'
DataPermTypeSelect
'
,
props
:
{
value
:
{},
// 模块名称
module
:
{},
placeholder
:
{
default
:
'
请选择权限类型
'
},
inline
:
{
default
:
true
},
disabled
:
{},
clearable
:
{
default
:
false
}
},
data
()
{
return
{
types
:
[]
}
},
computed
:
{
filterTypes
()
{
if
(
this
.
module
==
null
||
this
.
module
===
''
)
{
return
[]
}
const
types
=
[]
for
(
const
type
of
this
.
types
)
{
if
(
type
.
modules
.
length
===
0
||
type
.
modules
.
indexOf
(
this
.
module
)
!==
-
1
)
{
types
.
push
(
type
)
}
}
return
types
}
},
created
()
{
fetchTypes
()
.
cache
()
.
then
(
data
=>
{
this
.
types
=
data
})
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.select__block
{
display
:
block
;
}
</
style
>
tools/frontend/src/views/System/components/system/datapermission/OperaDataPermissionWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"业务模块"
prop=
"businessCode"
required
>
<DataPermModuleSelect
v-model=
"form.businessCode"
:disabled=
"form.id != null"
:inline=
"false"
@
change=
"handleBusinessChange"
/>
</el-form-item>
<el-form-item
label=
"角色"
prop=
"roleId"
required
>
<RoleSelect
v-model=
"form.roleId"
:disabled=
"form.id != null"
:inline=
"false"
/>
</el-form-item>
<el-form-item
label=
"权限类型"
prop=
"type"
required
>
<DataPermTypeSelect
v-model=
"form.type"
:module=
"form.businessCode"
:inline=
"false"
@
change=
"handleTypeChange"
/>
</el-form-item>
<el-form-item
v-show=
"showCustomData"
label=
"自定义数据"
prop=
"customData"
>
<CustomSelect
v-if=
"visible"
v-model=
"customData"
:business-code=
"form.businessCode"
@
change=
"handleCustomDataChange"
/>
</el-form-item>
<el-form-item
label=
"备注"
prop=
"remark"
>
<el-input
type=
"textarea"
v-model=
"form.remark"
placeholder=
"请输入备注"
v-trim
:rows=
"3"
maxlength=
"500"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
RoleSelect
from
'
@/components/system/role/RoleSelect
'
import
DataPermModuleSelect
from
'
./DataPermModuleSelect
'
import
DataPermTypeSelect
from
'
./DataPermTypeSelect
'
import
CustomSelect
from
'
./CustomSelect
'
export
default
{
name
:
'
OperaDataPermissionWindow
'
,
extends
:
BaseOpera
,
components
:
{
CustomSelect
,
RoleSelect
,
DataPermTypeSelect
,
DataPermModuleSelect
,
GlobalWindow
},
data
()
{
return
{
// 自定义数据
customData
:
[],
// 展示自定义数据标识
showCustomData
:
false
,
// 表单数据
form
:
{
id
:
null
,
businessCode
:
''
,
roleId
:
''
,
type
:
''
,
remark
:
''
,
customData
:
''
},
// 验证规则
rules
:
{
businessCode
:
[
{
required
:
true
,
message
:
'
请选择业务模块
'
}
],
roleId
:
[
{
required
:
true
,
message
:
'
请选择角色
'
}
],
type
:
[
{
required
:
true
,
message
:
'
请选择权限类型
'
}
]
}
}
},
methods
:
{
/**
* @title 窗口标题
* @target 编辑的对象
*/
open
(
title
,
target
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
$nextTick
(()
=>
{
this
.
customData
=
[]
this
.
showCustomData
=
false
this
.
$refs
.
form
.
resetFields
()
this
.
form
[
this
.
configData
[
'
field.id
'
]]
=
null
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
this
.
customData
=
this
.
form
.
customData
==
null
||
this
.
form
.
customData
===
''
?
[]
:
this
.
form
.
customData
.
split
(
'
,
'
)
this
.
handleTypeChange
()
})
},
// 业务模块切换
handleBusinessChange
()
{
this
.
form
.
customData
=
''
this
.
customData
=
[]
this
.
handleTypeChange
()
},
// 权限类型切换
handleTypeChange
()
{
if
((
this
.
form
.
type
===
11
||
this
.
form
.
type
===
21
)
&&
this
.
form
.
businessCode
!=
null
&&
this
.
form
.
businessCode
!==
''
)
{
this
.
showCustomData
=
true
}
else
{
this
.
showCustomData
=
false
}
},
// 自定义数据变化
handleCustomDataChange
(
values
)
{
this
.
form
.
customData
=
values
.
join
(
'
,
'
)
}
},
created
()
{
this
.
config
({
api
:
'
/system/dataPermission
'
,
'
field.id
'
:
'
id
'
})
}
}
</
script
>
tools/frontend/src/views/System/components/system/department/DepartmentUserWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
class=
"position-user-window"
width=
"80%"
:title=
"departmentName + '人员列表'"
:visible.sync=
"visible"
:with-footer=
"false"
>
<TableLayout
:with-breadcrumb=
"false"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"80px"
inline
>
<el-form-item
label=
"用户名"
prop=
"username"
>
<el-input
v-model=
"searchForm.username"
placeholder=
"请输入用户名"
v-trim
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"姓名"
prop=
"realname"
>
<el-input
v-model=
"searchForm.realname"
placeholder=
"请输入姓名"
v-trim
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"手机号码"
prop=
"mobile"
>
<el-input
v-model=
"searchForm.mobile"
placeholder=
"请输入手机号码"
v-trim
@
keypress.enter.native=
"search"
/>
</el-form-item>
<section>
<el-button
type=
"primary"
icon=
"el-icon-search"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<div
slot=
"space"
class=
"toolbar"
>
<el-switch
v-model=
"onlyCurrentDept"
@
change=
"search"
:disabled=
"isWorking.search"
/>
<label>
仅查看当前部门人员
</label>
</div>
<template
v-slot:table-wrap
>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
stripe
@
selection-change=
"handleSelectionChange"
>
<el-table-column
prop=
"avatar"
label=
"头像"
width=
"80px"
class-name=
"table-column-avatar"
fixed=
"left"
>
<template
slot-scope=
"
{row}">
<img
:src=
"row.avatar == null ? '/static/avatar/man.png' : row.avatar"
>
</
template
>
</el-table-column>
<el-table-column
prop=
"realname"
label=
"姓名"
min-width=
"100px"
fixed=
"left"
></el-table-column>
<el-table-column
prop=
"username"
label=
"用户名"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"empNo"
label=
"工号"
min-width=
"80px"
></el-table-column>
<el-table-column
prop=
"department"
label=
"部门"
min-width=
"120px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
department
==
null
?
''
:
row
.
department
.
name
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"position"
label=
"岗位"
min-width=
"120px"
class-name=
"table-column-strings"
>
<
template
slot-scope=
"{row}"
>
<ul>
<li
v-for=
"position in row.positions"
:key=
"position.id"
>
{{
position
.
name
}}
</li>
</ul>
</
template
>
</el-table-column>
<el-table-column
prop=
"sex"
label=
"性别"
min-width=
"80px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
sex
|
sex
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"mobile"
label=
"手机号码"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"email"
label=
"邮箱"
min-width=
"180px"
></el-table-column>
<el-table-column
prop=
"birthday"
label=
"生日"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"birthday"
label=
"角色"
min-width=
"160px"
class-name=
"table-column-role"
>
<
template
slot-scope=
"{row}"
>
<ul>
<li
v-for=
"role in row.roles"
:key=
"role.id"
>
{{
role
.
name
}}
</li>
</ul>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
</TableLayout>
</GlobalWindow>
</template>
<
script
>
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
Pagination
from
'
@/components/common/Pagination
'
import
{
fetchUserList
}
from
'
@/api/system/department
'
export
default
{
name
:
'
DepartmentUserWindow
'
,
extends
:
BaseTable
,
components
:
{
Pagination
,
GlobalWindow
,
TableLayout
},
data
()
{
return
{
departmentId
:
null
,
departmentName
:
''
,
visible
:
false
,
// 仅查看当前部门
onlyCurrentDept
:
true
,
// 搜索表单
searchForm
:
{
departmentId
:
null
,
username
:
''
,
realname
:
''
,
mobile
:
''
}
}
},
methods
:
{
// 打开查看人员窗口
open
(
departmentId
,
departmentName
)
{
this
.
departmentId
=
departmentId
this
.
departmentName
=
departmentName
this
.
searchForm
.
departmentId
=
departmentId
this
.
visible
=
true
this
.
search
()
},
// 处理分页
handlePageChange
(
pageIndex
)
{
// 仅查看当前部门处理
this
.
searchForm
.
strictDeptId
=
null
this
.
searchForm
.
rootDeptId
=
this
.
searchForm
.
departmentId
if
(
this
.
onlyCurrentDept
)
{
this
.
searchForm
.
strictDeptId
=
this
.
searchForm
.
departmentId
this
.
searchForm
.
rootDeptId
=
null
}
this
.
tableData
.
pagination
.
pageIndex
=
pageIndex
this
.
isWorking
.
search
=
true
fetchUserList
({
page
:
pageIndex
,
capacity
:
this
.
tableData
.
pagination
.
pageSize
,
model
:
this
.
searchForm
})
.
then
(
data
=>
{
this
.
tableData
.
list
=
data
.
records
this
.
tableData
.
pagination
.
total
=
data
.
total
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
search
=
false
})
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.position-user-window
{
/
deep
/
.table-search-form
{
padding
:
0
;
}
/
deep
/
.window__body
{
background
:
#f7f7f7
;
.table-content
{
padding
:
0
;
.table-wrap
{
padding
:
0
;
}
}
}
.toolbar
{
margin-top
:
10px
;
padding
:
6px
12px
;
background
:
#fff
;
font-size
:
13px
;
label
{
margin-left
:
8px
;
vertical-align
:
middle
;
color
:
#999
;
}
}
// 列表头像处理
.table-column-avatar
{
img
{
width
:
48px
;
}
}
// 列表角色处理
.table-column-role
{
ul
{
li
{
display
:
inline-block
;
background
:
#eee
;
border-radius
:
3px
;
padding
:
0
3px
;
margin-right
:
3px
;
}
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/department/OperaDepartmentWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"上级部门"
prop=
"parentId"
>
<DepartmentSelect
v-if=
"visible"
ref=
"departmentSelect"
placeholder=
"请选择上级部门"
v-model=
"form.parentId"
:exclude-id=
"excludeDeptId"
:inline=
"false"
/>
</el-form-item>
<el-form-item
label=
"部门编码"
prop=
"code"
required
>
<el-input
v-model=
"form.code"
placeholder=
"请输入部门编码"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"部门名称"
prop=
"name"
required
>
<el-input
v-model=
"form.name"
placeholder=
"请输入部门名称"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"联系电话"
prop=
"phone"
>
<el-input
v-model=
"form.phone"
placeholder=
"请输入联系电话"
v-trim
maxlength=
"11"
/>
</el-form-item>
<el-form-item
label=
"部门邮箱"
prop=
"email"
>
<el-input
v-model=
"form.email"
placeholder=
"请输入部门邮箱"
v-trim
maxlength=
"200"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
DepartmentSelect
from
'
@/components/common/DepartmentSelect
'
import
{
checkMobile
,
checkEmail
}
from
'
@/utils/form
'
export
default
{
name
:
'
OperaDepartmentWindow
'
,
extends
:
BaseOpera
,
components
:
{
DepartmentSelect
,
GlobalWindow
},
data
()
{
return
{
// 需排除选择的部门ID
excludeDeptId
:
null
,
// 表单数据
form
:
{
id
:
null
,
parentId
:
null
,
code
:
''
,
name
:
''
,
phone
:
''
,
email
:
''
},
// 验证规则
rules
:
{
code
:
[
{
required
:
true
,
message
:
'
请输入部门编码
'
}
],
name
:
[
{
required
:
true
,
message
:
'
请输入部门名称
'
}
],
phone
:
[
{
validator
:
checkMobile
}
],
email
:
[
{
validator
:
checkEmail
}
]
}
}
},
methods
:
{
/**
* @title 窗口标题
* @target 编辑的部门对象
* @parent 新建时的上级部门对象
* @departmentList 部门列表
*/
open
(
title
,
target
,
parent
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
excludeDeptId
=
null
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
.
id
=
null
this
.
form
.
parentId
=
parent
==
null
?
null
:
parent
.
id
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
this
.
excludeDeptId
=
target
.
id
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/department
'
})
}
}
</
script
>
tools/frontend/src/views/System/components/system/dict/DictDataManagerWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"dictName + '数据管理'"
width=
"78%"
:visible.sync=
"visible"
:with-footer=
"false"
>
<TableLayout
:with-breadcrumb=
"false"
>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaDictDataWindow.open('新建字典数据', searchForm.dictId)"
icon=
"el-icon-plus"
>
新建
</el-button></li>
<li><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
stripe
@
selection-change=
"handleSelectionChange"
>
<el-table-column
type=
"selection"
width=
"55"
></el-table-column>
<el-table-column
prop=
"label"
label=
"数据标签"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"code"
label=
"数据值"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"disabled"
label=
"状态"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
disabled
|
disabledText
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
min-width=
"100px"
></el-table-column>
<el-table-column
label=
"操作"
min-width=
"120"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
type=
"text"
@
click=
"$refs.operaDictDataWindow.open('编辑字典数据', dictId, row)"
icon=
"el-icon-edit"
>
编辑
</el-button>
<el-button
type=
"text"
@
click=
"deleteById(row)"
icon=
"el-icon-delete"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
<!-- 新建/修改 -->
<OperaDictDataWindow
ref=
"operaDictDataWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
</TableLayout>
</GlobalWindow>
</template>
<
script
>
import
BaseTable
from
'
@/components/base/BaseTable
'
import
Pagination
from
'
@/components/common/Pagination
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
OperaDictDataWindow
from
'
./OperaDictDataWindow
'
export
default
{
name
:
'
DictDataManagerWindow
'
,
extends
:
BaseTable
,
components
:
{
OperaDictDataWindow
,
TableLayout
,
GlobalWindow
,
Pagination
},
data
()
{
return
{
visible
:
false
,
searchForm
:
{
// 字典ID
dictId
:
null
},
// 字典名称
dictName
:
''
}
},
methods
:
{
// 打开数据管理
open
(
dictId
,
dictName
)
{
this
.
searchForm
.
dictId
=
dictId
this
.
dictName
=
dictName
this
.
visible
=
true
this
.
search
()
}
},
created
()
{
this
.
config
({
api
:
'
/system/dictData
'
,
'
field.main
'
:
'
label
'
})
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
/
deep
/
.window__body
{
.table-content
{
padding
:
0
;
.table-wrap
{
padding-top
:
0
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/dict/OperaDictDataWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking.create"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"数据标签"
prop=
"label"
required
>
<el-input
v-model=
"form.label"
placeholder=
"请输入数据标签"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"数据值"
prop=
"code"
required
>
<el-input
v-model=
"form.code"
placeholder=
"请输入数据值"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"状态"
prop=
"disabled"
required
class=
"form-item-status"
>
<el-switch
v-model=
"form.disabled"
:active-value=
"false"
:inactive-value=
"true"
/>
<span
class=
"status-text"
>
{{
form
.
disabled
|
disabledText
}}
</span>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
export
default
{
name
:
'
OperaDictDataWindow
'
,
extends
:
BaseOpera
,
components
:
{
GlobalWindow
},
data
()
{
return
{
// 表单数据
form
:
{
id
:
null
,
dictId
:
null
,
code
:
''
,
label
:
''
,
disabled
:
false
},
// 验证规则
rules
:
{
label
:
[
{
required
:
true
,
message
:
'
请输入数据标签
'
}
],
code
:
[
{
required
:
true
,
message
:
'
请输入数据值
'
}
]
}
}
},
methods
:
{
/**
* @title 窗口标题
* @dict 所属字典ID
* @target 编辑的字典数据对象
*/
open
(
title
,
dictId
,
target
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
.
id
=
null
this
.
form
.
dictId
=
dictId
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/dictData
'
})
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.form-item-status
{
.status-text
{
color
:
#999
;
margin-left
:
6px
;
font-size
:
13px
;
vertical-align
:
middle
;
}
}
</
style
>
tools/frontend/src/views/System/components/system/dict/OperaDictWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"字典编码"
prop=
"code"
required
>
<el-input
v-model=
"form.code"
placeholder=
"请输入字典编码"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"字典名称"
prop=
"name"
required
>
<el-input
v-model=
"form.name"
placeholder=
"请输入字典名称"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"备注"
prop=
"remark"
>
<el-input
v-model=
"form.remark"
type=
"textarea"
placeholder=
"请输入备注"
:rows=
"3"
v-trim
maxlength=
"500"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
export
default
{
name
:
'
OperaDictWindow
'
,
extends
:
BaseOpera
,
components
:
{
GlobalWindow
},
data
()
{
return
{
// 表单数据
form
:
{
id
:
null
,
code
:
''
,
name
:
''
,
remark
:
''
},
// 验证规则
rules
:
{
code
:
[
{
required
:
true
,
message
:
'
请输入字典编码
'
}
],
name
:
[
{
required
:
true
,
message
:
'
请输入字典名称
'
}
]
}
}
},
created
()
{
this
.
config
({
api
:
'
/system/dict
'
})
}
}
</
script
>
tools/frontend/src/views/System/components/system/location/OperaLocationWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"地区名称"
prop=
"name"
required
>
<el-input
v-model=
"form.name"
maxlength=
"50"
placeholder=
"请输入地区名称"
v-trim
/>
</el-form-item>
<el-form-item
label=
"地区简称"
prop=
"shortName"
required
>
<el-input
v-model=
"form.shortName"
maxlength=
"50"
placeholder=
"请输入地区简称"
v-trim
/>
</el-form-item>
<el-form-item
label=
"地区全称"
prop=
"fullName"
required
>
<el-input
v-model=
"form.fullName"
maxlength=
"100"
placeholder=
"请输入地区全称"
v-trim
/>
</el-form-item>
<el-form-item
label=
"地区拼音"
prop=
"pinyin"
required
>
<el-input
v-model=
"form.pinyin"
maxlength=
"100"
placeholder=
"请输入地区名称拼音"
v-trim
/>
</el-form-item>
<el-form-item
label=
"区号"
prop=
"areaCode"
>
<el-input
v-model=
"form.areaCode"
maxlength=
"20"
placeholder=
"请输入地区区号"
v-trim
/>
</el-form-item>
<el-form-item
label=
"邮编"
prop=
"postalCode"
>
<el-input
v-model=
"form.postalCode"
maxlength=
"20"
placeholder=
"请输入地区邮编"
v-trim
/>
</el-form-item>
<el-form-item
label=
"首字母"
prop=
"firstLetter"
required
>
<el-input
v-model=
"form.firstLetter"
maxlength=
"1"
placeholder=
"请输入地区名称首字母"
v-trim
/>
</el-form-item>
<el-form-item
label=
"经度"
prop=
"lng"
>
<el-input
v-model=
"form.lng"
maxlength=
"50"
placeholder=
"请输入地区经度"
v-trim
/>
</el-form-item>
<el-form-item
label=
"纬度"
prop=
"lat"
>
<el-input
v-model=
"form.lat"
maxlength=
"50"
placeholder=
"请输入地区纬度"
v-trim
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
export
default
{
name
:
'
OperaLocationWindow
'
,
extends
:
BaseOpera
,
components
:
{
GlobalWindow
},
data
()
{
return
{
// 表单数据
form
:
{
id
:
null
,
parentId
:
''
,
shortName
:
''
,
name
:
''
,
fullName
:
''
,
level
:
''
,
pinyin
:
''
,
areaCode
:
''
,
postalCode
:
''
,
firstLetter
:
''
,
lng
:
''
,
lat
:
''
},
// 验证规则
rules
:
{
name
:
[
{
required
:
true
,
message
:
'
请输入地区名称
'
}
],
shortName
:
[
{
required
:
true
,
message
:
'
请输入地区简称
'
}
],
fullName
:
[
{
required
:
true
,
message
:
'
请输入地区全称
'
}
],
pinyin
:
[
{
required
:
true
,
message
:
'
请输入地区名称拼音
'
}
],
firstLetter
:
[
{
required
:
true
,
message
:
'
请输入地区名称首字母
'
}
]
}
}
},
methods
:
{
/**
* @title 窗口标题
* @parent 父区域ID
* @target 编辑的区域对象
*/
open
(
title
,
target
,
parent
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
.
id
=
null
this
.
form
.
parentId
=
parent
==
null
?
null
:
parent
.
id
this
.
form
.
level
=
parent
==
null
?
1
:
parent
.
level
+
1
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/location
'
,
'
field.id
'
:
'
id
'
})
}
}
</
script
>
tools/frontend/src/views/System/components/system/menu/OperaMenuWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
class=
"handle-table-dialog"
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<p
class=
"tip"
v-if=
"form.parent != null && form.id == null"
>
为
<em>
{{
parentName
}}
</em>
新建子菜单
</p>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"上级菜单"
prop=
"parentId"
>
<MenuSelect
v-if=
"visible"
v-model=
"form.parentId"
placeholder=
"请选择上级菜单"
:exclude-id=
"excludeMenuId"
clearable
:inline=
"false"
/>
</el-form-item>
<el-form-item
label=
"菜单名称"
prop=
"name"
required
>
<el-input
v-model=
"form.name"
placeholder=
"请输入菜单名称"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"访问路径"
prop=
"path"
>
<el-input
v-model=
"form.path"
placeholder=
"请输入访问路径"
v-trim
maxlength=
"200"
/>
</el-form-item>
<el-form-item
label=
"图标"
prop=
"icon"
class=
"form-item-icon"
>
<el-radio-group
v-model=
"form.icon"
>
<el-radio
:label=
"icon"
v-for=
"icon in icons"
:key=
"icon"
>
<i
:class=
"
{[icon]: true}">
</i>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"备注"
prop=
"remark"
>
<el-input
type=
"textarea"
v-model=
"form.remark"
placeholder=
"请输入菜单备注"
v-trim
:rows=
"3"
maxlength=
"500"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
{
Form
,
FormItem
,
Radio
,
RadioGroup
,
Input
}
from
"
element-ui
"
import
'
element-ui/lib/theme-chalk/index.css
'
import
BaseOpera
from
'
@/src/views/System/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/src/views/System/components/common/GlobalWindow
'
import
MenuSelect
from
'
@/src/views/System/components/common/MenuSelect
'
import
icons
from
'
@/utils/icons
'
export
default
{
name
:
'
OperaMenuWindow
'
,
extends
:
BaseOpera
,
components
:
{
"
el-form
"
:
Form
,
"
el-form-item
"
:
FormItem
,
"
el-radio
"
:
Radio
,
"
el-radio-group
"
:
RadioGroup
,
"
el-input
"
:
Input
,
MenuSelect
,
GlobalWindow
},
data
()
{
return
{
icons
,
// 上级菜单名称
parentName
:
''
,
// 需排除选择的菜单ID
excludeMenuId
:
null
,
// 表单数据
form
:
{
id
:
null
,
parentId
:
null
,
name
:
''
,
path
:
''
,
icon
:
''
,
remark
:
''
},
// 验证规则
rules
:
{
name
:
[
{
required
:
true
,
message
:
'
请输入菜单名称
'
}
]
}
}
},
methods
:
{
/**
* @title: 窗口标题
* @target: 编辑的菜单对象
* @parent: 新建时的上级菜单
*/
open
(
title
,
target
,
parent
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建,menu为空时表示新建菜单
if
(
target
==
null
)
{
this
.
excludeMenuId
=
null
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
.
id
=
null
this
.
form
.
parentId
=
parent
==
null
?
null
:
parent
.
id
this
.
parentName
=
parent
==
null
?
null
:
parent
.
name
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
this
.
excludeMenuId
=
target
.
id
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/menu
'
})
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/styles/variables"
;
$icon-background-color
:
$primary-color
;
.global-window
{
.tip
{
margin-bottom
:
12px
;
em
{
font-style
:
normal
;
color
:
$primary-color
;
font-weight
:
bold
;
}
}
// 图标
/
deep
/
.form-item-icon
{
.el-form-item__content
{
height
:
193px
;
overflow-y
:
auto
;
}
.el-radio-group
{
background
:
$icon-background-color
;
padding
:
4px
;
.el-radio
{
margin-right
:
0
;
color
:
#fff
;
padding
:
6px
;
&
.is-checked
{
background
:
$icon-background-color
-
30
;
border-radius
:
10px
;
}
.el-radio__input.is-checked
+
.el-radio__label
{
color
:
#fff
;
}
}
.el-radio__input
{
display
:
none
;
}
.el-radio__label
{
padding-left
:
0
;
// element-ui图标
i
{
font-size
:
30px
;
}
// 自定义图标
[
class
^=
"eva-icon-"
],
[
class
*=
" eva-icon-"
]
{
width
:
30px
;
height
:
30px
;
background-size
:
25px
;
vertical-align
:
bottom
;
}
}
.el-radio--small
{
height
:
auto
;
padding
:
8px
;
margin-left
:
0
;
}
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/permission/OperaPermissionWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"权限编码"
prop=
"code"
required
>
<el-input
v-model=
"form.code"
placeholder=
"请输入权限编码"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"权限名称"
prop=
"name"
required
>
<el-input
v-model=
"form.name"
placeholder=
"请输入权限名称"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"权限备注"
prop=
"remark"
>
<el-input
v-model=
"form.remark"
placeholder=
"请输入权限备注"
type=
"textarea"
:rows=
"3"
v-trim
maxlength=
"500"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
export
default
{
name
:
'
OperaPermissionWindow
'
,
extends
:
BaseOpera
,
components
:
{
GlobalWindow
},
data
()
{
return
{
// 原权限码
originPermissionCode
:
''
,
// 表单数据
form
:
{
id
:
null
,
code
:
''
,
name
:
''
,
remark
:
''
},
// 验证规则
rules
:
{
code
:
[
{
required
:
true
,
message
:
'
请输入权限编码
'
}
],
name
:
[
{
required
:
true
,
message
:
'
请输入权限名称
'
}
]
}
}
},
methods
:
{
open
(
title
,
target
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
[
this
.
configData
[
'
field.id
'
]]
=
null
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
this
.
originPermissionCode
=
target
.
code
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
},
confirm
()
{
if
(
this
.
form
.
id
==
null
||
this
.
form
.
id
===
''
)
{
this
.
__confirmCreate
()
return
}
if
(
this
.
originPermissionCode
===
this
.
form
.
code
)
{
this
.
__confirmEdit
()
return
}
// 修改了权限编码
this
.
$dialog
.
confirm
(
'
检测到您修改了权限编码,权限编码修改后前后端均可能需要调整代码,确认修改吗?
'
,
'
提示
'
,
{
confirmButtonText
:
'
确认修改
'
,
type
:
'
warning
'
})
.
then
(()
=>
{
this
.
__confirmEdit
()
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/permission
'
})
}
}
</
script
>
tools/frontend/src/views/System/components/system/position/OperaPositionWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"上级岗位"
prop=
"parentId"
>
<PositionSelect
v-if=
"visible"
v-model=
"form.parentId"
placeholder=
"请选择上级岗位"
:exclude-id=
"excludePositionId"
clearable
:inline=
"false"
/>
</el-form-item>
<el-form-item
label=
"岗位编码"
prop=
"code"
required
>
<el-input
v-model=
"form.code"
placeholder=
"请输入岗位编码"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"岗位名称"
prop=
"name"
required
>
<el-input
v-model=
"form.name"
placeholder=
"请输入岗位名称"
v-trim
maxlength=
"50"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
PositionSelect
from
'
@/components/common/PositionSelect
'
export
default
{
name
:
'
OperaPositionWindow
'
,
extends
:
BaseOpera
,
components
:
{
PositionSelect
,
GlobalWindow
},
data
()
{
return
{
// 需排除选择的岗位ID
excludePositionId
:
null
,
// 表单数据
form
:
{
id
:
null
,
parentId
:
null
,
code
:
''
,
name
:
''
},
// 验证规则
rules
:
{
code
:
[
{
required
:
true
,
message
:
'
请输入岗位编码
'
}
],
name
:
[
{
required
:
true
,
message
:
'
请输入岗位名称
'
}
]
}
}
},
methods
:
{
/**
* @title 窗口标题
* @target 编辑的岗位对象
* @parent 新建时的上级岗位对象
* @positionList 岗位列表
*/
open
(
title
,
target
,
parent
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
excludePositionId
=
null
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
.
id
=
null
this
.
form
.
parentId
=
parent
==
null
?
null
:
parent
.
id
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
this
.
excludePositionId
=
target
.
id
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/position
'
})
}
}
</
script
>
tools/frontend/src/views/System/components/system/position/PositionUserWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
class=
"position-user-window"
width=
"80%"
:title=
"positionName + '人员列表'"
:visible.sync=
"visible"
:with-footer=
"false"
>
<TableLayout
:with-breadcrumb=
"false"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"80px"
inline
>
<el-form-item
label=
"用户名"
prop=
"username"
>
<el-input
v-model=
"searchForm.username"
v-trim
placeholder=
"请输入用户名"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"姓名"
prop=
"realname"
>
<el-input
v-model=
"searchForm.realname"
v-trim
placeholder=
"请输入姓名"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"手机号码"
prop=
"mobile"
>
<el-input
v-model=
"searchForm.mobile"
v-trim
placeholder=
"请输入手机号码"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<section>
<el-button
type=
"primary"
icon=
"el-icon-search"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<template
v-slot:table-wrap
>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
stripe
@
selection-change=
"handleSelectionChange"
>
<el-table-column
type=
"selection"
width=
"55"
></el-table-column>
<el-table-column
prop=
"avatar"
label=
"头像"
width=
"80px"
class-name=
"table-column-avatar"
fixed=
"left"
>
<template
slot-scope=
"
{row}">
<img
:src=
"row.avatar == null ? '/static/avatar/man.png' : row.avatar"
>
</
template
>
</el-table-column>
<el-table-column
prop=
"realname"
label=
"姓名"
min-width=
"100px"
fixed=
"left"
></el-table-column>
<el-table-column
prop=
"username"
label=
"用户名"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"empNo"
label=
"工号"
min-width=
"80px"
></el-table-column>
<el-table-column
prop=
"department"
label=
"部门"
min-width=
"120px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
department
==
null
?
''
:
row
.
department
.
name
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"position"
label=
"岗位"
min-width=
"120px"
class-name=
"table-column-strings"
>
<
template
slot-scope=
"{row}"
>
<ul>
<li
v-for=
"position in row.positions"
:key=
"position.id"
>
{{
position
.
name
}}
</li>
</ul>
</
template
>
</el-table-column>
<el-table-column
prop=
"sex"
label=
"性别"
min-width=
"80px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
sex
|
sex
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"mobile"
label=
"手机号码"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"email"
label=
"邮箱"
min-width=
"180px"
></el-table-column>
<el-table-column
prop=
"birthday"
label=
"生日"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"birthday"
label=
"角色"
min-width=
"160px"
class-name=
"table-column-role"
>
<
template
slot-scope=
"{row}"
>
<ul>
<li
v-for=
"role in row.roles"
:key=
"role.id"
>
{{
role
.
name
}}
</li>
</ul>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
</TableLayout>
</GlobalWindow>
</template>
<
script
>
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
Pagination
from
'
@/components/common/Pagination
'
export
default
{
name
:
'
PositionUserWindow
'
,
extends
:
BaseTable
,
components
:
{
Pagination
,
GlobalWindow
,
TableLayout
},
data
()
{
return
{
positionId
:
null
,
positionName
:
''
,
visible
:
false
,
// 搜索表单
searchForm
:
{
positionId
:
null
,
username
:
''
,
realname
:
''
,
mobile
:
''
}
}
},
methods
:
{
// 打开查看人员窗口
open
(
positionId
,
positionName
)
{
this
.
positionId
=
positionId
this
.
positionName
=
positionName
this
.
searchForm
.
positionId
=
positionId
this
.
visible
=
true
this
.
search
()
}
},
created
()
{
this
.
config
({
api
:
'
/system/user
'
})
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
.position-user-window
{
/
deep
/
.table-search-form
{
padding
:
0
;
}
/
deep
/
.window__body
{
background
:
#f7f7f7
;
.table-content
{
padding
:
0
;
.table-wrap
{
padding
:
0
;
}
}
}
// 列表头像处理
.table-column-avatar
{
img
{
width
:
48px
;
}
}
// 列表角色处理
.table-column-role
{
ul
{
li
{
display
:
inline-block
;
background
:
#eee
;
border-radius
:
3px
;
padding
:
0
3px
;
margin-right
:
3px
;
}
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/role/MenuConfigWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
class=
"menu-config-dialog"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
width=
"576px"
title=
"授权菜单"
@
confirm=
"confirm"
>
<p
class=
"tip"
v-if=
"role != null"
>
为角色
<em>
{{
role
.
name
}}
</em>
配置可访问的菜单
</p>
<el-tree
ref=
"menuTree"
:data=
"menus"
show-checkbox
node-key=
"id"
default-expand-all
:default-checked-keys=
"selectedIds"
:expand-on-click-node=
"false"
:check-on-click-node=
"true"
:props=
"
{children: 'children',label: 'name'}">
</el-tree>
</GlobalWindow>
</
template
>
<
script
>
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
{
createRoleMenu
}
from
'
@/api/system/role
'
import
{
fetchTree
as
fetchMenuList
}
from
'
@/api/system/menu
'
export
default
{
name
:
'
MenuConfigWindow
'
,
components
:
{
GlobalWindow
},
data
()
{
return
{
visible
:
false
,
isWorking
:
false
,
// 角色
role
:
null
,
// 所有菜单
menus
:
[],
// 已选中的菜单
selectedIds
:
[]
}
},
methods
:
{
/**
* @role 角色对象
*/
open
(
role
)
{
fetchMenuList
({})
.
then
(
records
=>
{
this
.
role
=
role
this
.
menus
=
records
// 如果为固定角色,则固定菜单不可更改
this
.
__resetDisabled
(
this
.
menus
,
this
.
role
)
// 找出叶节点
role
.
menus
=
role
.
menus
.
filter
(
menu
=>
role
.
menus
.
findIndex
(
m
=>
m
.
parentId
===
menu
.
id
)
===
-
1
)
// 初始化选中
this
.
selectedIds
=
role
.
menus
.
map
(
r
=>
r
.
id
)
this
.
visible
=
true
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
},
// 确认选择菜单
confirm
()
{
const
selectedMenus
=
this
.
$refs
.
menuTree
.
getCheckedNodes
(
false
,
true
)
this
.
isWorking
=
true
createRoleMenu
({
roleId
:
this
.
role
.
id
,
menuIds
:
selectedMenus
.
map
(
menu
=>
menu
.
id
)
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
菜单授权成功
'
)
this
.
visible
=
false
this
.
$emit
(
'
success
'
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
=
false
})
},
// 重置disabled
__resetDisabled
(
menus
,
role
)
{
if
(
menus
==
null
||
menus
.
length
===
0
)
{
return
}
for
(
const
menu
of
menus
)
{
menu
.
disabled
=
false
if
(
role
.
fixed
&&
menu
.
fixed
)
{
menu
.
disabled
=
true
}
this
.
__resetDisabled
(
menu
.
children
,
role
)
}
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
.global-window
{
.tip
{
margin-bottom
:
12px
;
em
{
font-style
:
normal
;
color
:
$primary-color
;
font-weight
:
bold
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/role/OperaRoleWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"角色编码"
prop=
"code"
required
>
<el-input
v-model=
"form.code"
placeholder=
"请输入角色编码"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"角色名称"
prop=
"name"
required
>
<el-input
v-model=
"form.name"
placeholder=
"请输入角色名称"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"角色备注"
prop=
"remark"
>
<el-input
v-model=
"form.remark"
type=
"textarea"
placeholder=
"请输入角色备注"
:rows=
"3"
v-trim
maxlength=
"500"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
export
default
{
name
:
'
OperaRoleWindow
'
,
extends
:
BaseOpera
,
components
:
{
GlobalWindow
},
data
()
{
return
{
// 原角色码
originRoleCode
:
''
,
// 表单数据
form
:
{
id
:
null
,
code
:
''
,
name
:
''
,
remark
:
''
},
// 验证规则
rules
:
{
code
:
[
{
required
:
true
,
message
:
'
请输入角色编码
'
}
],
name
:
[
{
required
:
true
,
message
:
'
请输入角色名称
'
}
]
}
}
},
methods
:
{
open
(
title
,
target
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
[
this
.
configData
[
'
field.id
'
]]
=
null
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
this
.
originRoleCode
=
target
.
code
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
})
},
confirm
()
{
if
(
this
.
form
.
id
==
null
||
this
.
form
.
id
===
''
)
{
this
.
__confirmCreate
()
return
}
if
(
this
.
originRoleCode
===
this
.
form
.
code
)
{
this
.
__confirmEdit
()
return
}
// 修改了角色编码
this
.
$dialog
.
confirm
(
'
检测到您修改了角色编码,角色编码修改后前后端均可能需要调整代码,确认修改吗?
'
,
'
提示
'
,
{
confirmButtonText
:
'
确认修改
'
,
type
:
'
warning
'
})
.
then
(()
=>
{
this
.
__confirmEdit
()
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/role
'
})
}
}
</
script
>
tools/frontend/src/views/System/components/system/role/PermissionConfigWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:visible.sync=
"visible"
:confirm-working=
"isWorking"
width=
"582px"
title=
"配置角色权限"
@
confirm=
"confirm"
>
<p
class=
"tip"
v-if=
"role != null"
>
为角色
<em>
{{
role
.
name
}}
</em>
配置权限
</p>
<p
class=
"tip-warn"
><i
class=
"el-icon-warning"
></i>
提醒:权限配置后需重新登录后生效
</p>
<el-transfer
ref=
"permissionTransfer"
v-model=
"selectedIds"
filterable
:filter-method=
"filterPermissions"
:titles=
"['未授权权限', '已授权权限']"
:props=
"
{
key: 'id',
label: 'name'
}"
:data="permissions">
</el-transfer>
</GlobalWindow>
</
template
>
<
script
>
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
{
createRolePermission
}
from
'
@/api/system/role
'
import
{
fetchAll
}
from
'
@/api/system/permission
'
export
default
{
name
:
'
PermissionConfigWindow
'
,
components
:
{
GlobalWindow
},
data
()
{
return
{
visible
:
false
,
isWorking
:
false
,
// 角色对象
role
:
null
,
// 权限列表
permissions
:
[],
// 已选中的权限ID
selectedIds
:
[]
}
},
methods
:
{
/**
* @role 角色对象
*/
open
(
role
)
{
if
(
this
.
$refs
.
permissionTransfer
)
{
this
.
$refs
.
permissionTransfer
.
clearQuery
(
'
left
'
)
this
.
$refs
.
permissionTransfer
.
clearQuery
(
'
right
'
)
}
fetchAll
()
.
then
(
records
=>
{
this
.
role
=
role
this
.
permissions
=
records
// 如果为固定角色,则固定权限不可更改
if
(
this
.
role
.
fixed
)
{
for
(
const
perm
of
this
.
permissions
)
{
if
(
perm
.
fixed
)
{
perm
.
disabled
=
true
}
}
}
this
.
selectedIds
=
role
.
permissions
.
map
(
r
=>
r
.
id
)
this
.
visible
=
true
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
},
// 确认选择权限
confirm
()
{
this
.
isWorking
=
true
createRolePermission
({
roleId
:
this
.
role
.
id
,
permissionIds
:
this
.
selectedIds
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
权限配置成功,用户重新登录后生效
'
)
this
.
visible
=
false
this
.
$emit
(
'
success
'
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
=
false
})
},
// 搜索权限
filterPermissions
(
query
,
item
)
{
const
lowerCaseQuery
=
query
.
toLowerCase
()
return
item
.
code
.
toLowerCase
().
indexOf
(
lowerCaseQuery
)
>
-
1
||
item
.
name
.
toLowerCase
().
indexOf
(
lowerCaseQuery
)
>
-
1
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
.global-window
{
.tip
{
em
{
font-style
:
normal
;
color
:
$primary-color
;
font-weight
:
bold
;
}
}
.tip-warn
{
margin
:
4px
0
12px
0
;
font-size
:
12px
;
color
:
#999
;
i
{
color
:
orange
;
margin-right
:
4px
;
font-size
:
14px
;
position
:
relative
;
top
:
1px
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/role/RoleSelect.vue
0 → 100644
View file @
0587447b
<
template
>
<el-select
class=
"role-select"
:class=
"
{select__block: !inline}"
:value="value"
:placeholder="placeholder"
:clearable="clearable"
:disabled="disabled"
@change="$emit('change', $event)"
@input="$emit('input', $event)"
>
<el-option
v-for=
"role in roles"
:key=
"role.id"
:value=
"role.id"
:label=
"role.name"
/>
</el-select>
</
template
>
<
script
>
import
{
fetchAll
}
from
'
@/api/system/role
'
export
default
{
name
:
'
RoleSelect
'
,
props
:
{
value
:
{},
placeholder
:
{
default
:
'
请选择角色
'
},
inline
:
{
default
:
true
},
disabled
:
{},
clearable
:
{
default
:
false
}
},
data
()
{
return
{
roles
:
[]
}
},
created
()
{
fetchAll
()
.
then
(
data
=>
{
this
.
roles
=
data
})
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.select__block
{
display
:
block
;
}
</
style
>
tools/frontend/src/views/System/components/system/user/OperaUserWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<!-- 新建/修改 -->
<GlobalWindow
:title=
"title"
:visible.sync=
"visible"
:confirm-working=
"isWorking"
@
confirm=
"confirm"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"用户名"
prop=
"username"
required
>
<el-input
v-model=
"form.username"
placeholder=
"请输入用户名"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"姓名"
prop=
"realname"
required
>
<el-input
v-model=
"form.realname"
placeholder=
"请输入姓名"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"性别"
prop=
"sex"
required
>
<el-radio-group
v-model=
"form.sex"
>
<el-radio
label=
"1"
>
男
</el-radio>
<el-radio
label=
"0"
>
女
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"头像"
prop=
"avatar"
required
>
<el-radio-group
v-model=
"form.avatar"
class=
"form-item-avatar"
>
<el-radio
label=
"/avatar/man.png"
border
><img
src=
"/avatar/man.png"
alt=
""
></el-radio>
<el-radio
label=
"/avatar/woman.png"
border
><img
src=
"/avatar/woman.png"
alt=
""
></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if=
"form.id == null"
label=
"初始密码"
prop=
"password"
required
>
<el-input
v-model=
"form.password"
type=
"password"
placeholder=
"请输入初始密码"
maxlength=
"30"
show-password
/>
</el-form-item>
<el-form-item
label=
"所属部门"
prop=
"departmentId"
required
>
<DepartmentSelect
v-model=
"form.departmentId"
placeholder=
"请选择用户所属部门"
:inline=
"false"
clearable
/>
</el-form-item>
<el-form-item
label=
"岗位"
prop=
"positionId"
>
<PositionSelect
v-model=
"form.positionIds"
placeholder=
"请选择用户所在岗位"
:inline=
"false"
:multiple=
"true"
clearable
/>
</el-form-item>
<el-form-item
label=
"工号"
prop=
"empNo"
>
<el-input
v-model=
"form.empNo"
placeholder=
"请输入工号"
v-trim
maxlength=
"50"
/>
</el-form-item>
<el-form-item
label=
"手机号码"
prop=
"mobile"
>
<el-input
v-model=
"form.mobile"
placeholder=
"请输入手机号码"
v-trim
maxlength=
"11"
/>
</el-form-item>
<el-form-item
label=
"邮箱"
prop=
"email"
>
<el-input
v-model=
"form.email"
placeholder=
"请输入邮箱"
v-trim
maxlength=
"200"
/>
</el-form-item>
<el-form-item
label=
"生日"
prop=
"birthday"
>
<el-date-picker
v-model=
"form.birthday"
value-format=
"yyyy-MM-dd"
placeholder=
"请选择用户生日"
/>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
BaseOpera
from
'
@/components/base/BaseOpera
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
DepartmentSelect
from
'
@/components/common/DepartmentSelect
'
import
PositionSelect
from
'
@/components/common/PositionSelect
'
import
{
checkMobile
,
checkEmail
}
from
'
@/utils/form
'
export
default
{
name
:
'
OperaUserWindow
'
,
extends
:
BaseOpera
,
components
:
{
PositionSelect
,
DepartmentSelect
,
GlobalWindow
},
data
()
{
return
{
// 表单数据
form
:
{
id
:
null
,
username
:
''
,
// 用户名
realname
:
''
,
// 姓名
empNo
:
''
,
// 工号
departmentId
:
null
,
// 所属部门ID
positionIds
:
[],
// 所属岗位ID集
avatar
:
'
/avatar/man.png
'
,
// 头像
password
:
''
,
// 密码
mobile
:
''
,
// 手机号码
email
:
''
,
// 邮箱
sex
:
'
1
'
,
// 性别
birthday
:
''
// 生日
},
// 验证规则
rules
:
{
username
:
[
{
required
:
true
,
message
:
'
请输入用户名
'
}
],
realname
:
[
{
required
:
true
,
message
:
'
请输入姓名
'
}
],
password
:
[
{
required
:
true
,
message
:
'
请输入密码
'
}
],
departmentId
:
[
{
required
:
true
,
message
:
'
请选择部门
'
}
],
avatar
:
[
{
required
:
true
,
message
:
'
请选择用户头像
'
}
],
sex
:
[
{
required
:
true
,
message
:
'
请选择用户性别
'
}
],
mobile
:
[
{
validator
:
checkMobile
}
],
email
:
[
{
validator
:
checkEmail
}
]
}
}
},
methods
:
{
/**
* @title 窗口标题
* @target 编辑的用户对象
*/
open
(
title
,
target
)
{
this
.
title
=
title
this
.
visible
=
true
// 新建
if
(
target
==
null
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
this
.
form
.
id
=
null
this
.
form
.
departmentId
=
null
this
.
form
.
positionIds
=
[]
})
return
}
// 编辑
this
.
$nextTick
(()
=>
{
for
(
const
key
in
this
.
form
)
{
this
.
form
[
key
]
=
target
[
key
]
}
this
.
form
.
departmentId
=
target
.
department
==
null
?
null
:
target
.
department
.
id
this
.
form
.
positionIds
=
target
.
positions
==
null
?
[]
:
target
.
positions
.
map
(
p
=>
p
.
id
)
})
}
},
created
()
{
this
.
config
({
api
:
'
/system/user
'
})
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.global-window
{
/
deep
/
.el-date-editor
{
width
:
100%
;
}
// 表单头像处理
/
deep
/
.form-item-avatar
{
.el-radio.is-bordered
{
height
:
auto
;
padding
:
6px
;
margin
:
0
10px
0
0
;
.el-radio__input
{
display
:
none
;
}
.el-radio__label
{
padding
:
0
;
width
:
48px
;
display
:
block
;
img
{
width
:
100%
;
}
}
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/user/ResetPwdWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:visible.sync=
"visible"
:confirm-working=
"isWorking"
width=
"576px"
title=
"重置密码"
@
confirm=
"confirm"
>
<p
class=
"tip"
v-if=
"user != null"
>
为用户
<em>
{{
user
.
realname
}}
</em>
重置密码
</p>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
>
<el-form-item
label=
"新密码"
prop=
"password"
required
>
<el-input
v-model=
"form.password"
type=
"password"
placeholder=
"请输入新密码"
maxlength=
"30"
show-password
></el-input>
</el-form-item>
</el-form>
</GlobalWindow>
</
template
>
<
script
>
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
{
resetPwd
}
from
'
@/api/system/user
'
export
default
{
name
:
'
ResetPwdWindow
'
,
components
:
{
GlobalWindow
},
data
()
{
return
{
isWorking
:
false
,
visible
:
false
,
user
:
null
,
form
:
{
password
:
''
},
rules
:
{
password
:
[
{
required
:
true
,
message
:
'
请输入密码
'
}
]
}
}
},
methods
:
{
open
(
user
)
{
this
.
user
=
user
this
.
visible
=
true
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
resetFields
()
})
},
// 确认重置密码
confirm
()
{
if
(
this
.
isWorking
)
{
return
}
this
.
$refs
.
form
.
validate
((
valid
)
=>
{
if
(
!
valid
)
{
return
}
this
.
isWorking
=
true
resetPwd
({
id
:
this
.
user
.
id
,
password
:
this
.
form
.
password
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
密码重置成功
'
)
this
.
visible
=
false
this
.
$emit
(
'
success
'
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
=
false
})
})
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
// 角色配置
.global-window
{
.tip
{
margin-bottom
:
12px
;
em
{
font-style
:
normal
;
color
:
$primary-color
;
font-weight
:
bold
;
}
}
}
</
style
>
tools/frontend/src/views/System/components/system/user/RoleConfigWindow.vue
0 → 100644
View file @
0587447b
<
template
>
<GlobalWindow
:visible.sync=
"visible"
:confirm-working=
"isWorking"
width=
"582px"
title=
"配置用户角色"
@
confirm=
"confirm"
>
<p
class=
"tip"
v-if=
"user != null"
>
为用户
<em>
{{
user
.
realname
}}
</em>
配置角色
</p>
<p
class=
"tip-warn"
><i
class=
"el-icon-warning"
></i>
提醒:角色配置后需重新登录后生效
</p>
<el-transfer
v-model=
"selectedIds"
:titles=
"['未授权角色', '已授权角色']"
:props=
"
{
key: 'id',
label: 'name'
}"
:data="roles">
</el-transfer>
</GlobalWindow>
</
template
>
<
script
>
import
BasePage
from
'
@/components/base/BasePage
'
import
GlobalWindow
from
'
@/components/common/GlobalWindow
'
import
{
createUserRole
}
from
'
@/api/system/user
'
import
{
fetchAll
as
fetchAllRoles
}
from
'
@/api/system/role
'
export
default
{
name
:
'
RoleConfigWindow
'
,
extends
:
BasePage
,
components
:
{
GlobalWindow
},
data
()
{
return
{
visible
:
false
,
isWorking
:
false
,
// 用户
user
:
null
,
// 角色列表
roles
:
null
,
// 已选中的角色ID
selectedIds
:
[]
}
},
methods
:
{
open
(
user
)
{
fetchAllRoles
()
.
then
(
records
=>
{
this
.
roles
=
records
this
.
user
=
user
// 如果为固定用户,则固定角色不可更改
if
(
this
.
user
.
fixed
)
{
for
(
const
role
of
this
.
roles
)
{
if
(
role
.
fixed
)
{
role
.
disabled
=
true
}
}
}
// 如果当前用户为非超级管理员用户,则不允许授权超级管理员角色
if
(
!
this
.
isAdmin
)
{
for
(
const
role
of
this
.
roles
)
{
if
(
role
.
code
===
this
.
adminCode
)
{
role
.
disabled
=
true
}
}
}
this
.
selectedIds
=
this
.
user
.
roles
.
map
(
r
=>
r
.
id
)
this
.
visible
=
true
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
},
// 确认选择角色
confirm
()
{
if
(
this
.
isWorking
)
{
return
}
this
.
isWorking
=
true
createUserRole
({
userId
:
this
.
user
.
id
,
roleIds
:
this
.
selectedIds
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
角色配置成功,用户重新登录后生效
'
)
this
.
visible
=
false
this
.
$emit
(
'
success
'
)
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
=
false
})
},
// 关闭
close
()
{
this
.
$emit
(
'
update:visible
'
,
false
)
}
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
// 角色配置
.global-window
{
.tip
{
em
{
font-style
:
normal
;
color
:
$primary-color
;
font-weight
:
bold
;
}
}
.tip-warn
{
margin
:
4px
0
12px
0
;
font-size
:
12px
;
color
:
#999
;
i
{
color
:
orange
;
margin-right
:
4px
;
font-size
:
14px
;
position
:
relative
;
top
:
1px
;
}
}
}
</
style
>
tools/frontend/src/views/System/data-permission.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:datapermission:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"100px"
inline
>
<el-form-item
label=
"业务模块"
prop=
"businessCode"
>
<DataPermModuleSelect
v-model=
"searchForm.businessCode"
clearable
@
change=
"search"
/>
</el-form-item>
<el-form-item
label=
"角色"
prop=
"roleId"
>
<RoleSelect
v-model=
"searchForm.roleId"
clearable
@
change=
"search"
/>
</el-form-item>
<section>
<el-button
type=
"primary"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:datapermission:create', 'system:datapermission:delete']"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaDataPermissionWindow.open('新建数据权限')"
icon=
"el-icon-plus"
v-permissions=
"['system:datapermission:create']"
>
新建
</el-button></li>
<li><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
v-permissions=
"['system:datapermission:delete']"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
stripe
@
selection-change=
"handleSelectionChange"
>
<el-table-column
type=
"selection"
width=
"55"
></el-table-column>
<el-table-column
prop=
"businessCode"
label=
"业务模块"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
businessCode
|
moduleText
(
modules
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"roleId"
label=
"角色"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
role
.
name
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"type"
label=
"权限类型"
min-width=
"140px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
type
|
typeText
(
types
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"disabled"
label=
"是否启用"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
<el-switch
v-model=
"row.disabled"
:active-value=
"false"
:inactive-value=
"true"
@
change=
"switchDisabled(row)"
/>
</
template
>
</el-table-column>
<el-table-column
prop=
"remark"
label=
"备注"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"修改人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"修改时间"
min-width=
"140px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:datapermission:update', 'system:datapermission:delete'])"
label=
"操作"
min-width=
"120"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
type=
"text"
@
click=
"$refs.operaDataPermissionWindow.open('编辑数据权限', row)"
icon=
"el-icon-edit"
v-permissions=
"['system:datapermission:update']"
>
编辑
</el-button>
<el-button
type=
"text"
@
click=
"deleteById(row)"
icon=
"el-icon-delete"
v-permissions=
"['system:datapermission:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
>
</pagination>
</template>
<!-- 新建/修改 -->
<OperaDataPermissionWindow
ref=
"operaDataPermissionWindow"
@
success=
"handlePageChange"
/>
</TableLayout>
</template>
<
script
>
import
{
Table
,
TableColumn
,
Button
}
from
"
element-ui
"
import
'
element-ui/lib/theme-chalk/index.css
'
import
BaseTable
from
'
./base/BaseTable
'
import
TableLayout
from
'
./TableLayout
'
import
Pagination
from
'
./common/Pagination
'
import
OperaDataPermissionWindow
from
'
./system/datapermission/OperaDataPermissionWindow
'
import
DataPermModuleSelect
from
'
./system/datapermission/DataPermModuleSelect
'
import
RoleSelect
from
'
./system/role/RoleSelect
'
// 获取模块名称
const
__getModuleName
=
function
(
businessCode
,
modules
)
{
for
(
const
module
of
modules
)
{
if
(
module
.
businessCode
===
businessCode
)
{
return
module
.
moduleName
}
}
return
'
未知
'
}
export
default
{
name
:
'
DataPermission
'
,
extends
:
BaseTable
,
components
:
{
"
el-table
"
:
Table
,
"
el-table-column
"
:
TableColumn
,
"
el-button
"
:
Button
,
RoleSelect
,
DataPermModuleSelect
,
TableLayout
,
Pagination
,
OperaDataPermissionWindow
},
data
()
{
return
{
// 数据权限模块
modules
:
[],
// 数据权限类型
types
:
[],
// 搜索
searchForm
:
{
businessCode
:
''
,
roleId
:
null
,
type
:
''
}
}
},
filters
:
{
// 数据权限类型文案
typeText
(
value
,
types
)
{
for
(
const
type
of
types
)
{
if
(
type
.
code
===
value
)
{
return
type
.
remark
}
}
return
'
未知
'
},
// 数据权限模块文案
moduleText
(
value
,
modules
)
{
return
__getModuleName
(
value
,
modules
)
}
},
methods
:
{
// 启用/禁用菜单
switchDisabled
(
row
)
{
if
(
!
row
.
disabled
)
{
this
.
__updateStatus
(
row
)
return
}
this
.
$dialog
.
disableConfirm
(
`确认禁用
${
__getModuleName
(
row
.
businessCode
,
this
.
modules
)}
/
${
row
.
role
.
name
}
数据权限吗?`
)
.
then
(()
=>
{
this
.
__updateStatus
(
row
)
}).
catch
(()
=>
{
row
.
disabled
=
!
row
.
disabled
})
},
// 删除行
deleteById
(
row
)
{
this
.
$dialog
.
deleteConfirm
(
`确认删除【
${
__getModuleName
(
row
.
businessCode
,
this
.
modules
)}
/
${
row
.
role
.
name
}
】数据权限吗?`
)
.
then
(()
=>
{
this
.
isWorking
.
delete
=
true
this
.
api
.
deleteById
(
row
.
id
)
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
删除成功
'
)
this
.
__afterDelete
()
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
delete
=
false
})
})
.
catch
(()
=>
{})
},
// 修改状态
__updateStatus
(
row
)
{
this
.
api
.
updateStatus
({
id
:
row
.
id
,
disabled
:
row
.
disabled
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
修改成功
'
)
})
.
catch
(
e
=>
{
row
.
disabled
=
!
row
.
disabled
this
.
$tip
.
apiFailed
(
e
)
})
}
},
async
created
()
{
this
.
config
({
module
:
'
数据权限
'
,
api
:
'
/system/dataPermission
'
})
// 初始化数据权限模块
await
this
.
api
.
fetchModules
()
.
then
(
data
=>
{
this
.
modules
=
data
})
// 初始化数据权限模块
await
this
.
api
.
fetchTypes
()
.
then
(
data
=>
{
this
.
types
=
data
})
.
catch
(
e
=>
{
console
.
log
(
e
)
})
// 执行搜索
this
.
search
()
}
}
</
script
>
tools/frontend/src/views/System/department.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:department:query']"
>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:department:create', 'system:department:delete']"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaDepartmentWindow.open('新建部门')"
icon=
"el-icon-plus"
v-permissions=
"['system:department:create']"
>
新建
</el-button></li>
<li><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
v-permissions=
"['system:department:delete']"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
:tree-props=
"
{children: 'children', hasChildren: 'hasChildren'}"
row-key="id"
stripe
default-expand-all
@selection-change="handleSelectionChange"
>
<el-table-column
type=
"selection"
fixed=
"left"
width=
"55"
></el-table-column>
<el-table-column
prop=
"name"
label=
"部门名称"
fixed=
"left"
min-width=
"200px"
></el-table-column>
<el-table-column
prop=
"code"
label=
"部门编码"
fixed=
"left"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"userCount"
label=
"部门人数"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"phone"
label=
"联系电话"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"email"
label=
"部门邮箱"
min-width=
"180px"
></el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
min-width=
"140px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:department:update', 'system:department:create', 'system:department:delete', 'system:department:queryUsers'])"
label=
"操作"
min-width=
"310"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
type=
"text"
@
click=
"$refs.operaDepartmentWindow.open('编辑部门', row)"
icon=
"el-icon-edit"
v-permissions=
"['system:department:update']"
>
编辑
</el-button>
<el-button
type=
"text"
@
click=
"$refs.operaDepartmentWindow.open('新建下级部门', null, row)"
icon=
"el-icon-edit"
v-permissions=
"['system:department:create']"
>
新建下级部门
</el-button>
<el-button
type=
"text"
@
click=
"$refs.departmentUserWindow.open(row.id, row.name)"
icon=
"el-icon-user-solid"
v-permissions=
"['system:department:queryUsers']"
>
查看人员
</el-button>
<el-button
type=
"text"
@
click=
"deleteById(row)"
icon=
"el-icon-delete"
v-permissions=
"['system:department:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</template>
<!-- 新建/修改 -->
<OperaDepartmentWindow
ref=
"operaDepartmentWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
<!-- 查看人员 -->
<DepartmentUserWindow
ref=
"departmentUserWindow"
/>
</TableLayout>
</template>
<
script
>
import
TableLayout
from
'
@/layouts/TableLayout
'
import
{
fetchTree
}
from
'
@/api/system/department
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
OperaDepartmentWindow
from
'
@/components/system/department/OperaDepartmentWindow
'
import
DepartmentUserWindow
from
'
@/components/system/department/DepartmentUserWindow
'
export
default
{
name
:
'
SystemDepartment
'
,
extends
:
BaseTable
,
components
:
{
DepartmentUserWindow
,
OperaDepartmentWindow
,
TableLayout
},
data
()
{
return
{
// 搜索
searchForm
:
{
name
:
''
}
}
},
methods
:
{
// 查询数据
handlePageChange
()
{
this
.
tableData
.
list
.
splice
(
0
,
this
.
tableData
.
list
.
length
)
this
.
isWorking
.
search
=
true
fetchTree
()
.
then
(
records
=>
{
this
.
tableData
.
list
=
records
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
search
=
false
})
}
},
created
()
{
this
.
config
({
module
:
'
部门
'
,
api
:
'
/system/department
'
})
this
.
search
()
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.table-layout
{
/
deep
/
.table-content
{
margin-top
:
0
;
}
}
</
style
>
tools/frontend/src/views/System/dict.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:dict:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"100px"
inline
>
<el-form-item
label=
"字典编码"
prop=
"code"
>
<el-input
v-model=
"searchForm.code"
v-trim
placeholder=
"请输入字典编码"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"字典名称"
prop=
"name"
>
<el-input
v-model=
"searchForm.name"
v-trim
placeholder=
"请输入字典名称"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<section>
<el-button
type=
"primary"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:dict:create', 'system:dict:delete']"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaDictWindow.open('新建字典')"
icon=
"el-icon-plus"
v-permissions=
"['system:dict:create']"
>
新建
</el-button></li>
<li><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
v-permissions=
"['system:dict:delete']"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
:default-sort =
"
{prop: 'createTime', order: 'descending'}"
stripe
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>
<el-table-column
type=
"selection"
fixed=
"left"
width=
"55"
></el-table-column>
<el-table-column
prop=
"code"
label=
"字典编码"
fixed=
"left"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"name"
label=
"字典名称"
fixed=
"left"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"remark"
label=
"备注"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"140px"
sortable=
"custom"
sort-by=
"dict.CREATE_TIME"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
min-width=
"140px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:dict:update', 'system:dict:delete'])"
label=
"操作"
min-width=
"210"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
type=
"text"
@
click=
"$refs.operaDictWindow.open('编辑字典', row)"
icon=
"el-icon-edit"
v-permissions=
"['system:dict:update']"
>
编辑
</el-button>
<el-button
type=
"text"
@
click=
"$refs.dictDataManagerWindow.open(row.id, row.name)"
icon=
"el-icon-edit"
v-permissions=
"['system:dict:update']"
>
数据管理
</el-button>
<el-button
type=
"text"
@
click=
"deleteById(row)"
icon=
"el-icon-delete"
v-permissions=
"['system:dict:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
<!-- 新建/修改 -->
<OperaDictWindow
ref=
"operaDictWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
<!-- 数据管理 -->
<DictDataManagerWindow
ref=
"dictDataManagerWindow"
/>
</TableLayout>
</template>
<
script
>
import
Pagination
from
'
@/components/common/Pagination
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
OperaDictWindow
from
'
@/components/system/dict/OperaDictWindow
'
import
DictDataManagerWindow
from
'
@/components/system/dict/DictDataManagerWindow
'
export
default
{
name
:
'
SystemDict
'
,
extends
:
BaseTable
,
components
:
{
DictDataManagerWindow
,
OperaDictWindow
,
TableLayout
,
Pagination
},
data
()
{
return
{
// 搜索
searchForm
:
{
code
:
''
,
name
:
''
}
}
},
created
()
{
this
.
config
({
module
:
'
字典
'
,
api
:
'
/system/dict
'
,
sorts
:
[{
property
:
'
dict.CREATE_TIME
'
,
direction
:
'
DESC
'
}]
})
this
.
search
()
}
}
</
script
>
tools/frontend/src/views/System/location.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:location:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"100px"
inline
>
<el-form-item
label=
"名称"
prop=
"name"
>
<el-input
v-model=
"searchForm.name"
placeholder=
"请输入名称"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"地区范围"
prop=
"parentId"
>
<LocationSelect
:city-id.sync=
"searchForm.parentId"
placeholder=
"请选择地区范围"
:level=
"2"
clearable
@
change=
"search"
/>
</el-form-item>
<el-form-item
label=
"地区层级"
prop=
"level"
>
<el-select
v-model=
"searchForm.level"
placeholder=
"请选择地区层级"
clearable
@
change=
"search"
>
<el-option
value=
"1"
label=
"省"
/>
<el-option
value=
"2"
label=
"市"
/>
<el-option
value=
"3"
label=
"区/县"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"区号"
prop=
"areaCode"
>
<el-input
v-model=
"searchForm.areaCode"
placeholder=
"请输入区号"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"邮编"
prop=
"postalCode"
>
<el-input
v-model=
"searchForm.postalCode"
placeholder=
"请输入邮编"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<section>
<el-button
type=
"primary"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:location:create']"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaLocationWindow.open('新建地区')"
icon=
"el-icon-plus"
v-permissions=
"['system:location:create']"
>
新建
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
stripe
>
<el-table-column
prop=
"name"
label=
"名称"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"shortName"
label=
"简称"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"fullName"
label=
"全称"
min-width=
"180px"
></el-table-column>
<el-table-column
prop=
"pinyin"
label=
"拼音"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"level"
label=
"层级"
min-width=
"80px"
>
<template
slot-scope=
"
{row}">
{{
row
.
level
|
levelText
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"areaCode"
label=
"区号"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"postalCode"
label=
"邮编"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"firstLetter"
label=
"首字母"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"disabled"
label=
"是否启用"
min-width=
"80px"
>
<
template
slot-scope=
"{row}"
>
<el-switch
v-model=
"row.disabled"
:active-value=
"false"
:inactive-value=
"true"
@
change=
"switchDisabled(row)"
/>
</
template
>
</el-table-column>
<el-table-column
prop=
"lng"
label=
"经度"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"lat"
label=
"纬度"
min-width=
"100px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:location:update', 'system:location:delete'])"
label=
"操作"
min-width=
"160"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
type=
"text"
@
click=
"$refs.operaLocationWindow.open('编辑地区', row)"
icon=
"el-icon-edit"
v-permissions=
"['system:location:update']"
>
编辑
</el-button>
<el-button
type=
"text"
v-if=
"row.level === 1"
@
click=
"$refs.operaLocationWindow.open('新增市', null, row)"
icon=
"el-icon-edit"
v-permissions=
"['system:location:create']"
>
新增市
</el-button>
<el-button
type=
"text"
v-if=
"row.level === 2"
@
click=
"$refs.operaLocationWindow.open('新增区/县', null, row)"
icon=
"el-icon-edit"
v-permissions=
"['system:location:create']"
>
新增区/县
</el-button>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
>
</pagination>
</template>
<!-- 新建/修改 -->
<OperaLocationWindow
ref=
"operaLocationWindow"
@
success=
"handlePageChange"
/>
</TableLayout>
</template>
<
script
>
import
BaseTable
from
'
@/components/base/BaseTable
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
Pagination
from
'
@/components/common/Pagination
'
import
OperaLocationWindow
from
'
@/components/system/location/OperaLocationWindow
'
import
LocationSelect
from
'
@/components/common/LocationSelect
'
import
{
updateStatus
}
from
'
@/api/system/location
'
export
default
{
name
:
'
SystemLocation
'
,
extends
:
BaseTable
,
components
:
{
LocationSelect
,
TableLayout
,
Pagination
,
OperaLocationWindow
},
data
()
{
return
{
// 搜索
searchForm
:
{
parentId
:
null
,
level
:
null
,
name
:
''
,
areaCode
:
''
,
postalCode
:
''
}
}
},
filters
:
{
levelText
(
value
)
{
if
(
value
===
1
)
{
return
'
省
'
}
if
(
value
===
2
)
{
return
'
市
'
}
if
(
value
===
3
)
{
return
'
区/县
'
}
return
'
未知
'
}
},
methods
:
{
// 启用/禁用
switchDisabled
(
row
)
{
if
(
!
row
.
disabled
)
{
this
.
__updateMenuStatus
(
row
)
return
}
this
.
$dialog
.
disableConfirm
(
`确认禁用
${
row
.
fullName
}
地区吗?`
)
.
then
(()
=>
{
this
.
__updateMenuStatus
(
row
)
}).
catch
(()
=>
{
row
.
disabled
=
!
row
.
disabled
})
},
// 修改菜单状态
__updateMenuStatus
(
row
)
{
updateStatus
({
id
:
row
.
id
,
disabled
:
row
.
disabled
})
.
then
(()
=>
{
this
.
$tip
.
apiSuccess
(
'
修改成功
'
)
})
.
catch
(
e
=>
{
row
.
disabled
=
!
row
.
disabled
this
.
$tip
.
apiFailed
(
e
)
})
}
},
created
()
{
this
.
config
({
module
:
'
地区
'
,
api
:
'
/system/location
'
,
'
field.id
'
:
'
id
'
,
'
field.main
'
:
'
fullName
'
})
this
.
search
()
}
}
</
script
>
tools/frontend/src/views/System/loginLog.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:loginLog:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"100px"
inline
>
<el-form-item
label=
"登录用户名"
prop=
"loginUsername"
>
<el-input
v-model=
"searchForm.loginUsername"
placeholder=
"请输入登录用户名"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"登录IP"
prop=
"ip"
>
<el-input
v-model=
"searchForm.ip"
placeholder=
"请输入登录IP"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"服务器IP"
prop=
"serverIp"
>
<el-input
v-model=
"searchForm.serverIp"
placeholder=
"请输入服务器IP"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"是否登录成功"
prop=
"success"
>
<el-select
v-model=
"searchForm.success"
placeholder=
"请选择是否登录状态"
clearable
@
change=
"search"
>
<el-option
value=
"true"
label=
"登录成功"
/>
<el-option
value=
"false"
label=
"登录失败"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"登录时间"
prop=
"loginTime"
>
<el-date-picker
v-model=
"searchDateRange"
type=
"datetimerange"
range-separator=
"至"
value-format=
"yyyy-MM-dd HH:mm:ss"
start-placeholder=
"开始时间"
end-placeholder=
"结束时间"
@
change=
"handleSearchTimeChange"
></el-date-picker>
</el-form-item>
<section>
<el-button
type=
"primary"
@
click=
"search"
>
搜索
</el-button>
<el-button
type=
"primary"
:loading=
"isWorking.export"
@
click=
"exportExcel"
>
导出
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
stripe
:default-sort=
"
{prop: 'loginTime', order: 'descending'}"
@sort-change="handleSortChange"
>
<el-table-column
prop=
"loginUsername"
label=
"登录用户名"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"ip"
label=
"登录IP"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"location"
label=
"登录地址"
min-width=
"160px"
></el-table-column>
<el-table-column
prop=
"clientInfo"
label=
"客户端"
min-width=
"160px"
></el-table-column>
<el-table-column
prop=
"osInfo"
label=
"操作系统"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"platform"
label=
"登录平台"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"loginTime"
label=
"登录时间"
min-width=
"160px"
sortable=
"custom"
sort-by=
"LOGIN_TIME"
></el-table-column>
<el-table-column
prop=
"systemVersion"
label=
"系统版本"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"serverIp"
label=
"服务器IP"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"success"
label=
"状态"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
success
|
statusText
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"reason"
label=
"失败原因"
min-width=
"160px"
></el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
</TableLayout>
</template>
<
script
>
import
BaseTable
from
'
@/components/base/BaseTable
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
Pagination
from
'
@/components/common/Pagination
'
export
default
{
name
:
'
SystemLoginLog
'
,
extends
:
BaseTable
,
components
:
{
TableLayout
,
Pagination
},
data
()
{
return
{
// 搜索时间范围
searchDateRange
:
[],
// 搜索
searchForm
:
{
loginUsername
:
''
,
ip
:
''
,
serverIp
:
''
,
success
:
''
,
startTime
:
null
,
endTime
:
null
}
}
},
filters
:
{
// 登录状态
statusText
(
value
)
{
if
(
value
)
{
return
'
登录成功
'
}
return
'
登录失败
'
}
},
methods
:
{
// 时间搜索范围变化
handleSearchTimeChange
(
value
)
{
this
.
searchForm
.
startTime
=
null
this
.
searchForm
.
endTime
=
null
if
(
value
!=
null
)
{
this
.
searchForm
.
startTime
=
value
[
0
]
this
.
searchForm
.
endTime
=
value
[
1
]
}
this
.
search
()
}
},
created
()
{
this
.
config
({
module
:
'
登录日志
'
,
api
:
'
/system/loginLog
'
,
'
field.id
'
:
'
id
'
,
'
field.main
'
:
'
id
'
,
sorts
:
[{
property
:
'
LOGIN_TIME
'
,
direction
:
'
DESC
'
}]
})
this
.
search
()
}
}
</
script
>
tools/frontend/src/views/System/monitor.vue
0 → 100644
View file @
0587447b
<
template
>
<Profile
:permissions=
"['system:monitor:query']"
>
<div
class=
"monitor"
>
<div
class=
"toolbar"
>
<el-switch
v-model=
"autoRefresh"
@
change=
"changeAutoRefresh"
/><label>
{{
autoRefresh
|
autoRefreshText
}}
</label>
</div>
<ul>
<li
class=
"wrap"
>
<h2>
CPU
<Light
v-if=
"data != null"
:warn=
"data.cpu.useRatio > 60"
:danger=
"data.cpu.useRatio > 80"
/></h2>
<div>
<dl>
<dt>
物理核数
</dt>
<dd><Value
:data=
"data"
prop=
"cpu.physicalCount"
/></dd>
</dl>
<dl>
<dt>
逻辑核数
</dt>
<dd><Value
:data=
"data"
prop=
"cpu.logicalCount"
/></dd>
</dl>
<dl
class=
"important"
>
<dt>
当前使用率
</dt>
<dd><Value
:data=
"data"
prop=
"cpu.useRatio"
:handler=
"keep2decimals"
suffix=
"%"
/></dd>
</dl>
<dl>
<dt>
当前空闲率
</dt>
<dd><Value
:data=
"data"
prop=
"cpu.freeRatio"
:handler=
"keep2decimals"
suffix=
"%"
/></dd>
</dl>
</div>
</li>
<li
class=
"wrap"
>
<h2>
内存
<Light
v-if=
"data != null"
:warn=
"data.memory.useRatio > 60"
:danger=
"data.memory.useRatio > 80"
/></h2>
<div>
<dl>
<dt>
总空间
</dt>
<dd><Value
:data=
"data"
prop=
"memory.size"
suffix=
"G"
:handler=
"toG"
/></dd>
</dl>
<dl>
<dt>
空闲空间
</dt>
<dd><Value
:data=
"data"
prop=
"memory.freeSpace"
suffix=
"G"
:handler=
"toG"
/></dd>
</dl>
<dl
class=
"important"
>
<dt>
已用空间
</dt>
<dd><Value
:data=
"data"
prop=
"memory.usedSpace"
suffix=
"G"
:handler=
"toG"
/></dd>
</dl>
<dl
class=
"important"
>
<dt>
使用率
</dt>
<dd><Value
:data=
"data"
prop=
"memory.useRatio"
suffix=
"%"
:handler=
"keep2decimals"
/></dd>
</dl>
</div>
</li>
<li
class=
"wrap"
>
<h2>
JVM
<Light
v-if=
"data != null"
:warn=
"data.jvm.memory.useRatio > 60"
:danger=
"data.jvm.memory.useRatio > 80"
/></h2>
<div>
<dl>
<dt>
安装路径
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.home"
/></dd>
</dl>
<dl>
<dt>
版本
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.version"
/></dd>
</dl>
<dl>
<dt>
启动时间
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.bootTime"
/></dd>
</dl>
<dl>
<dt>
运行时长
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.runtime"
/></dd>
</dl>
<dl>
<dt>
总空间
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.memory.size"
suffix=
"M"
:handler=
"keep2decimals"
/></dd>
</dl>
<dl>
<dt>
空闲空间
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.memory.freeSpace"
suffix=
"M"
:handler=
"keep2decimals"
/></dd>
</dl>
<dl
class=
"important"
>
<dt>
已用空间
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.memory.usedSpace"
suffix=
"M"
:handler=
"keep2decimals"
/></dd>
</dl>
<dl
class=
"important"
>
<dt>
使用率
</dt>
<dd><Value
:data=
"data"
prop=
"jvm.memory.useRatio"
suffix=
"%"
:handler=
"keep2decimals"
/></dd>
</dl>
</div>
</li>
<li
class=
"wrap"
>
<h2>
服务器
</h2>
<div>
<dl>
<dt>
操作系统
</dt>
<dd><Value
:data=
"data"
prop=
"osName"
/></dd>
</dl>
<dl>
<dt>
系统版本
</dt>
<dd><Value
:data=
"data"
prop=
"osVersion"
/></dd>
</dl>
<dl>
<dt>
系统架构
</dt>
<dd><Value
:data=
"data"
prop=
"osArch"
/></dd>
</dl>
<dl
class=
"important"
>
<dt>
IP地址
</dt>
<dd><Value
:data=
"data"
prop=
"ip"
/></dd>
</dl>
<dl>
<dt>
MAC地址
</dt>
<dd><Value
:data=
"data"
prop=
"mac"
/></dd>
</dl>
<dl>
<dt>
服务器时间
</dt>
<dd><Value
:data=
"data"
prop=
"currentTime"
/></dd>
</dl>
</div>
</li>
</ul>
<div
class=
"wrap"
>
<h2>
磁盘信息
</h2>
<el-table
:data=
"data ? data.disks : []"
v-loading=
"loading"
>
<el-table-column
prop=
"name"
label=
"磁盘名称"
/>
<el-table-column
prop=
"dir"
label=
"磁盘路径"
/>
<el-table-column
prop=
"fsType"
label=
"文件系统"
/>
<el-table-column
prop=
"size"
label=
"总空间"
>
<template
slot-scope=
"
{row}">
{{
toG
(
row
.
size
)
}}
G
</
template
>
</el-table-column>
<el-table-column
prop=
"freeSpace"
label=
"可用空间"
>
<
template
slot-scope=
"{row}"
>
{{
toG
(
row
.
freeSpace
)
}}
G
</
template
>
</el-table-column>
<el-table-column
prop=
"usedSpace"
label=
"已用空间"
>
<
template
slot-scope=
"{row}"
>
<label
class=
"important"
>
{{
toG
(
row
.
usedSpace
)
}}
G
</label>
</
template
>
</el-table-column>
<el-table-column
prop=
"useRatio"
label=
"已用占比"
>
<
template
slot-scope=
"{row}"
>
<label
class=
"important"
>
{{
keep2decimals
(
row
.
useRatio
)
}}
%
</label>
</
template
>
</el-table-column>
<el-table-column>
<
template
slot-scope=
"{row}"
>
<Light
:warn=
"row.useRatio > 60"
:danger=
"row.useRatio > 80"
:mini=
"true"
/>
</
template
>
</el-table-column>
</el-table>
</div>
</div>
</Profile>
</template>
<
script
>
import
{
getSystemInfo
}
from
'
@/api/system/monitor
'
import
Value
from
'
@/components/common/Value
'
import
Light
from
'
@/components/common/Light
'
import
Profile
from
'
../../components/common/Profile
'
export
default
{
name
:
'
SystemMonitor
'
,
components
:
{
Profile
,
Light
,
Value
},
data
()
{
return
{
// 加载中
loading
:
false
,
// 自动刷新标识
autoRefresh
:
false
,
// 数据
data
:
null
,
// 自动刷新定时器
interval
:
null
}
},
filters
:
{
autoRefreshText
(
value
)
{
if
(
value
)
{
return
'
已开启自动刷新
'
}
return
'
已关闭自动刷新
'
}
},
methods
:
{
// 切换自动刷新
changeAutoRefresh
(
value
)
{
if
(
this
.
interval
!=
null
)
{
clearInterval
(
this
.
interval
)
}
if
(
value
)
{
this
.
getSystemInfo
()
this
.
interval
=
setInterval
(()
=>
{
this
.
getSystemInfo
()
},
3000
)
}
},
// 获取系统信息
getSystemInfo
()
{
if
(
this
.
loading
)
{
return
}
this
.
loading
=
true
getSystemInfo
()
.
then
(
data
=>
{
this
.
data
=
data
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
loading
=
false
})
},
// 单位转为G
toG
(
value
)
{
return
Math
.
round
(
value
/
1024
*
100
)
/
100
},
// 转为比率
keep2decimals
(
value
)
{
return
Math
.
round
(
value
*
100
)
/
100
}
},
beforeRouteLeave
(
from
,
to
,
next
)
{
clearInterval
(
this
.
interval
)
next
()
},
created
()
{
this
.
getSystemInfo
()
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/assets/style/variables.scss"
;
.monitor
{
padding
:
20px
20px
;
}
// 工具栏
.toolbar
{
margin-bottom
:
12px
;
background
:
#fff
;
padding
:
8px
16px
;
label
{
font-size
:
12px
;
margin-left
:
8px
;
color
:
#999
;
}
}
ul
{
display
:
flex
;
flex-wrap
:
wrap
;
justify-content
:
space-between
;
li
{
width
:
49
.5%
;
min-height
:
200px
;
flex-shrink
:
0
;
}
}
// 信息模块
.wrap
{
background
:
#fff
;
box-shadow
:
2px
2px
10px
-5px
#999
;
border-radius
:
8px
;
margin-bottom
:
16px
;
h2
{
border-bottom
:
1px
solid
#eee
;
font-size
:
18px
;
font-weight
:
normal
;
line-height
:
40px
;
padding
:
0
16px
;
color
:
#555
;
position
:
relative
;
.light
{
position
:
absolute
;
top
:
12px
;
right
:
12px
;
}
}
&
>
div
{
padding
:
0
16px
;
font-size
:
14px
;
dl
{
display
:
flex
;
dt
{
width
:
80px
;
text-align
:
right
;
flex-shrink
:
0
;
color
:
#999
;
}
dd
{
width
:
100%
;
margin
:
0
;
padding-left
:
12px
;
color
:
#555
;
overflow
:
hidden
;
}
}
}
}
// 重要信息
.important
{
color
:
$primary-color
;
font-weight
:
bold
;
&
>
dd
>
div
{
color
:
$primary-color
;
font-weight
:
bold
;
}
}
</
style
>
tools/frontend/src/views/System/permission.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:permission:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"100px"
inline
>
<el-form-item
label=
"权限编码"
prop=
"code"
>
<el-input
v-model=
"searchForm.code"
v-trim
placeholder=
"请输入权限编码"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"权限名称"
prop=
"name"
>
<el-input
v-model=
"searchForm.name"
v-trim
placeholder=
"请输入权限名称"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<section>
<el-button
type=
"primary"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:permission:create', 'system:permission:delete']"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaPermissionWindow.open('新建系统权限')"
icon=
"el-icon-plus"
v-permissions=
"['system:permission:create']"
>
新建
</el-button></li>
<li><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
v-permissions=
"['system:permission:delete']"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
:default-sort =
"
{prop: 'createTime', order: 'descending'}"
stripe
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>
<el-table-column
type=
"selection"
fixed=
"left"
width=
"55"
></el-table-column>
<el-table-column
prop=
"code"
label=
"权限编码"
fixed=
"left"
min-width=
"200px"
></el-table-column>
<el-table-column
prop=
"name"
label=
"权限名称"
fixed=
"left"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"remark"
label=
"权限备注"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"140px"
sortable=
"custom"
sort-by=
"perm.CREATE_TIME"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
min-width=
"140px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:permission:update', 'system:permission:delete'])"
label=
"操作"
min-width=
"130"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
v-if=
"!row.fixed"
type=
"text"
@
click=
"$refs.operaPermissionWindow.open('编辑系统权限', row)"
icon=
"el-icon-edit"
v-permissions=
"['system:permission:update']"
>
编辑
</el-button>
<el-button
v-if=
"!row.fixed"
type=
"text"
@
click=
"deleteById(row)"
icon=
"el-icon-delete"
v-permissions=
"['system:permission:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
<!-- 新建/修改 -->
<OperaPermissionWindow
ref=
"operaPermissionWindow"
@
success=
"handlePageChange"
/>
</TableLayout>
</template>
<
script
>
import
Pagination
from
'
@/components/common/Pagination
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
OperaPermissionWindow
from
'
@/components/system/permission/OperaPermissionWindow
'
export
default
{
name
:
'
SystemPermission
'
,
extends
:
BaseTable
,
components
:
{
OperaPermissionWindow
,
TableLayout
,
Pagination
},
data
()
{
return
{
// 搜索
searchForm
:
{
code
:
''
,
name
:
''
,
remark
:
''
}
}
},
created
()
{
this
.
config
({
module
:
'
权限
'
,
api
:
'
/system/permission
'
,
sorts
:
[{
property
:
'
perm.CREATE_TIME
'
,
direction
:
'
DESC
'
}]
})
this
.
search
()
}
}
</
script
>
tools/frontend/src/views/System/position.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:position:query']"
>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:position:create', 'system:position:delete']"
>
<li><el-button
type=
"primary"
@
click=
"$refs.operaPositionWindow.open('新建岗位')"
icon=
"el-icon-plus"
v-permissions=
"['system:position:create']"
>
新建
</el-button></li>
<li><el-button
@
click=
"deleteByIdInBatch"
icon=
"el-icon-delete"
v-permissions=
"['system:position:delete']"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
:tree-props=
"
{children: 'children', hasChildren: 'hasChildren'}"
row-key="id"
stripe
default-expand-all
@selection-change="handleSelectionChange"
>
<el-table-column
type=
"selection"
width=
"55"
fixed=
"left"
></el-table-column>
<el-table-column
prop=
"name"
label=
"岗位名称"
fixed=
"left"
min-width=
"200px"
></el-table-column>
<el-table-column
prop=
"code"
label=
"岗位编码"
fixed=
"left"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"userCount"
label=
"岗位人数"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<template
slot-scope=
"
{row}">
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
min-width=
"140px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:position:update', 'system:position:query', 'system:position:delete'])"
label=
"操作"
min-width=
"310"
fixed=
"right"
>
<
template
slot-scope=
"{row}"
>
<el-button
type=
"text"
@
click=
"$refs.operaPositionWindow.open('编辑岗位', row)"
icon=
"el-icon-edit"
v-permissions=
"['system:position:update']"
>
编辑
</el-button>
<el-button
type=
"text"
@
click=
"$refs.operaPositionWindow.open('新增下级岗位', null, row)"
icon=
"el-icon-edit"
v-permissions=
"['system:position:update']"
>
新增下级岗位
</el-button>
<el-button
type=
"text"
@
click=
"$refs.positionUserWindow.open(row.id, row.name)"
icon=
"el-icon-user-solid"
v-permissions=
"['system:position:queryUsers']"
>
查看人员
</el-button>
<el-button
type=
"text"
@
click=
"deleteById(row)"
icon=
"el-icon-delete"
v-permissions=
"['system:position:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</template>
<!-- 新建/修改 -->
<OperaPositionWindow
ref=
"operaPositionWindow"
@
success=
"handlePageChange"
/>
<!-- 人员管理 -->
<PositionUserWindow
ref=
"positionUserWindow"
/>
</TableLayout>
</template>
<
script
>
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
OperaPositionWindow
from
'
@/components/system/position/OperaPositionWindow
'
import
PositionUserWindow
from
'
@/components/system/position/PositionUserWindow
'
import
{
fetchTree
}
from
'
@/api/system/position
'
export
default
{
name
:
'
SystemPosition
'
,
extends
:
BaseTable
,
components
:
{
PositionUserWindow
,
OperaPositionWindow
,
TableLayout
},
methods
:
{
// 查询数据
handlePageChange
()
{
this
.
isWorking
.
search
=
true
fetchTree
()
.
then
(
records
=>
{
this
.
tableData
.
list
=
records
})
.
catch
(
e
=>
{
this
.
$tip
.
apiFailed
(
e
)
})
.
finally
(()
=>
{
this
.
isWorking
.
search
=
false
})
}
},
created
()
{
this
.
config
({
module
:
'
岗位
'
,
api
:
'
/system/position
'
})
this
.
search
()
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.table-layout
{
/
deep
/
.table-content
{
margin-top
:
0
;
}
}
</
style
>
tools/frontend/src/views/System/table.vue
0 → 100644
View file @
0587447b
<
template
>
<el-table
:data=
"tableData"
style=
"width: 100%"
>
<el-table-column
prop=
"date"
label=
"日期"
width=
"180"
>
</el-table-column>
<el-table-column
prop=
"name"
label=
"姓名"
width=
"180"
>
</el-table-column>
<el-table-column
prop=
"address"
label=
"地址"
>
</el-table-column>
</el-table>
</
template
>
<
script
>
import
{
Table
,
TableColumn
}
from
"
element-ui
"
import
'
element-ui/lib/theme-chalk/index.css
'
export
default
{
data
()
{
return
{
tableData
:
[
{
date
:
"
2016-05-02
"
,
name
:
"
王小虎
"
,
address
:
"
上海市普陀区金沙江路 1518 弄
"
,
},
{
date
:
"
2016-05-04
"
,
name
:
"
王小虎
"
,
address
:
"
上海市普陀区金沙江路 1517 弄
"
,
},
{
date
:
"
2016-05-01
"
,
name
:
"
王小虎
"
,
address
:
"
上海市普陀区金沙江路 1519 弄
"
,
},
{
date
:
"
2016-05-03
"
,
name
:
"
王小虎
"
,
address
:
"
上海市普陀区金沙江路 1516 弄
"
,
},
],
};
},
components
:
{
"
el-table
"
:
Table
,
"
el-table-column
"
:
TableColumn
}
};
</
script
>
\ No newline at end of file
tools/frontend/src/views/System/traceLog.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:traceLog:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"100px"
inline
>
<el-form-item
label=
"用户姓名"
prop=
"userRealname"
>
<el-input
v-model=
"searchForm.userRealname"
placeholder=
"请输入固化用户姓名"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"业务模块"
prop=
"operaModule"
>
<el-input
v-model=
"searchForm.operaModule"
placeholder=
"请输入业务模块"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"请求地址"
prop=
"requestUri"
>
<el-input
v-model=
"searchForm.requestUri"
placeholder=
"请输入请求地址"
@
keypress.enter.native=
"search"
></el-input>
</el-form-item>
<el-form-item
label=
"状态"
prop=
"status"
>
<el-select
v-model=
"searchForm.status"
clearable
@
change=
"search"
>
<el-option
value=
"-1"
label=
"未处理"
/>
<el-option
value=
"0"
label=
"失败"
/>
<el-option
value=
"1"
label=
"成功"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"异常等级"
prop=
"exceptionLevel"
>
<el-select
v-model=
"searchForm.exceptionLevel"
clearable
@
change=
"search"
>
<el-option
value=
"10"
label=
"高"
/>
<el-option
value=
"5"
label=
"中"
/>
<el-option
value=
"0"
label=
"低"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"操作时间范围"
>
<el-date-picker
v-model=
"searchDateRange"
type=
"datetimerange"
range-separator=
"至"
value-format=
"yyyy-MM-dd HH:mm:ss"
start-placeholder=
"开始时间"
end-placeholder=
"结束时间"
@
change=
"handleSearchTimeChange"
></el-date-picker>
</el-form-item>
<section>
<el-button
type=
"primary"
@
click=
"search"
>
搜索
</el-button>
<el-button
type=
"primary"
:loading=
"isWorking.export"
@
click=
"exportExcel"
>
导出
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<div
slot=
"space"
class=
"status-bar"
>
<label
class=
"status-normal"
>
正常
</label>
<label
class=
"status-warn"
>
警告异常(需排查)
</label>
<label
class=
"status-danger"
>
系统异常(需修复)
</label>
</div>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
stripe
:default-sort=
"
{prop: 'operaTime', order: 'descending'}"
:row-class-name="tableRowClassName"
@sort-change="handleSortChange"
>
<el-table-column
prop=
"operaModule"
label=
"业务模块"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"operaRemark"
label=
"操作说明"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"requestMethod"
label=
"请求方式"
min-width=
"80px"
></el-table-column>
<el-table-column
prop=
"requestUri"
label=
"请求地址"
min-width=
"200px"
></el-table-column>
<el-table-column
prop=
"status"
label=
"状态"
min-width=
"80px"
>
<template
slot-scope=
"
{row}">
{{
row
.
status
|
statusText
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"requestParams"
label=
"请求参数"
min-width=
"80px"
>
<
template
slot-scope=
"{row}"
>
<ColumnDetail
v-if=
"row.requestParams != null"
:content=
"row.requestParams"
:limit=
"0"
/>
</
template
>
</el-table-column>
<el-table-column
prop=
"requestResult"
label=
"请求结果"
min-width=
"80px"
>
<
template
slot-scope=
"{row}"
>
<ColumnDetail
v-if=
"row.requestResult != null"
:content=
"row.requestResult"
/>
</
template
>
</el-table-column>
<el-table-column
prop=
"exceptionLevel"
label=
"异常等级"
sortable=
"custom"
sort-by=
"EXCEPTION_LEVEL"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
exceptionLevel
|
exceptionLevelText
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"exceptionStack"
label=
"异常信息"
min-width=
"170px"
>
<
template
slot-scope=
"{row}"
>
<ColumnDetail
v-if=
"row.exceptionStack != null"
:content=
"row.exceptionStack"
:button-type=
"getExceptionButtonType(row.exceptionLevel)"
/>
</
template
>
</el-table-column>
<el-table-column
prop=
"operaSpendTime"
label=
"请求耗时(ms)"
sortable=
"custom"
sort-by=
"OPERA_SPEND_TIME"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"userRealname"
label=
"操作人"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"operaTime"
label=
"操作时间"
sortable=
"custom"
sort-by=
"OPERA_TIME"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"platform"
label=
"操作平台"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"systemVersion"
label=
"系统版本"
min-width=
"80px"
></el-table-column>
<el-table-column
prop=
"serverIp"
label=
"处理服务器IP"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"ip"
label=
"用户IP"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"clientInfo"
label=
"用户客户端"
min-width=
"200px"
></el-table-column>
<el-table-column
prop=
"osInfo"
label=
"用户操作系统"
min-width=
"100px"
></el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
</TableLayout>
</template>
<
script
>
import
Pagination
from
'
@/components/common/Pagination
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
ColumnDetail
from
'
../../components/common/ColumnDetail
'
export
default
{
name
:
'
SystemTraceLog
'
,
extends
:
BaseTable
,
components
:
{
ColumnDetail
,
TableLayout
,
Pagination
},
data
()
{
return
{
// 搜索时间反胃
searchDateRange
:
[],
// 搜索
searchForm
:
{
userRealname
:
''
,
operaModule
:
''
,
requestUri
:
''
,
status
:
null
,
exceptionLevel
:
null
,
startTime
:
null
,
endTime
:
null
}
}
},
filters
:
{
// 状态
statusText
(
value
)
{
if
(
value
===
1
)
{
return
'
成功
'
}
if
(
value
===
0
)
{
return
'
失败
'
}
return
'
未处理
'
},
// 异常等级
exceptionLevelText
(
value
)
{
if
(
value
==
null
)
{
return
''
}
if
(
value
===
0
)
{
return
'
低
'
}
if
(
value
===
5
)
{
return
'
中
'
}
if
(
value
===
10
)
{
return
'
高
'
}
return
'
未知
'
}
},
methods
:
{
// 搜索框重置
reset
()
{
this
.
$refs
.
searchForm
.
resetFields
()
this
.
searchDateRange
=
[]
this
.
searchForm
.
startTime
=
null
this
.
searchForm
.
endTime
=
null
this
.
search
()
},
// 标记行class
tableRowClassName
({
row
})
{
if
(
row
.
exceptionLevel
===
5
||
row
.
status
===
-
1
)
{
return
'
warning-log
'
}
else
if
(
row
.
exceptionLevel
===
10
)
{
return
'
danger-log
'
}
return
''
},
// 获取异常查看按钮类型
getExceptionButtonType
(
level
)
{
if
(
level
===
5
)
{
return
'
warning
'
}
if
(
level
===
10
)
{
return
'
danger
'
}
return
null
},
// 时间搜索范围变化
handleSearchTimeChange
(
value
)
{
this
.
searchForm
.
startTime
=
null
this
.
searchForm
.
endTime
=
null
if
(
value
!=
null
)
{
this
.
searchForm
.
startTime
=
value
[
0
]
this
.
searchForm
.
endTime
=
value
[
1
]
}
this
.
search
()
}
},
created
()
{
this
.
config
({
api
:
'
/system/traceLog
'
,
sorts
:
[{
property
:
'
OPERA_TIME
'
,
direction
:
'
DESC
'
}]
})
this
.
search
()
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
// 状态栏
.status-bar
{
padding
:
0
16px
;
[
class
^=
status-
]
{
font-size
:
13px
;
margin-right
:
12px
;
line-height
:
40px
;
&
:
:
before
{
position
:
relative
;
top
:
2px
;
display
:
inline-block
;
content
:
''
;
width
:
12px
;
height
:
12px
;
border
:
1px
solid
#ccc
;
background
:
#fff
;
margin-right
:
6px
;
}
}
.
status-warn
:
:
before
{
background-color
:
oldlace
;
border-color
:
orange
;
}
.
status-danger
:
:
before
{
background-color
:
#fdf0f0
;
border-color
:
indianred
;
}
}
/
deep
/
.table-content
{
margin-top
:
0
;
}
// 警告级日志
/
deep
/
.warning-log
td
{
background-color
:
oldlace
!
important
;
}
// 危险级日志
/
deep
/
.danger-log
td
{
background-color
:
#fdf0f0
!
important
;
}
</
style
>
tools/frontend/src/views/System/user.vue
0 → 100644
View file @
0587447b
<
template
>
<TableLayout
:permissions=
"['system:user:query']"
>
<!-- 搜索表单 -->
<el-form
ref=
"searchForm"
slot=
"search-form"
:model=
"searchForm"
label-width=
"80px"
inline
>
<el-form-item
label=
"用户名"
prop=
"username"
>
<el-input
v-model=
"searchForm.username"
v-trim
placeholder=
"请输入用户名"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"姓名"
prop=
"realname"
>
<el-input
v-model=
"searchForm.realname"
v-trim
placeholder=
"请输入姓名"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"手机号码"
prop=
"mobile"
>
<el-input
v-model=
"searchForm.mobile"
v-trim
placeholder=
"请输入手机号码"
@
keypress.enter.native=
"search"
/>
</el-form-item>
<el-form-item
label=
"所属部门"
prop=
"rootDeptId"
>
<DepartmentSelect
v-model=
"searchForm.rootDeptId"
placeholder=
"请选择所属部门"
clearable
/>
</el-form-item>
<el-form-item
label=
"岗位"
prop=
"positionId"
>
<PositionSelect
v-model=
"searchForm.positionId"
placeholder=
"请选择岗位"
clearable
/>
</el-form-item>
<section>
<el-button
type=
"primary"
icon=
"el-icon-search"
@
click=
"search"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
</section>
</el-form>
<!-- 表格和分页 -->
<template
v-slot:table-wrap
>
<ul
class=
"toolbar"
v-permissions=
"['system:user:create', 'system:user:delete']"
>
<li
v-permissions=
"['system:user:create']"
><el-button
icon=
"el-icon-plus"
type=
"primary"
@
click=
"$refs.operaUserWindow.open('新建用户')"
>
新建
</el-button></li>
<li
v-permissions=
"['system:user:delete']"
><el-button
icon=
"el-icon-delete"
@
click=
"deleteByIdInBatch"
>
删除
</el-button></li>
</ul>
<el-table
v-loading=
"isWorking.search"
:data=
"tableData.list"
:default-sort =
"
{prop: 'createTime', order: 'descending'}"
stripe
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>
<el-table-column
type=
"selection"
width=
"55"
></el-table-column>
<el-table-column
prop=
"avatar"
label=
"头像"
width=
"80px"
class-name=
"table-column-avatar"
fixed=
"left"
>
<template
slot-scope=
"
{row}">
<img
:src=
"row.avatar == null ? '/static/avatar/man.png' : row.avatar"
>
</
template
>
</el-table-column>
<el-table-column
prop=
"realname"
label=
"姓名"
min-width=
"100px"
fixed=
"left"
></el-table-column>
<el-table-column
prop=
"username"
label=
"用户名"
min-width=
"120px"
></el-table-column>
<el-table-column
prop=
"empNo"
label=
"工号"
sortable=
"custom"
sort-by=
"EMP_NO"
min-width=
"80px"
></el-table-column>
<el-table-column
prop=
"department"
label=
"部门"
min-width=
"120px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
department
==
null
?
''
:
row
.
department
.
name
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"position"
label=
"岗位"
min-width=
"160px"
class-name=
"table-column-strings"
>
<
template
slot-scope=
"{row}"
>
<ul>
<li
v-for=
"position in row.positions"
:key=
"position.id"
>
{{
position
.
name
}}
</li>
</ul>
</
template
>
</el-table-column>
<el-table-column
prop=
"sex"
label=
"性别"
sortable=
"custom"
sort-by=
"SEX"
min-width=
"80px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
sex
|
sex
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"mobile"
label=
"手机号码"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"email"
label=
"邮箱"
min-width=
"180px"
></el-table-column>
<el-table-column
prop=
"birthday"
label=
"生日"
sortable=
"custom"
sort-by=
"BIRTHDAY"
min-width=
"100px"
></el-table-column>
<el-table-column
prop=
"roles"
label=
"角色"
min-width=
"160px"
class-name=
"table-column-strings"
>
<
template
slot-scope=
"{row}"
>
<ul>
<li
v-for=
"role in row.roles"
:key=
"role.id"
>
{{
role
.
name
}}
</li>
</ul>
</
template
>
</el-table-column>
<el-table-column
prop=
"createUser"
label=
"创建人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
createUserInfo
==
null
?
''
:
row
.
createUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"创建时间"
sortable=
"custom"
sort-by=
"CREATE_TIME"
min-width=
"140px"
></el-table-column>
<el-table-column
prop=
"updateUser"
label=
"更新人"
min-width=
"100px"
>
<
template
slot-scope=
"{row}"
>
{{
row
.
updateUserInfo
==
null
?
''
:
row
.
updateUserInfo
.
username
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
sortable=
"custom"
sort-by=
"UPDATE_TIME"
min-width=
"140px"
></el-table-column>
<el-table-column
v-if=
"containPermissions(['system:user:update', 'system:user:createUserRole', 'system:user:resetPwd', 'system:user:delete'])"
label=
"操作"
width=
"270"
fixed=
"right"
>
<
template
v-if=
"isAdmin || (row.id !== userInfo.id && row.roles.findIndex(r => r.code === adminCode) === -1)"
slot-scope=
"{row}"
>
<el-button
type=
"text"
icon=
"el-icon-edit"
@
click=
"$refs.operaUserWindow.open('编辑用户', row)"
v-permissions=
"['system:user:update']"
>
编辑
</el-button>
<el-button
type=
"text"
icon=
"el-icon-s-custom"
@
click=
"$refs.roleConfigWindow.open(row)"
v-permissions=
"['system:user:createUserRole']"
>
配置角色
</el-button>
<el-button
type=
"text"
@
click=
"$refs.resetPwdWindow.open(row)"
v-permissions=
"['system:user:resetPwd']"
>
重置密码
</el-button>
<el-button
v-if=
"!row.fixed"
type=
"text"
icon=
"el-icon-delete"
@
click=
"deleteById(row)"
v-permissions=
"['system:user:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
<pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handlePageChange"
:pagination=
"tableData.pagination"
></pagination>
</template>
<!-- 新建/修改 -->
<OperaUserWindow
ref=
"operaUserWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
<!-- 配置角色 -->
<RoleConfigWindow
ref=
"roleConfigWindow"
@
success=
"handlePageChange(tableData.pagination.pageIndex)"
/>
<!-- 重置密码 -->
<ResetPwdWindow
ref=
"resetPwdWindow"
/>
</TableLayout>
</template>
<
script
>
import
Pagination
from
'
@/components/common/Pagination
'
import
TableLayout
from
'
@/layouts/TableLayout
'
import
BaseTable
from
'
@/components/base/BaseTable
'
import
OperaUserWindow
from
'
@/components/system/user/OperaUserWindow
'
import
RoleConfigWindow
from
'
@/components/system/user/RoleConfigWindow
'
import
ResetPwdWindow
from
'
@/components/system/user/ResetPwdWindow
'
import
DepartmentSelect
from
'
@/components/common/DepartmentSelect
'
import
PositionSelect
from
'
@/components/common/PositionSelect
'
export
default
{
name
:
'
SystemUser
'
,
extends
:
BaseTable
,
components
:
{
PositionSelect
,
DepartmentSelect
,
ResetPwdWindow
,
RoleConfigWindow
,
OperaUserWindow
,
TableLayout
,
Pagination
},
data
()
{
return
{
// 搜索
searchForm
:
{
username
:
''
,
// 名字
realname
:
''
,
// 姓名
rootDeptId
:
null
,
// 部门ID
positionId
:
null
,
// 岗位ID
mobile
:
''
// 手机号码
}
}
},
created
()
{
this
.
config
({
module
:
'
用户
'
,
api
:
'
/system/user
'
,
'
field.main
'
:
'
realname
'
,
sorts
:
[{
property
:
'
CREATE_TIME
'
,
direction
:
'
DESC
'
}]
})
this
.
search
()
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
@import
"@/styles/variables.scss"
;
// 列表头像处理
.table-column-avatar
{
img
{
width
:
48px
;
}
}
</
style
>
tools/frontend/yarn.lock
View file @
0587447b
...
...
@@ -257,6 +257,14 @@
dependencies:
"@babel/types" "^7.14.5"
"@babel/helper-module-imports@7.0.0-beta.35":
version "7.0.0-beta.35"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.35.tgz#308e350e731752cdb4d0f058df1d704925c64e0a"
integrity sha512-vaC1KyIZSuyWb3Lj277fX0pxivyHwuDU4xZsofqgYAbkDxNieMg2vuhzP5AgMweMY7fCQUMTi+BgPqTLjkxXFg==
dependencies:
"@babel/types" "7.0.0-beta.35"
lodash "^4.2.0"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
...
...
@@ -803,6 +811,15 @@
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@7.0.0-beta.35":
version "7.0.0-beta.35"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.35.tgz#cf933a9a9a38484ca724b335b88d83726d5ab960"
integrity sha512-y9XT11CozHDgjWcTdxmhSj13rJVXpa5ZXwjjOiTedjaM0ba5ItqdS02t31EhPl7HtOWxsZkYCCUNrSfrOisA6w==
dependencies:
esutils "^2.0.2"
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@babel/types@^7.14.5", "@babel/types@^7.7.0":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff"
...
...
@@ -872,6 +889,122 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
"@interactjs/actions@1.10.11", "@interactjs/actions@^1.10.2":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/actions/-/actions-1.10.11.tgz#ec68fd60bee751f80c650964b5ba299eb6afe78c"
integrity sha512-P39zeefr4hkmKx+5nZ+mrH1s0l2YJ3gIHrthXmE81n6MlMa42m0WtHcTms4C5JTTNBP2EEDY+KGgGxSnmJKvUw==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/auto-scroll@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/auto-scroll/-/auto-scroll-1.10.11.tgz#0c0ac7dbb55aa7d7df6c0a04c77ebb3148cbdf54"
integrity sha512-feHNjhi0EMNLV2nQcEgjYPz2mI54aeSW2RiaoNtFLyBvtXKp0b4DmluwDv6DvuXmUpDwD5g/Hk1gGM2rgl7iqQ==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/auto-start@1.10.11", "@interactjs/auto-start@^1.10.2":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/auto-start/-/auto-start-1.10.11.tgz#5ce045740f35be36640ebad053db7b5652e18e70"
integrity sha512-cIg5CcalCPtC6AiGq6j/0hKUtL2MweEpvw12FuB19sz2Q9Dye0J4GliHKhOYvtumNinnvfVAZ4FZMqZEuX7YZA==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/core@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/core/-/core-1.10.11.tgz#8b0203492c1ba6f8432f20b718ae53707fcfc724"
integrity sha512-aJ50ccVeszpJt7wPH7Yfqm7f1aG1SA94qd90P0NaESh5/QUXn4CESO6igobo4DFHQ5z+1Rfdl8aphP4JxlH4gw==
"@interactjs/dev-tools@1.10.11", "@interactjs/dev-tools@^1.10.2":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/dev-tools/-/dev-tools-1.10.11.tgz#8d4b5b650cf74e800909f52962008700143a4304"
integrity sha512-BP2FNfMbF7zLuOAUGMkDhCo1e1B0fnqyb9ih/Y8yAIJuoLrZxP/9htbsS1vZOIVZ4UgtrId4cYOwfcAZBMQtmw==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/inertia@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/inertia/-/inertia-1.10.11.tgz#58864173310985b8247d84e347148ea6cd7b88a8"
integrity sha512-h+sknCzRqBSyHy4ctPNsq56mxkAMMdwHWD6en7rDEw899gdGKYaXVDVdv1jMfiwNRw0eRFBNoCiol8r3a/a3Jw==
dependencies:
"@interactjs/offset" "1.10.11"
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/interact@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/interact/-/interact-1.10.11.tgz#d96e3f949ee4001a6a34dc363a232646f9dd2b1b"
integrity sha512-0iZJ9l547JuBA/lKxK4ARGYVmMqRSsAdA8gXL1zWe51qEIQq8PyWmMipoi8JbDaL7exC2THKwkXu5uq5ndT+iA==
dependencies:
"@interactjs/core" "1.10.11"
"@interactjs/types" "1.10.11"
"@interactjs/utils" "1.10.11"
"@interactjs/interactjs@^1.10.2":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/interactjs/-/interactjs-1.10.11.tgz#d0fdd6b03c1c855043b1f608a10b2f5ccac4b4b7"
integrity sha512-cGOxf6rp3Y8/sk88LhIT0XDn4gCiCzAnUG5Kkj9SAqiUO6BK/9+Wbp1IBkNaPgl/8uG8gNHh/dXBrlBBNcqJAg==
dependencies:
"@interactjs/actions" "1.10.11"
"@interactjs/auto-scroll" "1.10.11"
"@interactjs/auto-start" "1.10.11"
"@interactjs/core" "1.10.11"
"@interactjs/dev-tools" "1.10.11"
"@interactjs/inertia" "1.10.11"
"@interactjs/interact" "1.10.11"
"@interactjs/modifiers" "1.10.11"
"@interactjs/offset" "1.10.11"
"@interactjs/pointer-events" "1.10.11"
"@interactjs/reflow" "1.10.11"
"@interactjs/utils" "1.10.11"
"@interactjs/modifiers@1.10.11", "@interactjs/modifiers@^1.10.2":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/modifiers/-/modifiers-1.10.11.tgz#f40962a97fd3e110e66b79664796c24f3c4e6cd6"
integrity sha512-ltqX1RSqeAIikixlQBlyEUdclT5+rbfIGi3sIdLLYaIZQnltYkWqL9MHKx/w5b+hV+Mc0p5MLUFWJbTdkSCZ9g==
dependencies:
"@interactjs/snappers" "1.10.11"
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/offset@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/offset/-/offset-1.10.11.tgz#512242f330dc80cdbda4feda8fb34c0491f50496"
integrity sha512-mBT7eIfy5ivofECiv+VwtEwwIMLV54fT9ujSMWJPduxdSYIHepUWgEf/3zjJknFh6jQc7pqz9dtjvVvyzRCLlQ==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/pointer-events@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/pointer-events/-/pointer-events-1.10.11.tgz#ff4c74a75d7711fc1006ebf32ea344e35bffe938"
integrity sha512-yBT8JJVMZ+MgBay5l1WAHnL8ch/mZsRfaFahti+QFYeQyRloDtsWmEMDSYI/Onyy9+hS3gN/ge77ArGciZZ0Ow==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/reflow@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/reflow/-/reflow-1.10.11.tgz#43d2ad8ca002bf98091273d179fd70b1cabfb9e2"
integrity sha512-NSCtcCkjImOYSbxzzv2kFqR9t49J8KlhEr9UoePc7GyLbNXsiv3WQ3n0ehZd7CgZXQDiVXnP2UnmIOv5Zd4HQg==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/snappers@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/snappers/-/snappers-1.10.11.tgz#3eb6e45ab8319c0dd4b60b284c55c87561aaadb1"
integrity sha512-yYtOMUZ7aFUZ1IYheq9Tj5hZ4J1r5dnaXhLF44WsI/awQ5L0DjZf07GPWof0B+7rZHEVudxyQNbPfFmb+1K94Q==
optionalDependencies:
"@interactjs/interact" "1.10.11"
"@interactjs/types@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/types/-/types-1.10.11.tgz#29be25d503f9c7842df062fa3cda5b044a47cf2a"
integrity sha512-YRsVFWjL8Gkkvlx3qnjeaxW4fnibSJ9791g8BA7Pv5ANByI64WmtR1vU7A2rXcrOn8XvyCEfY0ss1s8NhZP+MA==
"@interactjs/utils@1.10.11":
version "1.10.11"
resolved "https://registry.yarnpkg.com/@interactjs/utils/-/utils-1.10.11.tgz#939d0f128dfa96c673276cca3eb7f313d92daabf"
integrity sha512-410ZoxKF+r1roeSelL+WHXfdryUMg5iykC1XwQ3l6XqNw43IMACzyvTH6k6Pwxj7w7x42nce0Qdn1GQ3Y8xyCw==
"@intervolga/optimize-cssnano-plugin@^1.0.5":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz#be7c7846128b88f6a9b1d1261a0ad06eb5c0fdf8"
...
...
@@ -1140,7 +1273,7 @@
resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-3.12.1.tgz#bdfde8f7123561ab06e4e4c60b854cc5092f5ab1"
integrity sha512-Bym92EN+lj+cNRN2ozbYyH+V8DMXWGbCDUk+hiJ4EYDBZfBkZKvalk1/mOBFwyxiopnnbOEBAAhL/UuMQ1xARg==
"@vue/cli-plugin-babel@^3.
0.3
":
"@vue/cli-plugin-babel@^3.
3.0
":
version "3.12.1"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-3.12.1.tgz#9a79159de8cd086b013fa6d78a39830b2e2ec706"
integrity sha512-Zetvz8PikLCGomeKOKu8pC9YQ7cfxs7pGpvEOzaxGdhMnebhjAYR6i6dOB57A6N5lhxQksXCtYTv26QgfiIpdg==
...
...
@@ -1151,7 +1284,7 @@
babel-loader "^8.0.5"
webpack "^4.0.0"
"@vue/cli-plugin-eslint@^3.
0.3
":
"@vue/cli-plugin-eslint@^3.
3.0
":
version "3.12.1"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.12.1.tgz#302c463867f38e790bb996eafdf7159c782dc8cf"
integrity sha512-tVTZlEZsy3sQbO4LLWFK11yzlWwqVAqaM+IY+BeWHITBzEJKh2KmouG+x6x/reXiU3qROsMJ4Ej3Hs8buSMWyQ==
...
...
@@ -1166,7 +1299,7 @@
eslint "^4.19.1"
eslint-plugin-vue "^4.7.1"
"@vue/cli-service@^3.
0.3
":
"@vue/cli-service@^3.
3.0
":
version "3.12.1"
resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-3.12.1.tgz#13220b1c189254e7c003390df329086f9b6e77e6"
integrity sha512-PDxNrTGnSKzeV1ruFlsRIAO8JcPizwT0EJXq9GeyooU+p+sOkv7aKkCBJQVYNjZapD1NOGWx6CvAAC/wAW+gew==
...
...
@@ -1879,6 +2012,13 @@ async-validator@^3.0.3:
resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500"
integrity sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ==
async-validator@~1.8.1:
version "1.8.5"
resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-1.8.5.tgz#dc3e08ec1fd0dddb67e60842f02c0cd1cec6d7f0"
integrity sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==
dependencies:
babel-runtime "6.x"
async@^2.4.0, async@^2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
...
...
@@ -1948,7 +2088,7 @@ babel-eslint@^10.0.1, babel-eslint@^10.1.0:
eslint-visitor-keys "^1.0.0"
resolve "^1.12.0"
babel-helper-vue-jsx-merge-props@^2.0.3:
babel-helper-vue-jsx-merge-props@^2.0.
0, babel-helper-vue-jsx-merge-props@^2.0.
3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6"
integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==
...
...
@@ -1963,6 +2103,13 @@ babel-loader@^8.0.5:
make-dir "^3.1.0"
schema-utils "^2.6.5"
babel-plugin-component@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/babel-plugin-component/-/babel-plugin-component-1.1.1.tgz#9b023a23ff5c9aae0fd56c5a18b9cab8c4d45eea"
integrity sha512-WUw887kJf2GH80Ng/ZMctKZ511iamHNqPhd9uKo14yzisvV7Wt1EckIrb8oq/uCz3B3PpAW7Xfl7AkTLDYT6ag==
dependencies:
"@babel/helper-module-imports" "7.0.0-beta.35"
babel-plugin-dynamic-import-node@^2.2.0, babel-plugin-dynamic-import-node@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
...
...
@@ -2044,6 +2191,11 @@ base@^0.11.1:
mixin-deep "^1.2.0"
pascalcase "^0.1.1"
batch-processor@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8"
integrity sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=
batch@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
...
...
@@ -3694,7 +3846,7 @@ deepmerge@1.3.2:
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.3.2.tgz#1663691629d4dbfe364fa12a2a4f0aa86aa3a050"
integrity sha1-FmNpFinU2/42T6EqKk8KqGqjoFA=
deepmerge@^1.5.2:
deepmerge@^1.
2.0, deepmerge@^1.
5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==
...
...
@@ -4049,6 +4201,14 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
echarts@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.1.2.tgz#aa1ab0cef5b74fa2f7c620261a5f286893d30fd1"
integrity sha512-okUhO4sw22vwZp+rTPNjd/bvTdpug4K4sHNHyrV8NdAncIX9/AarlolFqtJCAYKGFYhUBNjIWu1EznFrSWTFxg==
dependencies:
tslib "2.0.3"
zrender "5.1.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
...
...
@@ -4064,6 +4224,25 @@ electron-to-chromium@^1.3.723:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09"
integrity sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==
element-resize-detector@^1.2.1:
version "1.2.3"
resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.2.3.tgz#5078d9b99398fe4c589f8c8df94ff99e5d413ff3"
integrity sha512-+dhNzUgLpq9ol5tyhoG7YLoXL3ssjfFW+0gpszXPwRU6NjGr1fVHMEAF8fVzIiRJq57Nre0RFeIjJwI8Nh2NmQ==
dependencies:
batch-processor "1.0.0"
element-ui@^2.15.3:
version "2.15.3"
resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.15.3.tgz#55108ab82a3bcc646e7b0570871c48ba96300652"
integrity sha512-yGcK0AspuC827Nq7GUHct83cywAKIDo+kpp/rtov5ptmK1hZ8FMlt2SKbcozmSabmpdBNroMgqxqXl6IT1zy1A==
dependencies:
async-validator "~1.8.1"
babel-helper-vue-jsx-merge-props "^2.0.0"
deepmerge "^1.2.0"
normalize-wheel "^1.0.1"
resize-observer-polyfill "^1.5.0"
throttle-debounce "^1.0.1"
elliptic@^6.5.3:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
...
...
@@ -6896,7 +7075,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.10:
lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.
2.0, lodash@^4.
3.0, lodash@~4.17.10:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
...
...
@@ -7298,7 +7477,6 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2:
resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a"
integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ==
dependencies:
encoding "^0.1.12"
minipass "^3.1.0"
minipass-sized "^1.0.3"
minizlib "^2.0.0"
...
...
@@ -7727,6 +7905,11 @@ normalize-url@^4.1.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
normalize-wheel@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
integrity sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=
npm-bundled@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1"
...
...
@@ -9506,7 +9689,7 @@ reselect@^3.0.1:
resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147"
integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=
resize-observer-polyfill@^1.5.1:
resize-observer-polyfill@^1.5.
0, resize-observer-polyfill@^1.5.
1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
...
...
@@ -10752,6 +10935,11 @@ thread-loader@^2.1.2:
loader-utils "^1.1.0"
neo-async "^2.6.0"
throttle-debounce@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz#51853da37be68a155cb6e827b3514a3c422e89cd"
integrity sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==
through2@^2.0.0:
version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
...
...
@@ -10922,6 +11110,11 @@ tryer@^1.0.1:
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
tslib@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
tslib@^1.10.0, tslib@^1.9.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
...
...
@@ -11417,6 +11610,18 @@ vue-eslint-parser@^7.8.0:
lodash "^4.17.21"
semver "^6.3.0"
vue-grid-layout@^2.3.12:
version "2.3.12"
resolved "https://registry.yarnpkg.com/vue-grid-layout/-/vue-grid-layout-2.3.12.tgz#b6396357b86a66805c117431d7c193d2e066edda"
integrity sha512-x9l4KxfH0MeB4xImanrnnTihksq8LYk3f40hm1sdiTHF2bYM+Xhae6eQsvFWEFwbYq7RVNvB80qwis1vInB+WQ==
dependencies:
"@interactjs/actions" "^1.10.2"
"@interactjs/auto-start" "^1.10.2"
"@interactjs/dev-tools" "^1.10.2"
"@interactjs/interactjs" "^1.10.2"
"@interactjs/modifiers" "^1.10.2"
element-resize-detector "^1.2.1"
vue-hot-reload-api@^2.3.0:
version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
...
...
@@ -11469,7 +11674,12 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue@^2.5.17:
vue-treeselect@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/vue-treeselect/-/vue-treeselect-1.0.7.tgz#589f689019b6c91d1b3bba946dbdb63565b13f05"
integrity sha512-NNtr0elV/diw9KSmkiXhKzUaG9FPx8PSg5dHXcl288ip94qfUyXnA5M6mgtWREMlw4Ufs4+lQHDuCONy7sIccg==
vue@^2.6.12:
version "2.6.14"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==
...
...
@@ -11922,3 +12132,10 @@ yorkie@^2.0.0:
is-ci "^1.0.10"
normalize-path "^1.0.0"
strip-indent "^2.0.0"
zrender@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.1.1.tgz#0515f4f8cc0f4742f02a6b8819550a6d13d64c5c"
integrity sha512-oeWlmUZPQdS9f5hK4pV21tHPqA3wgQ7CkKkw7l0CCBgWlJ/FP+lRgLFtUBW6yam4JX8y9CdHJo1o587VVrbcoQ==
dependencies:
tslib "2.0.3"
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment