redesign: 仪表盘全新高级简约风设计 — 完全重写布局和样式
All checks were successful
continuous-integration/drone/push Build is passing

- home.php: 三行卡片式网格布局(用户+统计/余额+推介/产品+工单)
- home.css: Stripe/Linear风格设计系统(16px圆角/微阴影/渐变头像/色彩标记统计卡)
- 响应式适配 @media 1200px 断点
- Element UI表格样式覆写
This commit is contained in:
yiqiu
2026-03-20 21:39:27 +08:00
parent c70c0ab203
commit 53c1cdccb8
2 changed files with 845 additions and 1121 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -19,237 +19,187 @@
<el-container>
<top-menu></top-menu>
<el-main class="home-main">
<!-- 自己的东西 -->
<div class="main-card">
<div class="main-content">
<div class="left-box">
<div class="info-box">
<div class="info-first" @click="goUser" v-loading="nameLoading">
<div class="name-first" ref="headBoxRef">
{{account.firstName}}
<div class="dashboard">
<!-- ====== Row 1: 用户信息 + 统计卡片 ====== -->
<div class="row-1">
<!-- 用户信息卡 -->
<div class="card card-user" @click="goUser" v-loading="nameLoading">
<div class="user-header">
<div class="user-avatar" ref="headBoxRef">{{account.firstName}}</div>
<div class="user-meta">
<div class="user-greeting">
{{lang.index_hello}}, <strong>{{account.username}}</strong>
<span v-if="idcsmart_client_level.id" class="user-level"
:style="{'color':idcsmart_client_level.background_color}">{{idcsmart_client_level.name}}</span>
</div>
<div class="name-box">
<p class="hello" :title="account.username">
{{lang.index_hello}},{{account.username}}
<span v-if="idcsmart_client_level.id"
:style="{'color':idcsmart_client_level.background_color}">({{idcsmart_client_level.name}})
<div class="user-id">ID: {{account.id}}</div>
</div>
</div>
<div class="user-contacts">
<div class="contact-item">
<i class="ph ph-envelope"></i>
<span>{{account.email ? account.email : '--'}}</span>
</div>
<div class="contact-item">
<i class="ph ph-phone"></i>
<span>{{account.phone ? account.phone : '--'}}</span>
</div>
</div>
</div>
<!-- 统计卡片组 -->
<div class="stats-group">
<div class="card card-stat stat-purple" @click="goProductList('using')">
<i class="ph ph-pulse"></i>
<div class="stat-value">{{account.host_active_num}}</div>
<div class="stat-label">{{lang.index_text6}}</div>
</div>
<div class="card card-stat stat-blue" @click="goProductList()">
<i class="ph ph-cube"></i>
<div class="stat-value">{{account.host_num}}</div>
<div class="stat-label">{{lang.index_text7}}</div>
</div>
<div class="card card-stat stat-amber" @click="goOrderList('Unpaid')">
<i class="ph ph-receipt"></i>
<div class="stat-value">{{account.unpaid_order}}</div>
<div class="stat-label">{{lang.index_text8}}</div>
</div>
</div>
</div>
<!-- ====== Row 2: 余额 + 推介/认证 ====== -->
<div class="row-2">
<!-- 余额卡片 -->
<div class="card card-balance" v-loading="nameLoading">
<div class="balance-header">
<span class="balance-title">{{lang.index_text3}}</span>
<span class="balance-btn" @click="showCz" v-if="commonData.recharge_open == 1">{{lang.index_text2}}</span>
</div>
<div class="balance-amount">
<span class="currency">{{commonData.currency_prefix}}</span>{{account.credit}}
</div>
<div class="balance-extra" v-if="commonData.recharge_open == 1 && coinRecharge.length > 0">
<span class="recharge-link" @click="showCz">{{lang.index_text35}}</span>
</div>
<div class="balance-footer">
<div class="consume-item">
<span class="consume-label">{{lang.index_text4}}</span>
<span class="consume-value">{{commonData.currency_prefix}}{{account.this_month_consume}}</span>
<span :class="Number(account.this_month_consume_percent) >= 0 ? 'trend-up' : 'trend-down'">
{{Number(account.this_month_consume_percent)}}%
</span>
</p>
<p class="name">
ID<span class="id-text">{{account.id}}</span>
</p>
</div>
<div class="consume-divider"></div>
<div class="consume-item">
<span class="consume-label">{{lang.index_text5}}</span>
<span class="consume-value">{{commonData.currency_prefix}}{{account.consume}}</span>
</div>
</div>
<el-divider class="divider-box" direction="vertical"></el-divider>
<div class="info-second" v-loading="nameLoading">
<div class="email-box">
<span><i class="ph ph-envelope"></i>{{lang.index_email}}</span>
<span class="phone-number">{{account.email ? account.email : '--'}}</span>
<!-- 信用额度 -->
<div class="credit-section" v-if="isShowCredit && creditData.status">
<div class="credit-row">
<span class="credit-label">{{lang.finance_text42}}({{commonData.currency_suffix}})</span>
<span class="credit-val">{{commonData.currency_prefix}}{{creditData.account?.status === 'Repaid' ? '0.00' : creditData.account?.amount}}</span>
</div>
<div class="phone-box">
<span><i class="ph ph-phone"></i>{{lang.index_tel}}</span>
<span class="phone-number">{{account.phone ? account.phone : '--'}}</span>
</div>
</div>
<el-divider class="divider-box" direction="vertical"></el-divider>
<div class="info-three" v-plugin="'IdcsmartCertification'"
v-if="certificationObj.certification_open === 1">
<div class="compny-box">
<div class="left-icon">
<i class="ph ph-buildings"></i>
<span class="left-type">{{lang.index_compny}}</span>
</div>
<div class="right-text">
<div class="right-title">
<span class="company-name"
v-if="certificationObj.company?.status === 1">{{certificationObj.company.certification_company}}</span>
<span class="company-name bule-text" @click="handelAttestation"
v-else>{{lang.index_goAttestation}}</span>
</div>
<div class="certify-id" v-if="certificationObj.certification_show_certify_id === 1">
<div class="right-type">{{lang.finance_custom23}}</div>
<div class="company-name certify-bottom" :title="certificationObj.company?.certify_id">
<span
class="certify-text">{{certificationObj.company?.certify_id ? certificationObj.company.certify_id : '--'}}</span>
<i class="ph ph-copy cpoy-btn" v-copy="certificationObj.company.certify_id"
v-if="certificationObj.company?.certify_id"></i>
</div>
</div>
</div>
</div>
<div class="person-box">
<div class="left-icon">
<i class="ph ph-user"></i>
<span class="left-type">{{lang.index_name}}</span>
</div>
<div class="right-text">
<div class="right-title">
<span class="company-name" v-if="certificationObj.is_certification"
:title="certificationObj.company.status === 1 ? certificationObj.company.card_name : certificationObj.person.card_name">
{{certificationObj.company.status === 1 ? certificationObj.company.card_name : certificationObj.person.card_name}}
</span>
<span class="company-name bule-text" @click="handelAttestation"
v-else>{{lang.index_goAttestation}}</span>
</div>
<div class="certify-id" v-if="certificationObj.certification_show_certify_id === 1">
<div class="right-type">{{lang.finance_custom24}}</div>
<div class="company-name certify-bottom" :title="certificationObj.person?.certify_id">
<span
class="certify-text">{{certificationObj.person?.certify_id ? certificationObj.person.certify_id : '--'}}
</span>
<i class="ph ph-copy cpoy-btn" v-copy="certificationObj.person?.certify_id"
v-if="certificationObj.person?.certify_id"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="statistics-box">
<div class="statistics-content" v-loading="nameLoading">
<div class="money-box">
<div class="money-top">
<div class="money-credit">
<div class="credit-btn" @click="showCz" v-if="commonData.recharge_open == 1">
{{lang.index_text2}}
</div>
<div class="credit-title" v-if="commonData.balance_notice_show == 1">
<div class="credit-name">{{lang.index_text3}}</div>
<div class="create-notice">
<div class="notice-status" :class="{'active': account.credit_remind === 1}"></div>
<div class="notice-btn" @click="setAccoutCredit">{{lang.coin_text66}}</div>
</div>
</div>
<div class="voucher-box" v-if="voucherList.length > 0">
{{lang.index_text24}}
<a href="/finance.htm?tab=4" target="_blank" class="bule-text">
{{lang.index_text25}}
</a>
</div>
<div class="credit-money">
<div class="credit-num">
<span class="s-24">{{commonData.currency_prefix}}</span>{{account.credit}}
<span v-if="commonData.recharge_open == 1 && coinRecharge.length > 0"
class="recharge-text" @click="showCz">{{lang.index_text35}}</span>
</div>
</div>
</div>
<div class="money-credit" v-plugin="'Coin'" v-if="coinData.name">
<div class="credit-title coin-title">
<div class="credit-name" style="display: flex; align-items: center;">
{{coinData.name}}
<el-tooltip effect="dark" placement="top" v-if="coinData.coin_description_open == 1">
<div slot="content" v-html="coinData.coin_description"></div>
<svg t="1745803081479" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="14138" width="16" height="16"
xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m45.32419 487.619047v73.142857h-68.510476l-0.024381-73.142857h68.534857z m-4.047238-362.008381c44.251429 8.923429 96.889905 51.126857 96.889905 112.518096 0 61.415619-50.151619 84.650667-68.120381 96.134095-17.993143 11.50781-24.722286 24.771048-24.722286 38.863238V609.52381h-68.534857v-90.672762c0-21.504 6.89981-36.571429 26.087619-49.883429l4.315429-2.852571 38.497524-25.6c24.551619-16.530286 24.210286-49.712762 9.020952-64.365715a68.998095 68.998095 0 0 0-60.391619-15.481904c-42.715429 8.387048-47.640381 38.521905-47.932952 67.779047v16.554667H390.095238c0-56.953905 6.534095-82.773333 36.912762-115.395048 34.03581-36.449524 81.993143-42.300952 126.268952-33.328762z"
p-id="14139" fill="currentColor"></path>
</svg>
</el-tooltip>
</div>
<a href="/finance.htm?tab=7" target="_blank" class="credit-detail">
{{lang.index_text34}}
</a>
</div>
<div class="credit-money">
<div class="credit-num">
<span class="s-24">{{commonData.currency_prefix}}</span>{{coinData.leave_amount}}
</div>
</div>
</div>
</div>
<template v-if="isShowCredit && creditData.status">
<div class="money-order">
<div class="money-order-item">
<span
class="money-order-title">{{lang.finance_text42}}({{commonData.currency_suffix}})</span>
<span class="money-order-value"><span
class="s-12">{{commonData.currency_prefix}}</span>{{creditData.account?.status === 'Repaid' ? '0.00' : creditData.account?.amount}}</span>
</div>
<div class="money-order-divider"></div>
<div class="money-order-item">
<div class="money-order-title">
<div class="credit-row">
<span class="credit-label">
{{lang.finance_text38}}
<div class="credit-tag" v-if="creditData.status === 'Expired'">{{lang.finance_text93}}
</div>
<div class="credit-tag" v-if="creditData.status === 'Overdue'">{{lang.finance_text94}}
</div>
<div class="credit-tag" v-if="creditData.status === 'Active'">{{lang.finance_text95}}
</div>
<div class="credit-tag" v-if="creditData.status === 'Suspended'">{{lang.finance_text96}}
<span class="credit-tag" v-if="creditData.status === 'Expired'">{{lang.finance_text93}}</span>
<span class="credit-tag" v-if="creditData.status === 'Overdue'">{{lang.finance_text94}}</span>
<span class="credit-tag" v-if="creditData.status === 'Active'">{{lang.finance_text95}}</span>
<span class="credit-tag" v-if="creditData.status === 'Suspended'">{{lang.finance_text96}}</span>
</span>
<span class="credit-val">{{commonData.currency_prefix}}{{creditData.remaining_amount}}</span>
</div>
</div>
<div class="money-order-value">
<span class="s-12">{{commonData.currency_prefix}}</span>{{creditData.remaining_amount}}
<!-- 代币 -->
<div class="coin-section" v-plugin="'Coin'" v-if="coinData.name">
<div class="coin-header">
<span>{{coinData.name}}</span>
<a href="/finance.htm?tab=7" target="_blank" class="coin-link">{{lang.index_text34}}</a>
</div>
<div class="coin-amount">{{commonData.currency_prefix}}{{coinData.leave_amount}}</div>
</div>
</div>
</template>
</div>
<div class="order-box">
<div class="order-item order-box-1" @click="goProductList('using')">
<div class="order-type-icon"><i class="ph ph-pulse"></i></div>
<h3 class="order-title">{{lang.index_text6}}</h3>
<div class="order-nums">{{account.host_active_num}}</div>
</div>
<div class="order-item order-box-2" @click="goProductList()">
<div class="order-type-icon"><i class="ph ph-cube"></i></div>
<h3 class="order-title">{{lang.index_text7}}</h3>
<div class="order-nums">{{account.host_num}}</div>
</div>
<div class="order-item order-box-3" @click="goOrderList('Unpaid')">
<div class="order-type-icon"><i class="ph ph-receipt"></i></div>
<h3 class="order-title">{{lang.index_text8}}</h3>
<div class="order-nums">{{account.unpaid_order}}</div>
</div>
</div>
</div>
</div>
<div class="statistics-bottom">
<div class="statistics-item">
<div class="statistics-item-name">
<span>{{lang.index_text4}}({{commonData.currency_suffix}})</span>
<span
:class="Number(account.this_month_consume_percent) >= 0 ? 'green-text' : 'red-text'"><span></span>{{Number(account.this_month_consume_percent)}}%</span>
</div>
<div class="statistics-item-value">
<span class="s-12">{{commonData.currency_prefix}}</span>{{account.this_month_consume}}
<div class="voucher-row" v-if="voucherList.length > 0">
{{lang.index_text24}}
<a href="/finance.htm?tab=4" target="_blank" class="voucher-link">{{lang.index_text25}}</a>
</div>
</div>
<div class="statistics-item-divider"></div>
<div class="statistics-item">
<div class="statistics-item-name">
<span>{{lang.index_text5}}({{commonData.currency_suffix}})</span>
<!-- 右侧: 认证 + 推介 -->
<div class="card card-side">
<!-- 实名认证 -->
<div class="cert-section" v-plugin="'IdcsmartCertification'" v-if="certificationObj.certification_open === 1">
<div class="cert-item">
<div class="cert-icon"><i class="ph ph-buildings"></i></div>
<div class="cert-info">
<div class="cert-label">{{lang.index_compny}}</div>
<div class="cert-value" v-if="certificationObj.company?.status === 1">{{certificationObj.company.certification_company}}</div>
<div class="cert-action" @click="handelAttestation" v-else>{{lang.index_goAttestation}}</div>
</div>
</div>
<div class="cert-item">
<div class="cert-icon"><i class="ph ph-user"></i></div>
<div class="cert-info">
<div class="cert-label">{{lang.index_name}}</div>
<div class="cert-value" v-if="certificationObj.is_certification">
{{certificationObj.company.status === 1 ? certificationObj.company.card_name : certificationObj.person.card_name}}
</div>
<div class="cert-action" @click="handelAttestation" v-else>{{lang.index_goAttestation}}</div>
</div>
<div class="statistics-item-value">
<span class="s-12">{{commonData.currency_prefix}}</span>{{account.consume}}
</div>
</div>
<!-- 推介计划(已开启) -->
<div class="referral-open" v-if="showRight && isOpen" v-plugin="'IdcsmartRecommend'">
<div class="referral-title">
<span>{{lang.referral_title1}}</span>
<span class="referral-reward" @click="toReferral"><i class="ph-fill ph-gift"></i> {{lang.referral_text14}}</span>
</div>
<div class="product-list-box">
<h3 class="title-text">{{lang.index_text9}}</h3>
<div class="referral-url">
<div class="url-text" :title="promoterData.url">{{promoterData.url}}</div>
<div class="url-copy" @click="copyUrl(promoterData.url)">{{lang.referral_btn2}}</div>
</div>
<div class="referral-stats">
<div class="ref-stat">
<div class="ref-amount">{{commonData.currency_prefix}}{{promoterData.withdrawable_amount}}</div>
<div class="ref-label">{{lang.referral_title2}}</div>
</div>
<div class="ref-stat">
<div class="ref-amount">{{commonData.currency_prefix}}{{promoterData.pending_amount}}</div>
<div class="ref-label">{{lang.referral_title4}}</div>
</div>
</div>
</div>
<!-- 推介计划(未开启) -->
<div class="referral-closed" v-if="!showRight || !isOpen">
<i class="ph ph-megaphone"></i>
<div v-if="showRight">
<h3>{{lang.index_text17}}</h3>
<p>{{lang.index_text18}}</p>
<div class="referral-open-btn" @click="openVisible = true">{{lang.index_text28}}</div>
</div>
<div v-else class="referral-disabled">{{lang.index_text21}}</div>
</div>
</div>
</div>
<!-- ====== Row 3: 产品列表 + 工单/公告 ====== -->
<div class="row-3">
<div class="card card-products">
<h3 class="section-title">{{lang.index_text9}}</h3>
<el-table :data="productList" style="width: 100%" v-if="productList.length !== 0"
v-loading="productListLoading">
<el-table-column prop="product_name" :label="lang.index_text10">
<template slot-scope="{row}">
<a :href="`/productdetail.htm?id=${row.id}`" class="product-name"
target="_blank">{{row.product_name}}</a>
</template>
</el-table-column>
<el-table-column prop="name" :label="lang.index_text12">
<template slot-scope="{row}">
<span>{{row.name}}</span>
<a :href="`/productdetail.htm?id=${row.id}`" class="product-link" target="_blank">{{row.product_name}}</a>
</template>
</el-table-column>
<el-table-column prop="name" :label="lang.index_text12"></el-table-column>
<el-table-column prop="due_time" :label="lang.index_text13">
<template slot-scope="{row}">
<span :class="row.isOverdue ? 'red-time' : ''">{{row.due_time | formateTime}}</span>
<span :class="row.isOverdue ? 'text-danger' : ''">{{row.due_time | formateTime}}</span>
</template>
</el-table-column>
<el-table-column prop="client_notes" :label="lang.invoice_text139" show-overflow-tooltip>
@@ -258,91 +208,51 @@
</template>
</el-table-column>
</el-table>
<div v-if="productList.length === 0 && !productListLoading" class="no-product">
<h2>{{lang.index_text14}}</h2>
<div v-if="productList.length === 0 && !productListLoading" class="empty-state">
<i class="ph ph-package"></i>
<h3>{{lang.index_text14}}</h3>
<p>{{lang.index_text15}}</p>
<el-button @click="goGoodsList" type="primary">{{lang.index_text16}}</el-button>
<el-button @click="goGoodsList" type="primary" round>{{lang.index_text16}}</el-button>
</div>
</div>
</div>
<div class="right-box">
<!-- 推介计划开始 -->
<div class="recommend-box-open" v-if="showRight && isOpen" v-plugin="'IdcsmartRecommend'">
<div class="recommend-top">
<div class="left">
<div class="row1">
<div class="title-text">{{lang.referral_title1}}</div>
<span class="reword" @click="toReferral"><i class="ph-fill ph-gift"></i>{{lang.referral_text14}}</span>
<!-- 工单 + 公告侧栏 -->
<div class="card card-sidebar" v-if="ticketList.length !== 0 || homeNewList.length !== 0">
<!-- 工单 -->
<div class="sidebar-section" v-if="ticketList.length !== 0" v-plugin="'IdcsmartTicket'">
<div class="sidebar-header">
<span>{{lang.index_text22}}</span>
<span class="sidebar-more" @click="goWorkPage">···</span>
</div>
<div class="row2">{{lang.referral_title6}}</div>
<div class="row3">{{lang.referral_text15}}</div>
<div class="row4">{{lang.referral_text16}}</div>
</div>
<div class="right recommend-illustration"><i class="ph ph-credit-card"></i></div>
</div>
<div class="url">
<div class="url-text" :title="promoterData.url">{{promoterData.url}}</div>
<div class="copy-btn" @click="copyUrl(promoterData.url)">{{lang.referral_btn2}}</div>
</div>
<div class="top-statistic">
<div class="top-item">
<div class="top-money">{{commonData.currency_prefix}}{{promoterData.withdrawable_amount}}</div>
<div class="top-text">{{lang.referral_title2}}</div>
</div>
<div class="top-item">
<div class="top-money">{{commonData.currency_prefix}}{{promoterData.pending_amount}}
</div>
<div class="top-text">{{lang.referral_title4}}</div>
<div class="ticket-list">
<div class="ticket-item" v-for="item in ticketList" :key="item.id" @click="goTickDetail(item.id)">
<span class="ticket-status" :style="{'background': item.color}">{{item.status}}</span>
<div class="ticket-info">
<div class="ticket-title">#{{item.ticket_num}} - {{item.title}}</div>
<div class="ticket-dept">{{item.name}}</div>
</div>
</div>
</div>
</div>
<!-- 公告 -->
<div class="sidebar-section" v-if="homeNewList.length !== 0" v-plugin="'IdcsmartNews'">
<div class="sidebar-header">
<span>{{lang.index_text23}}</span>
<span class="sidebar-more" @click="goNoticePage">···</span>
</div>
<div class="notice-list">
<div class="notice-item" v-for="item in homeNewList" :key="item.id" @click="goNoticeDetail(item.id)">
<div class="notice-info">
<div class="notice-title">{{item.title}}</div>
<div class="notice-meta">{{item.type}} · {{item.create_time | formareDay}}</div>
</div>
<i class="el-icon-arrow-right"></i>
</div>
</div>
</div>
<div class="recommend-box" v-if="!showRight || !isOpen">
<div class="recommend-placeholder-icon"><i class="ph ph-megaphone"></i></div>
<div v-if="showRight">
<h2>{{lang.index_text17}}</h2>
<p>{{lang.index_text18}}</p>
<div class="no-recommend" @click="openVisible = true">{{lang.index_text28}}</div>
</div>
<div v-else class="recommend-text">{{lang.index_text21}}</div>
</div>
<!-- 推介计划结束 -->
<div class="WorkOrder-box" v-if="ticketList.length !==0 " v-plugin="'IdcsmartTicket'">
<div class="title-text WorkOrder-title">
<div>{{lang.index_text22}}</div>
<div class="more" @click="goWorkPage">···</div>
</div>
<div class="WorkOrder-content">
<div class="WorkOrder-item" v-for="item in ticketList" :key="item.id"
@click="goTickDetail(item.id)">
<div class="replay-div" :style="{'background':`${item.color}`}">{{item.status}}</div>
<div class="replay-box">
<div class="replay-title">#{{item.ticket_num}} - {{item.title}}</div>
<div class="replay-name">{{item.name}}</div>
</div>
</div>
</div>
</div>
<div class="notice-box" v-if="homeNewList.length !==0" v-plugin="'IdcsmartNews'">
<div class="title-text WorkOrder-title">
<div>{{lang.index_text23}}</div>
<div class="more" @click="goNoticePage">···</div>
</div>
<div class="WorkOrder-content">
<div v-for="item in homeNewList" :key="item.id" class="notice-item"
@click="goNoticeDetail(item.id)">
<div class="notice-item-left">
<h3 class="notice-time">{{item.create_time | formareDay}}</h3>
<h4 class="notice-title">{{item.title}}</h4>
<h5 class="notice-type">{{item.type}}</h5>
</div>
<div class="notice-item-right"><i class="el-icon-arrow-right"></i></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 确认开启弹窗 -->
<el-dialog :title="lang.referral_title8" :visible.sync="openVisible" width="4.8rem"