feat: 完善工作台界面

This commit is contained in:
liulujian 2026-04-06 00:16:20 +08:00
parent f238c23e19
commit d85aadcf9a
4 changed files with 506 additions and 187 deletions

View File

@ -0,0 +1,246 @@
<template>
<div class="enterprise-cert">
<div class="cert-header">
<div class="cert-icon">
<t-icon name="user" />
</div>
<div class="cert-title">
<h3>企业认证</h3>
<span class="cert-status" :class="statusClass">{{ statusText }}</span>
</div>
</div>
<div class="cert-body">
<template v-if="certData.qymc">
<div class="cert-info">
<div class="info-row">
<span class="info-label">企业名称</span>
<span class="info-value">{{ certData.qymc }}</span>
</div>
<div class="info-row">
<span class="info-label">统一社会信用代码</span>
<span class="info-value">{{ certData.nsrsbh }}</span>
</div>
<div class="info-row">
<span class="info-label">认证时间</span>
<span class="info-value">{{ formatDate(certData.lrrq) }}</span>
</div>
</div>
</template>
<template v-else>
<div class="cert-empty">
<span>暂未认证企业信息</span>
</div>
</template>
<div class="cert-actions">
<button class="cert-btn" @click="handleClick">
<span>{{ certData.qymc ? '查看详情' : '立即认证' }}</span>
<t-icon name="arrow-right" />
</button>
</div>
</div>
</div>
</template>
<script>
import api from '@/pages/index/api/gxzx/index.js';
export default {
name: 'EnterpriseCert',
data() {
return {
certData: {},
};
},
computed: {
statusText() {
if (!this.certData.qymc) return '未认证';
return '已认证';
},
statusClass() {
if (!this.certData.qymc) return 'status--unreviewed';
return 'status--certified';
},
},
mounted() {
this.fetchCertData();
},
methods: {
async fetchCertData() {
try {
const { data } = await api.init();
this.certData = data || {};
} catch (error) {
console.error('获取企业认证信息失败', error);
this.certData = {};
}
},
formatDate(dateStr) {
if (!dateStr) return '-';
const date = new Date(dateStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
handleClick() {
this.$router.push('/yhzx/qyrenzheng');
},
},
};
</script>
<style lang="less" scoped>
@green-primary: #48C666;
@green-dark: #2D8A45;
@green-light: #E8FFEA;
@text-dark: #1A2B3C;
@text-muted: #6B7C8D;
@text-light: #94A3B8;
@bg-card: #FFFFFF;
@border-light: rgba(0, 0, 0, 0.06);
@shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.1);
.enterprise-cert {
width: 100%;
overflow: hidden;
background: @bg-card;
border: 1px solid @border-light;
border-radius: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
transition: box-shadow 0.3s ease;
&:hover {
box-shadow: @shadow-hover;
}
}
.cert-header {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 20px;
background: linear-gradient(180deg, #FAFFFE 0%, @bg-card 100%);
border-bottom: 1px solid @border-light;
.cert-icon {
display: flex;
width: 40px;
height: 40px;
font-size: 20px;
color: #fff;
background: linear-gradient(135deg, @green-primary 0%, @green-dark 100%);
border-radius: 10px;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.cert-title {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
h3 {
margin: 0;
font-size: 15px;
font-weight: 600;
color: @text-dark;
}
.cert-status {
padding: 3px 10px;
font-size: 11px;
font-weight: 600;
border-radius: 20px;
&.status--certified {
color: @green-dark;
background: @green-light;
}
&.status--pending {
color: #F9A825;
background: #FFF8E1;
}
&.status--unreviewed {
color: @text-muted;
background: #F5F5F5;
}
}
}
}
.cert-body {
padding: 16px 20px;
}
.cert-info {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 14px;
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
.info-label {
font-size: 12px;
color: @text-muted;
}
.info-value {
font-size: 12px;
font-weight: 500;
color: @text-dark;
}
}
}
.cert-empty {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
margin-bottom: 14px;
font-size: 13px;
color: @text-muted;
}
.cert-actions {
.cert-btn {
display: flex;
width: 100%;
padding: 10px;
font-size: 13px;
font-weight: 600;
color: @green-primary;
cursor: pointer;
background: @green-light;
border: 1px solid rgba(72, 198, 102, 0.2);
border-radius: 8px;
transition: all 0.3s ease;
align-items: center;
justify-content: center;
gap: 6px;
&:hover {
color: #fff;
background: @green-primary;
border-color: @green-primary;
}
.t-icon {
font-size: 14px;
transition: transform 0.3s ease;
}
&:hover .t-icon {
transform: translateX(3px);
}
}
}
</style>

View File

@ -86,6 +86,7 @@ export default {
@shadow-hover: 0 8px 24px rgba(0, 0, 0, 0.08); @shadow-hover: 0 8px 24px rgba(0, 0, 0, 0.08);
.policy-news { .policy-news {
width: 100%;
overflow: hidden; overflow: hidden;
background: @bg-card; background: @bg-card;
border-radius: 16px; border-radius: 16px;

View File

@ -1,22 +1,25 @@
<template> <template>
<div class="quick-actions"> <div class="quick-actions">
<div class="actions-grid">
<button <button
v-for="(action, index) in actions" v-for="(action, index) in actions"
:key="action.label" :key="action.label"
class="action-btn" class="action-item"
:class="`action-btn--${index}`" :style="{ '--delay': `${index * 0.06}s` }"
@click="handleClick(action.to)" @click="handleClick(action.to)"
> >
<span class="btn-icon"> <div class="item-icon-wrap">
<div class="item-icon" :style="{ background: action.bgColor, color: action.color }">
<t-icon :name="action.icon" /> <t-icon :name="action.icon" />
</span> </div>
<span class="btn-label">{{ action.label }}</span> </div>
<span class="btn-arrow"> <span class="item-label">{{ action.label }}</span>
<div class="item-arrow">
<t-icon name="arrow-right" /> <t-icon name="arrow-right" />
</span> </div>
<div class="btn-bg"></div>
</button> </button>
</div> </div>
</div>
</template> </template>
<script> <script>
@ -25,10 +28,10 @@ export default {
data() { data() {
return { return {
actions: [ actions: [
{ label: '发布服务', to: '/yhzx/tfwgj', icon: 'upload' }, { label: '发布服务', to: '/yhzx/tfwgj', icon: 'upload', bgColor: '#E8FFEA', color: '#48C666' },
{ label: '发布数据', to: '/yhzx/tfwgj?action=publishData', icon: 'cloud-upload' }, { label: '发布数据', to: '/yhzx/tfwgj?action=publishData', icon: 'cloud-upload', bgColor: '#E3F2FD', color: '#2196F3' },
{ label: '发布需求', to: '/yhzx/tfwxq', icon: 'edit' }, { label: '发布需求', to: '/yhzx/tfwxq', icon: 'edit', bgColor: '#FFF8E1', color: '#FF9800' },
{ label: '质证申请', to: '/yhzx/zzgl', icon: 'certificate' }, { label: '质证申请', to: '/yhzx/zzgl', icon: 'bulletpoint', bgColor: '#FCE4EC', color: '#E91E63' },
], ],
}; };
}, },
@ -44,112 +47,129 @@ export default {
@green-primary: #48C666; @green-primary: #48C666;
@green-dark: #2D8A45; @green-dark: #2D8A45;
@green-light: #E8FFEA; @green-light: #E8FFEA;
@green-glow: rgba(72, 198, 102, 0.2);
@text-dark: #1A2B3C; @text-dark: #1A2B3C;
@text-muted: #6B7C8D; @text-muted: #6B7C8D;
@bg-card: #FFFFFF; @bg-card: #FFFFFF;
@border-light: rgba(72, 198, 102, 0.3); @border-light: rgba(0, 0, 0, 0.06);
@shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.1);
.quick-actions { .quick-actions {
display: flex; .actions-grid {
flex-wrap: wrap; display: grid;
gap: 14px; grid-template-columns: repeat(4, 1fr);
align-items: center; gap: 12px;
}
} }
.action-btn { .action-item {
position: relative; position: relative;
display: inline-flex; display: flex;
padding: 0; padding: 20px 12px;
font-family: inherit; overflow: hidden;
cursor: pointer; cursor: pointer;
background: transparent; background: @bg-card;
border: none; border: 1px solid @border-light;
outline: none; border-radius: 14px;
animation: fadeIn 0.4s ease backwards;
animation-delay: var(--delay);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
flex-direction: column;
align-items: center; align-items: center;
gap: 8px; gap: 10px;
.btn-bg { &::before {
position: absolute; position: absolute;
z-index: 0; top: 0;
background: linear-gradient(135deg, @green-light 0%, #fff 100%); right: 0;
border: 2px solid @green-primary; left: 0;
border-radius: 10px; height: 3px;
opacity: 0; background: linear-gradient(90deg, @green-primary, @green-dark);
transform: scale(0.95); content: '';
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transform: scaleX(0);
inset: 0; transition: transform 0.3s ease;
}
.btn-icon {
position: relative;
z-index: 1;
display: flex;
width: 40px;
height: 40px;
font-size: 18px;
color: #fff;
background: linear-gradient(135deg, @green-primary 0%, @green-dark 100%);
border-radius: 10px 0 0 10px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
align-items: center;
justify-content: center;
}
.btn-label {
position: relative;
z-index: 1;
padding: 10px 14px 10px 6px;
font-size: 14px;
font-weight: 600;
color: @green-dark;
white-space: nowrap;
background: transparent;
border: 2px solid @green-primary;
border-left: none;
border-radius: 0 10px 10px 0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-arrow {
position: relative;
z-index: 1;
display: flex;
width: 24px;
height: 24px;
margin-right: 10px;
color: @green-primary;
opacity: 0;
transform: translateX(-8px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
align-items: center;
justify-content: center;
} }
&:hover { &:hover {
.btn-bg { border-color: transparent;
opacity: 1; transform: translateY(-3px);
transform: scale(1); box-shadow: @shadow-hover;
&::before {
transform: scaleX(1);
} }
.btn-icon { .item-icon {
transform: translateX(4px); transform: scale(1.1);
box-shadow: 0 4px 16px @green-glow;
} }
.btn-label { .item-arrow {
padding-left: 10px;
color: @green-primary;
}
.btn-arrow {
opacity: 1; opacity: 1;
transform: translateX(0); transform: translateX(0);
} }
} }
&:active { &:active {
transform: scale(0.98); transform: translateY(-1px);
}
.item-icon-wrap {
display: flex;
align-items: center;
justify-content: center;
}
.item-icon {
display: flex;
width: 48px;
height: 48px;
font-size: 22px;
border-radius: 12px;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
align-items: center;
justify-content: center;
}
.item-label {
font-size: 13px;
font-weight: 600;
color: @text-dark;
text-align: center;
}
.item-arrow {
position: absolute;
top: 50%;
right: 12px;
display: flex;
font-size: 14px;
color: @green-primary;
opacity: 0;
transform: translateX(-4px);
transition: all 0.3s ease;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 1200px) {
.quick-actions .actions-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.quick-actions .actions-grid {
grid-template-columns: 1fr 1fr;
gap: 10px;
} }
} }
</style> </style>

View File

@ -8,15 +8,14 @@
</div> </div>
<div class="gzt-container"> <div class="gzt-container">
<!-- 欢迎区域 --> <!-- 第一行欢迎区域 + 企业认证 -->
<div class="top-row">
<!-- 左侧欢迎 + 统计 -->
<div class="left-column">
<div class="welcome-section"> <div class="welcome-section">
<div class="welcome-left"> <div class="welcome-left">
<div class="welcome-badge">
<t-icon name="leaf" />
<span>碳信网</span>
</div>
<h1 class="welcome-title">欢迎回来</h1> <h1 class="welcome-title">欢迎回来</h1>
<p class="welcome-subtitle">今天是碳市场活跃日把握每一个交易机会</p> <p class="welcome-subtitle">{{ currentGreeting }}祝您工作顺利</p>
</div> </div>
<div class="welcome-right"> <div class="welcome-right">
<div class="time-widget"> <div class="time-widget">
@ -28,10 +27,6 @@
<!-- 统计卡片区域 --> <!-- 统计卡片区域 -->
<div class="stats-section"> <div class="stats-section">
<div class="section-header">
<div class="header-line"></div>
<span class="section-label">数据概览</span>
</div>
<div class="stats-grid"> <div class="stats-grid">
<StatsCard <StatsCard
title="我的供给" title="我的供给"
@ -48,7 +43,7 @@
unit="条" unit="条"
bgColor="#E3F2FD" bgColor="#E3F2FD"
borderColor="#2196F3" borderColor="#2196F3"
iconName="request" iconName="search"
to="/yhzx/wdxq" to="/yhzx/wdxq"
/> />
<StatsCard <StatsCard
@ -71,6 +66,13 @@
/> />
</div> </div>
</div> </div>
</div>
<!-- 右侧企业认证 -->
<div class="right-column">
<EnterpriseCert />
</div>
</div>
<!-- 主要内容区 --> <!-- 主要内容区 -->
<div class="main-content"> <div class="main-content">
@ -82,9 +84,11 @@
<div class="header-icon"> <div class="header-icon">
<t-icon name="app" /> <t-icon name="app" />
</div> </div>
<div class="header-text">
<h2 class="panel-title">常用功能</h2> <h2 class="panel-title">常用功能</h2>
<p class="panel-desc">快速访问核心功能</p> <p class="panel-desc">快速访问核心功能</p>
</div> </div>
</div>
<div class="panel-body"> <div class="panel-body">
<QuickActions /> <QuickActions />
</div> </div>
@ -96,9 +100,11 @@
<div class="header-icon header-icon--account"> <div class="header-icon header-icon--account">
<t-icon name="setting" /> <t-icon name="setting" />
</div> </div>
<div class="header-text">
<h2 class="panel-title">账号管理</h2> <h2 class="panel-title">账号管理</h2>
<p class="panel-desc">账户设置与安全</p> <p class="panel-desc">账户设置与安全</p>
</div> </div>
</div>
<div class="panel-body"> <div class="panel-body">
<AccountShortcuts /> <AccountShortcuts />
</div> </div>
@ -119,6 +125,7 @@ import StatsCard from './components/StatsCard.vue';
import QuickActions from './components/QuickActions.vue'; import QuickActions from './components/QuickActions.vue';
import PolicyNews from './components/PolicyNews.vue'; import PolicyNews from './components/PolicyNews.vue';
import AccountShortcuts from './components/AccountShortcuts.vue'; import AccountShortcuts from './components/AccountShortcuts.vue';
import EnterpriseCert from './components/EnterpriseCert.vue';
export default { export default {
name: 'GztIndex', name: 'GztIndex',
@ -127,11 +134,13 @@ export default {
QuickActions, QuickActions,
PolicyNews, PolicyNews,
AccountShortcuts, AccountShortcuts,
EnterpriseCert,
}, },
data() { data() {
return { return {
currentDate: '', currentDate: '',
currentWeekday: '', currentWeekday: '',
currentGreeting: '',
}; };
}, },
mounted() { mounted() {
@ -140,9 +149,19 @@ export default {
methods: { methods: {
updateDateTime() { updateDateTime() {
const now = new Date(); const now = new Date();
const hours = now.getHours();
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
this.currentDate = `${now.getFullYear()}${now.getMonth() + 1}${now.getDate()}`; this.currentDate = `${now.getFullYear()}${now.getMonth() + 1}${now.getDate()}`;
this.currentWeekday = weekdays[now.getDay()]; this.currentWeekday = weekdays[now.getDay()];
if (hours < 12) {
this.currentGreeting = '早上好';
} else if (hours < 14) {
this.currentGreeting = '中午好';
} else if (hours < 18) {
this.currentGreeting = '下午好';
} else {
this.currentGreeting = '晚上好';
}
}, },
}, },
}; };
@ -216,16 +235,35 @@ export default {
margin: 0 auto; margin: 0 auto;
} }
// ========== ==========
.top-row {
display: grid;
grid-template-columns: 1fr 300px;
gap: 20px;
margin-bottom: 24px;
animation: fadeSlideUp 0.5s ease backwards;
}
.left-column {
display: flex;
flex-direction: column;
gap: 16px;
}
.right-column {
display: flex;
width: 300px;
flex-shrink: 0;
}
// ========== ========== // ========== ==========
.welcome-section { .welcome-section {
display: flex; display: flex;
padding: 28px 32px; padding: 20px 24px;
margin-bottom: 28px;
background: linear-gradient(135deg, #FFF 0%, #FAFFFE 50%, @green-soft 100%); background: linear-gradient(135deg, #FFF 0%, #FAFFFE 50%, @green-soft 100%);
border: 1px solid rgba(72, 198, 102, 0.1); border: 1px solid rgba(72, 198, 102, 0.1);
border-radius: 20px; border-radius: 16px;
box-shadow: @shadow-soft; box-shadow: @shadow-soft;
animation: fadeSlideUp 0.5s ease backwards;
align-items: flex-start; align-items: flex-start;
justify-content: space-between; justify-content: space-between;
} }
@ -233,8 +271,8 @@ export default {
.welcome-left { .welcome-left {
.welcome-badge { .welcome-badge {
display: inline-flex; display: inline-flex;
padding: 6px 14px; padding: 5px 12px;
margin-bottom: 12px; margin-bottom: 8px;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: @green-dark; color: @green-dark;
@ -245,13 +283,13 @@ export default {
gap: 6px; gap: 6px;
.t-icon { .t-icon {
font-size: 14px; font-size: 13px;
} }
} }
.welcome-title { .welcome-title {
margin: 0 0 8px; margin: 0 0 6px;
font-size: 28px; font-size: 24px;
font-weight: 700; font-weight: 700;
letter-spacing: -0.5px; letter-spacing: -0.5px;
color: @text-dark; color: @text-dark;
@ -259,28 +297,28 @@ export default {
.welcome-subtitle { .welcome-subtitle {
margin: 0; margin: 0;
font-size: 14px; font-size: 13px;
color: @text-muted; color: @text-muted;
} }
} }
.welcome-right { .welcome-right {
.time-widget { .time-widget {
padding: 12px 16px; padding: 8px 12px;
text-align: right; text-align: right;
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
border: 1px solid @border-light; border: 1px solid @border-light;
border-radius: 12px; border-radius: 8px;
.time-date { .time-date {
margin-bottom: 2px; margin-bottom: 2px;
font-size: 14px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: @text-dark; color: @text-dark;
} }
.time-weekday { .time-weekday {
font-size: 12px; font-size: 11px;
color: @text-muted; color: @text-muted;
} }
} }
@ -288,7 +326,6 @@ export default {
// ========== ========== // ========== ==========
.stats-section { .stats-section {
margin-bottom: 28px;
animation: fadeSlideUp 0.5s ease backwards; animation: fadeSlideUp 0.5s ease backwards;
animation-delay: 0.1s; animation-delay: 0.1s;
} }
@ -336,6 +373,11 @@ export default {
gap: 24px; gap: 24px;
} }
.right-panel {
width: 300px;
flex-shrink: 0;
}
.panel-card { .panel-card {
overflow: hidden; overflow: hidden;
background: @bg-card; background: @bg-card;
@ -345,22 +387,25 @@ export default {
} }
.panel-header { .panel-header {
padding: 24px 28px 20px; display: flex;
align-items: center;
gap: 14px;
padding: 18px 24px 16px;
background: linear-gradient(180deg, #FAFFFE 0%, @bg-card 100%); background: linear-gradient(180deg, #FAFFFE 0%, @bg-card 100%);
border-bottom: 1px solid @border-light; border-bottom: 1px solid @border-light;
.header-icon { .header-icon {
display: inline-flex; display: flex;
width: 40px; width: 40px;
height: 40px; height: 40px;
margin-bottom: 12px;
font-size: 20px; font-size: 20px;
color: #fff; color: #fff;
background: linear-gradient(135deg, @green-primary 0%, @green-dark 100%); background: linear-gradient(135deg, @green-primary 0%, @green-dark 100%);
border-radius: 12px; border-radius: 10px;
box-shadow: 0 4px 12px rgba(72, 198, 102, 0.3); box-shadow: 0 4px 12px rgba(72, 198, 102, 0.3);
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-shrink: 0;
&--account { &--account {
background: linear-gradient(135deg, #78909C 0%, #546E7A 100%); background: linear-gradient(135deg, #78909C 0%, #546E7A 100%);
@ -368,22 +413,29 @@ export default {
} }
} }
.header-text {
flex: 1;
min-width: 0;
}
.panel-title { .panel-title {
margin: 0 0 4px; margin: 0;
font-size: 18px; font-size: 16px;
font-weight: 600; font-weight: 600;
line-height: 1.4;
color: @text-dark; color: @text-dark;
} }
.panel-desc { .panel-desc {
margin: 0; margin: 2px 0 0;
font-size: 13px; font-size: 12px;
line-height: 1.4;
color: @text-light; color: @text-light;
} }
} }
.panel-body { .panel-body {
padding: 24px 28px; padding: 18px 24px;
} }
// ========== ========== // ========== ==========