opt: adjust ItemList component styles

This commit is contained in:
RockYang 2023-10-15 15:47:42 +08:00
parent 7d1d88a32f
commit a688d3feb5
11 changed files with 195 additions and 105 deletions

View File

@ -145,7 +145,10 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc {
c.Request.URL.Path == "/api/mj/notify" || c.Request.URL.Path == "/api/mj/notify" ||
c.Request.URL.Path == "/api/chat/history" || c.Request.URL.Path == "/api/chat/history" ||
c.Request.URL.Path == "/api/chat/detail" || c.Request.URL.Path == "/api/chat/detail" ||
c.Request.URL.Path == "/api/role/list" ||
c.Request.URL.Path == "/api/mj/jobs" ||
c.Request.URL.Path == "/api/mj/proxy" || c.Request.URL.Path == "/api/mj/proxy" ||
c.Request.URL.Path == "/api/sd/jobs" ||
strings.HasPrefix(c.Request.URL.Path, "/api/sms/") || strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") || strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") ||
strings.HasPrefix(c.Request.URL.Path, "/static/") || strings.HasPrefix(c.Request.URL.Path, "/static/") ||

View File

@ -48,13 +48,15 @@ func (h *ChatRoleHandler) List(c *gin.Context) {
return return
} }
user, err := utils.GetLoginUser(c, h.db) userId := h.GetInt(c, "user_id", 0)
if err != nil { if userId == 0 {
resp.NotAuth(c) resp.NotAuth(c)
return return
} }
var user model.User
h.db.First(&user, userId)
var roleKeys []string var roleKeys []string
err = utils.JsonDecode(user.ChatRoles, &roleKeys) err := utils.JsonDecode(user.ChatRoles, &roleKeys)
if err != nil { if err != nil {
resp.ERROR(c, "角色解析失败!") resp.ERROR(c, "角色解析失败!")
return return

View File

@ -14,12 +14,59 @@
.inner { .inner {
display flex display flex
color #ffffff color #ffffff
padding 20px; padding 15px;
overflow-y visible
overflow-x hidden
.left-menu { .list-box {
width 160px .app-item {
border 1px solid #666666
border-radius 6px
overflow hidden
transition: all 0.3s ease; /* */
.el-image {
padding 6px
.el-image__inner {
border-radius 10px
}
}
.title {
display flex
padding 10px
.name {
width 100%
text-align left
font-size 16px
font-weight bold
color #47fff1
}
.opt {
position: relative;
top -5px
}
}
.hello-msg {
overflow: hidden;
white-space normal
text-overflow: ellipsis;
height 60px
padding 10px
font-size 14px
color #999999
}
&:hover {
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* */
transform: translateY(-10px); /* 10 */
}
}
} }
} }
} }

View File

