Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/components/Language-selector/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@ const changeLanguage = (lang: string) => {
.selected-lang {
color: var(--el-color-primary);
}
</style>
</style>
25 changes: 11 additions & 14 deletions frontend/src/utils/xss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,13 @@ export function highlightKeyword(
highlightClass: string = 'highlight'
): string {
if (!keyword) return escapeHtml(text)

const escapedText = escapeHtml(text)
const escapedKeyword = escapeHtml(keyword)

// Use case-insensitive replace
const regex = new RegExp(escapedKeyword, 'gi')
return escapedText.replace(
regex,
(match) => `<span class="${highlightClass}">${match}</span>`
)
return escapedText.replace(regex, (match) => `<span class="${highlightClass}">${match}</span>`)
}

/**
Expand All @@ -48,33 +45,33 @@ export function sanitizeHtml(html: string): string {
// Create a temporary div to parse HTML
const temp = document.createElement('div')
temp.innerHTML = html

// List of allowed tags
const allowedTags = ['b', 'i', 'u', 'strong', 'em', 'span', 'p', 'br', 'a']

// List of allowed attributes
const allowedAttrs = ['class', 'href', 'title']

// Remove disallowed tags and attributes
const sanitize = (node: Node): void => {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element

// Check if tag is allowed
if (!allowedTags.includes(element.tagName.toLowerCase())) {
// Replace with text content
const textNode = document.createTextNode(element.textContent || '')
element.parentNode?.replaceChild(textNode, element)
return
}

// Remove disallowed attributes
Array.from(element.attributes).forEach((attr) => {
if (!allowedAttrs.includes(attr.name.toLowerCase())) {
element.removeAttribute(attr.name)
}
})

// For links, ensure they don't use javascript: protocol
if (element.tagName.toLowerCase() === 'a') {
const href = element.getAttribute('href') || ''
Expand All @@ -83,11 +80,11 @@ export function sanitizeHtml(html: string): string {
}
}
}

// Recursively sanitize child nodes
Array.from(node.childNodes).forEach(sanitize)
}

sanitize(temp)
return temp.innerHTML
}
2 changes: 1 addition & 1 deletion frontend/src/views/chat/ChatTokenTime.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function getLogList() {
<span>{{ $t('parameter.tokens_required') }} {{ totalTokens }}</span>
<span style="margin-left: 12px">{{ $t('parameter.time_execution') }} {{ duration }} s</span>

<div @click="getLogList" class="detail">
<div class="detail" @click="getLogList">
<el-icon style="margin-right: 4px" size="16">
<icon_logs_outlined></icon_logs_outlined>
</el-icon>
Expand Down
132 changes: 38 additions & 94 deletions frontend/src/views/chat/component/charts/Table.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { BaseChart, type ChartAxis, type ChartData } from '@/views/chat/component/BaseChart.ts'
import {
copyToClipboard,
type Node,
type S2DataConfig,
S2Event,
type S2MountContainer,
Expand Down Expand Up @@ -71,106 +70,51 @@ export class Table extends BaseChart {
data: this.data,
}

const sortState: Record<string, string> = {}

const handleSortClick = (params: any) => {
const { meta } = params
const s2 = meta.spreadsheet
if (s2 && meta.isLeaf) {
const fieldId = meta.field
const currentMethod = sortState[fieldId] || 'none'
const sortOrder = ['none', 'desc', 'asc']
const nextMethod = sortOrder[(sortOrder.indexOf(currentMethod) + 1) % sortOrder.length]
sortState[fieldId] = nextMethod
s2.groupSortByMethod(nextMethod === 'none' ? 'none' : (nextMethod as SortMethod), meta)
s2.render()
}
}

const s2Options: S2Options = {
width: 600,
height: 360,
showDefaultHeaderActionIcon: true,
showDefaultHeaderActionIcon: false,
headerActionIcons: [
{
icons: ['GlobalDesc'],
belongsCell: 'colCell',
displayCondition: (node: any) => node.isLeaf && sortState[node.field] === 'desc',
onClick: handleSortClick,
},
{
icons: ['GlobalAsc'],
belongsCell: 'colCell',
displayCondition: (node: any) => node.isLeaf && sortState[node.field] === 'asc',
onClick: handleSortClick,
},
{
icons: ['SortDown'],
belongsCell: 'colCell',
displayCondition: (node: any) =>
node.isLeaf && (!sortState[node.field] || sortState[node.field] === 'none'),
onClick: handleSortClick,
},
],
tooltip: {
operation: {
// 开启组内排序
sort: true,
},
dataCell: {
enable: true,
content: (cell) => {
const meta = cell.getMeta()
const container = document.createElement('div')
container.style.padding = '8px 0'
container.style.minWidth = '100px'
container.style.maxWidth = '400px'
container.style.display = 'flex'
container.style.alignItems = 'center'
container.style.padding = '8px 16px'
container.style.cursor = 'pointer'
container.style.color = '#606266'
container.style.fontSize = '14px'
container.style.whiteSpace = 'pre-wrap'

const text = document.createTextNode(meta.fieldValue)
container.appendChild(text)

return container
},
},
colCell: {
enable: true,
content: (cell) => {
const meta = cell.getMeta()
const { spreadsheet: s2 } = meta
if (!meta.isLeaf) {
return null
}

// 创建类似Element Plus下拉菜单的结构
const container = document.createElement('div')
container.className = 'el-dropdown'
container.style.padding = '8px 0'
container.style.minWidth = '100px'

const menuItems = [
{
label: t('chat.sort_desc'),
method: 'desc' as SortMethod,
icon: 'el-icon-sort-down',
},
{ label: t('chat.sort_asc'), method: 'asc' as SortMethod, icon: 'el-icon-sort-up' },
{ label: t('chat.sort_none'), method: 'none' as SortMethod, icon: 'el-icon-close' },
]

menuItems.forEach((item) => {
const itemEl = document.createElement('div')
itemEl.className = 'el-dropdown-menu__item'
itemEl.style.display = 'flex'
itemEl.style.alignItems = 'center'
itemEl.style.padding = '8px 16px'
itemEl.style.cursor = 'pointer'
itemEl.style.color = '#606266'
itemEl.style.fontSize = '14px'

// 鼠标悬停效果
itemEl.addEventListener('mouseenter', () => {
itemEl.style.backgroundColor = '#f5f7fa'
itemEl.style.color = '#409eff'
})
itemEl.addEventListener('mouseleave', () => {
itemEl.style.backgroundColor = 'transparent'
itemEl.style.color = '#606266'
})

// 添加图标(如果需要)
if (item.icon) {
const icon = document.createElement('i')
icon.className = item.icon
icon.style.marginRight = '8px'
icon.style.fontSize = '16px'
itemEl.appendChild(icon)
}

const text = document.createTextNode(item.label)
itemEl.appendChild(text)

itemEl.addEventListener('click', (e) => {
e.stopPropagation()
s2.groupSortByMethod(item.method, meta as Node)
// 可以在这里添加关闭tooltip的逻辑
})

container.appendChild(itemEl)
})

return container
},
},
},
// 如果有省略号, 复制到的是完整文本
interaction: {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/views/ds/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ const btnSelectClick = (val: any) => {
</el-icon>
<div class="name">{{ info.name }}</div>
<div class="export-remark">
<el-button style="margin-right: 12px" @click="downloadTemplate" secondary>
<el-button style="margin-right: 12px" secondary @click="downloadTemplate">
<template #icon>
<icon_import_outlined></icon_import_outlined>
</template>
Expand All @@ -387,7 +387,7 @@ const btnSelectClick = (val: any) => {
:content="$t('ds.form.choose_tables')"
placement="top"
>
<el-button style="margin-right: -4px" @click="handleSelectTableList" text>
<el-button style="margin-right: -4px" text @click="handleSelectTableList">
<el-icon size="18">
<icon_form_outlined></icon_form_outlined>
</el-icon>
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/views/ds/TableRelationship.vue
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,12 @@ const save = () => {
<svg style="position: fixed; top: -9999px" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter
id="filter-dropShadow-v0-3329848037"
x="-1"
y="-1"
width="3"
height="3"
filterUnits="objectBoundingBox"
id="filter-dropShadow-v0-3329848037"
>
<feDropShadow
stdDeviation="4"
Expand All @@ -436,18 +436,18 @@ const save = () => {
</filter>
</defs>
</svg>
<div v-loading="loading" v-if="!nodeIds.length" class="relationship-empty">
<div v-if="!nodeIds.length" v-loading="loading" class="relationship-empty">
{{ t('training.add_it_here') }}
</div>
<div v-loading="loading" v-else id="container"></div>
<div v-else id="container" v-loading="loading"></div>
<div
@dragover.prevent.stop="dragover"
@drop.prevent.stop="drop"
v-show="dragging"
class="drag-mask"
@dragover.prevent.stop="dragover"
@drop.prevent.stop="drop"
></div>
<div class="save-btn">
<el-button type="primary" v-if="nodeIds.length" @click="save">
<el-button v-if="nodeIds.length" type="primary" @click="save">
{{ t('common.save') }}
</el-button>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/views/system/appearance/LoginPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
<div class="config-area">
<div class="login-logo">
<div class="login-logo-icon">
<img height="52" v-if="pageLogin" :src="pageLogin" alt="" />
<el-icon size="52" v-else
<img v-if="pageLogin" height="52" :src="pageLogin" alt="" />
<el-icon v-else size="52"
><custom_small v-if="themeColor !== 'default'"></custom_small>
<LOGO_fold v-else></LOGO_fold
></el-icon>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/system/appearance/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
<div class="navigate-preview" style="height: 425px">
<div class="navigate-head">
<div class="header-sql">
<img height="30" width="30" v-if="pageLogin" :src="pageLogin" alt="" />
<img v-if="pageLogin" height="30" width="30" :src="pageLogin" alt="" />
<custom_small v-else-if="themeColor !== 'default'" class="logo" />
<logo v-else></logo>
<span style="margin-left: 8px">{{ loginForm.name }}</span>
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/views/system/model/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ defineExpose({ showErrorMask })
</div>
<div class="type-value">
<span class="type">{{ $t('model.model_type') }}</span>
<span class="value"> {{ modelType.startsWith('modelType.') ? $t(modelType) : modelType }}</span>
<span class="value">
{{ modelType.startsWith('modelType.') ? $t(modelType) : modelType }}</span
>
</div>
<div class="type-value">
<span class="type">{{ $t('model.basic_model') }}</span>
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/views/system/model/Model.vue
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,9 @@ const submit = (item: any) => {
>
<template #header="{ close }">
<span style="white-space: nowrap">{{
editModel ? $t('dashboard.edit') + $t('common.empty') + $t(activeNameI18nKey) : t('model.add_model')
editModel
? $t('dashboard.edit') + $t('common.empty') + $t(activeNameI18nKey)
: t('model.add_model')
}}</span>
<div v-if="!editModel" class="flex-center" style="width: 100%">
<el-steps custom style="max-width: 500px; flex: 1" :active="activeStep" align-center>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/views/system/user/SyncUserDing.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
:filter-node-method="filterNode"
show-checkbox
:default-checked-keys="defaultCheckedKeys"
@check="handleCheck"
:props="defaultProps"
node-key="id"
default-expand-all
:expand-on-click-node="false"
@check="handleCheck"
>
<template #default="{ node, data }">
<div class="custom-tree-node flex">
Expand Down Expand Up @@ -95,7 +95,7 @@
</div>

<template #footer>
<el-checkbox style="float: left" v-model="existingUser">
<el-checkbox v-model="existingUser" style="float: left">
{{ $t('sync.the_existing_user') }}
</el-checkbox>
<el-button secondary @click="centerDialogVisible = false">
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/views/system/variables/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -453,9 +453,9 @@ const handleCurrentChange = (val: number) => {
</el-form-item>
<el-form-item :label="t('variables.variable_type')">
<el-radio-group
v-model="pageForm.var_type"
:disabled="!!pageForm.id"
@change="varTypeChange"
v-model="pageForm.var_type"
>
<el-radio value="text">{{ $t('model.text') }}</el-radio>
<el-radio value="number">
Expand All @@ -481,8 +481,8 @@ const handleCurrentChange = (val: number) => {
</template>
<div class="value-list">
<el-form-item
:key="index"
v-for="(_, index) in pageForm.value"
:key="index"
:prop="'value.' + index"
:rules="{
required: true,
Expand Down Expand Up @@ -510,7 +510,7 @@ const handleCurrentChange = (val: number) => {
</el-form-item>
</div>
</el-form-item>
<el-form-item prop="value" v-else :label="t('variables.variable_value')">
<el-form-item v-else prop="value" :label="t('variables.variable_value')">
<div class="value-number_date">
<template v-if="pageForm.var_type === 'number'">
<el-input
Expand Down