Files
X-Financial/web/src/views/AuditView.vue

352 lines
14 KiB
Vue
Raw Normal View History

<template>
<section class="skill-center">
<Transition name="skill-view" mode="out-in">
<article v-if="selectedSkill" key="detail" class="skill-detail">
<div class="detail-scroll">
<section class="detail-hero panel">
<div class="hero-title">
<div class="skill-badge" :class="selectedSkill.badgeTone">{{ selectedSkill.typeLabel }}</div>
<h2>{{ selectedSkill.name }}</h2>
<p>{{ selectedSkill.summary }}</p>
<div v-if="selectedSkill.type === 'skills'" class="hero-review-meta">
<span>
<i class="mdi mdi-account-check-outline"></i>
审核人{{ selectedSkill.reviewer }}
</span>
<b :class="['status-pill', selectedSkill.statusTone]">{{ selectedSkill.status }}</b>
<span>{{ selectedSkill.status === '已上线' ? '审核通过,可上线' : '待审核通过后上线' }}</span>
</div>
</div>
</section>
<div class="detail-grid" :class="{ 'skill-md-detail-grid': selectedSkill.type === 'skills' }">
<section class="detail-main">
<article v-if="selectedSkill.type === 'skills'" class="detail-card panel markdown-card">
<div class="card-head">
<div>
<h3>Markdown 规则内容</h3>
<p>管理员直接编辑该 Skill 对应的 .md 审查规则文件内容</p>
</div>
<button class="mini-btn">
<i class="mdi mdi-content-save-outline"></i>
<span>保存 .md</span>
</button>
</div>
<label class="field">
<span>{{ selectedSkill.fields.find((field) => field.label === '文件路径')?.value }}</span>
<textarea
v-model="selectedSkill.markdownContent"
class="markdown-editor"
spellcheck="false"
></textarea>
</label>
</article>
<article v-else class="detail-card panel">
<div class="card-head">
<div>
<h3>{{ selectedSkill.configTitle }}</h3>
<p>{{ selectedSkill.configDesc }}</p>
</div>
<button class="mini-btn">保存草稿</button>
</div>
<div class="form-grid">
<label v-for="field in selectedSkill.fields" :key="field.label" class="field">
<span>{{ field.label }}</span>
<input :value="field.value" />
</label>
<label class="field span-2">
<span>说明</span>
<textarea rows="3" :value="selectedSkill.summary"></textarea>
</label>
</div>
</article>
<article v-if="selectedSkill.type !== 'skills'" class="detail-card panel">
<div class="card-head">
<div>
<h3>{{ selectedSkill.detailTitle }}</h3>
<p>{{ selectedSkill.detailDesc }}</p>
</div>
<span class="edit-badge">{{ selectedSkill.promptSections.length }} </span>
</div>
<div class="prompt-stack">
<section v-for="section in selectedSkill.promptSections" :key="section.title" class="prompt-block">
<header>
<strong>{{ section.title }}</strong>
<span>{{ section.intent }}</span>
</header>
<textarea rows="5" :value="section.content"></textarea>
</section>
</div>
</article>
<article v-if="selectedSkill.type !== 'skills'" class="detail-card panel">
<div class="card-head">
<div>
<h3>{{ selectedSkill.outputTitle }}</h3>
<p>{{ selectedSkill.outputDesc }}</p>
</div>
<button class="mini-btn primary">运行测试</button>
</div>
<div class="contract-grid">
<div class="contract-panel">
<h4>{{ selectedSkill.ruleListTitle }}</h4>
<ul>
<li v-for="rule in selectedSkill.outputRules" :key="rule">{{ rule }}</li>
</ul>
</div>
<div class="contract-panel">
<h4>{{ selectedSkill.checkListTitle }}</h4>
<div v-for="test in selectedSkill.tests" :key="test.name" class="test-row">
<div>
<strong>{{ test.name }}</strong>
<span>{{ test.input }}</span>
</div>
<b :class="['test-state', test.tone]">{{ test.result }}</b>
</div>
</div>
</div>
</article>
</section>
<aside v-if="selectedSkill.type === 'skills'" class="detail-side skill-review-side">
<article class="side-card panel review-card">
<div class="card-head">
<div>
<h3>版本信息</h3>
<p>最近 5 个规则版本</p>
</div>
</div>
<div class="version-list">
<button
v-for="item in selectedSkill.history.slice(0, 5)"
:key="item.version + item.time"
class="version-row"
:class="{ active: item.version === selectedSkill.version }"
type="button"
@click="openVersionSwitch(item)"
>
<div class="version-row-head">
<strong>{{ item.version }}</strong>
<span class="version-current-slot">
<b v-if="item.version === selectedSkill.version" class="current-version">当前</b>
</span>
<span>{{ item.time }}</span>
</div>
<p>{{ item.note }}</p>
</button>
</div>
</article>
</aside>
<aside v-else class="detail-side">
<article class="side-card panel">
<div class="card-head">
<div>
<h3>{{ selectedSkill.triggerTitle }}</h3>
<p>{{ selectedSkill.triggerDesc }}</p>
</div>
</div>
<div class="tag-list">
<span v-for="item in selectedSkill.triggers" :key="item">{{ item }}</span>
</div>
</article>
<article class="side-card panel">
<div class="card-head">
<div>
<h3>{{ selectedSkill.toolTitle }}</h3>
<p>{{ selectedSkill.toolDesc }}</p>
</div>
</div>
<div class="tool-list">
<div v-for="tool in selectedSkill.tools" :key="tool.name" class="tool-row">
<div>
<strong>{{ tool.name }}</strong>
<span>{{ tool.scope }}</span>
</div>
<b :class="['tool-state', tool.tone]">{{ tool.mode }}</b>
</div>
</div>
</article>
<article class="side-card panel">
<div class="card-head">
<div>
<h3>{{ selectedSkill.historyTitle }}</h3>
<p>{{ selectedSkill.historyDesc }}</p>
</div>
</div>
<div class="history-list">
<div v-for="item in selectedSkill.history" :key="item.version" class="history-row">
<strong>{{ item.version }}</strong>
<span>{{ item.note }}</span>
<small>{{ item.time }}</small>
</div>
</div>
</article>
<article class="side-card panel publish-card">
<div>
<h3>{{ selectedSkill.publishTitle }}</h3>
<p>{{ selectedSkill.publishDesc }}</p>
</div>
<div class="publish-summary">
<span>{{ selectedSkill.publishMeta }}</span>
<strong>{{ selectedSkill.publishState }}</strong>
</div>
</article>
</aside>
</div>
</div>
<footer class="detail-actions">
<button class="back-action" type="button" @click="selectedSkill = null">
<i class="mdi mdi-arrow-left"></i>
<span>返回能力列表</span>
</button>
<div class="detail-action-group">
<button class="minor-action" type="button">
<i class="mdi mdi-content-save-outline"></i>
<span>保存草稿</span>
</button>
<button class="minor-action" type="button">
<i class="mdi mdi-flask-outline"></i>
<span>运行测试</span>
</button>
<button class="major-action" type="button">
<i class="mdi mdi-rocket-launch-outline"></i>
<span>正式上线</span>
</button>
</div>
</footer>
</article>
<article v-else key="list" class="skill-list panel">
<nav class="status-tabs" aria-label="能力类型">
<button
v-for="tab in tabs"
:key="tab.id"
type="button"
:class="{ active: activeType === tab.id }"
@click="activeType = tab.id"
>
{{ tab.label }}
</button>
</nav>
<div class="list-toolbar">
<div class="filter-set">
<button v-for="filter in filters" :key="filter" type="button" class="filter-btn">
<span>{{ filter }}</span>
<i class="mdi mdi-chevron-down"></i>
</button>
</div>
<button class="create-btn" type="button">
<i class="mdi mdi-plus"></i>
<span>{{ createButtonLabel }}</span>
</button>
</div>
<p class="hint"><i class="mdi mdi-information-outline"></i> {{ hintText }}</p>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>{{ tableColumns.name }}</th>
<th>{{ tableColumns.category }}</th>
<th>{{ tableColumns.owner }}</th>
<th>{{ tableColumns.scope }}</th>
<th>{{ tableColumns.runtime }}</th>
<th>{{ tableColumns.version }}</th>
<th>状态</th>
<th>{{ tableColumns.metric }}</th>
<th>最近更新</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="skill in visibleSkills"
:key="skill.id"
:class="{ spotlight: skill.spotlight }"
@click="selectedSkill = skill"
>
<td>
<div class="skill-name-cell">
<span class="skill-avatar" :class="skill.badgeTone">{{ skill.short }}</span>
<div>
<strong>{{ skill.name }}</strong>
<span>{{ skill.summary }}</span>
</div>
</div>
</td>
<td>{{ skill.category }}</td>
<td>{{ skill.owner }}</td>
<td><span class="scope-pill">{{ skill.scope }}</span></td>
<td>{{ skill.model }}</td>
<td>{{ skill.version }}</td>
<td><span class="status-pill" :class="skill.statusTone">{{ skill.status }}</span></td>
<td>{{ skill.hitRate }}</td>
<td>{{ skill.updatedAt }}</td>
<td>
<button class="row-action" type="button" @click.stop="selectedSkill = skill">
编辑
</button>
</td>
</tr>
</tbody>
</table>
</div>
</article>
</Transition>
<Teleport to="body">
<div v-if="versionSwitchTarget" class="modal-backdrop" @click.self="cancelVersionSwitch">
<section class="version-modal panel" role="dialog" aria-modal="true" aria-labelledby="version-switch-title">
<div class="card-head">
<div>
<h3 id="version-switch-title">切换规则版本</h3>
<p>切换后编辑器会加载该版本的 .md 内容当前未保存内容不会自动发布</p>
</div>
</div>
<div class="version-modal-summary">
<div>
<span>当前版本</span>
<strong>{{ selectedSkill?.version }}</strong>
</div>
<i class="mdi mdi-arrow-right"></i>
<div>
<span>目标版本</span>
<strong>{{ versionSwitchTarget.version }}</strong>
</div>
</div>
<div class="version-modal-note">
<strong>{{ versionSwitchTarget.note }}</strong>
<span>{{ versionSwitchTarget.time }}</span>
</div>
<footer class="modal-actions">
<button class="minor-action" type="button" @click="cancelVersionSwitch">取消</button>
<button class="major-action" type="button" @click="confirmVersionSwitch">确认切换</button>
</footer>
</section>
</div>
</Teleport>
</section>
</template>
<script src="./scripts/AuditView.js"></script>
<style scoped src="../assets/styles/views/audit-view.css"></style>