@ -1,96 +1,115 @@
.task-list-box { .task-list-box {
width: 100%; width: 100%;
padding: 10px; padding: 10px;
color: #fff; color: #fff;
overflow-x: hidden; overflow-x: hidden;
} }
.task-list-box .running-job-list .job-item { .task-list-box .running-job-list .job-item {
width: 100%; width: 100%;
padding: 2px; padding: 2px;
background-color: #555; background-color: #555;
} }
.task-list-box .running-job-list .job-item .job-item-inner { .task-list-box .running-job-list .job-item .job-item-inner {
position: relative; position: relative;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }
.task-list-box .running-job-list .job-item .job-item-inner .progress { .task-list-box .running-job-list .job-item .job-item-inner .progress {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
top: 0; top: 0;
left: 0; left: 0;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.task-list-box .running-job-list .job-item .job-item-inner .progress span { .task-list-box .running-job-list .job-item .job-item-inner .progress span {
font-size: 20px; font-size: 20px;
color: #fff; color: #fff;
} }
.task-list-box .finish-job-list .job-item { .task-list-box .finish-job-list .job-item {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.task-list-box .finish-job-list .job-item .opt .opt-line { .task-list-box .finish-job-list .job-item .opt .opt-line {
margin: 6px 0; margin: 6px 0;
} }
.task-list-box .finish-job-list .job-item .opt .opt-line ul { .task-list-box .finish-job-list .job-item .opt .opt-line ul {
display: flex; display: flex;
flex-flow: row; flex-flow: row;
} }
.task-list-box .finish-job-list .job-item .opt .opt-line ul li { .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
margin-right: 10px; margin-right: 10px;
} }
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a { .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
padding: 3px 0; padding: 3px 0;
width: 44px; width: 44px;
text-align: center; text-align: center;
border-radius: 5px; border-radius: 5px;
display: block; display: block;
cursor: pointer; cursor: pointer;
background-color: #4e5058; background-color: #4e5058;
color: #fff; color: #fff;
} }
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover { .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
background-color: #6d6f78; background-color: #6d6f78;
} }
.task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt { .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
font-size: 20px; font-size: 20px;
cursor: pointer; cursor: pointer;
} }
.task-list-box .el-image { .task-list-box .el-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
max-height: 240px; max-height: 240px;
} }
.task-list-box .el-image img { .task-list-box .el-image img {
height: 240px; height: 240px;
} }
.task-list-box .el-image .el-image-viewer__wrapper img { .task-list-box .el-image .el-image-viewer__wrapper img {
width: auto; width: auto;
height: auto; height: auto;
} }
.task-list-box .el-image .image-slot { .task-list-box .el-image .image-slot {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100%; height: 100%;
min-height: 200px; min-height: 200px;
color: #fff; color: #fff;
} }
.task-list-box .el-image .image-slot .iconfont { .task-list-box .el-image .image-slot .iconfont {
font-size: 50px; font-size: 50px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.task-list-box .el-image.upscale { .task-list-box .el-image.upscale {
max-height: 304px; max-height: 312px;
} }
.task-list-box .el-image.upscale img { .task-list-box .el-image.upscale img {
height: 304px; height: 312px;
} }
.task-list-box .el-image.upscale .el-image-viewer__wrapper img { .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
width: auto; width: auto;
height: auto; height: auto;
} }

View File

@ -40,6 +40,10 @@
.job-item { .job-item {
width 100% width 100%
height 100% height 100%
border 1px solid #666666
padding 6px
overflow hidden
border-radius 6px
.opt { .opt {
.opt-line { .opt-line {
@ -54,7 +58,7 @@
a { a {
padding 3px 0 padding 3px 0
width 44px width 40px
text-align center text-align center
border-radius 5px border-radius 5px
display block display block
@ -113,10 +117,10 @@
} }
.el-image.upscale { .el-image.upscale {
max-height 304px max-height 310px
img { img {
height 304px height 310px
} }
.el-image-viewer__wrapper { .el-image-viewer__wrapper {

View File

@ -5,11 +5,11 @@
class="list-item" class="list-item"
v-for="(item, index) in items" v-for="(item, index) in items"
:key="index" :key="index"
:style="{width:itemWidth + 'px', marginBottom: margin*2+'px'}" :style="{width:itemWidth + 'px'}"
> >
<div :style="{marginLeft: margin+'px', marginRight: margin+'px'}"> <div class="item-inner" :style="{padding: gap/2+'px'}">
<div class="item-wrapper"> <div class="item-wrapper">
<slot :item="item" :index="index"></slot> <slot :item="item" :index="index" :width="itemWidth"></slot>
</div> </div>
</div> </div>
</div> </div>
@ -21,6 +21,7 @@
// //
import {onMounted, ref} from "vue"; import {onMounted, ref} from "vue";
// eslint-disable-next-line no-undef
const props = defineProps({ const props = defineProps({
items: { items: {
type: Array, type: Array,
@ -28,38 +29,25 @@ const props = defineProps({
}, },
gap: { gap: {
type: Number, type: Number,
default: 10 default: 12
}, },
width: { width: {
type: Number, type: Number,
default: 240 default: 240
},
height: {
type: Number,
default: 240
} }
}); });
const container = ref(null) const container = ref(null)
const itemWidth = ref(props.width) const itemWidth = ref(props.width)
const margin = ref(props.gap)
onMounted(() => { onMounted(() => {
computeSize() computeSize()
}) })
const computeSize = () => { const computeSize = () => {
const w = container.value.offsetWidth - 10 // const w = container.value.offsetWidth - 8 //
let cols = Math.floor(w / props.width) let cols = Math.floor(w / props.width)
itemWidth.value = Math.floor(w / cols) - 1 itemWidth.value = Math.floor(w / cols)
while (itemWidth.value < props.width && cols > 1) {
cols -= 1
itemWidth.value = Math.floor(w / cols) - 1
}
if (props.gap > 0) {
margin.value = props.gap / 2
}
} }
window.onresize = () => { window.onresize = () => {
@ -76,15 +64,14 @@ window.onresize = () => {
flex-wrap wrap flex-wrap wrap
.list-item { .list-item {
.item-inner {
div {
display flex display flex
height 100%
overflow hidden
.item-wrapper { .item-wrapper {
height 100% height 100%
width 100% width 100%
display flex
justify-content center
} }
} }
} }

View File

@ -1,16 +1,30 @@
<template> <template>
<div class="page-apps"> <div class="page-apps custom-scroll">
<div class="title"> <div class="title">
AI 助手应用中心 AI 助手应用中心
</div> </div>
<div class="inner custom-scroll"> <div class="inner" :style="{height: listBoxHeight + 'px'}">
<div class="app-list"> <ItemList :items="list" v-if="list.length > 0" gap="20" width="250">
<div class="list-item" v-for="item in list" :key="item.id"> <template #default="scope">
<div v-if="item.key !=='gpt'"> <div class="app-item" :style="{width: scope.width+'px'}">
<el-image :src="item.icon"/> <el-image :src="scope.item.icon" fit="cover" :style="{height: scope.width+'px'}"/>
<div class="title">
<span class="name">{{ scope.item.name }}</span>
<div class="opt">
<el-button size="small"
style="--el-color-primary:#009999"
@click="addRole(scope.item)">
<el-icon>
<Plus/>
</el-icon>
<span>添加应用</span>
</el-button>
</div>
</div>
<div class="hello-msg">{{ scope.item['hello_msg'] }}</div>
</div> </div>
</div> </template>
</div> </ItemList>
</div> </div>
</div> </div>
</template> </template>
@ -19,18 +33,31 @@
import {onMounted, ref} from "vue" import {onMounted, ref} from "vue"
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import {httpGet} from "@/utils/http"; import {httpGet} from "@/utils/http";
import ItemList from "@/components/ItemList.vue";
import {Plus} from "@element-plus/icons-vue";
const listBoxHeight = window.innerHeight - 97
const list = ref([]) const list = ref([])
onMounted(() => { onMounted(() => {
httpGet("/api/role/list?all=true").then((res) => { httpGet("/api/role/list?all=true").then((res) => {
list.value = res.data const data = res.data
for (let i = 0; i < data.length; i++) {
if (data[i].key === 'gpt') {
continue
}
list.value.push(data[i])
}
}).catch(e => { }).catch(e => {
ElMessage.error("获取应用失败:" + e.message) ElMessage.error("获取应用失败:" + e.message)
}) })
}) })
const addRole = (row) => {
}
</script> </script>
<style lang="stylus" scoped> <style lang="stylus">
@import "@/assets/css/chat-app.styl" @import "@/assets/css/chat-app.styl"
@import "@/assets/css/custom-scroll.styl" @import "@/assets/css/custom-scroll.styl"
</style> </style>

View File

@ -553,7 +553,7 @@ const connect = function (chat_id, role_id) {
content: _role['hello_msg'], content: _role['hello_msg'],
orgContent: _role['hello_msg'], orgContent: _role['hello_msg'],
}) })
ElMessage.success({message: "对话连接成功!", duration: 500}) ElMessage.success({message: "对话连接成功!", duration: 1000})
} else { // } else { //
loadChatHistory(chat_id); loadChatHistory(chat_id);
} }

View File

@ -286,7 +286,7 @@
<h2>创作记录</h2> <h2>创作记录</h2>
<div class="finish-job-list"> <div class="finish-job-list">
<ItemList :items="finishedJobs" v-if="finishedJobs.length > 0"> <ItemList :items="finishedJobs" v-if="finishedJobs.length > 0" width="240">
<template #default="scope"> <template #default="scope">
<div class="job-item"> <div class="job-item">
<el-image <el-image
@ -503,7 +503,7 @@ onMounted(() => {
const clipboard = new Clipboard('.copy-prompt'); const clipboard = new Clipboard('.copy-prompt');
clipboard.on('success', () => { clipboard.on('success', () => {
ElMessage.success({message: "复制成功!", duration: 500}); ElMessage.success("复制成功!");
}) })
clipboard.on('error', () => { clipboard.on('error', () => {

View File

@ -289,7 +289,7 @@
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }"> <div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
<h2>任务列表</h2> <h2>任务列表</h2>
<div class="running-job-list"> <div class="running-job-list">
<ItemList :items="runningJobs" v-if="runningJobs.length > 0"> <ItemList :items="runningJobs" v-if="runningJobs.length > 0" width="240">
<template #default="scope"> <template #default="scope">
<div class="job-item"> <div class="job-item">
<el-popover <el-popover
@ -645,7 +645,7 @@ onMounted(() => {
const clipboard = new Clipboard('.copy-prompt'); const clipboard = new Clipboard('.copy-prompt');
clipboard.on('success', () => { clipboard.on('success', () => {
ElMessage.success({message: "复制成功!", duration: 500}); ElMessage.success("复制成功!");
}) })
clipboard.on('error', () => { clipboard.on('error', () => {

View File

@ -281,7 +281,7 @@ getNext()
onMounted(() => { onMounted(() => {
const clipboard = new Clipboard('.copy-prompt'); const clipboard = new Clipboard('.copy-prompt');
clipboard.on('success', () => { clipboard.on('success', () => {
ElMessage.success({message: "复制成功!", duration: 500}); ElMessage.success("复制成功!");
}) })
clipboard.on('error', () => { clipboard.on('error', () => {
@ -293,6 +293,7 @@ const changeImgType = () => {
document.getElementById('waterfall-box').scrollTo(0, 0) document.getElementById('waterfall-box').scrollTo(0, 0)
page.value = 0 page.value = 0
list.value = [] list.value = []
loading.value = true
isOver.value = false isOver.value = false
nextTick(() => getNext()) nextTick(() => getNext())
} }