| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828 |
- <template>
- <div class="ai-page ai-expert">
- <!-- 数据来源入口(设计稿 hero 区):「上传文件」按历史逻辑跳转上传数据页;「开始分析」=新建对话并绑定默认内置研判方案 -->
- <section class="ai-expert__hero-grid">
- <article class="ai-expert__hero">
- <div class="ai-expert__hero-icon"><Icon icon="mdi:upload" :size="22" /></div>
- <div class="ai-expert__hero-body">
- <div class="ai-expert__hero-title">数据清洗与治理</div>
- <p class="ai-expert__hero-desc"
- >上传案件文件后自动执行清洗与治理,形成规范、可分析的数据,供后续智能研判使用。</p
- >
- </div>
- <button class="ai-expert__hero-start" type="button" @click="onUploadFile">
- 上传文件
- <Icon icon="mdi:arrow-right" :size="15" />
- </button>
- </article>
- <article class="ai-expert__hero">
- <div class="ai-expert__hero-icon"><Icon icon="mdi:filter-variant" :size="22" /></div>
- <div class="ai-expert__hero-body">
- <div class="ai-expert__hero-title">基于清洗结果智能分析</div>
- <p class="ai-expert__hero-desc">
- 基于当前案件上传数据经模板库自动清洗治理后的结果,通过系统内置的研判方案进行分析。
- </p>
- </div>
- <button class="ai-expert__hero-start" type="button" @click="onCleanAnalysis">
- 开始分析
- <Icon icon="mdi:arrow-right" :size="15" />
- </button>
- </article>
- </section>
- <!-- 面板:顶栏(标题 + 摘要胶囊 + 搜索 + 操作)+ 提示 + 研判方案卡片网格 -->
- <section class="ai-expert__panel">
- <div class="ai-expert__bar">
- <span class="ai-expert__title">研判方案</span>
- <span class="ai-expert__count">{{ summary }}</span>
- <label class="ai-expert__search">
- <Icon icon="mdi:magnify" :size="14" />
- <input v-model="keyword" type="text" placeholder="输入方案名称搜索" />
- </label>
- <span class="ai-expert__bar-sp"></span>
- <a-button :loading="loading" @click="load">
- <template #icon><Icon icon="mdi:refresh" :size="14" /></template>
- 刷新
- </a-button>
- <a-button type="primary" @click="openCreate">
- <template #icon><Icon icon="mdi:plus" :size="14" /></template>
- 新建研判方案
- </a-button>
- </div>
- <a-spin :spinning="loading">
- <!-- 卡片混合展示(内置在前),右上角标签区分内置 / 自定义 -->
- <div v-if="filteredExperts.length" class="ai-expert__grid">
- <article v-for="e in filteredExperts" :key="e.id" class="ai-expert__card">
- <div class="ai-expert__card-head">
- <span class="ai-expert__card-icon">{{ (e.name || '?').charAt(0) }}</span>
- <span class="ai-expert__name" :title="e.name">{{ e.name }}</span>
- </div>
- <span class="ai-expert__badge" :class="e.builtin ? 'is-builtin' : 'is-custom'">
- {{ e.builtin ? '内置' : '自定义' }}
- </span>
- <p class="ai-expert__desc">{{ e.description || '暂无描述' }}</p>
- <div class="ai-expert__chips">
- <span class="ai-expert__chip">
- <i class="ai-expert__chip-dot" style="background: #0969da"></i>
- {{ groupSummary(e) }}
- </span>
- <span class="ai-expert__chip">
- <i class="ai-expert__chip-dot" style="background: #8250df"></i>
- {{ skillSummary(e) }}
- </span>
- </div>
- <div class="ai-expert__card-ops">
- <!-- 「开始分析」视觉取设计稿的主色文字按钮,点击行为不变(emit chat 进入新对话草稿) -->
- <button class="ai-expert__run" type="button" @click="onChat(e)">
- 开始分析
- <Icon icon="mdi:arrow-right" :size="13" />
- </button>
- <span class="ai-expert__card-ops-sp"></span>
- <!-- 查看详情:内置/自定义都可看(只读弹框),放最左与编辑/删除分隔开 -->
- <a-tooltip title="查看详情">
- <button
- class="ai-expert__iconbtn"
- type="button"
- aria-label="查看方案详情"
- @click="openDetail(e)"
- >
- <Icon icon="mdi:eye-outline" :size="15" />
- </button>
- </a-tooltip>
- <!-- 内置只可对话(编辑/删除入口根本不渲染,后端另有双重校验) -->
- <template v-if="!e.builtin">
- <a-tooltip title="编辑研判方案">
- <button class="ai-expert__iconbtn" type="button" aria-label="编辑研判方案" @click="openEdit(e)">
- <Icon icon="mdi:pencil-outline" :size="15" />
- </button>
- </a-tooltip>
- <a-tooltip title="删除研判方案">
- <button
- class="ai-expert__iconbtn is-danger"
- type="button"
- aria-label="删除研判方案"
- @click="onDelete(e)"
- >
- <Icon icon="mdi:trash-can-outline" :size="15" />
- </button>
- </a-tooltip>
- </template>
- </div>
- </article>
- </div>
- <div v-else-if="!loading && !experts.length" class="ai-expert__empty">
- 还没有研判方案。点击右上角「新建研判方案」创建第一个。
- </div>
- <div v-else-if="!loading" class="ai-expert__empty"> 没有找到与「{{ keyword.trim() }}」匹配的研判方案。 </div>
- </a-spin>
- </section>
- <!-- 新建/编辑:默认窗口弹框(可右上角全屏),内容为新建方案页设计版式 -->
- <AgentFormModal v-model:open="formOpen" :record="editing" @saved="onSaved" />
- <!-- 方案详情(只读):卡片眼睛图标打开,展示提示词与勾选的工具包/技能 -->
- <BasicModal
- :open="detailOpen"
- :title="detail ? `方案详情:${detail.name}` : '方案详情'"
- :width="760"
- :show-ok-btn="false"
- cancel-text="关闭"
- wrap-class-name="ae-detail-modal"
- @cancel="detailOpen = false"
- >
- <div v-if="detail" class="ae-detail">
- <div class="ae-detail__head">
- <span class="ae-detail__badge" :class="detail.builtin ? 'is-builtin' : 'is-custom'">
- {{ detail.builtin ? '内置' : '自定义' }}
- </span>
- <span class="ae-detail__name">{{ detail.name }}</span>
- </div>
- <p class="ae-detail__desc">{{ detail.description || '暂无描述' }}</p>
- <section class="ae-detail__panel">
- <div class="ae-detail__label">系统提示词</div>
- <pre class="ae-detail__prompt">{{ detail.sysPrompt || '(未填写)' }}</pre>
- </section>
- <section class="ae-detail__panel">
- <div class="ae-detail__label">可用工具包</div>
- <div v-if="detailToolChips.length" class="ae-detail__chips">
- <span v-for="n in detailToolChips" :key="n" class="ae-detail__chip">{{ n }}</span>
- </div>
- <div v-else class="ae-detail__none">不限制(全部业务工具可用)</div>
- </section>
- <section class="ae-detail__panel">
- <div class="ae-detail__label">可用技能</div>
- <div v-if="(detail.skillNames || []).length" class="ae-detail__chips">
- <span v-for="n in detail.skillNames" :key="n" class="ae-detail__chip">{{ n }}</span>
- </div>
- <div v-else class="ae-detail__none">不限制(该账号安装的技能全部可用)</div>
- </section>
- </div>
- </BasicModal>
- </div>
- </template>
- <script lang="ts" setup name="AiExpertIndex">
- /**
- * 研判方案(智能体)管理页:卡片混合列表,右上角标签区分「内置 / 自定义」。
- *
- * 对接 AgentExpertController(/agents/**,Result 包装):列表本身就是「内置 + 自己的」
- * (后端过滤,内置在前),页面不再分组,只靠卡片角标区分来源。
- *
- * 操作语义:
- * - 「上传文件」入口:按历史逻辑跳转上传数据页(CaseDataUpload,/data/upload 全屏流程首步);
- * - 「开始分析」(第二块卡):=新建对话逻辑,预选默认内置研判方案绑定(复用研判方案卡的 chat 事件);
- * - 开始分析(研判方案卡):向上层 emit(工作台切到对话视图并预选该研判方案,下一条消息创建的会话即绑定它);
- * - 编辑/删除:仅自建卡片出现,内置的按钮根本不渲染(后端另有双重校验)。
- */
- import { computed, onMounted, ref } from 'vue';
- import { useRouter } from 'vue-router';
- import { Button, Modal, Spin, Tooltip } from 'ant-design-vue';
- import Icon from '@/core/components/Icon/src/Icon.vue';
- import { useMessage } from '@/core/hooks/web/useMessage';
- import { BasicModal } from '@/core/components/Modal';
- import * as agentApi from '../../api/agentApi';
- import * as toolPackApi from '../../api/toolPackApi';
- import type { AiExpert, ToolPack } from '../../api/types';
- import AgentFormModal from '../../components/AgentFormModal.vue';
- import { toolGroupLabel } from '../../utils/constants';
- const AButton = Button;
- const ASpin = Spin;
- const ATooltip = Tooltip;
- const emit = defineEmits<{
- chat: [expert: AiExpert];
- }>();
- const { showMessage } = useMessage();
- const router = useRouter();
- const experts = ref<AiExpert[]>([]);
- const loading = ref(false);
- const formOpen = ref(false);
- const editing = ref<AiExpert | null>(null);
- /** 方案详情(只读弹框) */
- const detailOpen = ref(false);
- const detail = ref<AiExpert | null>(null);
- /** 工具包清单(id → 名,懒加载一次;详情弹框把 toolPackIds 显示成人话) */
- const packCatalog = ref<ToolPack[] | null>(null);
- async function openDetail(e: AiExpert) {
- detail.value = e;
- detailOpen.value = true;
- if (!packCatalog.value) {
- packCatalog.value = (await toolPackApi.listToolPacks().catch(() => [])) || [];
- }
- }
- /** 详情里的工具包 chip:内置/自建包按 id 取名;旧方案残留的工具组按组名显示;去重防同名并列 */
- const detailToolChips = computed(() => {
- const d = detail.value;
- if (!d) return [];
- const chips = new Set<string>();
- for (const id of d.toolPackIds || []) {
- chips.add(packCatalog.value?.find((p) => p.id === id)?.name || `工具包 ${id}`);
- }
- for (const g of d.toolGroups || []) chips.add(toolGroupLabel(g));
- return [...chips];
- });
- /** 研判方案名称搜索(纯前端过滤,不改接口与加载逻辑) */
- const keyword = ref('');
- const filteredExperts = computed(() => {
- const k = keyword.value.trim().toLowerCase();
- if (!k) return experts.value;
- return experts.value.filter((e) => (e.name || '').toLowerCase().includes(k));
- });
- const builtinExperts = computed(() => experts.value.filter((e) => e.builtin));
- const mineExperts = computed(() => experts.value.filter((e) => !e.builtin));
- const summary = computed(() => {
- const parts: string[] = [];
- if (builtinExperts.value.length) parts.push(`内置 ${builtinExperts.value.length}`);
- if (mineExperts.value.length) parts.push(`我的 ${mineExperts.value.length}`);
- return parts.length ? parts.join(' · ') : '尚未创建研判方案';
- });
- onMounted(load);
- async function load() {
- loading.value = true;
- try {
- experts.value = (await agentApi.listExperts()) || [];
- } catch (e: any) {
- showMessage(e?.message || '加载研判方案列表失败', 'error');
- } finally {
- loading.value = false;
- }
- }
- /** 工具摘要:勾了工具包显示包数,旧方案还有工具组的列出组名,都没有 = 不限制 */
- function groupSummary(e: AiExpert): string {
- const groups = e.toolGroups || [];
- const packIds = e.toolPackIds || [];
- if (!groups.length && !packIds.length) return '工具:不限制';
- const parts: string[] = [];
- if (packIds.length) parts.push(`${packIds.length} 个工具包`);
- if (groups.length) parts.push(groups.map((g) => toolGroupLabel(g)).join('、'));
- return `工具:${parts.join(' + ')}`;
- }
- function skillSummary(e: AiExpert): string {
- const names = e.skillNames || [];
- if (!names.length) return '技能:不限制';
- return `技能:${names.length} 个已绑定`;
- }
- function onChat(e: AiExpert) {
- emit('chat', e);
- }
- /**
- * 「开始分析」(基于清洗结果):=新建对话的逻辑,用默认内置研判方案绑定——
- * 复用研判方案卡的 chat 事件(工作台 startDraft 预选该研判方案 → 切对话视图 → 首条消息创建会话即绑定)。
- * 内置研判方案列表按后端约定在最前,取第一个 builtin 即默认内置研判方案。
- */
- function onCleanAnalysis() {
- const builtin = experts.value.find((e) => e.builtin);
- if (builtin) emit('chat', builtin);
- }
- /**
- * 「上传文件」入口:跳转上传数据页(上传 → 清洗 → 导入全屏流程的首步)。
- * 带 redirect 来源地址:上传页的「返回」按它回到本界面,view=agent 让工作台恢复到研判方案视图。
- */
- function onUploadFile() {
- router.push({ name: 'CaseDataUpload', query: { redirect: '/workbench?view=agent' } });
- }
- function openCreate() {
- editing.value = null;
- formOpen.value = true;
- }
- function openEdit(e: AiExpert) {
- editing.value = e;
- formOpen.value = true;
- }
- async function onSaved() {
- await load();
- }
- function onDelete(e: AiExpert) {
- Modal.confirm({
- title: '删除研判方案',
- content: `确认删除「${e.name}」?绑定它的会话将无法继续对话(会得到「智能体不存在或已删除」错误)。`,
- okText: '删除',
- okType: 'primary',
- okButtonProps: { danger: true },
- cancelText: '取消',
- onOk: async () => {
- try {
- await agentApi.deleteExpert(e.id);
- showMessage('已删除', 'success');
- await load();
- } catch (err: any) {
- showMessage(err?.message || '删除失败', 'error');
- }
- },
- });
- }
- </script>
- <style lang="less" scoped>
- .ai-expert {
- display: flex;
- flex-direction: column;
- gap: var(--ai-sp-3);
- /* 页级内边距与现状一致(8px),四边不贴边 —— 不用设计稿的宽页边距 */
- padding: var(--ai-sp-2);
- /* 设计稿是灰底白卡:工作台中栏直接给灰色画布,卡片才能像设计稿一样立起来 */
- background: var(--ai-bg-page);
- /* ── 数据来源入口(设计稿 hero 区) ── */
- &__hero-grid {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: var(--ai-sp-3);
- flex: 0 0 auto;
- }
- &__hero {
- position: relative;
- display: flex;
- align-items: center;
- gap: 14px;
- padding: 18px 20px;
- overflow: hidden;
- background: var(--ai-bg-card, #fff);
- border: 1px solid var(--ai-border);
- border-radius: var(--ai-radius-lg);
- box-shadow: var(--ai-shadow-sm);
- transition:
- box-shadow 0.15s ease,
- transform 0.15s ease;
- &:hover {
- box-shadow: var(--ai-shadow);
- transform: translateY(-1px);
- }
- /* 角部淡蓝光晕:只做一点品牌氛围,保持克制 */
- &::after {
- content: '';
- position: absolute;
- top: -60px;
- right: -40px;
- width: 220px;
- height: 160px;
- background: radial-gradient(closest-side, fade(@primary-color, 7%), transparent);
- pointer-events: none;
- }
- }
- &__hero-icon {
- display: flex;
- align-items: center;
- justify-content: center;
- flex: 0 0 auto;
- width: 46px;
- height: 46px;
- color: var(--ai-primary);
- background: var(--ai-primary-soft);
- border-radius: 10px;
- }
- &__hero-body {
- flex: 1 1 auto;
- min-width: 0;
- }
- &__hero-title {
- font-size: 16px;
- font-weight: 700;
- letter-spacing: 0.01em;
- color: var(--ai-text);
- }
- &__hero-desc {
- margin-top: 5px;
- font-size: 12.5px;
- line-height: 1.7;
- color: var(--ai-text-2);
- }
- &__hero-start {
- position: relative;
- z-index: 1;
- display: inline-flex;
- align-items: center;
- gap: 7px;
- flex: 0 0 auto;
- padding: 0 16px;
- height: 36px;
- font-size: 13px;
- font-weight: 600;
- font-family: inherit;
- color: #fff;
- background: var(--ai-primary);
- border: none;
- border-radius: var(--ai-radius);
- box-shadow: 0 2px 6px fade(@primary-color, 28%);
- cursor: pointer;
- transition: background 0.15s ease;
- &:hover {
- background: darken(@primary-color, 8%);
- }
- }
- /* ── 面板(设计稿 panel:白卡承载标题行 + 提示 + 卡片网格) ── */
- &__panel {
- display: flex;
- flex-direction: column;
- gap: var(--ai-sp-3);
- flex: 1 1 auto;
- min-height: 0;
- padding: 12px;
- background: var(--ai-bg-card, #fff);
- border: 1px solid var(--ai-border);
- border-radius: var(--ai-radius-lg);
- }
- &__bar {
- display: flex;
- align-items: center;
- gap: var(--ai-sp-3);
- flex: 0 0 auto;
- }
- &__title {
- font-size: 15px;
- font-weight: 700;
- letter-spacing: 0.01em;
- color: var(--ai-text);
- }
- /* 摘要胶囊(设计稿 panel-head 的 count pill 形态),文案仍是研判方案页原有 summary */
- &__count {
- /* 上下不对称内边距:胶囊文字墨迹比同行标题/按钮低 1px,据此上移对齐同一光学中线 */
- padding: 1px 10px 3px;
- font-size: 11.5px;
- font-weight: 500;
- color: var(--ai-text-2);
- background: var(--ai-bg-subtle);
- border-radius: 99px;
- }
- /* 研判方案名称搜索(设计稿 panel-head 的 search 形态) */
- &__search {
- display: flex;
- align-items: center;
- gap: 8px;
- width: 240px;
- height: 32px;
- padding: 0 11px;
- color: var(--ai-text-3);
- background: var(--ai-bg-card, #fff);
- border: 1px solid var(--ai-border-strong);
- border-radius: var(--ai-radius);
- cursor: text;
- transition:
- border-color 0.15s ease,
- box-shadow 0.15s ease;
- &:focus-within {
- border-color: var(--ai-primary);
- box-shadow: 0 0 0 3px var(--ai-primary-soft);
- }
- input {
- flex: 1;
- min-width: 0;
- font-size: 12.5px;
- font-family: inherit;
- color: var(--ai-text);
- background: transparent;
- border: none;
- outline: none;
- /* 占位符文字用灰色(--ai-text-3 未在 token 中定义,直接写色值) */
- &::placeholder {
- color: #9aa1ac;
- }
- }
- }
- &__bar-sp {
- flex: 1 1 auto;
- }
- &__grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
- gap: var(--ai-sp-3);
- }
- &__card {
- position: relative;
- display: flex;
- flex-direction: column;
- gap: 8px;
- padding: 14px 14px 10px;
- background: var(--ai-bg-card, #fff);
- border: 1px solid var(--ai-border);
- border-radius: var(--ai-radius);
- transition:
- box-shadow 0.15s ease,
- transform 0.15s ease,
- border-color 0.15s ease;
- &:hover {
- border-color: fade(@primary-color, 35%);
- box-shadow: var(--ai-shadow, 0 4px 16px rgba(15, 23, 42, 0.08));
- transform: translateY(-1px);
- }
- }
- &__card-head {
- display: flex;
- align-items: center;
- gap: 9px;
- /* 给右上角「内置 / 自定义」角标让位 */
- padding-right: 52px;
- }
- /* 设计稿卡片头的图标位:用研判方案名称第一个字作图标 */
- &__card-icon {
- display: flex;
- align-items: center;
- justify-content: center;
- flex: 0 0 auto;
- width: 30px;
- height: 30px;
- font-size: 15px;
- font-weight: 600;
- line-height: 1;
- color: var(--ai-primary);
- background: var(--ai-primary-soft);
- border-radius: var(--ai-radius);
- }
- &__name {
- overflow: hidden;
- font-size: 13.5px;
- font-weight: 700;
- color: var(--ai-text);
- white-space: nowrap;
- text-overflow: ellipsis;
- letter-spacing: 0.01em;
- }
- /* 右上角来源标签:内置(全局)/ 自定义(我的) */
- &__badge {
- position: absolute;
- top: 12px;
- right: 12px;
- padding: 0 7px;
- font-size: 11px;
- line-height: 18px;
- border-radius: 99px;
- &.is-builtin {
- color: var(--ai-text-2);
- background: var(--ai-bg-subtle);
- }
- &.is-custom {
- color: var(--ai-primary);
- background: var(--ai-primary-soft);
- }
- }
- &__desc {
- min-height: 54px;
- margin: 0;
- font-size: 12.25px;
- line-height: 1.7;
- color: var(--ai-text-2);
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
- &__chips {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
- }
- &__chip {
- display: inline-flex;
- align-items: center;
- gap: 5px;
- max-width: 100%;
- padding: 1px 8px;
- overflow: hidden;
- font-size: 11px;
- color: var(--ai-text-2);
- white-space: nowrap;
- text-overflow: ellipsis;
- background: var(--ai-bg-subtle);
- border-radius: 99px;
- }
- &__chip-dot {
- flex: 0 0 auto;
- width: 5px;
- height: 5px;
- border-radius: 50%;
- }
- &__card-ops {
- display: flex;
- align-items: center;
- gap: var(--ai-sp-1);
- padding-top: 8px;
- /* 卡片等高时把操作行压到底部,多张卡片的操作行对齐 */
- margin-top: auto;
- border-top: 1px solid var(--ai-border);
- }
- /* 「开始分析」=设计稿的主色文字按钮,行为不变(emit chat) */
- &__run {
- display: inline-flex;
- align-items: center;
- gap: 5px;
- padding: 4px 8px;
- font-size: 12.5px;
- font-weight: 600;
- font-family: inherit;
- color: var(--ai-primary);
- background: none;
- border: none;
- border-radius: 6px;
- cursor: pointer;
- &:hover {
- background: var(--ai-primary-soft);
- }
- }
- &__card-ops-sp {
- flex: 1 1 auto;
- }
- /* 编辑/删除收成图标按钮:与主按钮同排但视觉退后一档,hover 才显形 */
- &__iconbtn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 26px;
- height: 26px;
- padding: 0;
- color: var(--ai-text-2);
- background: transparent;
- border: none;
- border-radius: 6px;
- cursor: pointer;
- transition:
- color 0.15s ease,
- background 0.15s ease;
- &:hover {
- color: var(--ai-text);
- background: var(--ai-bg-subtle);
- }
- &.is-danger:hover {
- color: @error-color;
- background: fade(@error-color, 8%);
- }
- }
- &__empty {
- padding: 48px;
- font-size: 13px;
- color: var(--ai-text-2);
- text-align: center;
- border: 1px dashed var(--ai-border);
- border-radius: 10px;
- }
- }
- /* ── 方案详情弹框(只读):头 + 提示词 + 工具包/技能勾选 ── */
- .ae-detail {
- display: flex;
- flex-direction: column;
- gap: var(--ai-sp-3);
- &__head {
- display: flex;
- align-items: center;
- gap: var(--ai-sp-2);
- }
- &__badge {
- flex: 0 0 auto;
- padding: 0 7px;
- font-size: 11px;
- line-height: 18px;
- border-radius: 99px;
- &.is-builtin {
- color: var(--ai-text-2);
- background: var(--ai-bg-subtle);
- }
- &.is-custom {
- color: var(--ai-primary);
- background: var(--ai-primary-soft);
- }
- }
- &__name {
- overflow: hidden;
- font-size: 15px;
- font-weight: 700;
- color: var(--ai-text);
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- &__desc {
- margin: 0;
- font-size: 13px;
- line-height: 1.7;
- color: var(--ai-text-2);
- }
- &__panel {
- display: flex;
- flex-direction: column;
- gap: 8px;
- padding: 12px 14px;
- background: var(--ai-bg-card, #fff);
- border: 1px solid var(--ai-border);
- border-radius: var(--ai-radius-lg);
- }
- &__label {
- font-size: 12.5px;
- font-weight: 700;
- color: var(--ai-text);
- }
- /* 提示词可能很长:限高 + 内部滚动,保留换行 */
- &__prompt {
- overflow-y: auto;
- max-height: 240px;
- padding: 10px 12px;
- margin: 0;
- font-family: inherit;
- font-size: 12.5px;
- line-height: 1.7;
- color: var(--ai-text);
- white-space: pre-wrap;
- word-break: break-word;
- background: var(--ai-bg-subtle);
- border-radius: var(--ai-radius);
- }
- &__chips {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
- }
- &__chip {
- overflow: hidden;
- max-width: 260px;
- padding: 1px 10px;
- font-size: 11.5px;
- font-weight: 500;
- color: var(--ai-primary);
- background: var(--ai-primary-soft);
- border-radius: 99px;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- &__none {
- font-size: 12.5px;
- color: var(--ai-text-2);
- }
- }
- </style>
|