style: 调整后台管理框架样式

This commit is contained in:
RockYang 2023-06-21 18:44:18 +08:00
parent 17713d05ec
commit 2700b63887
24 changed files with 566 additions and 338 deletions

View File

@ -5,6 +5,7 @@ import (
"chatplus/core/types" "chatplus/core/types"
"chatplus/handler" "chatplus/handler"
logger2 "chatplus/logger" logger2 "chatplus/logger"
"chatplus/store/model"
"chatplus/utils" "chatplus/utils"
"chatplus/utils/resp" "chatplus/utils/resp"
@ -70,3 +71,37 @@ func (h *ManagerHandler) Session(c *gin.Context) {
resp.SUCCESS(c) resp.SUCCESS(c)
} }
} }
// TestUser 修正用户配置数据接口
// 将用户订阅角色的数据结构从 map 改成数组
func (h *ManagerHandler) TestUser(c *gin.Context) {
var users []model.User
h.db.Find(&users)
for _, u := range users {
var m map[string]int
var roleKeys = make([]string, 0)
err := utils.JsonDecode(u.ChatRoles, &m)
if err != nil {
continue
}
for k, _ := range m {
roleKeys = append(roleKeys, k)
}
u.ChatRoles = utils.JsonEncode(roleKeys)
h.db.Updates(&u)
}
resp.SUCCESS(c, "SUCCESS")
}
// TestRole 修改角色图片,改成绝对路径
func (h *ManagerHandler) TestRole(c *gin.Context) {
var roles []model.ChatRole
h.db.Find(&roles)
for _, r := range roles {
r.Icon = "/" + r.Icon
h.db.Updates(&r)
}
resp.SUCCESS(c, "SUCCESS")
}

View File

@ -143,20 +143,3 @@ func (h *UserHandler) LoginLog(c *gin.Context) {
resp.SUCCESS(c, vo.NewPage(total, page, pageSize, logs)) resp.SUCCESS(c, vo.NewPage(total, page, pageSize, logs))
} }
func (h *UserHandler) InitUser(c *gin.Context) {
var users []model.User
h.db.Find(&users)
for _, u := range users {
var m map[string]int
var roleKeys = make([]string, 0)
utils.JsonDecode(u.ChatRoles, &m)
for k, _ := range m {
roleKeys = append(roleKeys, k)
}
u.ChatRoles = utils.JsonEncode(roleKeys)
h.db.Updates(&u)
}
resp.SUCCESS(c, "SUCCESS")
}

View File

@ -125,19 +125,19 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
if userVo.Status == false { if userVo.Status == false {
replyMessage(ws, "您的账号已经被禁用,如果疑问,请联系管理员!") replyMessage(ws, "您的账号已经被禁用,如果疑问,请联系管理员!")
replyMessage(ws, "![](images/wx.png)") replyMessage(ws, "![](/images/wx.png)")
return nil return nil
} }
if userVo.Calls <= 0 { if userVo.Calls <= 0 {
replyMessage(ws, "您的对话次数已经用尽,请联系管理员充值!") replyMessage(ws, "您的对话次数已经用尽,请联系管理员充值!")
replyMessage(ws, "![](images/wx.png)") replyMessage(ws, "![](/images/wx.png)")
return nil return nil
} }
if userVo.ExpiredTime > 0 && userVo.ExpiredTime <= time.Now().Unix() { if userVo.ExpiredTime > 0 && userVo.ExpiredTime <= time.Now().Unix() {
replyMessage(ws, "您的账号已经过期,请联系管理员!") replyMessage(ws, "您的账号已经过期,请联系管理员!")
replyMessage(ws, "![](images/wx.png)") replyMessage(ws, "![](/images/wx.png)")
return nil return nil
} }
var req = types.ApiRequest{ var req = types.ApiRequest{
@ -189,7 +189,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
} }
replyMessage(ws, ErrorMsg) replyMessage(ws, ErrorMsg)
replyMessage(ws, "![](images/wx.png)") replyMessage(ws, "![](/images/wx.png)")
return err return err
} else { } else {
defer response.Body.Close() defer response.Body.Close()
@ -221,7 +221,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession
if err != nil { // 数据解析出错 if err != nil { // 数据解析出错
logger.Error(err, line) logger.Error(err, line)
replyMessage(ws, ErrorMsg) replyMessage(ws, ErrorMsg)
replyMessage(ws, "![](images/wx.png)") replyMessage(ws, "![](/images/wx.png)")
break break
} }

