fix: 修复管理端前端 lint 与构建问题
- 收敛 easyflow-ui-admin 的 lint、格式和类型问题 - 修正 demo 页面与管理端前端构建失败点 - 验证 pnpm lint 与 pnpm build 均已通过
This commit is contained in:
@@ -144,143 +144,8 @@ const filteredActions = computed(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-list-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 20px 0 0 0;
|
||||
}
|
||||
.card-item {
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
height: 165px;
|
||||
min-width: 0;
|
||||
}
|
||||
:deep(.delete-dropdown-item) {
|
||||
color: #ff4d4f !important;
|
||||
}
|
||||
:deep(.delete-dropdown-item:hover) {
|
||||
color: #ff7875 !important;
|
||||
}
|
||||
:deep(.delete-dropdown-item .el-icon) {
|
||||
color: inherit !important;
|
||||
}
|
||||
.card-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: var(--el-bg-color);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
flex: 1;
|
||||
padding: 0 20px 0 20px;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
height: 42px;
|
||||
min-height: 42px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
background:
|
||||
linear-gradient(
|
||||
180deg,
|
||||
var(--el-color-primary-light-9),
|
||||
var(--el-bg-color)
|
||||
),
|
||||
var(--el-bg-color);
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
color: #acb7c6;
|
||||
position: relative;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.divider {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
/* 确保按钮内容不换行 */
|
||||
.action-btn .el-button {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
.empty-state {
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
|
||||
/* 超大屏幕 */
|
||||
@media (min-width: 1920px) {
|
||||
.card-list {
|
||||
@@ -305,8 +170,8 @@ const filteredActions = computed(() => {
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 0 16px 0 16px;
|
||||
gap: 12px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
@@ -314,9 +179,9 @@ const filteredActions = computed(() => {
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 13px;
|
||||
height: 36px;
|
||||
min-height: 36px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,21 +191,22 @@ const filteredActions = computed(() => {
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 0 12px 0 12px;
|
||||
gap: 10px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 12px;
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
-webkit-line-clamp: 2;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
@@ -351,4 +217,147 @@ const filteredActions = computed(() => {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-list-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 20px 0 0;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
height: 165px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.delete-dropdown-item) {
|
||||
color: #ff4d4f !important;
|
||||
}
|
||||
|
||||
:deep(.delete-dropdown-item:hover) {
|
||||
color: #ff7875 !important;
|
||||
}
|
||||
|
||||
:deep(.delete-dropdown-item .el-icon) {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.card-item:hover {
|
||||
box-shadow: 0 4px 20px rgb(0 0 0 / 10%);
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: var(--el-bg-color);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 15px;
|
||||
align-items: flex-start;
|
||||
padding: 0 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
display: -webkit-box;
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
min-height: 42px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #606266;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
background:
|
||||
linear-gradient(
|
||||
180deg,
|
||||
var(--el-color-primary-light-9),
|
||||
var(--el-bg-color)
|
||||
),
|
||||
var(--el-bg-color);
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
color: #acb7c6;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.divider {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background-color: #e0e0e0;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/* 确保按钮内容不换行 */
|
||||
.action-btn .el-button {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -283,28 +283,28 @@ const handleDeleteClick = (event: any, item: any) => {
|
||||
|
||||
<style scoped>
|
||||
.crud-category-item:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
cursor: pointer;
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.crud-category-item.selected {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.no-data {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.crud-category-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.crud-category-item :deep(.el-icon) {
|
||||
@@ -316,8 +316,8 @@ const handleDeleteClick = (event: any, item: any) => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ const props = defineProps({
|
||||
categories: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
@@ -125,6 +124,14 @@ const panelWidth = computed(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const categoryIconStyle = computed(() => ({
|
||||
'--category-icon-color': props.iconColor,
|
||||
'--category-icon-size':
|
||||
typeof props.iconSize === 'number'
|
||||
? `${props.iconSize}px`
|
||||
: `${props.iconSize}`,
|
||||
}));
|
||||
|
||||
// 切换面板收缩状态
|
||||
const togglePanel = () => {
|
||||
isCollapsed.value = !isCollapsed.value;
|
||||
@@ -177,18 +184,24 @@ onMounted(() => {
|
||||
@click="handleCategoryClick(category)"
|
||||
>
|
||||
<!-- 图标 -->
|
||||
<div v-if="category[iconKey]" class="category-icon">
|
||||
<div
|
||||
v-if="category[iconKey]"
|
||||
class="category-icon"
|
||||
:style="categoryIconStyle"
|
||||
>
|
||||
<!-- 1. 组件类型图标(Element Plus / 自定义 SVG 组件) -->
|
||||
<ElIcon v-if="isComponent(category[iconKey])">
|
||||
<component :is="category[iconKey]" />
|
||||
</ElIcon>
|
||||
<!-- 2. SVG 字符串:支持 v-html 或 img 两种渲染方式 -->
|
||||
<template v-else-if="isSvgString(category[iconKey])">
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
v-if="!useImgForSvg"
|
||||
v-html="category[iconKey]"
|
||||
class="custom-svg"
|
||||
></div>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<img
|
||||
v-else
|
||||
:src="svgToDataUrl(category[iconKey])"
|
||||
@@ -221,14 +234,14 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.category-panel {
|
||||
position: relative; /* 相对定位,用于按钮绝对定位 */
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 平滑宽度过渡 */
|
||||
box-sizing: border-box;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #f0f0f0;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 平滑宽度过渡 */
|
||||
}
|
||||
|
||||
/* 右上角收缩/展开按钮 */
|
||||
@@ -242,18 +255,19 @@ onMounted(() => {
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
background-color: var(--el-color-white);
|
||||
border: 1px solid #e5e7eb;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border-radius: 50%;
|
||||
|
||||
/* 按钮不随面板收缩移动 */
|
||||
transform: translateX(0);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.toggle-panel-btn:hover {
|
||||
background-color: #f3f4f6;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.toggle-panel-btn .el-icon {
|
||||
@@ -264,15 +278,12 @@ onMounted(() => {
|
||||
|
||||
/* 分类列表容器 */
|
||||
.category-list {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.category-item:last-child {
|
||||
@@ -281,15 +292,15 @@ onMounted(() => {
|
||||
|
||||
.category-item-content {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 6px 0 6px 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
gap: 12px;
|
||||
padding: 6px 0 6px 14px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.category-item-content:hover {
|
||||
@@ -297,49 +308,36 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: v-bind(iconSize);
|
||||
height: v-bind(iconSize);
|
||||
color: v-bind(iconColor);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: var(--category-icon-size);
|
||||
height: var(--category-icon-size);
|
||||
vertical-align: middle;
|
||||
color: var(--category-icon-color);
|
||||
}
|
||||
|
||||
.category-icon .el-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.category-item-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-family: PingFangSC, 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
transition:
|
||||
opacity 0.2s,
|
||||
transform 0.2s;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* 收缩状态样式 */
|
||||
@@ -360,11 +358,11 @@ onMounted(() => {
|
||||
/* 新增:选中态样式 */
|
||||
.category-item-content.selected {
|
||||
font-weight: 600;
|
||||
background: rgba(0, 102, 255, 0.06);
|
||||
color: #0066ff;
|
||||
color: #06f;
|
||||
background: rgb(0 102 255 / 6%);
|
||||
}
|
||||
|
||||
.category-item-content.selected:hover {
|
||||
background: rgba(0, 102, 255, 0.06);
|
||||
background: rgb(0 102 255 / 6%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ChatThinkingBlock } from '@easyflow/common-ui';
|
||||
import ElXMarkdown from 'vue-element-plus-x/es/XMarkdown/index.js';
|
||||
|
||||
import { ChatThinkingBlock } from '@easyflow/common-ui';
|
||||
import { IconifyIcon } from '@easyflow/icons';
|
||||
|
||||
import { CircleCheck, Close } from '@element-plus/icons-vue';
|
||||
@@ -22,7 +22,7 @@ interface ChatHistoryDetailDrawerProps {
|
||||
session?: any;
|
||||
messages?: any[];
|
||||
hasMore?: boolean;
|
||||
onLoadMore?: (() => void | Promise<void>) | undefined;
|
||||
onLoadMore?: (() => Promise<void> | void) | undefined;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ChatHistoryDetailDrawerProps>(), {
|
||||
@@ -128,12 +128,7 @@ async function handleLoadMore() {
|
||||
|
||||
<div class="chat-history-detail__stream">
|
||||
<div class="chat-history-detail__stream-toolbar">
|
||||
<ElButton
|
||||
v-if="hasMore"
|
||||
text
|
||||
type="primary"
|
||||
@click="handleLoadMore"
|
||||
>
|
||||
<ElButton v-if="hasMore" text type="primary" @click="handleLoadMore">
|
||||
加载更早消息
|
||||
</ElButton>
|
||||
</div>
|
||||
@@ -233,48 +228,48 @@ async function handleLoadMore() {
|
||||
<style scoped>
|
||||
.chat-history-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
background:
|
||||
radial-gradient(
|
||||
circle at top right,
|
||||
hsl(var(--nav-ambient) / 0.1),
|
||||
hsl(var(--nav-ambient) / 10%),
|
||||
transparent 26%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
hsl(var(--glass-border) / 0.18) 0%,
|
||||
hsl(var(--glass-tint) / 0.34) 10%,
|
||||
hsl(var(--surface-panel) / 0.88) 28%,
|
||||
hsl(var(--surface-panel) / 0.96) 100%
|
||||
hsl(var(--glass-border) / 18%) 0%,
|
||||
hsl(var(--glass-tint) / 34%) 10%,
|
||||
hsl(var(--surface-panel) / 88%) 28%,
|
||||
hsl(var(--surface-panel) / 96%) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.chat-history-detail__summary {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 20px 24px 12px;
|
||||
border-bottom: 1px solid hsl(var(--divider-faint) / 0.16);
|
||||
border-bottom: 1px solid hsl(var(--divider-faint) / 16%);
|
||||
}
|
||||
|
||||
.chat-history-detail__summary-main {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-history-detail__title-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chat-history-detail__title {
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--text-strong));
|
||||
@@ -285,19 +280,19 @@ async function handleLoadMore() {
|
||||
align-items: center;
|
||||
min-height: 26px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid hsl(var(--glass-border) / 0.38);
|
||||
border-radius: 999px;
|
||||
background: hsl(var(--glass-tint) / 0.54);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--nav-item-active-foreground));
|
||||
background: hsl(var(--glass-tint) / 54%);
|
||||
border: 1px solid hsl(var(--glass-border) / 38%);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.chat-history-detail__meta-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@@ -316,28 +311,28 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__meta-inline {
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-history-detail__meta-divider {
|
||||
display: inline-flex;
|
||||
height: 12px;
|
||||
width: 1px;
|
||||
background: hsl(var(--divider-faint) / 0.42);
|
||||
height: 12px;
|
||||
background: hsl(var(--divider-faint) / 42%);
|
||||
}
|
||||
|
||||
.chat-history-detail__close {
|
||||
display: inline-flex;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid hsl(var(--divider-faint) / 0.24);
|
||||
border-radius: 999px;
|
||||
background: hsl(var(--glass-tint) / 0.46);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
color: hsl(var(--text-muted));
|
||||
background: hsl(var(--glass-tint) / 46%);
|
||||
border: 1px solid hsl(var(--divider-faint) / 24%);
|
||||
border-radius: 999px;
|
||||
transition:
|
||||
transform 0.18s ease,
|
||||
background-color 0.18s ease,
|
||||
@@ -346,15 +341,15 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__close:hover {
|
||||
color: hsl(var(--text-strong));
|
||||
background: hsl(var(--surface-contrast-soft) / 0.92);
|
||||
background: hsl(var(--surface-contrast-soft) / 92%);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.chat-history-detail__stream {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding: 14px 24px 20px;
|
||||
}
|
||||
|
||||
@@ -365,8 +360,8 @@ async function handleLoadMore() {
|
||||
}
|
||||
|
||||
.chat-history-detail__scrollbar {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.chat-history-detail__message-list {
|
||||
@@ -391,9 +386,9 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__message-meta {
|
||||
display: inline-flex;
|
||||
max-width: 88%;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
max-width: 88%;
|
||||
font-size: 11px;
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
@@ -409,30 +404,30 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__message-bubble {
|
||||
width: min(88%, 760px);
|
||||
border: 1px solid hsl(var(--divider-faint) / 0.34);
|
||||
border-radius: 22px;
|
||||
padding: 14px 16px;
|
||||
font-size: 14px;
|
||||
line-height: 1.72;
|
||||
border: 1px solid hsl(var(--divider-faint) / 34%);
|
||||
border-radius: 22px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.chat-history-detail__message-bubble.is-assistant {
|
||||
background: hsl(var(--glass-tint) / 0.88);
|
||||
background: hsl(var(--glass-tint) / 88%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 0.48),
|
||||
0 12px 26px -24px hsl(var(--foreground) / 0.18);
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 48%),
|
||||
0 12px 26px -24px hsl(var(--foreground) / 18%);
|
||||
}
|
||||
|
||||
.chat-history-detail__message-bubble.is-user {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
hsl(var(--primary) / 0.14) 0%,
|
||||
hsl(var(--surface-panel) / 0.96) 100%
|
||||
hsl(var(--primary) / 14%) 0%,
|
||||
hsl(var(--surface-panel) / 96%) 100%
|
||||
);
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 0.42),
|
||||
0 16px 30px -26px hsl(var(--primary) / 0.24);
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 42%),
|
||||
0 16px 30px -26px hsl(var(--primary) / 24%);
|
||||
}
|
||||
|
||||
.chat-history-detail__message-chains {
|
||||
@@ -448,23 +443,23 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__tool-panel {
|
||||
overflow: hidden;
|
||||
border: 1px solid hsl(var(--divider-faint) / 0.24);
|
||||
background: hsl(var(--surface-panel) / 68%);
|
||||
border: 1px solid hsl(var(--divider-faint) / 24%);
|
||||
border-radius: 14px;
|
||||
background: hsl(var(--surface-panel) / 0.68);
|
||||
box-shadow: inset 0 1px 0 hsl(var(--glass-border) / 0.18);
|
||||
box-shadow: inset 0 1px 0 hsl(var(--glass-border) / 18%);
|
||||
}
|
||||
|
||||
.chat-history-detail__tool-title {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.chat-history-detail__tool-name {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -472,13 +467,13 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__tool-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
background: hsl(var(--surface-contrast-soft) / 0.92);
|
||||
font-size: 12px;
|
||||
color: hsl(var(--text-muted));
|
||||
background: hsl(var(--surface-contrast-soft) / 92%);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.chat-history-detail__markdown {
|
||||
@@ -488,9 +483,9 @@ async function handleLoadMore() {
|
||||
}
|
||||
|
||||
.chat-history-detail__markdown :deep(.markdown-body) {
|
||||
background: transparent;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.chat-history-detail__markdown :deep(.markdown-body > :first-child) {
|
||||
@@ -508,8 +503,8 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__markdown :deep(pre) {
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
margin-top: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.chat-history-detail__tool-panel :deep(.el-collapse-item__wrap) {
|
||||
@@ -520,7 +515,7 @@ async function handleLoadMore() {
|
||||
min-height: 44px;
|
||||
padding-right: 14px;
|
||||
background: transparent;
|
||||
border-bottom-color: hsl(var(--divider-faint) / 0.16);
|
||||
border-bottom-color: hsl(var(--divider-faint) / 16%);
|
||||
}
|
||||
|
||||
.chat-history-detail__message-bubble :deep(.el-collapse-item__content) {
|
||||
@@ -550,8 +545,8 @@ async function handleLoadMore() {
|
||||
|
||||
.chat-history-detail__message-bubble,
|
||||
.chat-history-detail__message-meta {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,18 +5,23 @@ import type {
|
||||
} from 'vue-element-plus-x/types/BubbleList';
|
||||
import type { TypewriterInstance } from 'vue-element-plus-x/types/Typewriter';
|
||||
|
||||
import {
|
||||
ChatThinkingBlock,
|
||||
type ChatThinkingBlockStatus,
|
||||
} from '@easyflow/common-ui';
|
||||
import type { ChatThinkingBlockStatus } from '@easyflow/common-ui';
|
||||
import type { BotInfo, ChatMessage } from '@easyflow/types';
|
||||
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref, watch, watchEffect } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import {
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
import ElBubbleList from 'vue-element-plus-x/es/BubbleList/index.js';
|
||||
import ElSender from 'vue-element-plus-x/es/Sender/index.js';
|
||||
import ElXMarkdown from 'vue-element-plus-x/es/XMarkdown/index.js';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { ChatThinkingBlock } from '@easyflow/common-ui';
|
||||
import { IconifyIcon } from '@easyflow/icons';
|
||||
import { $t } from '@easyflow/locales';
|
||||
import { useBotStore } from '@easyflow/stores';
|
||||
@@ -51,8 +56,8 @@ import SendingIcon from '../icons/SendingIcon.vue';
|
||||
|
||||
type Think = {
|
||||
reasoning_content?: string;
|
||||
thinkingStatus?: ChatThinkingBlockStatus;
|
||||
thinkingExpanded?: boolean;
|
||||
thinkingStatus?: ChatThinkingBlockStatus;
|
||||
};
|
||||
|
||||
type Tool = {
|
||||
@@ -168,7 +173,8 @@ const updateBackToBottomButtonVisible = () => {
|
||||
showBackToBottomButton.value = false;
|
||||
return;
|
||||
}
|
||||
const { scrollTop, scrollHeight, clientHeight } = bubbleListScrollElement.value;
|
||||
const { scrollTop, scrollHeight, clientHeight } =
|
||||
bubbleListScrollElement.value;
|
||||
showBackToBottomButton.value =
|
||||
scrollHeight - (scrollTop + clientHeight) > BACK_TO_BOTTOM_THRESHOLD;
|
||||
};
|
||||
@@ -571,7 +577,6 @@ onBeforeUnmount(() => {
|
||||
</ElCollapse>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<!-- 自定义头像 -->
|
||||
@@ -725,7 +730,7 @@ onBeforeUnmount(() => {
|
||||
background: var(--el-bg-color-overlay);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.08);
|
||||
box-shadow: 0 6px 10px rgb(0 0 0 / 8%);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
@@ -775,10 +780,10 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
:deep(.chat-tool-panel.el-collapse) {
|
||||
border: 1px solid hsl(var(--divider-faint) / 0.26);
|
||||
background: hsl(var(--surface-panel) / 70%);
|
||||
border: 1px solid hsl(var(--divider-faint) / 26%);
|
||||
border-radius: 14px;
|
||||
background: hsl(var(--surface-panel) / 0.7);
|
||||
box-shadow: inset 0 1px 0 hsl(var(--glass-border) / 0.2);
|
||||
box-shadow: inset 0 1px 0 hsl(var(--glass-border) / 20%);
|
||||
}
|
||||
|
||||
:deep(.chat-tool-panel .el-collapse-item) {
|
||||
@@ -794,7 +799,7 @@ onBeforeUnmount(() => {
|
||||
min-height: 44px;
|
||||
padding-right: 14px;
|
||||
background: transparent;
|
||||
border-bottom-color: hsl(var(--divider-faint) / 0.16);
|
||||
border-bottom-color: hsl(var(--divider-faint) / 16%);
|
||||
}
|
||||
|
||||
:deep(.chat-tool-panel .el-collapse-item__content) {
|
||||
|
||||
@@ -122,162 +122,6 @@ const collapseAll = () => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.accordion-container {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 8px;
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 30px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* 控制面板样式 */
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: var(--el-bg-color);
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #3498db;
|
||||
background: var(--el-bg-color);
|
||||
color: var(--el-text-color-secondary);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: #3498db;
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
/* 折叠面板列表 */
|
||||
.accordion-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.accordion-item {
|
||||
border: 1px solid #e1e5e9;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: var(--el-bg-color);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.accordion-item:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.accordion-item--active {
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
/* 面板头部 */
|
||||
.accordion-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
background: var(--el-bg-color);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.accordion-header:hover {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.accordion-title {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.accordion-icon {
|
||||
transition: transform 0.3s ease;
|
||||
color: #7f8c8d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.accordion-icon--rotated {
|
||||
transform: rotate(180deg);
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.accordion-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.4s ease;
|
||||
background: var(--el-bg-color);
|
||||
}
|
||||
|
||||
.accordion-content--open {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.accordion-content-inner {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #e1e5e9;
|
||||
}
|
||||
|
||||
.accordion-content-inner p {
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
.column-header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.accordion-container {
|
||||
@@ -306,4 +150,162 @@ const collapseAll = () => {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.accordion-container {
|
||||
max-width: 100%;
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 8px;
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-bottom: 30px;
|
||||
font-size: 1.1rem;
|
||||
color: var(--el-text-color-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 控制面板样式 */
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
background: var(--el-bg-color);
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-secondary);
|
||||
cursor: pointer;
|
||||
background: var(--el-bg-color);
|
||||
border: 1px solid #3498db;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: #3498db;
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
/* 折叠面板列表 */
|
||||
.accordion-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.accordion-item {
|
||||
overflow: hidden;
|
||||
background: var(--el-bg-color);
|
||||
border: 1px solid #e1e5e9;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 5%);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.accordion-item:hover {
|
||||
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.accordion-item--active {
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
/* 面板头部 */
|
||||
.accordion-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 20px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background: var(--el-bg-color);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.accordion-header:hover {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.accordion-title {
|
||||
padding-left: 12px;
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.accordion-icon {
|
||||
font-size: 12px;
|
||||
color: #7f8c8d;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.accordion-icon--rotated {
|
||||
color: #3498db;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.accordion-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
background: var(--el-bg-color);
|
||||
transition: max-height 0.4s ease;
|
||||
}
|
||||
|
||||
.accordion-content--open {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.accordion-content-inner {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #e1e5e9;
|
||||
}
|
||||
|
||||
.accordion-content-inner p {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.column-header-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -171,9 +171,9 @@ const handleDropdownClick = (button) => {
|
||||
<style scoped>
|
||||
.custom-header {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -192,8 +192,8 @@ const handleDropdownClick = (button) => {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-middle {
|
||||
@@ -208,20 +208,20 @@ const handleDropdownClick = (button) => {
|
||||
|
||||
.search-input :deep(.el-input__wrapper) {
|
||||
min-height: 40px;
|
||||
background: hsl(var(--surface-contrast-soft) / 0.92);
|
||||
background: hsl(var(--surface-contrast-soft) / 92%);
|
||||
border-radius: 14px;
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 0.44),
|
||||
0 10px 24px -24px hsl(var(--foreground) / 0.24);
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 44%),
|
||||
0 10px 24px -24px hsl(var(--foreground) / 24%);
|
||||
}
|
||||
|
||||
.search-input :deep(.el-input__wrapper:hover),
|
||||
.search-input :deep(.el-input__wrapper.is-focus) {
|
||||
background: hsl(var(--surface-subtle) / 0.98);
|
||||
background: hsl(var(--surface-subtle) / 98%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 0.56),
|
||||
0 0 0 3px hsl(var(--primary) / 0.08),
|
||||
0 12px 24px -24px hsl(var(--foreground) / 0.24);
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 56%),
|
||||
0 0 0 3px hsl(var(--primary) / 8%),
|
||||
0 12px 24px -24px hsl(var(--foreground) / 24%);
|
||||
}
|
||||
|
||||
.search-prefix {
|
||||
@@ -231,8 +231,8 @@ const handleDropdownClick = (button) => {
|
||||
.header-right {
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-right :deep(.el-button),
|
||||
@@ -241,24 +241,24 @@ const handleDropdownClick = (button) => {
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
padding-inline: 18px;
|
||||
border-radius: 10px;
|
||||
border-color: transparent;
|
||||
border-radius: 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.header-right :deep(.el-button:not(.el-button--primary)),
|
||||
.search-group :deep(.el-button:not(.el-button--primary)) {
|
||||
color: hsl(var(--text-strong));
|
||||
background: hsl(var(--surface-contrast-soft) / 0.86);
|
||||
background: hsl(var(--surface-contrast-soft) / 86%);
|
||||
border-color: transparent;
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 0.34),
|
||||
0 10px 24px -24px hsl(var(--foreground) / 0.2);
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 34%),
|
||||
0 10px 24px -24px hsl(var(--foreground) / 20%);
|
||||
}
|
||||
|
||||
.header-right :deep(.el-button--primary),
|
||||
.search-group :deep(.el-button--primary) {
|
||||
box-shadow: 0 16px 28px -22px hsl(var(--primary) / 0.48);
|
||||
box-shadow: 0 16px 28px -22px hsl(var(--primary) / 48%);
|
||||
}
|
||||
|
||||
.dropdown-label {
|
||||
@@ -268,8 +268,8 @@ const handleDropdownClick = (button) => {
|
||||
@media (max-width: 768px) {
|
||||
.custom-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
|
||||
@@ -6,9 +6,10 @@ import { preferences } from '@easyflow/preferences';
|
||||
import { ElEmpty } from 'element-plus';
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
|
||||
import 'vue3-json-viewer/dist/vue3-json-viewer.css';
|
||||
import { getEmptyStateImageUrl } from '#/utils/assets';
|
||||
|
||||
import 'vue3-json-viewer/dist/vue3-json-viewer.css';
|
||||
|
||||
defineProps({
|
||||
value: {
|
||||
required: true,
|
||||
|
||||
@@ -84,14 +84,25 @@ const resolvedPrimaryAction = computed(() => {
|
||||
return props.primaryAction;
|
||||
});
|
||||
|
||||
function resolveActionPlacement(
|
||||
action: ActionButton,
|
||||
index: number,
|
||||
): ActionPlacement {
|
||||
if (action.placement) {
|
||||
return action.placement;
|
||||
}
|
||||
if (resolvedPrimaryAction.value) {
|
||||
return 'menu';
|
||||
}
|
||||
return index < 3 ? 'inline' : 'menu';
|
||||
}
|
||||
|
||||
const resolvedActions = computed<ResolvedActionButton[]>(() => {
|
||||
return props.actions
|
||||
.filter((action) => hasPermission(action.permission))
|
||||
.map((action, index) => ({
|
||||
...action,
|
||||
placement:
|
||||
action.placement ||
|
||||
(resolvedPrimaryAction.value ? 'menu' : index < 3 ? 'inline' : 'menu'),
|
||||
placement: resolveActionPlacement(action, index),
|
||||
tone:
|
||||
action.tone ||
|
||||
(action.className?.includes('danger') ? 'danger' : 'default'),
|
||||
@@ -111,8 +122,8 @@ const menuActions = computed(() => {
|
||||
const showFooter = computed(() => {
|
||||
return Boolean(
|
||||
resolvedPrimaryAction.value ||
|
||||
inlineActions.value.length ||
|
||||
menuActions.value.length,
|
||||
inlineActions.value.length > 0 ||
|
||||
menuActions.value.length > 0,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -132,10 +143,8 @@ function handleActionClick(event: Event, action: ActionButton, item: any) {
|
||||
v-for="(item, index) in props.data"
|
||||
:key="item.id ?? index"
|
||||
shadow="never"
|
||||
:class="[
|
||||
'card-item',
|
||||
{ 'card-item--interactive': resolvedPrimaryAction },
|
||||
]"
|
||||
class="card-item"
|
||||
:class="[{ 'card-item--interactive': resolvedPrimaryAction }]"
|
||||
:role="resolvedPrimaryAction ? 'button' : undefined"
|
||||
:tabindex="resolvedPrimaryAction ? 0 : undefined"
|
||||
@click="handlePrimaryAction(item)"
|
||||
@@ -201,7 +210,7 @@ function handleActionClick(event: Event, action: ActionButton, item: any) {
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="inlineActions.length || menuActions.length"
|
||||
v-if="inlineActions.length > 0 || menuActions.length > 0"
|
||||
class="card-actions"
|
||||
@click.stop
|
||||
>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type {CSSProperties} from 'vue';
|
||||
import {computed, useSlots} from 'vue';
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import { computed, useSlots } from 'vue';
|
||||
|
||||
interface Props {
|
||||
contentPadding?: number | string;
|
||||
@@ -67,8 +68,8 @@ const contentStyle = computed((): CSSProperties => {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
gap: 16px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.list-page-shell.is-dense {
|
||||
@@ -78,9 +79,9 @@ const contentStyle = computed((): CSSProperties => {
|
||||
.list-page-shell__toolbar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 8px 6px 2px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
@@ -92,8 +93,8 @@ const contentStyle = computed((): CSSProperties => {
|
||||
top: 12px;
|
||||
z-index: 5;
|
||||
padding: 10px 10px 6px;
|
||||
background: hsl(var(--glass-tint) / 0.7);
|
||||
border: 1px solid hsl(var(--glass-border) / 0.44);
|
||||
background: hsl(var(--glass-tint) / 70%);
|
||||
border: 1px solid hsl(var(--glass-border) / 44%);
|
||||
border-radius: 20px;
|
||||
box-shadow: var(--shadow-toolbar);
|
||||
backdrop-filter: blur(var(--glass-blur)) saturate(155%);
|
||||
@@ -110,40 +111,41 @@ const contentStyle = computed((): CSSProperties => {
|
||||
|
||||
.list-page-shell__content {
|
||||
--list-page-shell-content-padding: 0px;
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: var(--list-page-shell-content-padding);
|
||||
overflow: hidden;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
hsl(var(--glass-border) / 0.84) 0%,
|
||||
hsl(var(--surface-panel) / 0.94) 28%,
|
||||
hsl(var(--surface-panel) / 0.97) 100%
|
||||
hsl(var(--glass-border) / 84%) 0%,
|
||||
hsl(var(--surface-panel) / 94%) 28%,
|
||||
hsl(var(--surface-panel) / 97%) 100%
|
||||
);
|
||||
border: 1px solid hsl(var(--glass-border) / 0.58);
|
||||
border: 1px solid hsl(var(--glass-border) / 58%);
|
||||
border-radius: 24px;
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 0.72),
|
||||
0 24px 54px -42px hsl(var(--primary) / 0.22),
|
||||
0 18px 42px -34px hsl(var(--foreground) / 0.1);
|
||||
inset 0 1px 0 hsl(var(--glass-border) / 72%),
|
||||
0 24px 54px -42px hsl(var(--primary) / 22%),
|
||||
0 18px 42px -34px hsl(var(--foreground) / 10%);
|
||||
backdrop-filter: blur(calc(var(--glass-blur) * 0.7)) saturate(145%);
|
||||
}
|
||||
|
||||
.list-page-shell.is-subtle .list-page-shell__content {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
hsl(var(--glass-tint) / 0.84) 0%,
|
||||
hsl(var(--surface-contrast-soft) / 0.94) 100%
|
||||
hsl(var(--glass-tint) / 84%) 0%,
|
||||
hsl(var(--surface-contrast-soft) / 94%) 100%
|
||||
);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.list-page-shell.is-plain .list-page-shell__content {
|
||||
background: hsl(var(--surface-panel) / 0.96);
|
||||
border: 1px solid hsl(var(--divider-faint) / 0.58);
|
||||
background: hsl(var(--surface-panel) / 96%);
|
||||
border: 1px solid hsl(var(--divider-faint) / 58%);
|
||||
border-radius: 20px;
|
||||
box-shadow: none;
|
||||
backdrop-filter: none;
|
||||
@@ -162,14 +164,10 @@ const contentStyle = computed((): CSSProperties => {
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse 84% 72% at 10% 0%,
|
||||
hsl(var(--nav-ambient) / 0.18) 0%,
|
||||
hsl(var(--nav-ambient) / 18%) 0%,
|
||||
transparent 68%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
hsl(var(--nav-flow-trace) / 0.08) 0%,
|
||||
transparent 78%
|
||||
);
|
||||
linear-gradient(180deg, hsl(var(--nav-flow-trace) / 8%) 0%, transparent 78%);
|
||||
}
|
||||
|
||||
.list-page-shell__content > * {
|
||||
@@ -195,6 +193,7 @@ const contentStyle = computed((): CSSProperties => {
|
||||
--el-table-border-color: hsl(var(--table-row-border));
|
||||
--el-table-current-row-bg-color: hsl(var(--table-row-hover));
|
||||
--el-fill-color-blank: transparent;
|
||||
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@@ -216,8 +215,8 @@ const contentStyle = computed((): CSSProperties => {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--text-strong));
|
||||
background: hsl(var(--table-header-bg) / 0.86);
|
||||
border-bottom: 1px solid hsl(var(--divider-faint) / 0.38);
|
||||
background: hsl(var(--table-header-bg) / 86%);
|
||||
border-bottom: 1px solid hsl(var(--divider-faint) / 38%);
|
||||
}
|
||||
|
||||
.list-page-shell__content :deep(.el-table th.el-table__cell:first-child) {
|
||||
@@ -232,7 +231,7 @@ const contentStyle = computed((): CSSProperties => {
|
||||
.list-page-shell__content :deep(.el-table--border .el-table__cell) {
|
||||
padding: 14px 0;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid hsl(var(--divider-faint) / 0.46);
|
||||
border-bottom: 1px solid hsl(var(--divider-faint) / 46%);
|
||||
}
|
||||
|
||||
.list-page-shell__content :deep(.el-table__row:hover > td.el-table__cell) {
|
||||
|
||||
@@ -138,7 +138,7 @@ onMounted(() => {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding: 18px 20px 18px;
|
||||
padding: 18px 20px;
|
||||
}
|
||||
|
||||
.page-data-container__body {
|
||||
|
||||
@@ -114,6 +114,7 @@ const isSvgString = (icon: any) => {
|
||||
v-if="item.icon"
|
||||
class="ml-[-3px] flex items-center justify-center"
|
||||
>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
v-if="isSvgString(item.icon)"
|
||||
v-html="item.icon"
|
||||
@@ -127,6 +128,7 @@ const isSvgString = (icon: any) => {
|
||||
}"
|
||||
class="svg-container"
|
||||
></div>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<img
|
||||
v-else-if="
|
||||
typeof item.icon === 'string' && !isComponent(item.icon)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {mount} from '@vue/test-utils';
|
||||
import { mount } from '@vue/test-utils';
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {describe, expect, it, vi} from 'vitest';
|
||||
import CardList from '../CardList.vue';
|
||||
|
||||
const { hasAccessByCodes } = vi.hoisted(() => ({
|
||||
@@ -13,7 +14,7 @@ vi.mock('@easyflow/access', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('CardList', () => {
|
||||
describe('cardList', () => {
|
||||
function mountCardList(props: Record<string, unknown>) {
|
||||
return mount(CardList, {
|
||||
props: {
|
||||
|
||||
@@ -233,14 +233,14 @@ onMounted(() => {
|
||||
v-loading="loading"
|
||||
:element-loading-text="loadingText"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<template #default="{ node, data: itemData }">
|
||||
<span class="tree-node">
|
||||
<span class="node-label">{{ $t(node.label) }}</span>
|
||||
<span
|
||||
v-if="showCount && data[defaultProps.children]"
|
||||
v-if="showCount && itemData[defaultProps.children]"
|
||||
class="node-count"
|
||||
>
|
||||
({{ data[defaultProps.children].length }})
|
||||
({{ itemData[defaultProps.children].length }})
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -278,17 +278,17 @@ onMounted(() => {
|
||||
|
||||
.tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
overflow: hidden;
|
||||
color: var(--el-text-color-primary);
|
||||
text-overflow: ellipsis;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--el-text-color-primary);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -299,22 +299,22 @@ onMounted(() => {
|
||||
min-width: 22px;
|
||||
height: 20px;
|
||||
padding: 0 6px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: var(--el-text-color-secondary);
|
||||
background: var(--el-fill-color);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
:deep(.el-tree) {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__content) {
|
||||
height: 38px;
|
||||
margin-bottom: 4px;
|
||||
padding: 0 10px;
|
||||
margin-bottom: 4px;
|
||||
border-radius: 10px;
|
||||
transition:
|
||||
background-color 0.18s ease,
|
||||
|
||||
@@ -9,7 +9,10 @@ import { useAccessStore } from '@easyflow/stores';
|
||||
import { UploadFilled } from '@element-plus/icons-vue';
|
||||
import { ElIcon, ElMessage, ElUpload } from 'element-plus';
|
||||
|
||||
import { normalizeUploadError, resolveUploadPath } from '#/utils/upload-response';
|
||||
import {
|
||||
normalizeUploadError,
|
||||
resolveUploadPath,
|
||||
} from '#/utils/upload-response';
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
|
||||
@@ -8,8 +8,11 @@ import { useAccessStore } from '@easyflow/stores';
|
||||
|
||||
import { ElButton, ElMessage, ElUpload } from 'element-plus';
|
||||
|
||||
import { normalizeUploadError, resolveUploadPath } from '#/utils/upload-response';
|
||||
import { $t } from '#/locales';
|
||||
import {
|
||||
normalizeUploadError,
|
||||
resolveUploadPath,
|
||||
} from '#/utils/upload-response';
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
|
||||
@@ -10,7 +10,10 @@ import { Plus } from '@element-plus/icons-vue';
|
||||
import { ElIcon, ElImage, ElMessage, ElUpload } from 'element-plus';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { normalizeUploadError, resolveUploadPath } from '#/utils/upload-response';
|
||||
import {
|
||||
normalizeUploadError,
|
||||
resolveUploadPath,
|
||||
} from '#/utils/upload-response';
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
@@ -134,20 +137,20 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||
|
||||
<style>
|
||||
.avatar-uploader .el-upload {
|
||||
width: var(--avatar-size);
|
||||
height: var(--avatar-size);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: var(--avatar-size);
|
||||
height: var(--avatar-size);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border: 1px solid hsl(var(--input) / 0.92);
|
||||
background: hsl(var(--modal-surface-strong) / 92%);
|
||||
border: 1px solid hsl(var(--input) / 92%);
|
||||
border-radius: 50%;
|
||||
background: hsl(var(--modal-surface-strong) / 0.92);
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(0 0% 100% / 0.72),
|
||||
0 16px 28px -24px hsl(var(--foreground) / 0.24);
|
||||
inset 0 1px 0 hsl(0deg 0% 100% / 72%),
|
||||
0 16px 28px -24px hsl(var(--foreground) / 24%);
|
||||
transition:
|
||||
transform var(--el-transition-duration-fast),
|
||||
border-color var(--el-transition-duration-fast),
|
||||
@@ -155,26 +158,26 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: hsl(var(--primary) / 0.52);
|
||||
transform: translateY(-1px);
|
||||
border-color: hsl(var(--primary) / 52%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 hsl(0 0% 100% / 0.76),
|
||||
0 20px 36px -26px hsl(var(--modal-preview-glow) / 0.3);
|
||||
inset 0 1px 0 hsl(0deg 0% 100% / 76%),
|
||||
0 20px 36px -26px hsl(var(--modal-preview-glow) / 30%);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.avatar-uploader--soft-panel .el-upload {
|
||||
background:
|
||||
radial-gradient(
|
||||
circle at top,
|
||||
hsl(var(--modal-preview-glow) / 0.14),
|
||||
hsl(var(--modal-preview-glow) / 14%),
|
||||
transparent 60%
|
||||
),
|
||||
linear-gradient(
|
||||
145deg,
|
||||
hsl(var(--modal-preview-surface) / 0.98),
|
||||
hsl(var(--modal-preview-surface-strong) / 0.9)
|
||||
hsl(var(--modal-preview-surface) / 98%),
|
||||
hsl(var(--modal-preview-surface-strong) / 90%)
|
||||
);
|
||||
border-color: hsl(var(--modal-preview-border) / 0.9);
|
||||
border-color: hsl(var(--modal-preview-border) / 90%);
|
||||
}
|
||||
|
||||
.avatar-uploader--rounded .el-upload {
|
||||
|
||||
Reference in New Issue
Block a user