优化修改
This commit is contained in:
@@ -38,10 +38,10 @@ module.exports = {
|
|||||||
'@typescript-eslint/no-redeclare': 'error',
|
'@typescript-eslint/no-redeclare': 'error',
|
||||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
|
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': [
|
'@typescript-eslint/no-unused-vars': [
|
||||||
'error',
|
'warn',
|
||||||
{
|
{
|
||||||
argsIgnorePattern: '^_',
|
argsIgnorePattern: '^_',
|
||||||
varsIgnorePattern: '^_',
|
varsIgnorePattern: '^_|props|watch',
|
||||||
caughtErrorsIgnorePattern: '^_',
|
caughtErrorsIgnorePattern: '^_',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -64,6 +64,11 @@ module.exports = {
|
|||||||
'vue/no-parsing-error': 'off',
|
'vue/no-parsing-error': 'off',
|
||||||
'vue/no-deprecated-v-on-native-modifier': 'off',
|
'vue/no-deprecated-v-on-native-modifier': 'off',
|
||||||
'vue/multi-word-component-names': 'off',
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/no-reserved-component-names': 'off',
|
||||||
|
'vue/no-v-for-template-key': 'off',
|
||||||
|
'vue/no-unused-vars': 'off',
|
||||||
|
'vue/no-mutating-props': 'off',
|
||||||
|
'no-mixed-spaces-and-tabs': 'off',
|
||||||
'no-useless-escape': 'off',
|
'no-useless-escape': 'off',
|
||||||
'no-sparse-arrays': 'off',
|
'no-sparse-arrays': 'off',
|
||||||
'no-prototype-builtins': 'off',
|
'no-prototype-builtins': 'off',
|
||||||
@@ -77,8 +82,8 @@ module.exports = {
|
|||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
'no-v-model-argument': 'off',
|
'no-v-model-argument': 'off',
|
||||||
'no-case-declarations': 'off',
|
'no-case-declarations': 'off',
|
||||||
'no-console': 'error',
|
'no-console': 'off',
|
||||||
'no-debugger': 'error',
|
'no-debugger': 'warn',
|
||||||
'no-redeclare': 'off',
|
'no-redeclare': 'off',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,22 +32,12 @@
|
|||||||
<div class="table-body">
|
<div class="table-body">
|
||||||
<div v-for="(attr, index) in ruleForm.attrs" :key="index" class="table-row">
|
<div v-for="(attr, index) in ruleForm.attrs" :key="index" class="table-row">
|
||||||
<div class="col col-name">
|
<div class="col col-name">
|
||||||
<el-input
|
<el-input v-if="!isDictType(attr.type)" v-model="attr.name" placeholder="请输入属性名称" clearable />
|
||||||
v-if="!isDictType(attr.type)"
|
|
||||||
v-model="attr.name"
|
|
||||||
placeholder="请输入属性名称"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
<span v-else class="dict-name">{{ attr.name || '请选择字典' }}</span>
|
<span v-else class="dict-name">{{ attr.name || '请选择字典' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col-type">
|
<div class="col col-type">
|
||||||
<el-select v-model="attr.type" placeholder="属性类型" class="w100" @change="onAttrTypeChange(attr)">
|
<el-select v-model="attr.type" placeholder="属性类型" class="w100" @change="onAttrTypeChange(attr)">
|
||||||
<el-option
|
<el-option v-for="item in attrTypeOptions" :key="item.key" :label="item.value" :value="item.key" />
|
||||||
v-for="item in attrTypeOptions"
|
|
||||||
:key="item.key"
|
|
||||||
:label="item.value"
|
|
||||||
:value="item.key"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col-required">
|
<div class="col col-required">
|
||||||
@@ -155,13 +145,6 @@ interface DictInfo {
|
|||||||
remark: string;
|
remark: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DictValue {
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
isDefault: number;
|
|
||||||
remark: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RuleForm {
|
interface RuleForm {
|
||||||
id: string | '';
|
id: string | '';
|
||||||
parentId: string;
|
parentId: string;
|
||||||
@@ -219,15 +202,11 @@ const fetchDictTypeOptions = () => {
|
|||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
const list = res.data?.list ?? [];
|
const list = res.data?.list ?? [];
|
||||||
// 提取所有字典类型信息
|
// 提取所有字典类型信息
|
||||||
dictTypeOptions.value = list
|
dictTypeOptions.value = list.map((item: any) => item.info).filter((info: DictInfo) => info && info.name);
|
||||||
.map((item: any) => item.info)
|
|
||||||
.filter((info: DictInfo) => info && info.name);
|
|
||||||
// 保存完整的字典数据列表(包含info和values)
|
// 保存完整的字典数据列表(包含info和values)
|
||||||
dictValueOptions.value = list;
|
dictValueOptions.value = list;
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
|
|
||||||
dictTypeOptions.value = [];
|
dictTypeOptions.value = [];
|
||||||
dictValueOptions.value = [];
|
dictValueOptions.value = [];
|
||||||
})
|
})
|
||||||
@@ -270,7 +249,9 @@ const isDictOptionDisabled = (dictName: string, currentAttr: CustomAttr) => {
|
|||||||
const dictInfo = dictTypeOptions.value.find((item) => item.name === dictName);
|
const dictInfo = dictTypeOptions.value.find((item) => item.name === dictName);
|
||||||
const dictType = dictInfo?.type || '';
|
const dictType = dictInfo?.type || '';
|
||||||
// 检查该字典是否已被其他属性使用(使用 dictType 判断)
|
// 检查该字典是否已被其他属性使用(使用 dictType 判断)
|
||||||
return ruleForm.attrs.some((attr) => attr !== currentAttr && isDictType(attr.type) && (attr.dictType === dictType || (!attr.dictType && attr.name === dictName)));
|
return ruleForm.attrs.some(
|
||||||
|
(attr) => attr !== currentAttr && isDictType(attr.type) && (attr.dictType === dictType || (!attr.dictType && attr.name === dictName))
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加自定义属性
|
// 添加自定义属性
|
||||||
@@ -328,10 +309,10 @@ const openDialog = (row?: CategoryRow | string, edit?: boolean) => {
|
|||||||
|
|
||||||
// 递归函数,将所有id字段转换为字符串
|
// 递归函数,将所有id字段转换为字符串
|
||||||
const convertIdsToString = (items: any[]): any[] => {
|
const convertIdsToString = (items: any[]): any[] => {
|
||||||
return items.map(item => ({
|
return items.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
id: item.id?.toString(),
|
id: item.id?.toString(),
|
||||||
children: item.children && item.children.length > 0 ? convertIdsToString(item.children) : []
|
children: item.children && item.children.length > 0 ? convertIdsToString(item.children) : [],
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="msg.isUser" class="avatar-wrap">
|
<div v-if="msg.isUser" class="avatar-wrap">
|
||||||
<el-avatar :size="36" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
|
<div class="user-avatar">你</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,6 +111,23 @@ const messages = ref<Message[]>([
|
|||||||
0 2px 8px rgba(59, 130, 246, 0.15);
|
0 2px 8px rgba(59, 130, 246, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ffffff;
|
||||||
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||||||
|
border: 1px solid #f59e0b;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.25),
|
||||||
|
0 2px 8px rgba(245, 158, 11, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.bubble-wrap {
|
.bubble-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
<div class="brand-dot"></div>
|
<div class="brand-dot"></div>
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<el-avatar :size="42" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
|
<div class="ai-avatar">
|
||||||
|
<span class="ai-icon">AI</span>
|
||||||
|
</div>
|
||||||
<div class="user-details">
|
<div class="user-details">
|
||||||
<div class="user-name">助手</div>
|
<div class="user-name">AI 助手</div>
|
||||||
<div class="user-status">在线</div>
|
<div class="user-status"><span class="status-dot"></span>在线</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-button class="new-chat-btn" @click="handleNewChat">
|
<el-button class="new-chat-btn" @click="handleNewChat">
|
||||||
@@ -124,12 +126,13 @@ const handleDeleteHistory = (id: number) => {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 252px;
|
width: 252px;
|
||||||
background: linear-gradient(180deg, #ffffff 0%, #f9fbff 100%);
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95) 0%, rgba(249, 251, 255, 0.92) 100%);
|
||||||
border-right: 1px solid #e7edf7;
|
border-right: 1px solid #e7edf7;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-shadow: 6px 0 20px rgba(15, 23, 42, 0.04);
|
box-shadow: 8px 0 30px rgba(15, 23, 42, 0.06);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
@@ -139,20 +142,24 @@ const handleDeleteHistory = (id: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.new-chat-btn {
|
.new-chat-btn {
|
||||||
margin-top: 10px;
|
margin-top: 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 34px;
|
height: 40px;
|
||||||
border-radius: 10px;
|
border-radius: 12px;
|
||||||
border: 1px solid #dbe7f7;
|
border: 1px solid rgba(59, 130, 246, 0.28);
|
||||||
background: linear-gradient(135deg, #f8fbff 0%, #eff6ff 100%);
|
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||||
color: #1f4db8;
|
color: #ffffff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.2px;
|
letter-spacing: 0.2px;
|
||||||
|
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.26);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #1e40af;
|
transform: translateY(-1px);
|
||||||
border-color: #bfdbfe;
|
color: #ffffff;
|
||||||
background: linear-gradient(135deg, #eff6ff 0%, #e0eeff 100%);
|
border-color: rgba(59, 130, 246, 0.4);
|
||||||
|
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
|
||||||
|
box-shadow: 0 6px 16px rgba(37, 99, 235, 0.32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,10 +178,32 @@ const handleDeleteHistory = (id: number) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 8px 8px 6px;
|
padding: 10px 10px 8px;
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.86) 0%, rgba(248, 251, 255, 0.92) 100%);
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(147, 197, 253, 0.04) 100%);
|
||||||
border: 1px solid #e7edf7;
|
border: 1px solid rgba(59, 130, 246, 0.18);
|
||||||
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-avatar {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 50%, #1d4ed8 100%);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 2px 8px rgba(255, 255, 255, 0.35),
|
||||||
|
0 4px 12px rgba(37, 99, 235, 0.28);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-icon {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-details {
|
.user-details {
|
||||||
@@ -193,6 +222,23 @@ const handleDeleteHistory = (id: number) => {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #10b981;
|
color: #10b981;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #10b981;
|
||||||
|
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-menu {
|
.sidebar-menu {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { Clock, Plus, Delete } from '@element-plus/icons-vue';
|
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import Sidebar from './components/Sidebar.vue';
|
import Sidebar from './components/Sidebar.vue';
|
||||||
import MainContent from './components/MainContent.vue';
|
import MainContent from './components/MainContent.vue';
|
||||||
@@ -87,8 +86,10 @@ const handleDeleteHistory = (id: number) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background:
|
background:
|
||||||
radial-gradient(1200px 460px at 65% -140px, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0) 68%),
|
radial-gradient(1000px 500px at 70% -180px, rgba(59, 130, 246, 0.12) 0%, rgba(59, 130, 246, 0) 65%),
|
||||||
linear-gradient(180deg, #f7faff 0%, #f4f7fc 100%);
|
radial-gradient(800px 400px at 10% 10%, rgba(139, 92, 246, 0.06) 0%, rgba(139, 92, 246, 0) 60%),
|
||||||
|
radial-gradient(600px 300px at 90% 110%, rgba(16, 185, 129, 0.05) 0%, rgba(16, 185, 129, 0) 60%),
|
||||||
|
linear-gradient(180deg, #f8fbff 0%, #f0f4fa 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-wrapper {
|
.main-wrapper {
|
||||||
@@ -105,8 +106,8 @@ const handleDeleteHistory = (id: number) => {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background:
|
background:
|
||||||
linear-gradient(180deg, rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0) 28%),
|
linear-gradient(180deg, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 32%),
|
||||||
radial-gradient(1000px 380px at 10% 110%, rgba(99, 102, 241, 0.08) 0%, rgba(99, 102, 241, 0) 72%);
|
radial-gradient(900px 360px at 15% 115%, rgba(99, 102, 241, 0.06) 0%, rgba(99, 102, 241, 0) 70%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -724,11 +724,15 @@ const parseFieldsUnified = (raw: unknown): Array<{ key: string; value: string }>
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是对象格式 { key: value }
|
// 如果是对象格式 { key: value } 或者 { key: { value: value } }
|
||||||
if (typeof raw === 'object') {
|
if (typeof raw === 'object') {
|
||||||
const fields: Array<{ key: string; value: string }> = [];
|
const fields: Array<{ key: string; value: string }> = [];
|
||||||
Object.keys(raw as Record<string, unknown>).forEach((key) => {
|
Object.keys(raw as Record<string, unknown>).forEach((key) => {
|
||||||
const v = (raw as Record<string, unknown>)[key];
|
let v = (raw as Record<string, unknown>)[key];
|
||||||
|
// 处理 { key: { value: "value" } } 格式(后端可能返回这种结构)
|
||||||
|
if (v && typeof v === 'object' && !Array.isArray(v) && 'value' in v) {
|
||||||
|
v = (v as { value: unknown }).value;
|
||||||
|
}
|
||||||
const value = String(v ?? '');
|
const value = String(v ?? '');
|
||||||
fields.push({ key, value });
|
fields.push({ key, value });
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user