View File

@ -125,6 +125,8 @@ func main() {
group.POST("login", h.Login) group.POST("login", h.Login)
group.GET("logout", h.Logout) group.GET("logout", h.Logout)
group.GET("session", h.Session) group.GET("session", h.Session)
group.GET("test/user", h.TestUser)
group.GET("test/role", h.TestRole)
}), }),
fx.Invoke(func(s *core.AppServer, h *admin.ApiKeyHandler) { fx.Invoke(func(s *core.AppServer, h *admin.ApiKeyHandler) {
group := s.Engine.Group("/api/admin/apikey/") group := s.Engine.Group("/api/admin/apikey/")
@ -138,7 +140,6 @@ func main() {
group.POST("update", h.Update) group.POST("update", h.Update)
group.GET("remove", h.Remove) group.GET("remove", h.Remove)
group.GET("loginLog", h.LoginLog) group.GET("loginLog", h.LoginLog)
group.GET("test", h.InitUser)
}), }),
fx.Invoke(func(s *core.AppServer, h *admin.ChatRoleHandler) { fx.Invoke(func(s *core.AppServer, h *admin.ChatRoleHandler) {
group := s.Engine.Group("/api/admin/role/") group := s.Engine.Group("/api/admin/role/")

View File

@ -1,23 +1,22 @@
.header{ .admin-home .header {
background-color: #242f42; background-color: #242f42;
} }
.login-wrap{ .admin-home .login-wrap {
background: #324157; background: #324157;
} }
.plugins-tips{ .admin-home .plugins-tips {
background: #eef1f6; background: #eef1f6;
} }
.plugins-tips a{ .admin-home .plugins-tips a {
color: #20a0ff; color: #20a0ff;
} }
.admin-home .tags-li.active {
.tags-li.active { border: 1px solid #409eff;
border: 1px solid #409EFF; background-color: #409eff;
background-color: #409EFF;
} }
.message-title{ .admin-home .message-title {
color: #20a0ff; color: #20a0ff;
} }
.collapse-btn:hover{ .admin-home .collapse-btn:hover {
background: rgb(40,52,70); background: #283446;
} }

View File

@ -0,0 +1,30 @@
.admin-home {
.header {
background-color: #242f42;
}
.login-wrap {
background: #324157;
}
.plugins-tips {
background: #eef1f6;
}
.plugins-tips a {
color: #20a0ff;
}
.tags-li.active {
border: 1px solid #409EFF;
background-color: #409EFF;
}
.message-title {
color: #20a0ff;
}
.collapse-btn:hover {
background: rgb(40, 52, 70);
}
}

View File

@ -1,138 +1,118 @@
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
html, html,
body, body,
#app, #app,
.wrapper { .wrapper {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }
body { body {
font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif; font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
} }
.admin-home a {
a { text-decoration: none;
text-decoration: none
} }
.admin-home .content-box {
position: absolute;
.content-box { left: 250px;
position: absolute; right: 0;
left: 250px; top: 70px;
right: 0; bottom: 0;
top: 70px; padding-bottom: 30px;
bottom: 0; -webkit-transition: left 0.3s ease-in-out;
padding-bottom: 30px; transition: left 0.3s ease-in-out;
-webkit-transition: left .3s ease-in-out; background: #f0f0f0;
transition: left .3s ease-in-out;
background: #f0f0f0;
} }
.admin-home .content-box .content {
.content { width: auto;
width: auto; height: 100%;
height: 100%; padding: 10px;
padding: 10px; overflow-y: scroll;
overflow-y: scroll; box-sizing: border-box;
box-sizing: border-box;
}
.content-collapse {
left: 65px;
}
.container {
padding: 30px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
}
.crumbs {
margin: 10px 0;
}
.el-table th {
background-color: #f5f7fa !important;
}
.pagination {
margin: 20px 0;
text-align: right;
}
.plugins-tips {
padding: 20px 10px;
margin-bottom: 20px;
}
.el-button + .el-tooltip {
margin-left: 10px;
}
.el-table tr:hover {
background: #f6faff;
}
.mgb20 {
margin-bottom: 20px;
}
.move-enter-active,
.move-leave-active {
transition: opacity .1s ease;
}
.move-enter-from,
.move-leave-to {
opacity: 0;
}
/*BaseForm*/ /*BaseForm*/
.form-box {
width: 600px;
} }
.admin-home .content-box .content .container {
.form-box .line { padding: 30px;
text-align: center; background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
} }
.admin-home .content-box .content .container .handle-box {
.el-time-panel__content::after, margin-bottom: 20px;
.el-time-panel__content::before {
margin-top: -7px;
} }
.admin-home .content-box .content .crumbs {
.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) { margin: 10px 0;
padding-bottom: 0;
} }
.admin-home .content-box .content .el-table th {
background-color: #f5f7fa !important;
[class*=" el-icon-"], [class^=el-icon-] {
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} }
.admin-home .content-box .content .pagination {
.el-sub-menu [class^=el-icon-] { margin: 20px 0;
vertical-align: middle; display: flex;
margin-right: 5px; justify-content: center;
width: 24px; width: 100%;
text-align: center;
font-size: 18px;
} }
.admin-home .content-box .content .plugins-tips {
[hidden] { padding: 20px 10px;
display: none !important; margin-bottom: 20px;
}
.admin-home .content-box .content .el-button + .el-tooltip {
margin-left: 10px;
}
.admin-home .content-box .content .el-table tr:hover {
background: #f6faff;
}
.admin-home .content-box .content .mgb20 {
margin-bottom: 20px;
}
.admin-home .content-box .content .move-enter-active,
.admin-home .content-box .content .move-leave-active {
transition: opacity 0.1s ease;
}
.admin-home .content-box .content .move-enter-from,
.admin-home .content-box .content .move-leave-to {
opacity: 0;
}
.admin-home .content-box .content .form-box {
width: 600px;
}
.admin-home .content-box .content .form-box .line {
text-align: center;
}
.admin-home .content-box .content .el-time-panel__content::after,
.admin-home .content-box .content .el-time-panel__content::before {
margin-top: -7px;
}
.admin-home .content-box .content .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
padding-bottom: 0;
}
.admin-home .content-box .content [class*=" el-icon-"],
.admin-home .content-box .content [class^=el-icon-] {
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.admin-home .content-box .content .el-sub-menu [class^=el-icon-] {
vertical-align: middle;
margin-right: 5px;
width: 24px;
text-align: center;
font-size: 18px;
}
.admin-home .content-box .content [hidden] {
display: none !important;
}
.admin-home .content-collapse {
left: 65px;
} }

View File

@ -0,0 +1,149 @@
* {
margin: 0;
padding: 0;
}
html,
body,
#app,
.wrapper {
width: 100%;
height: 100%;
overflow: hidden;
}
body {
font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
}
.admin-home {
a {
text-decoration: none
}
.content-box {
position: absolute;
left: 250px;
right: 0;
top: 70px;
bottom: 0;
padding-bottom: 30px;
-webkit-transition: left .3s ease-in-out;
transition: left .3s ease-in-out;
background: #f0f0f0;
.content {
width: auto;
height: 100%;
padding: 10px;
overflow-y: scroll;
box-sizing: border-box;
.container {
padding: 30px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
.handle-box {
margin-bottom: 20px;
}
}
.crumbs {
margin: 10px 0;
}
.el-table th {
background-color: #f5f7fa !important;
}
.pagination {
margin: 20px 0;
display: flex;
justify-content: center;
width: 100%;
}
.plugins-tips {
padding: 20px 10px;
margin-bottom: 20px;
}
.el-button + .el-tooltip {
margin-left: 10px;
}
.el-table tr:hover {
background: #f6faff;
}
.mgb20 {
margin-bottom: 20px;
}
.move-enter-active,
.move-leave-active {
transition: opacity .1s ease;
}
.move-enter-from,
.move-leave-to {
opacity: 0;
}
/*BaseForm*/
.form-box {
width: 600px;
}
.form-box .line {
text-align: center;
}
.el-time-panel__content::after,
.el-time-panel__content::before {
margin-top: -7px;
}
.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
padding-bottom: 0;
}
[class*=" el-icon-"], [class^=el-icon-] {
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.el-sub-menu [class^=el-icon-] {
vertical-align: middle;
margin-right: 5px;
width: 24px;
text-align: center;
font-size: 18px;
}
[hidden] {
display: none !important;
}
}
}
.content-collapse {
left: 65px;
}
}

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4125778 */ font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1687330009953') format('woff2'), src: url('iconfont.woff2?t=1687341905766') format('woff2'),
url('iconfont.woff?t=1687330009953') format('woff'), url('iconfont.woff?t=1687341905766') format('woff'),
url('iconfont.ttf?t=1687330009953') format('truetype'); url('iconfont.ttf?t=1687341905766') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-sub-menu:before {
content: "\e86b";
}
.icon-wechat-pay:before { .icon-wechat-pay:before {
content: "\e639"; content: "\e639";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,13 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "6343824",
"name": "menu",
"font_class": "sub-menu",
"unicode": "e86b",
"unicode_decimal": 59499
},
{ {
"icon_id": "1487626", "icon_id": "1487626",
"name": "微信支付", "name": "微信支付",

Binary file not shown.

View File

@ -1,5 +1,9 @@
<template> <template>
<div class="header"> <div class="header admin-header">
<div class="logo">
<el-image :src="logo"/>
<span class="text">{{ title }}</span>
</div>
<!-- 折叠按钮 --> <!-- 折叠按钮 -->
<div class="collapse-btn" @click="collapseChange"> <div class="collapse-btn" @click="collapseChange">
<el-icon v-if="sidebar.collapse"> <el-icon v-if="sidebar.collapse">
@ -9,8 +13,6 @@
<Fold/> <Fold/>
</el-icon> </el-icon>
</div> </div>
<div class="logo">后台管理系统</div>
<div class="header-right"> <div class="header-right">
<div class="header-user-con"> <div class="header-user-con">
<!-- 消息中心 --> <!-- 消息中心 -->
@ -87,6 +89,21 @@ const avatar = ref('/images/user-info.jpg')
const donateImg = ref('/images/wechat-pay.png') const donateImg = ref('/images/wechat-pay.png')
const showDialog = ref(false) const showDialog = ref(false)
const sidebar = useSidebarStore(); const sidebar = useSidebarStore();
const title = ref('Chat-Plus 控制台')
const logo = ref('/images/logo.png')
//
httpGet("/api/admin/session").then(() => {
//
httpGet('/api/admin/config/get?key=system').then(res => {
title.value = res.data['admin_title'];
}).catch(e => {
ElMessage.error("加载系统配置失败: " + e.message)
})
}).catch(() => {
router.replace('/admin/login')
})
// //
const collapseChange = () => { const collapseChange = () => {
sidebar.handleCollapse(); sidebar.handleCollapse();
@ -101,7 +118,7 @@ onMounted(() => {
const router = useRouter(); const router = useRouter();
const logout = function () { const logout = function () {
httpGet("/api/admin/logout").then(() => { httpGet("/api/admin/logout").then(() => {
router.push('/admin/login') router.replace('/admin/login')
}).catch((e) => { }).catch((e) => {
ElMessage.error("注销失败: " + e.message); ElMessage.error("注销失败: " + e.message);
}) })
@ -128,13 +145,18 @@ const logout = function () {
.logo { .logo {
float: left; float: left;
width: 250px; padding-left 10px;
line-height: 70px; display flex
.text {
line-height: 66px;
margin-left 10px;
}
} }
.header-right { .header-right {
float: right; float: right;
padding-right: 50px; padding-right: 20px;
.header-user-con { .header-user-con {
display: flex; display: flex;
@ -208,4 +230,17 @@ const logout = function () {
} }
} }
} }
.admin-header {
.logo {
.el-image {
padding-top 10px
.el-image__inner {
height 40px
}
}
}
}
</style> </style>

View File

@ -14,7 +14,7 @@
<template v-if="item.subs"> <template v-if="item.subs">
<el-sub-menu :index="item.index" :key="item.index"> <el-sub-menu :index="item.index" :key="item.index">
<template #title> <template #title>
<i :class="'iconfont '+item.icon"></i> <i :class="'iconfont icon-'+item.icon"></i>
<span>{{ item.title }}</span> <span>{{ item.title }}</span>
</template> </template>
<template v-for="subItem in item.subs"> <template v-for="subItem in item.subs">
@ -29,6 +29,7 @@
</el-menu-item> </el-menu-item>
</el-sub-menu> </el-sub-menu>
<el-menu-item v-else :index="subItem.index"> <el-menu-item v-else :index="subItem.index">
<i v-if="subItem.icon" :class="'iconfont icon-'+subItem.icon"></i>
{{ subItem.title }} {{ subItem.title }}
</el-menu-item> </el-menu-item>
</template> </template>
@ -62,13 +63,33 @@ const items = [
title: '系统设置', title: '系统设置',
}, },
{
icon: 'user-fill',
index: '/admin/user',
title: '用户管理',
},
{
icon: 'role',
index: '/admin/role',
title: '角色管理',
},
{
icon: 'api-key',
index: '/admin/apikey',
title: 'API-KEY 管理',
},
{
icon: 'log',
index: '/admin/loginLog',
title: '用户登录日志',
},
{ {
icon: 'menu', icon: 'menu',
index: '1', index: '1',
title: '常用模板页面', title: '常用模板页面',
subs: [ subs: [
{ {
icon: 'menu',
index: '/admin/demo/form', index: '/admin/demo/form',
title: '表单页面', title: '表单页面',
}, },
@ -108,16 +129,12 @@ const sidebar = useSidebarStore();
ul { ul {
height: 100%; height: 100%;
.el-menu-item { .el-menu-item, .el-sub-menu {
.iconfont { .iconfont {
font-size 16px; font-size 16px;
margin-right 5px; margin-right 5px;
} }
} }
.el-menu-item.is-active {
background-color #242f42
}
} }
.sidebar-el-menu:not(.el-menu--collapse) { .sidebar-el-menu:not(.el-menu--collapse) {

View File

@ -52,7 +52,7 @@ const closeTags = (index) => {
if (item) { if (item) {
delItem.path === route.fullPath && router.push(item.path); delItem.path === route.fullPath && router.push(item.path);
} else { } else {
router.push('/'); router.push('/admin');
} }
}; };
@ -78,7 +78,7 @@ onBeforeRouteUpdate(to => {
// //
const closeAll = () => { const closeAll = () => {
tags.clearTags(); tags.clearTags();
router.push('/'); router.push('/admin');
}; };
// //
const closeOther = () => { const closeOther = () => {

View File

@ -1,13 +1,9 @@
<template> <template>
<div class="list" v-loading="loading"> <div class="container list" v-loading="loading">
<el-row class="opt-box">
<el-button type="primary" @click="add" size="small"> <div class="handle-box">
<el-icon> <el-button type="primary" :icon="Plus" @click="add">新增</el-button>
<Plus/> </div>
</el-icon>
新增
</el-button>
</el-row>
<el-row> <el-row>
<el-table :data="items" :row-key="row => row.id"> <el-table :data="items" :row-key="row => row.id">
@ -158,13 +154,6 @@ const remove = function (row) {
} }
} }
.pagination {
padding-top 20px;
display flex
justify-content center
width 100%
}
.el-select { .el-select {
width: 100% width: 100%
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="admin-home">
<admin-header/> <admin-header/>
<admin-sidebar/> <admin-sidebar/>
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }"> <div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
@ -27,7 +27,7 @@ const sidebar = useSidebarStore();
const tags = useTagsStore(); const tags = useTagsStore();
</script> </script>
<style lang="stylus"> <style scoped lang="stylus">
@import '@/assets/css/main.css'; @import '@/assets/css/main.css';
@import '@/assets/css/color-dark.css'; @import '@/assets/css/color-dark.css';
@import '@/assets/iconfont/iconfont.css'; @import '@/assets/iconfont/iconfont.css';

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="admin-login">
<div class="bg"></div> <div class="bg"></div>
<div class="main"> <div class="main">
<div class="contain"> <div class="contain">
@ -85,91 +85,98 @@ const login = function () {
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.bg { .admin-login {
position fixed .bg {
left 0
right 0
top 0
bottom 0
background-color #313237
background-image url("~@/assets/img/admin-login-bg.jpg")
background-size cover
background-position center
background-repeat no-repeat
filter: blur(10px); /* 调整模糊程度,可以根据需要修改值 */
}
.main {
.contain {
position fixed position fixed
left 50% left 0
top 40% right 0
width 90% top 0
max-width 400px; bottom 0
transform translate(-50%, -50%) background-color #313237
padding 20px 40px; background-image url("~@/assets/img/admin-login-bg.jpg")
color #ffffff background-size cover
border-radius 10px; background-position center
background rgba(255, 255, 255, 0.3) background-repeat no-repeat
filter: blur(10px); /* 调整模糊程度,可以根据需要修改值 */
}
.logo { .main {
text-align center .contain {
position fixed
left 50%
top 40%
width 90%
max-width 400px;
transform translate(-50%, -50%)
padding 20px 40px;
color #ffffff
border-radius 10px;
background rgba(255, 255, 255, 0.3)
.el-image { .logo {
width 120px; text-align center
}
}
.header { .el-image {
width 100% width 120px;
margin-bottom 24px
font-size 24px
color $white_v1
letter-space 2px
text-align center
}
.content { .el-image__inner {
width 100% height 100%
height: auto
border-radius 3px
.block {
margin-bottom 16px
.el-input__inner {
border 1px solid $gray-v6 !important
.el-icon-user, .el-icon-lock {
font-size 20px
} }
} }
} }
.btn-row { .header {
padding-top 10px; width 100%
margin-bottom 24px
font-size 24px
color $white_v1
letter-space 2px
text-align center
}
.login-btn { .content {
width 100% width 100%
font-size 16px height: auto
letter-spacing 2px border-radius 3px
.block {
margin-bottom 16px
.el-input__inner {
border 1px solid $gray-v6 !important
.el-icon-user, .el-icon-lock {
font-size 20px
}
}
}
.btn-row {
padding-top 10px;
.login-btn {
width 100%
font-size 16px
letter-spacing 2px
}
}
.text-line {
justify-content center
padding-top 10px;
font-size 14px;
} }
} }
.text-line {
justify-content center
padding-top 10px;
font-size 14px;
}
} }
}
.footer { .footer {
color #ffffff; color #ffffff;
.container { .container {
padding 20px; padding 20px;
}
} }
} }
} }
</style> </style>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="list" v-loading="loading"> <div class="container list" v-loading="loading">
<el-row> <el-row>
<el-table :data="items" :row-key="row => row.id"> <el-table :data="items" border :row-key="row => row.id">
<el-table-column label="用户名" prop="username"/> <el-table-column label="用户名" prop="username"/>
<el-table-column label="登录IP" prop="login_ip"/> <el-table-column label="登录IP" prop="login_ip"/>
<el-table-column label="登录地址" prop="login_address"/> <el-table-column label="登录地址" prop="login_address"/>
@ -15,7 +15,7 @@
<div class="pagination"> <div class="pagination">
<el-pagination v-if="total > 0" background <el-pagination v-if="total > 0" background
layout="prev, pager, next" layout="total,prev, pager, next"
:hide-on-single-page="true" :hide-on-single-page="true"
v-model:current-page="page" v-model:current-page="page"
v-model:page-size="pageSize" v-model:page-size="pageSize"

View File

@ -1,14 +1,8 @@
<template> <template>
<div class="role-list"> <div class="container role-list">
<el-row class="opt-box"> <div class="handle-box">
<el-button type="primary" @click="addRole" size="small"> <el-button type="primary" :icon="Plus" @click="addRole">新增</el-button>
<el-icon> </div>
<Plus/>
</el-icon>
新增
</el-button>
</el-row>
<el-row> <el-row>
<el-table :data="tableData" :border="parentBorder" style="width: 100%"> <el-table :data="tableData" :border="parentBorder" style="width: 100%">
<el-table-column type="expand"> <el-table-column type="expand">
@ -157,7 +151,7 @@ import {copyObj, removeArrayItem} from "@/utils/libs";
import {Sortable} from "sortablejs" import {Sortable} from "sortablejs"
const showDialog = ref(false) const showDialog = ref(false)
const parentBorder = ref(false) const parentBorder = ref(true)
const childBorder = ref(true) const childBorder = ref(true)
const tableData = ref([]) const tableData = ref([])
const sortedTableData = ref([]) const sortedTableData = ref([])

View File

@ -1,8 +1,9 @@
<template> <template>
<div class="user-list" v-loading="loading"> <div class="container user-list" v-loading="loading">
<el-row> <el-row>
<el-table :data="users.items" :row-key="row => row.id" @selection-change="handleSelectionChange"> <el-table :data="users.items" border class="table" :row-key="row => row.id"
<el-table-column type="selection" width="55"/> @selection-change="handleSelectionChange">
<el-table-column type="selection" width="38"/>
<el-table-column prop="username" label="用户名"/> <el-table-column prop="username" label="用户名"/>
<el-table-column prop="nickname" label="昵称"/> <el-table-column prop="nickname" label="昵称"/>
<el-table-column prop="calls" label="提问次数" width="100"/> <el-table-column prop="calls" label="提问次数" width="100"/>
@ -34,13 +35,14 @@
</el-table> </el-table>
<div class="pagination"> <div class="pagination">
<el-pagination v-if="users.total > 0" background <el-pagination v-if="users.total > 0"
layout="prev, pager, next" background
:hide-on-single-page="true" layout="total, prev, pager, next"
v-model:current-page="users.page" :current-page="users.page"
v-model:page-size="users.page_size" :page-size="users.page_size"
:total="users.total"
@current-change="fetchUserList(users.page, users.page_size)" @current-change="fetchUserList(users.page, users.page_size)"
:total="users.total"/> />
</div> </div>
</el-row> </el-row>
@ -148,6 +150,9 @@ const fetchUserList = function (page, pageSize) {
arr[i].expired_time = dateFormat(arr[i].expired_time) arr[i].expired_time = dateFormat(arr[i].expired_time)
} }
users.value.items = arr users.value.items = arr
users.value.total = res.data.total
users.value.page = res.data.page
user.value.page_size = res.data.page_size
} }
}).catch(() => { }).catch(() => {
ElMessage.error('加载用户列表失败') ElMessage.error('加载用户列表失败')

View File

@ -2,20 +2,18 @@
<div> <div>
<div class="container"> <div class="container">
<div class="handle-box"> <div class="handle-box">
<el-select v-model="query.address" placeholder="地址" class="handle-select mr10"> <el-select v-model="query.address" placeholder="模型" class="handle-select mr10">
<el-option key="1" label="广东省" value="广东省"></el-option> <el-option key="1" label="GPT-3.5" value="GPT-3.5"></el-option>
<el-option key="2" label="湖南省" value="湖南省"></el-option> <el-option key="2" label="GPT-4.0" value="GPT-4.0"></el-option>
<el-option key="2" label="GPT-5.0" value="GPT-5.0"></el-option>
</el-select> </el-select>
<el-input v-model="query.name" placeholder="用户名" class="handle-input mr10"></el-input> <el-input v-model="query.name" placeholder="名" class="handle-input mr10"></el-input>
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button> <el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
<el-button type="primary" :icon="Plus">新增</el-button> <el-button type="primary" :icon="Plus">新增</el-button>
</div> </div>
<el-table :data="tableData" border class="table" ref="multipleTable" header-cell-class-name="table-header"> <el-table :data="tableData" border class="table" ref="multipleTable" header-cell-class-name="table-header">
<el-table-column prop="id" label="ID" width="55" align="center"></el-table-column> <el-table-column prop="id" label="ID" width="55" align="center"></el-table-column>
<el-table-column prop="name" label="用户名"></el-table-column> <el-table-column prop="name" label="姓名"></el-table-column>
<el-table-column label="账户余额">
<template #default="scope">{{ scope.row.money }}</template>
</el-table-column>
<el-table-column label="头像(查看大图)" align="center"> <el-table-column label="头像(查看大图)" align="center">
<template #default="scope"> <template #default="scope">
<el-image <el-image
@ -28,11 +26,11 @@
</el-image> </el-image>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="address" label="地址"></el-table-column> <el-table-column prop="info" label="简介"></el-table-column>
<el-table-column label="状态" align="center"> <el-table-column label="状态" align="center">
<template #default="scope"> <template #default="scope">
<el-tag <el-tag
:type="scope.row.state === '成功' ? 'success' : scope.row.state === '失败' ? 'danger' : ''" :type="scope.row.state === '启用' ? 'success' : scope.row.state === '禁用' ? 'danger' : ''"
> >
{{ scope.row.state }} {{ scope.row.state }}
</el-tag> </el-tag>
@ -66,11 +64,11 @@
<!-- 编辑弹出框 --> <!-- 编辑弹出框 -->
<el-dialog title="编辑" v-model="editVisible" width="30%"> <el-dialog title="编辑" v-model="editVisible" width="30%">
<el-form label-width="70px"> <el-form label-width="70px">
<el-form-item label="用户名"> <el-form-item label="名">
<el-input v-model="form.name"></el-input> <el-input v-model="form.name"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="地址"> <el-form-item label="简介">
<el-input v-model="form.address"></el-input> <el-input v-model="form.info"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -89,7 +87,6 @@ import {ElMessage, ElMessageBox} from 'element-plus';
import {Delete, Edit, Plus, Search} from '@element-plus/icons-vue'; import {Delete, Edit, Plus, Search} from '@element-plus/icons-vue';
const query = reactive({ const query = reactive({
address: '',
name: '', name: '',
pageIndex: 1, pageIndex: 1,
pageSize: 10 pageSize: 10
@ -100,39 +97,35 @@ const pageTotal = ref(0);
const getData = () => { const getData = () => {
tableData.value = [{ tableData.value = [{
"id": 1, "id": 1,
"name": "张三", "name": "孔子",
"money": 123, "info": "有朋自远方来,不亦说乎?",
"address": "广东省东莞市长安镇", "state": "禁用",
"state": "成功", "date": "2023-06-21",
"date": "2019-11-1", "thumb": "/images/avatar/kong_zi.jpg"
"thumb": "https://lin-xin.gitee.io/images/post/wms.png"
}, },
{ {
"id": 2, "id": 2,
"name": "李四", "name": "乔布斯",
"money": 456, "info": "活着就是为了改变世界!难道还有其他原因吗?",
"address": "广东省广州市白云区", "state": "禁用",
"state": "成功", "date": "2023-06-21",
"date": "2019-10-11", "thumb": "/images/avatar/steve_jobs.jpg"
"thumb": "https://lin-xin.gitee.io/images/post/node3.png"
}, },
{ {
"id": 3, "id": 3,
"name": "王五", "name": "马斯克",
"money": 789, "info": "梦想要远大,如果你的梦想没有吓到你,说明你做得不对。",
"address": "湖南省长沙市", "state": "启用",
"state": "失败", "date": "2023-06-21",
"date": "2019-11-11", "thumb": "/images/avatar/elon_musk.jpg"
"thumb": "https://lin-xin.gitee.io/images/post/parcel.png"
}, },
{ {
"id": 4, "id": 4,
"name": "赵六", "name": "鲁迅",
"money": 1011, "info": "自由之歌,永不过时,横眉冷对千夫指,俯首甘为孺子牛。",
"address": "福建省厦门市鼓浪屿", "state": "启用",
"state": "成功", "date": "2023-06-21",
"date": "2019-10-20", "thumb": "/images/avatar/lu_xun.jpg"
"thumb": "https://lin-xin.gitee.io/images/post/notice.png"
} }
] ]
pageTotal.value = 5 pageTotal.value = 5
@ -168,20 +161,20 @@ const handleDelete = (index) => {
const editVisible = ref(false); const editVisible = ref(false);
let form = reactive({ let form = reactive({
name: '', name: '',
address: '' info: ''
}); });
let idx = -1; let idx = -1;
const handleEdit = (index, row) => { const handleEdit = (index, row) => {
idx = index; idx = index;
form.name = row.name; form.name = row.name;
form.address = row.address; form.info = row.info;
editVisible.value = true; editVisible.value = true;
}; };
const saveEdit = () => { const saveEdit = () => {
editVisible.value = false; editVisible.value = false;
ElMessage.success(`修改第 ${idx + 1} 行成功`); ElMessage.success(`修改第 ${idx + 1} 行成功`);
tableData.value[idx].name = form.name; tableData.value[idx].name = form.name;
tableData.value[idx].address = form.address; tableData.value[idx].info = form.info;
}; };
</script> </script>