|
@@ -1,14 +1,56 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="expert-step-table">
|
|
<div class="expert-step-table">
|
|
|
- <!-- <header class="table-intro">
|
|
|
|
|
- <div class="intro-copy">
|
|
|
|
|
- <h1>专家梯队</h1>
|
|
|
|
|
|
|
+ <el-card>
|
|
|
|
|
+ <div class="search-layout">
|
|
|
|
|
+ <section class="filter-panel">
|
|
|
|
|
+ <div class="filter-fields">
|
|
|
|
|
+ <el-input v-model="searchQuery" placeholder="姓名 / 专业 / 业绩" clearable />
|
|
|
|
|
+
|
|
|
|
|
+ <el-select v-model="selectedEducation" placeholder="选择学历" clearable>
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="option in educationOptions"
|
|
|
|
|
+ :key="option.value"
|
|
|
|
|
+ :label="option.label"
|
|
|
|
|
+ :value="option.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+
|
|
|
|
|
+ <el-select v-model="selectedTeam" placeholder="选择梯队" clearable>
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="option in teamOptions"
|
|
|
|
|
+ :key="option.value"
|
|
|
|
|
+ :label="option.label"
|
|
|
|
|
+ :value="option.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
|
|
+ <aside class="word-cloud-panel">
|
|
|
|
|
+ <div class="word-cloud">
|
|
|
|
|
+ <button
|
|
|
|
|
+ v-for="word in wordCloudWords"
|
|
|
|
|
+ :key="word.text"
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ class="cloud-word"
|
|
|
|
|
+ :style="{
|
|
|
|
|
+ left: word.left,
|
|
|
|
|
+ top: word.top,
|
|
|
|
|
+ '--rotate': `${word.rotate}deg`,
|
|
|
|
|
+ fontSize: `${word.size}px`,
|
|
|
|
|
+ color: word.color,
|
|
|
|
|
+ transform: `rotate(${word.rotate}deg)`
|
|
|
|
|
+ }"
|
|
|
|
|
+ @click="handleWordClick(word.text)"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ word.text }}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </aside>
|
|
|
</div>
|
|
</div>
|
|
|
- </header> -->
|
|
|
|
|
|
|
|
|
|
- <el-card>
|
|
|
|
|
<div
|
|
<div
|
|
|
- v-for="(row, rowIndex) in tableData"
|
|
|
|
|
|
|
+ v-for="(row, rowIndex) in filteredTableData"
|
|
|
:key="row.teamName"
|
|
:key="row.teamName"
|
|
|
class="ladder-row"
|
|
class="ladder-row"
|
|
|
:style="{ width: row.tableWidth, '--row-bg': row.backgroundColor }"
|
|
:style="{ width: row.tableWidth, '--row-bg': row.backgroundColor }"
|
|
@@ -21,7 +63,7 @@
|
|
|
<table class="content-table" :aria-label="row.teamName">
|
|
<table class="content-table" :aria-label="row.teamName">
|
|
|
<colgroup>
|
|
<colgroup>
|
|
|
<col
|
|
<col
|
|
|
- v-for="(_, index) in row.columnWidths"
|
|
|
|
|
|
|
+ v-for="(_, index) in row.columnWidths || []"
|
|
|
:key="index"
|
|
:key="index"
|
|
|
:style="{ width: `${getColumnPercent(row, index)}%` }"
|
|
:style="{ width: `${getColumnPercent(row, index)}%` }"
|
|
|
/>
|
|
/>
|
|
@@ -30,7 +72,7 @@
|
|
|
<tbody>
|
|
<tbody>
|
|
|
<tr>
|
|
<tr>
|
|
|
<td
|
|
<td
|
|
|
- v-for="(cell, index) in row.cells"
|
|
|
|
|
|
|
+ v-for="(cell, index) in row.cells || []"
|
|
|
:key="index"
|
|
:key="index"
|
|
|
:class="['step-cell', cell.tone === 'danger' ? 'notice-cell' : '']"
|
|
:class="['step-cell', cell.tone === 'danger' ? 'notice-cell' : '']"
|
|
|
>
|
|
>
|
|
@@ -58,16 +100,94 @@
|
|
|
</div>
|
|
</div>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
|
|
|
|
|
- <div v-if="selectedMember" class="member-detail">
|
|
|
|
|
- <span class="detail-label">SELECTED EXPERT</span>
|
|
|
|
|
- <strong>{{ selectedMember }}</strong>
|
|
|
|
|
- <span class="detail-copy">已选中,可在此处接入人员详情面板。</span>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <el-drawer
|
|
|
|
|
+ v-model="drawerVisible"
|
|
|
|
|
+ size="56%"
|
|
|
|
|
+ :with-header="false"
|
|
|
|
|
+ class="expert-drawer"
|
|
|
|
|
+ destroy-on-close
|
|
|
|
|
+ >
|
|
|
|
|
+ <template v-if="activeExpert">
|
|
|
|
|
+ <div class="drawer-shell">
|
|
|
|
|
+ <header class="drawer-header">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h2>{{ activeExpert.name }} - 专家详情</h2>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ {{ activeExpert.role }} / {{ activeExpert.team }} /
|
|
|
|
|
+ {{ activeExpert.direction.join('、') }}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </header>
|
|
|
|
|
+
|
|
|
|
|
+ <section class="drawer-profile">
|
|
|
|
|
+ <div class="profile-aside">
|
|
|
|
|
+ <div class="profile-avatar">{{ activeExpert.name.slice(0, 1) }}</div>
|
|
|
|
|
+ <div class="profile-tags">
|
|
|
|
|
+ <span v-for="tag in activeExpert.tags" :key="tag" class="profile-tag">{{
|
|
|
|
|
+ tag
|
|
|
|
|
+ }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="profile-main">
|
|
|
|
|
+ <div class="info-block">
|
|
|
|
|
+ <span class="info-label">基础信息</span>
|
|
|
|
|
+ <strong>年龄 {{ activeExpert.age }},学历 {{ activeExpert.education }}</strong>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="info-block">
|
|
|
|
|
+ <span class="info-label">专业方向</span>
|
|
|
|
|
+ <strong>{{ activeExpert.direction.join('、') }}</strong>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="info-block">
|
|
|
|
|
+ <span class="info-label">主要业绩</span>
|
|
|
|
|
+ <strong>{{ activeExpert.achievement }}</strong>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="info-block">
|
|
|
|
|
+ <span class="info-label">可参与事项</span>
|
|
|
|
|
+ <strong>{{ activeExpert.tasks.join(' / ') }}</strong>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="profile-actions">
|
|
|
|
|
+ <el-button type="success" size="large">邀请评审</el-button>
|
|
|
|
|
+ <el-button size="large" plain>维护档案</el-button>
|
|
|
|
|
+ <el-button size="large" plain>下载履历</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
|
|
+ <section class="record-panel">
|
|
|
|
|
+ <div class="record-header">
|
|
|
|
|
+ <h3>调用记录</h3>
|
|
|
|
|
+ <p>用于判断专家近期占用情况和评审适配度</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table :data="activeExpert.records" class="record-table" table-layout="fixed">
|
|
|
|
|
+ <el-table-column prop="date" label="日期" min-width="110" />
|
|
|
|
|
+ <el-table-column prop="event" label="事项" min-width="130" />
|
|
|
|
|
+ <el-table-column
|
|
|
|
|
+ prop="project"
|
|
|
|
|
+ label="关联项目"
|
|
|
|
|
+ min-width="170"
|
|
|
|
|
+ show-overflow-tooltip
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-table-column label="结果" min-width="96">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <span :class="['record-badge', row.statusType]">{{ row.status }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </section>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-drawer>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref } from 'vue'
|
|
|
|
|
|
|
+import { computed, ref } from 'vue'
|
|
|
|
|
|
|
|
type CellItem =
|
|
type CellItem =
|
|
|
| {
|
|
| {
|
|
@@ -82,15 +202,40 @@ type CellItem =
|
|
|
|
|
|
|
|
type TableRow = {
|
|
type TableRow = {
|
|
|
teamName: string
|
|
teamName: string
|
|
|
|
|
+ education: string
|
|
|
|
|
+ searchText: string
|
|
|
tableWidth: string
|
|
tableWidth: string
|
|
|
backgroundColor: string
|
|
backgroundColor: string
|
|
|
columnWidths: number[]
|
|
columnWidths: number[]
|
|
|
cells: CellItem[]
|
|
cells: CellItem[]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+type ExpertRecord = {
|
|
|
|
|
+ date: string
|
|
|
|
|
+ event: string
|
|
|
|
|
+ project: string
|
|
|
|
|
+ status: string
|
|
|
|
|
+ statusType: 'success' | 'primary'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type ExpertProfile = {
|
|
|
|
|
+ name: string
|
|
|
|
|
+ role: string
|
|
|
|
|
+ team: string
|
|
|
|
|
+ age: number
|
|
|
|
|
+ education: string
|
|
|
|
|
+ direction: string[]
|
|
|
|
|
+ achievement: string
|
|
|
|
|
+ tasks: string[]
|
|
|
|
|
+ tags: string[]
|
|
|
|
|
+ records: ExpertRecord[]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const tableData: TableRow[] = [
|
|
const tableData: TableRow[] = [
|
|
|
{
|
|
{
|
|
|
teamName: '第一梯队',
|
|
teamName: '第一梯队',
|
|
|
|
|
+ education: '博士',
|
|
|
|
|
+ searchText: '朱桂林 软件工程 50项成果',
|
|
|
tableWidth: '64%',
|
|
tableWidth: '64%',
|
|
|
backgroundColor: '#eef6ff',
|
|
backgroundColor: '#eef6ff',
|
|
|
columnWidths: [1.5, 1.5, 1.5, 1.5, 1.5],
|
|
columnWidths: [1.5, 1.5, 1.5, 1.5, 1.5],
|
|
@@ -122,6 +267,8 @@ const tableData: TableRow[] = [
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
teamName: '第二梯队',
|
|
teamName: '第二梯队',
|
|
|
|
|
+ education: '硕士',
|
|
|
|
|
+ searchText: '孙乐臻 金融风险 30项业绩',
|
|
|
tableWidth: '75%',
|
|
tableWidth: '75%',
|
|
|
backgroundColor: '#f2faef',
|
|
backgroundColor: '#f2faef',
|
|
|
columnWidths: [1.8, 1.8, 1.8, 1.8, 1.8],
|
|
columnWidths: [1.8, 1.8, 1.8, 1.8, 1.8],
|
|
@@ -135,6 +282,8 @@ const tableData: TableRow[] = [
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
teamName: '第三梯队',
|
|
teamName: '第三梯队',
|
|
|
|
|
+ education: '本科',
|
|
|
|
|
+ searchText: '王军 互联网产品 20项成果',
|
|
|
tableWidth: '100%',
|
|
tableWidth: '100%',
|
|
|
backgroundColor: '#fff8e8',
|
|
backgroundColor: '#fff8e8',
|
|
|
columnWidths: [2, 2, 2, 2, 2],
|
|
columnWidths: [2, 2, 2, 2, 2],
|
|
@@ -167,16 +316,163 @@ const tableData: TableRow[] = [
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
const selectedMember = ref('')
|
|
const selectedMember = ref('')
|
|
|
|
|
+const searchQuery = ref('')
|
|
|
|
|
+const selectedEducation = ref('')
|
|
|
|
|
+const selectedTeam = ref('')
|
|
|
|
|
+const drawerVisible = ref(false)
|
|
|
|
|
+const activeExpert = ref<ExpertProfile | null>(null)
|
|
|
|
|
+
|
|
|
|
|
+const expertProfileMap: Record<string, ExpertProfile> = {
|
|
|
|
|
+ 朱桂林: {
|
|
|
|
|
+ name: '朱桂林',
|
|
|
|
|
+ role: '技术专家',
|
|
|
|
|
+ team: '第一梯队',
|
|
|
|
|
+ age: 52,
|
|
|
|
|
+ education: '博士',
|
|
|
|
|
+ direction: ['材料工程', '可靠性验证', '失效分析'],
|
|
|
|
|
+ achievement:
|
|
|
|
|
+ '高温井下材料可靠性验证、材料失效分析专家评审;参与 6 个科研项目评审,2 个项目验收专家组。',
|
|
|
|
|
+ tasks: ['立项预审', '中期检查', '结题验收', '成果评审', '专项咨询'],
|
|
|
|
|
+ tags: ['技术专家', '第一梯队', '启用'],
|
|
|
|
|
+ records: [
|
|
|
|
|
+ {
|
|
|
|
|
+ date: '2026-08-01',
|
|
|
|
|
+ event: '中期检查评审',
|
|
|
|
|
+ project: '智能检测算法研发',
|
|
|
|
|
+ status: '已完成',
|
|
|
|
|
+ statusType: 'success'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ date: '2026-07-18',
|
|
|
|
|
+ event: '立项预审',
|
|
|
|
|
+ project: '平台数据治理标准',
|
|
|
|
|
+ status: '已反馈意见',
|
|
|
|
|
+ statusType: 'primary'
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ 孙乐臻: {
|
|
|
|
|
+ name: '孙乐臻',
|
|
|
|
|
+ role: '经营专家',
|
|
|
|
|
+ team: '第二梯队',
|
|
|
|
|
+ age: 46,
|
|
|
|
|
+ education: '硕士',
|
|
|
|
|
+ direction: ['经营管理', '投资评估', '风险控制'],
|
|
|
|
|
+ achievement: '参与多个经营分析专项评审,长期负责重点项目投资效益与风险识别。',
|
|
|
|
|
+ tasks: ['立项预审', '预算审查', '经营诊断', '专项咨询'],
|
|
|
|
|
+ tags: ['经营专家', '第二梯队', '启用'],
|
|
|
|
|
+ records: [
|
|
|
|
|
+ {
|
|
|
|
|
+ date: '2026-08-05',
|
|
|
|
|
+ event: '预算审查',
|
|
|
|
|
+ project: '区域经营提升计划',
|
|
|
|
|
+ status: '已完成',
|
|
|
|
|
+ statusType: 'success'
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ 王军: {
|
|
|
|
|
+ name: '王军',
|
|
|
|
|
+ role: '技术专家',
|
|
|
|
|
+ team: '第三梯队',
|
|
|
|
|
+ age: 41,
|
|
|
|
|
+ education: '本科',
|
|
|
|
|
+ direction: ['互联网产品', '数字化平台', '需求架构'],
|
|
|
|
|
+ achievement: '参与多个数字平台项目方案论证和成果评审,具备丰富的产品落地经验。',
|
|
|
|
|
+ tasks: ['方案评审', '中期检查', '成果评审'],
|
|
|
|
|
+ tags: ['技术专家', '第三梯队', '启用'],
|
|
|
|
|
+ records: [
|
|
|
|
|
+ {
|
|
|
|
|
+ date: '2026-07-26',
|
|
|
|
|
+ event: '成果评审',
|
|
|
|
|
+ project: '协同应用平台升级',
|
|
|
|
|
+ status: '已完成',
|
|
|
|
|
+ statusType: 'success'
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const wordCloudWords = [
|
|
|
|
|
+ { text: '地质', size: 24, rotate: -8, color: '#7fcd72', left: '8%', top: '52%' },
|
|
|
|
|
+ { text: '油藏', size: 30, rotate: 6, color: '#8ea4d8', left: '26%', top: '38%' },
|
|
|
|
|
+ { text: '钻井', size: 27, rotate: -10, color: '#f0df62', left: '46%', top: '22%' },
|
|
|
|
|
+ { text: '采油工艺', size: 22, rotate: 8, color: '#b26c41', left: '60%', top: '14%' },
|
|
|
|
|
+ { text: '压裂', size: 21, rotate: -16, color: '#3d79a5', left: '74%', top: '48%' },
|
|
|
|
|
+ { text: '酸化', size: 18, rotate: 12, color: '#f1b477', left: '14%', top: '16%' },
|
|
|
|
|
+ { text: '修井', size: 19, rotate: -12, color: '#d26ea8', left: '36%', top: '66%' },
|
|
|
|
|
+ { text: '博士', size: 17, rotate: 5, color: '#7e8d64', left: '58%', top: '64%' },
|
|
|
|
|
+ { text: '研究生', size: 16, rotate: -5, color: '#7f85b5', left: '79%', top: '28%' }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const educationOptions = [
|
|
|
|
|
+ { label: '全部', value: '' },
|
|
|
|
|
+ { label: '博士', value: '博士' },
|
|
|
|
|
+ { label: '硕士', value: '硕士' },
|
|
|
|
|
+ { label: '本科', value: '本科' },
|
|
|
|
|
+ { label: '专科', value: '专科' }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const teamOptions = [
|
|
|
|
|
+ { label: '全部', value: '' },
|
|
|
|
|
+ { label: '第一梯队', value: '第一梯队' },
|
|
|
|
|
+ { label: '第二梯队', value: '第二梯队' },
|
|
|
|
|
+ { label: '第三梯队', value: '第三梯队' }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
const cellBookmarks = ['技术', '经营', '商务', '安全', '法务']
|
|
const cellBookmarks = ['技术', '经营', '商务', '安全', '法务']
|
|
|
|
|
|
|
|
const getColumnPercent = (row: TableRow, index: number) => {
|
|
const getColumnPercent = (row: TableRow, index: number) => {
|
|
|
|
|
+ if (!row.columnWidths || row.columnWidths.length === 0) {
|
|
|
|
|
+ return '0'
|
|
|
|
|
+ }
|
|
|
const total = row.columnWidths.reduce((sum, width) => sum + width, 0)
|
|
const total = row.columnWidths.reduce((sum, width) => sum + width, 0)
|
|
|
-
|
|
|
|
|
- return ((row.columnWidths[index] / total) * 100).toFixed(4)
|
|
|
|
|
|
|
+ const value = row.columnWidths[index] ?? 0
|
|
|
|
|
+ if (total === 0) {
|
|
|
|
|
+ return '0'
|
|
|
|
|
+ }
|
|
|
|
|
+ return ((value / total) * 100).toFixed(4)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const filteredTableData = computed(() => {
|
|
|
|
|
+ return tableData.filter((row) => {
|
|
|
|
|
+ const query = searchQuery.value.trim().toLowerCase()
|
|
|
|
|
+ const nameMatch = row.searchText.toLowerCase().includes(query)
|
|
|
|
|
+ const teamMatch = row.teamName.toLowerCase().includes(query)
|
|
|
|
|
+ const educationMatch = selectedEducation.value
|
|
|
|
|
+ ? row.education === selectedEducation.value
|
|
|
|
|
+ : true
|
|
|
|
|
+ const selectedTeamMatch = selectedTeam.value ? row.teamName === selectedTeam.value : true
|
|
|
|
|
+
|
|
|
|
|
+ if (query && !nameMatch && !teamMatch) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!educationMatch || !selectedTeamMatch) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
const handleMemberClick = (member: string) => {
|
|
const handleMemberClick = (member: string) => {
|
|
|
selectedMember.value = member
|
|
selectedMember.value = member
|
|
|
|
|
+ activeExpert.value = expertProfileMap[member] || {
|
|
|
|
|
+ name: member,
|
|
|
|
|
+ role: '专家',
|
|
|
|
|
+ team: '待分配梯队',
|
|
|
|
|
+ age: 45,
|
|
|
|
|
+ education: '研究生',
|
|
|
|
|
+ direction: ['领域研究', '项目评审'],
|
|
|
|
|
+ achievement: '已录入专家基础信息,详细履历可继续补充。',
|
|
|
|
|
+ tasks: ['立项预审', '专项咨询'],
|
|
|
|
|
+ tags: ['专家', '待补充'],
|
|
|
|
|
+ records: []
|
|
|
|
|
+ }
|
|
|
|
|
+ drawerVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleWordClick = (word: string) => {
|
|
|
|
|
+ searchQuery.value = word
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -186,15 +482,194 @@ const handleMemberClick = (member: string) => {
|
|
|
--muted-ink: #718092;
|
|
--muted-ink: #718092;
|
|
|
--line: rgba(28, 44, 62, 0.18);
|
|
--line: rgba(28, 44, 62, 0.18);
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
- max-width: 1440px;
|
|
|
|
|
|
|
+ max-width: none;
|
|
|
margin: 0 auto;
|
|
margin: 0 auto;
|
|
|
- padding: 34px;
|
|
|
|
|
|
|
+ // padding: 34px;
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
|
|
|
|
|
color: var(--ink);
|
|
color: var(--ink);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.expert-step-table el-card {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-layout {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: minmax(220px, 0.34fr) minmax(0, 1fr);
|
|
|
|
|
+ gap: 18px;
|
|
|
|
|
+ margin-bottom: 15px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-panel,
|
|
|
|
|
+.word-cloud-panel {
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ border: 1px solid rgba(28, 44, 62, 0.1);
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-panel {
|
|
|
|
|
+ background: linear-gradient(160deg, rgba(237, 245, 249, 0.95), rgba(247, 249, 248, 0.9)), #f4f7f8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.word-cloud-panel {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: radial-gradient(circle at 84% 20%, rgba(177, 205, 210, 0.3), transparent 32%), #f8f7f2;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.word-cloud-panel::after {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: -70px;
|
|
|
|
|
+ bottom: -90px;
|
|
|
|
|
+ width: 220px;
|
|
|
|
|
+ height: 220px;
|
|
|
|
|
+ border: 1px solid rgba(161, 94, 63, 0.16);
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ content: '';
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ 0 0 0 20px rgba(161, 94, 63, 0.04),
|
|
|
|
|
+ 0 0 0 42px rgba(161, 94, 63, 0.03);
|
|
|
|
|
+ pointer-events: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.panel-kicker {
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ color: #8a98a5;
|
|
|
|
|
+ font-family: 'Courier New', monospace;
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ letter-spacing: 0.14em;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-panel h2,
|
|
|
|
|
+.word-cloud-panel h2 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ color: var(--ink);
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ letter-spacing: -0.03em;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.panel-description {
|
|
|
|
|
+ margin: 8px 0 18px;
|
|
|
|
|
+ color: var(--muted-ink);
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 1.65;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-fields {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-fields :deep(.el-input__wrapper),
|
|
|
|
|
+.filter-fields :deep(.el-select__wrapper) {
|
|
|
|
|
+ min-height: 38px;
|
|
|
|
|
+ border-radius: 7px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.78);
|
|
|
|
|
+ box-shadow: 0 0 0 1px rgba(28, 44, 62, 0.09) inset;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-fields :deep(.el-input__wrapper.is-focus),
|
|
|
|
|
+.filter-fields :deep(.el-select__wrapper.is-focused) {
|
|
|
|
|
+ box-shadow: 0 0 0 1px #467da4 inset;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-status {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-top: 18px;
|
|
|
|
|
+ padding-top: 14px;
|
|
|
|
|
+ border-top: 1px dashed rgba(28, 44, 62, 0.15);
|
|
|
|
|
+ color: #718092;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-dot {
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+ height: 6px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ background: #6f9878;
|
|
|
|
|
+ box-shadow: 0 0 0 4px rgba(111, 152, 120, 0.13);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cloud-heading {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cloud-hint {
|
|
|
|
|
+ padding-top: 18px;
|
|
|
|
|
+ color: #9aa6b3;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.word-cloud {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ min-height: 180px;
|
|
|
|
|
+ margin-top: 4px;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ background: radial-gradient(circle at 24% 20%, rgba(255, 255, 255, 0.72), transparent 28%),
|
|
|
|
|
+ linear-gradient(180deg, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0.18));
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cloud-word {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ border: 0;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ // font-weight: 600;
|
|
|
|
|
+ letter-spacing: -0.03em;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ transform-origin: center;
|
|
|
|
|
+ transition:
|
|
|
|
|
+ transform 180ms ease,
|
|
|
|
|
+ opacity 180ms ease,
|
|
|
|
|
+ text-shadow 180ms ease;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cloud-word:hover {
|
|
|
|
|
+ opacity: 0.72;
|
|
|
|
|
+ text-shadow: 0 5px 12px rgba(49, 91, 115, 0.18);
|
|
|
|
|
+ transform: scale(1.06) rotate(var(--rotate, 0deg));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cloud-word:focus-visible {
|
|
|
|
|
+ outline: 2px solid #a15e3f;
|
|
|
|
|
+ outline-offset: 5px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.table-divider {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ margin: 4px 2px 14px;
|
|
|
|
|
+ color: var(--ink);
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.result-count {
|
|
|
|
|
+ color: #9aa6b3;
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ letter-spacing: 0.1em;
|
|
|
|
|
+ text-transform: uppercase;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.table-intro {
|
|
.table-intro {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: flex-end;
|
|
align-items: flex-end;
|
|
@@ -283,6 +758,7 @@ const handleMemberClick = (member: string) => {
|
|
|
max-width: 100%;
|
|
max-width: 100%;
|
|
|
min-width: 0;
|
|
min-width: 0;
|
|
|
min-height: 78px;
|
|
min-height: 78px;
|
|
|
|
|
+
|
|
|
transform: translateX(0);
|
|
transform: translateX(0);
|
|
|
animation: row-in 0.55s both;
|
|
animation: row-in 0.55s both;
|
|
|
transition:
|
|
transition:
|
|
@@ -325,7 +801,6 @@ const handleMemberClick = (member: string) => {
|
|
|
|
|
|
|
|
.team-index {
|
|
.team-index {
|
|
|
color: rgba(24, 37, 54, 0.38);
|
|
color: rgba(24, 37, 54, 0.38);
|
|
|
- font-family: 'Courier New', monospace;
|
|
|
|
|
font-size: 10px;
|
|
font-size: 10px;
|
|
|
font-weight: 700;
|
|
font-weight: 700;
|
|
|
letter-spacing: 0.08em;
|
|
letter-spacing: 0.08em;
|
|
@@ -452,7 +927,6 @@ const handleMemberClick = (member: string) => {
|
|
|
|
|
|
|
|
.detail-label {
|
|
.detail-label {
|
|
|
color: #467da4;
|
|
color: #467da4;
|
|
|
- font-family: 'Courier New', monospace;
|
|
|
|
|
font-size: 10px;
|
|
font-size: 10px;
|
|
|
font-weight: 700;
|
|
font-weight: 700;
|
|
|
letter-spacing: 0.12em;
|
|
letter-spacing: 0.12em;
|
|
@@ -484,6 +958,14 @@ const handleMemberClick = (member: string) => {
|
|
|
gap: 18px;
|
|
gap: 18px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ .search-layout {
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .word-cloud {
|
|
|
|
|
+ min-height: 160px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
.intro-meta {
|
|
.intro-meta {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
@@ -515,4 +997,224 @@ const handleMemberClick = (member: string) => {
|
|
|
gap: 8px;
|
|
gap: 8px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.expert-drawer) {
|
|
|
|
|
+ --el-drawer-padding-primary: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.expert-drawer.el-drawer.rtl) {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.el-drawer__wrapper) {
|
|
|
|
|
+ overflow: hidden !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.el-overlay) {
|
|
|
|
|
+ overflow: hidden !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.expert-drawer .el-drawer__body) {
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ background: #eef2ef;
|
|
|
|
|
+ overflow-x: hidden !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.drawer-shell {
|
|
|
|
|
+ min-height: 100%;
|
|
|
|
|
+ background: #eef2ef;
|
|
|
|
|
+ overflow-x: hidden !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.drawer-header {
|
|
|
|
|
+ padding: 20px 28px 18px;
|
|
|
|
|
+ border-bottom: 1px solid rgba(24, 37, 54, 0.08);
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.drawer-header h2 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ color: #102234;
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ font-weight: 800;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.drawer-header p {
|
|
|
|
|
+ margin: 8px 0 0;
|
|
|
|
|
+ color: #6f7f8e;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.drawer-profile {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: 230px minmax(0, 1fr);
|
|
|
|
|
+ gap: 24px;
|
|
|
|
|
+ margin: 26px;
|
|
|
|
|
+ padding: 26px;
|
|
|
|
|
+ border: 1px solid rgba(28, 44, 62, 0.08);
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ box-shadow: 0 8px 30px rgba(28, 44, 62, 0.06);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-aside {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-avatar {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ min-height: 260px;
|
|
|
|
|
+ border-radius: 18px;
|
|
|
|
|
+ background: linear-gradient(145deg, #b7d3cd, #e8f0ee);
|
|
|
|
|
+ color: #0f5b58;
|
|
|
|
|
+ font-size: 84px;
|
|
|
|
|
+ font-weight: 800;
|
|
|
|
|
+ letter-spacing: -0.08em;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-tags {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-tag {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 8px 14px;
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ background: #f1f4f2;
|
|
|
|
|
+ color: #6e7d7a;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-main {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.info-block {
|
|
|
|
|
+ padding: 18px 18px 16px;
|
|
|
|
|
+ border: 1px solid #dbe3df;
|
|
|
|
|
+ border-radius: 14px;
|
|
|
|
|
+ background: #fdfefe;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.info-label {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ color: #7a8a89;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ // font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.info-block strong {
|
|
|
|
|
+ color: #203130;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 800;
|
|
|
|
|
+ line-height: 1.7;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-actions {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
+ gap: 14px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-actions :deep(.el-button) {
|
|
|
|
|
+ height: 54px;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.profile-actions :deep(.el-button--success) {
|
|
|
|
|
+ background: #237b63;
|
|
|
|
|
+ border-color: #237b63;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-panel {
|
|
|
|
|
+ margin: 0 26px 26px;
|
|
|
|
|
+ border: 1px solid rgba(28, 44, 62, 0.08);
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ box-shadow: 0 8px 30px rgba(28, 44, 62, 0.05);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-header {
|
|
|
|
|
+ padding: 22px 24px 18px;
|
|
|
|
|
+ border-bottom: 1px solid rgba(28, 44, 62, 0.08);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-header h3 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ color: #1e2d3a;
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ font-weight: 800;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-header p {
|
|
|
|
|
+ margin: 6px 0 0;
|
|
|
|
|
+ color: #8a97a3;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-table :deep(.el-table__header th) {
|
|
|
|
|
+ background: #f7faf8;
|
|
|
|
|
+ color: #556574;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-table :deep(.el-table__inner-wrapper) {
|
|
|
|
|
+ overflow-x: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-table :deep(.el-table__row td) {
|
|
|
|
|
+ padding-top: 14px;
|
|
|
|
|
+ padding-bottom: 14px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-badge {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ min-width: 88px;
|
|
|
|
|
+ padding: 7px 12px;
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-badge.success {
|
|
|
|
|
+ background: #e4f4ec;
|
|
|
|
|
+ color: #1f8a61;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-badge.primary {
|
|
|
|
|
+ background: #e8f0fb;
|
|
|
|
|
+ color: #3d6ea8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@media (max-width: 900px) {
|
|
|
|
|
+ .drawer-profile {
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .profile-avatar {
|
|
|
|
|
+ min-height: 180px;
|
|
|
|
|
+ font-size: 64px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .profile-actions {
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|