Files
effekt-interface-frontend/src/components/EffektHome.vue

370 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="effekt-home">
<el-row :gutter="20" class="top-row">
<el-col :xs="24" :md="10">
<el-card shadow="never" class="greet-card">
<div class="greet-line">{{ greetingPrefix }}{{ greetingTime }}</div>
<div class="greet-date">{{ todayText }}</div>
<div v-if="currentUser" class="greet-progress">
<span class="greet-progress-label">待处理进度</span>
<el-progress :percentage="100" :stroke-width="10" status="success" />
<span class="greet-progress-tip">已完成 100%</span>
</div>
<div v-else class="greet-login-tip">
<el-link type="primary" @click="goLogin">登录后查看个人工作台</el-link>
</div>
</el-card>
</el-col>
<el-col :xs="24" :md="14">
<el-card shadow="never" class="work-card">
<div class="work-card-title">今天剩余工作总计</div>
<div class="work-stats">
<div class="work-stat">
<div class="work-stat-value">{{ formatCount(workCountOpportunity) }}</div>
<div class="work-stat-label">我的机会</div>
</div>
<div class="work-stat work-stat--click" @click="goMyBugs">
<div class="work-stat-value">{{ formatCount(workCountBug) }}</div>
<div class="work-stat-label">我的 BUG</div>
<div class="work-stat-hint">点击查看指派给我</div>
</div>
<div class="work-stat work-stat--click" @click="goMyPlans">
<div class="work-stat-value">{{ formatCount(workCountPlan) }}</div>
<div class="work-stat-label">我的计划</div>
<div class="work-stat-hint">点击查看我负责的</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<el-card shadow="never" class="links-card">
<div class="links-card-title">环境与文档</div>
<p class="home-desc">这里汇总各项目常用环境地址和文档链接方便快速进入</p>
<div
v-for="project in projectLinks"
:key="project.name"
class="project-block">
<div class="project-title">{{ project.name }}</div>
<div class="project-links">
<div v-for="item in project.links" :key="item.name" class="link-item">
<span class="link-label">{{ item.name }}</span>
<el-link
:href="item.url"
target="_blank"
type="primary"
class="doc-link">
{{ item.url }}
</el-link>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script>
import { getBugList } from '@/api/bugApi'
import { getPlanList } from '@/api/planApi'
import { readLastProductProjectCache } from '@/utils/lastProductProjectCache'
export default {
name: 'EffektHome',
data() {
return {
workCountOpportunity: null,
workCountBug: null,
workCountPlan: null,
projectLinks: [
{
name: '智慧运营',
links: [
{ name: '测试环境地址', url: 'https://smart-management-web-st.best-envision.com/' },
{ name: 'pre环境地址', url: 'https://smart-management-web-pre.best-envision.com/' },
{ name: '需求文档', url: 'https://vcncbabzm4lv.feishu.cn/wiki/UsvzwMzV0i7Lrgk9VIhc2XgJn6e?fromScene=spaceOverview' },
{ name: '资源连接地址(包含数据库连接jenkins配置git仓库日志查询xxjob等)', url: 'https://vcncbabzm4lv.feishu.cn/wiki/ZKmown7QuiXtwTkONhUcagpQnWh' },
{ name: '领星地址', url: 'https://envision.lingxing.com/erp/home' },
{ name: '禅道地址', url: 'http://39.170.26.156:8888/my.html' }
]
},
{
name: '独立站项目',
links: [
{ name: '管理后台测试环境地址', url: 'https://joyhub-website-manager-web-test.best-envision.com/' },
{ name: 'web的C端测试环境地址', url: 'https://joyhub-website-frontend-test.best-envision.com/' },
{ name: '接口开发地址', url: 'https://joyhub-website-manager-api-test.best-envision.com/doc.html#/home' },
{ name: '资源连接地址', url: 'https://vcncbabzm4lv.feishu.cn/wiki/PXTmw6BBviMjNDkKCxCcewD7nMd' },
{ name: '禅道地址', url: 'http://192.168.16.4:8956/index.php?m=my&f=index' }
]
}
]
}
},
computed: {
currentUser() {
return this.$store.state.currentUser
},
greetingPrefix() {
const u = this.currentUser
if (!u) return ''
const name = u.realName || u.username || ''
return name ? `${name}` : ''
},
greetingTime() {
const h = new Date().getHours()
if (h < 12) return '上午好!'
if (h < 18) return '下午好!'
return '晚上好!'
},
todayText() {
const d = new Date()
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${y}${m}${day}`
}
},
mounted() {
this.refreshWorkCounts()
},
methods: {
formatCount(n) {
if (n === null || n === undefined || Number.isNaN(Number(n))) return '—'
return String(n)
},
goLogin() {
this.$router.push({ name: 'login' })
},
goMyBugs() {
if (!this.currentUser) {
this.$message.warning('请先登录')
this.goLogin()
return
}
const c = readLastProductProjectCache()
const q = { assignToMe: '1' }
if (c && c.productId !== undefined && c.productId !== null && String(c.productId).trim() !== '') {
q.productId = String(c.productId)
}
if (c && c.projectId !== undefined && c.projectId !== null && String(c.projectId).trim() !== '') {
q.projectId = String(c.projectId)
}
this.$router.push({ path: '/bug/list', query: q })
},
goMyPlans() {
if (!this.currentUser) {
this.$message.warning('请先登录')
this.goLogin()
return
}
const c = readLastProductProjectCache()
const q = { planOwnerSelf: '1' }
if (c && c.productId !== undefined && c.productId !== null && String(c.productId).trim() !== '') {
q.productId = String(c.productId)
}
if (c && c.projectId !== undefined && c.projectId !== null && String(c.projectId).trim() !== '') {
q.projectId = String(c.projectId)
}
this.$router.push({ path: '/test-platform/plan', query: q })
},
refreshWorkCounts() {
const u = this.currentUser
const c = readLastProductProjectCache()
this.workCountOpportunity = null
this.workCountBug = null
this.workCountPlan = null
if (!u || u.id == null || u.id === '' || !c || !c.projectId) {
return
}
getBugList({
productId: c.productId,
projectId: c.projectId,
assigneeId: u.id,
pageNo: 1,
pageSize: 1
})
.then(res => {
const data = (res && res.data) || res || {}
this.workCountBug = Number(data.total != null ? data.total : 0)
})
.catch(() => {
this.workCountBug = null
})
getPlanList(c.projectId, {
owner_id: u.id,
owner: u.id,
pageNo: 1,
pageSize: 1
})
.then(res => {
const data = (res && res.data) || res || {}
this.workCountPlan = Number(data.total != null ? data.total : 0)
})
.catch(() => {
this.workCountPlan = null
})
}
}
}
</script>
<style scoped>
.effekt-home {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.top-row {
margin-bottom: 20px;
}
.greet-card,
.work-card {
border-radius: 10px;
border: 1px solid #ebeef5;
min-height: 160px;
}
.greet-line {
font-size: 20px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.greet-date {
color: #909399;
font-size: 13px;
margin-bottom: 16px;
}
.greet-progress-label {
font-size: 12px;
color: #606266;
display: block;
margin-bottom: 6px;
}
.greet-progress-tip {
font-size: 12px;
color: #67c23a;
margin-top: 6px;
display: block;
}
.greet-login-tip {
margin-top: 8px;
}
.work-card-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.work-stats {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 12px;
}
.work-stat {
flex: 1;
min-width: 120px;
text-align: center;
padding: 12px 8px;
border-radius: 8px;
background: #f5f9ff;
border: 1px solid #e4ecfb;
cursor: default;
transition: box-shadow 0.2s, border-color 0.2s;
}
.work-stat--click {
cursor: pointer;
}
.work-stat--click:hover {
border-color: #409eff;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}
.work-stat-value {
font-size: 28px;
font-weight: 600;
color: #409eff;
line-height: 1.2;
}
.work-stat-label {
margin-top: 8px;
font-size: 14px;
color: #606266;
}
.work-stat-hint {
margin-top: 4px;
font-size: 11px;
color: #909399;
}
.links-card {
border-radius: 10px;
}
.links-card-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.home-content {
display: flex;
flex-direction: column;
}
.home-desc {
margin: 0 0 16px;
color: #606266;
font-size: 13px;
}
.project-block {
padding: 16px 0;
border-bottom: 1px solid #ebeef5;
}
.project-block:last-child {
border-bottom: none;
}
.project-title {
margin-bottom: 12px;
font-size: 16px;
font-weight: 600;
color: #303133;
}
.link-item {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
line-height: 22px;
}
.link-label {
min-width: 140px;
color: #606266;
}
.doc-link {
word-break: break-all;
}
</style>