- 删除应用配置页面及相关组件,重构路由为 /developer/config/[id].vue - 移除开发者文档页面及其导航与样式实现 - 清理开发者侧功能完善工作日志文件 - 删除全局.gitignore配置文件,清理无用忽略规则 - 优化应用配置页面的参数读取和路由结构,解决刷新404问题 - 解决数据库配置唯一键冲突,调整保存逻辑避免重复插入 - 移除对后端配置加密字段的 secret 标记,修正加密异常问题
170 lines
7.4 KiB
Vue
170 lines
7.4 KiB
Vue
<script setup lang="ts">
|
||
definePageMeta({ layout: 'admin' })
|
||
const { activeTab } = useNav()
|
||
activeTab.value = 'product-marketing'
|
||
|
||
// 销售数据
|
||
const salesData = ref([
|
||
{ month: '1月', orders: 245, revenue: 128.5, target: 120 },
|
||
{ month: '2月', orders: 312, revenue: 156.8, target: 130 },
|
||
{ month: '3月', orders: 278, revenue: 142.3, target: 135 },
|
||
])
|
||
|
||
const stats = ref([
|
||
{ label: '本月订单', value: 156, unit: '单', change: '+12%', icon: '📋', color: '#6366f1' },
|
||
{ label: '本月营收', value: 86.5, unit: '万', change: '+8%', icon: '💰', color: '#10b981' },
|
||
{ label: '新增客户', value: 23, unit: '家', change: '+15%', icon: '🏢', color: '#f59e0b' },
|
||
{ label: '平均单价', value: 5546, unit: '元', change: '-2%', icon: '💎', color: '#3b82f6' },
|
||
])
|
||
|
||
// 客户列表
|
||
const customers = ref([
|
||
{ id: 'C001', name: '比亚迪股份有限公司', industry: '汽车制造', contact: '李经理', phone: '0755-89888888', orderCount: 45, amount: 280.5, status: 'VIP' },
|
||
{ id: 'C002', name: '宁德时代新能源', industry: '新能源', contact: '王经理', phone: '0591-87654321', orderCount: 32, amount: 198.2, status: 'VIP' },
|
||
{ id: 'C003', name: '华为技术有限公司', industry: '电子通信', contact: '张经理', phone: '0755-28780808', orderCount: 28, amount: 156.8, status: '重点' },
|
||
{ id: 'C004', name: '富士康科技集团', industry: '电子制造', contact: '刘经理', phone: '0755-28129999', orderCount: 21, amount: 125.3, status: '普通' },
|
||
{ id: 'C005', name: '美的集团', industry: '家电制造', contact: '陈经理', phone: '0757-26608888', orderCount: 18, amount: 98.6, status: '重点' },
|
||
])
|
||
|
||
// 跟进记录
|
||
const followRecords = ref([
|
||
{ id: 1, customer: '比亚迪股份有限公司', content: '拜访客户,沟通轴承采购需求,预计月订单量增加30%', contact: '李经理', nextDate: '2026-04-15', status: '跟进中' },
|
||
{ id: 2, customer: '宁德时代新能源', content: '技术方案对接完成,等待客户内部评审', contact: '王经理', nextDate: '2026-04-12', status: '待联系' },
|
||
{ id: 3, customer: '华为技术有限公司', content: '完成样品交付,客户反馈良好', contact: '张经理', nextDate: '-', status: '已完成' },
|
||
])
|
||
|
||
const statusColor: Record<string, string> = {
|
||
'VIP': 'purple',
|
||
'重点': 'blue',
|
||
'普通': 'default',
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="page-container">
|
||
<div class="page-header">
|
||
<h2 class="page-title">营销管理</h2>
|
||
<a-space>
|
||
<a-button @click="() => {}">导出数据</a-button>
|
||
<a-button type="primary" @click="() => {}">
|
||
<template #icon><PlusOutlined /></template>
|
||
新建客户
|
||
</a-button>
|
||
</a-space>
|
||
</div>
|
||
|
||
<!-- 统计卡片 -->
|
||
<a-row :gutter="[16, 16]" class="mb-6">
|
||
<a-col :xs="12" :sm="6" v-for="stat in stats" :key="stat.label">
|
||
<div class="stat-card" :style="{ '--accent': stat.color }">
|
||
<div class="stat-icon">{{ stat.icon }}</div>
|
||
<div class="stat-body">
|
||
<div class="stat-value">{{ stat.value }}<span class="stat-unit">{{ stat.unit }}</span></div>
|
||
<div class="stat-label">{{ stat.label }}</div>
|
||
<div :class="['stat-change', stat.change.startsWith('+') ? 'up' : 'down']">{{ stat.change }}</div>
|
||
</div>
|
||
</div>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<a-row :gutter="[20, 20]">
|
||
<a-col :xs="24" :xl="16">
|
||
<!-- 客户列表 -->
|
||
<div class="card">
|
||
<div class="card-title">客户列表</div>
|
||
<a-table :dataSource="customers" :pagination="{ pageSize: 8 }" size="small" rowKey="id">
|
||
<a-table-column title="客户编码" dataIndex="id" width="90" />
|
||
<a-table-column title="客户名称" dataIndex="name" />
|
||
<a-table-column title="行业" dataIndex="industry" width="100" />
|
||
<a-table-column title="联系人" dataIndex="contact" width="80" />
|
||
<a-table-column title="订单数" dataIndex="orderCount" width="80" align="center" />
|
||
<a-table-column title="累计金额(万)" dataIndex="amount" width="110" align="right" />
|
||
<a-table-column title="等级" dataIndex="status" width="80" align="center">
|
||
<template #default="{ text }">
|
||
<a-tag :color="statusColor[text]">{{ text }}</a-tag>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="操作" width="100" align="center">
|
||
<template #default>
|
||
<a-space>
|
||
<a>详情</a>
|
||
<a-divider type="vertical" />
|
||
<a>跟进</a>
|
||
</a-space>
|
||
</template>
|
||
</a-table-column>
|
||
</a-table>
|
||
</div>
|
||
</a-col>
|
||
|
||
<a-col :xs="24" :xl="8">
|
||
<!-- 跟进记录 -->
|
||
<div class="card">
|
||
<div class="card-title">跟进记录</div>
|
||
<div class="follow-list">
|
||
<div v-for="record in followRecords" :key="record.id" class="follow-item">
|
||
<div class="follow-header">
|
||
<span class="follow-customer">{{ record.customer }}</span>
|
||
<a-tag size="small" :color="record.status === '已完成' ? 'success' : record.status === '跟进中' ? 'processing' : 'warning'">
|
||
{{ record.status }}
|
||
</a-tag>
|
||
</div>
|
||
<div class="follow-content">{{ record.content }}</div>
|
||
<div class="follow-meta">
|
||
<span><UserOutlined /> {{ record.contact }}</span>
|
||
<span v-if="record.nextDate !== '-'"><CalendarOutlined /> 下次: {{ record.nextDate }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.page-container { padding: 24px; }
|
||
.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||
.page-title { font-size: 20px; font-weight: 600; color: #1f2937; margin: 0; }
|
||
|
||
.stat-card {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
||
border: 1px solid #f0f0f0;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.stat-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0; left: 0;
|
||
width: 4px; height: 100%;
|
||
background: var(--accent);
|
||
}
|
||
.stat-icon { font-size: 28px; }
|
||
.stat-value { font-size: 22px; font-weight: 700; color: #1f2937; }
|
||
.stat-unit { font-size: 12px; color: #9ca3af; margin-left: 2px; }
|
||
.stat-label { font-size: 12px; color: #9ca3af; margin: 2px 0; }
|
||
.stat-change { font-size: 12px; }
|
||
.stat-change.up { color: #10b981; }
|
||
.stat-change.down { color: #ef4444; }
|
||
|
||
.card { background: white; border-radius: 12px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); border: 1px solid #f0f0f0; }
|
||
.card-title { font-size: 16px; font-weight: 600; color: #1f2937; margin-bottom: 16px; }
|
||
|
||
.follow-list { display: flex; flex-direction: column; gap: 12px; }
|
||
.follow-item { padding: 12px; background: #fafafa; border-radius: 8px; }
|
||
.follow-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
|
||
.follow-customer { font-size: 13px; font-weight: 600; color: #374151; }
|
||
.follow-content { font-size: 12px; color: #6b7280; margin-bottom: 8px; line-height: 1.5; }
|
||
.follow-meta { display: flex; gap: 16px; font-size: 11px; color: #9ca3af; }
|
||
.follow-meta span { display: flex; align-items: center; gap: 4px; }
|
||
|
||
.mb-6 { margin-bottom: 20px; }
|
||
</style>
|