| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- <!-- 叁强鲜配工作台页面 -->
- <template>
- <div class="console-container">
- <div class="page-head">
- <h1>运营工作台</h1>
- <p class="page-desc">统一查看订单、配送、结算与待办事项,优先处理时效预警任务。</p>
- </div>
- <!-- 预警提示 -->
- <div v-if="warningCount > 0" class="alert-bar" @click="goToWarning">
- <el-icon class="alert-icon"><WarningFilled /></el-icon>
- <span class="alert-text">您有 <strong>{{ warningCount }}</strong> 个配送单即将超时,请及时处理!</span>
- <span class="alert-btn">立即处理</span>
- </div>
- <!-- 数据统计卡片 -->
- <el-row :gutter="16" class="stats-row">
- <el-col :xs="12" :sm="8" :md="5" v-for="stat in statsData" :key="stat.key">
- <div class="stats-card" @click="handleStatClick(stat.key)">
- <div class="stats-icon" :class="stat.colorClass">
- <el-icon :size="24"><component :is="stat.icon" /></el-icon>
- </div>
- <div class="stats-info">
- <div class="stats-num">{{ stat.value }}</div>
- <div class="stats-label">{{ stat.label }}</div>
- </div>
- </div>
- </el-col>
- </el-row>
- <!-- 待办事项 -->
- <el-row :gutter="16" class="todo-row">
- <el-col :xs="24" :sm="12" :md="6" v-for="todo in todoList" :key="todo.type">
- <div class="todo-card" :class="todo.className" @click="handleTodoClick(todo.path)">
- <div class="todo-header">
- <span class="todo-title">{{ todo.title }}</span>
- <span class="todo-count" :class="todo.countClass">{{ todo.count }}</span>
- </div>
- <div class="todo-list">
- <div v-for="(item, index) in todo.items.slice(0, 2)" :key="index" class="todo-item">
- <span class="todo-item-text">{{ item }}</span>
- <span class="todo-item-action" @click.stop="handleTodoAction(todo.path)">
- {{ todo.actionText }}
- </span>
- </div>
- </div>
- </div>
- </el-col>
- </el-row>
- <!-- 快捷操作 -->
- <el-row :gutter="16" class="quick-row">
- <el-col :xs="12" :sm="6" v-for="action in quickActions" :key="action.key">
- <div class="quick-btn" @click="handleQuickAction(action.path)">
- <div class="quick-icon" :class="action.colorClass">
- <el-icon :size="20"><component :is="action.icon" /></el-icon>
- </div>
- <span class="quick-text">{{ action.text }}</span>
- <el-icon class="quick-arrow"><ArrowRight /></el-icon>
- </div>
- </el-col>
- </el-row>
- <!-- 最新订单 -->
- <section class="panel orders-card">
- <div class="panel-top">
- <h2>最新订单</h2>
- <el-button type="primary" link @click="goToOrderList">查看更多</el-button>
- </div>
- <div class="list-body list-wrap">
- <el-table class="list-table" :data="orderList" v-loading="loading" stripe>
- <el-table-column prop="orderNo" label="订单编号" min-width="180" />
- <el-table-column prop="clientName" label="客户名称" min-width="150" />
- <el-table-column prop="region" label="区域" min-width="100" />
- <el-table-column prop="totalAmount" label="订单金额(元)" min-width="120">
- <template #default="{ row }">
- <span class="amount-text">¥{{ row.totalAmount }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="status" label="订单状态" min-width="130">
- <template #default="{ row }">
- <el-tag :type="getStatusType(row.status)" size="small">
- {{ getStatusText(row.status) }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="createTime" label="下单时间" min-width="180" />
- <el-table-column label="操作" min-width="100">
- <template #default="{ row }">
- <el-button type="primary" link @click="goToOrderDetail(row.id)">查看</el-button>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </section>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onMounted } from 'vue'
- import { useRouter } from 'vue-router'
- import {
- WarningFilled,
- UserFilled,
- User,
- Shop,
- ShoppingCart,
- Van,
- Money,
- Plus,
- Wallet,
- ArrowRight
- } from '@element-plus/icons-vue'
- import { ElMessage } from 'element-plus'
- defineOptions({ name: 'Console' })
- const router = useRouter()
- const loading = ref(false)
- const warningCount = ref(3)
- // 统计数据
- const statsData = ref([
- { key: 'client', label: '客户总数', value: '1,286', icon: UserFilled, colorClass: 'blue', path: '/admin/user' },
- { key: 'supplier', label: '供应商数量', value: '86', icon: Shop, colorClass: 'green', path: '/admin/user' },
- { key: 'todayOrder', label: '今日订单', value: '328', icon: ShoppingCart, colorClass: 'orange', path: '/admin/order' },
- { key: 'pendingDelivery', label: '待配送', value: '45', icon: Van, colorClass: 'red', path: '/admin/delivery' },
- { key: 'todayAmount', label: '今日交易额', value: '¥58,620', icon: Money, colorClass: 'purple', path: '/admin/statistics' }
- ])
- // 待办事项
- const todoList = ref([
- {
- type: 'quotation',
- title: '待审核报价',
- count: 5,
- className: 'warning',
- countClass: 'orange',
- actionText: '处理',
- path: '/inquiry/quotation-list',
- items: ['2026年3月第3周询价...', '城东区域供应商报价...']
- },
- {
- type: 'delivery',
- title: '待分配配送',
- count: 12,
- className: 'danger',
- countClass: '',
- actionText: '分配',
- path: '/admin/delivery',
- items: ['ORD202603190003', 'ORD202603190004']
- },
- {
- type: 'cancel',
- title: '取消待审核',
- count: 3,
- className: 'primary',
- countClass: 'blue',
- actionText: '审核',
- path: '/admin/order',
- items: ['客户BB超市申请取消...', '客户CC商行申请取消...']
- },
- {
- type: 'settlement',
- title: '待结算确认',
- count: 8,
- className: 'success',
- countClass: 'green',
- actionText: '确认',
- path: '/admin/settlement',
- items: ['AA餐厅线下结算...', 'BB超市线下结算...']
- }
- ])
- // 快捷操作
- const quickActions = [
- { key: 'inquiry', text: '发布询价', icon: Plus, colorClass: 'blue', path: '/inquiry/inquiry-list' },
- { key: 'delivery', text: '分配配送', icon: Van, colorClass: 'orange', path: '/admin/delivery' },
- { key: 'settlement', text: '确认结算', icon: Wallet, colorClass: 'green', path: '/admin/settlement' },
- { key: 'user', text: '新增用户', icon: User, colorClass: 'red', path: '/admin/user' }
- ]
- // 订单列表
- const orderList = ref([
- {
- id: '1',
- orderNo: 'ORD202603190001',
- clientName: 'XX餐厅',
- region: '城东',
- totalAmount: '1,286.00',
- status: 'pending_payment',
- createTime: '2026-03-19 10:23:45'
- },
- {
- id: '2',
- orderNo: 'ORD202603190002',
- clientName: 'YY超市',
- region: '城西',
- totalAmount: '2,560.00',
- status: 'pending_delivery',
- createTime: '2026-03-19 09:15:22'
- },
- {
- id: '3',
- orderNo: 'ORD202603180056',
- clientName: 'ZZ食堂',
- region: '城南',
- totalAmount: '856.00',
- status: 'delivering',
- createTime: '2026-03-18 16:45:30'
- },
- {
- id: '4',
- orderNo: 'ORD202603180055',
- clientName: 'AA饭店',
- region: '城东',
- totalAmount: '3,200.00',
- status: 'completed',
- createTime: '2026-03-18 14:20:18'
- }
- ])
- // 获取状态类型
- const getStatusType = (status: string) => {
- const typeMap: Record<string, string> = {
- pending_payment: 'warning',
- pending_delivery: 'primary',
- delivering: 'info',
- partial_delivered: 'warning',
- pending_confirm: 'primary',
- completed: 'success',
- cancelled: 'info'
- }
- return typeMap[status] || 'info'
- }
- // 获取状态文本
- const getStatusText = (status: string) => {
- const textMap: Record<string, string> = {
- pending_payment: '待支付',
- pending_delivery: '待分配配送',
- delivering: '配送中',
- partial_delivered: '部分送达',
- pending_confirm: '待确认收货',
- completed: '已完成',
- cancelled: '已取消'
- }
- return textMap[status] || status
- }
- // 跳转预警页面
- const goToWarning = () => {
- router.push('/admin/delivery-warning')
- }
- // 统计卡片点击
- const handleStatClick = (key: string) => {
- const stat = statsData.value.find((s) => s.key === key)
- if (stat?.path) {
- router.push(stat.path)
- }
- }
- // 待办点击
- const handleTodoClick = (path: string) => {
- router.push(path)
- }
- // 待办操作
- const handleTodoAction = (path: string) => {
- router.push(path)
- }
- // 快捷操作
- const handleQuickAction = (path: string) => {
- router.push(path)
- }
- // 跳转订单列表
- const goToOrderList = () => {
- router.push('/admin/order')
- }
- // 跳转订单详情
- const goToOrderDetail = (id: string) => {
- router.push(`/admin/order/${id}`)
- }
- onMounted(() => {
- // TODO: 从后端获取数据
- })
- </script>
- <style lang="scss" scoped>
- .console-container {
- .alert-bar {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px 16px;
- margin-bottom: 16px;
- background: #fffbeb;
- border: 1px solid #fde68a;
- border-radius: var(--mint-radius);
- cursor: pointer;
- transition: 0.2s ease;
- &:hover {
- background: #fef3c7;
- }
- .alert-icon {
- color: #d97706;
- font-size: 18px;
- }
- .alert-text {
- flex: 1;
- color: #92400e;
- font-size: 13px;
- }
- .alert-btn {
- color: var(--mint-primary-dark);
- font-size: 13px;
- font-weight: 600;
- }
- }
- }
- .stats-row,
- .todo-row,
- .quick-row {
- margin-bottom: 16px;
- }
- .stats-card,
- .todo-card,
- .quick-btn {
- background: var(--mint-surface);
- border: 1px solid var(--mint-border);
- border-radius: var(--mint-radius);
- box-shadow: var(--mint-shadow);
- margin-bottom: 0;
- }
- .stats-card {
- min-height: 112px;
- padding: 12px 14px;
- gap: 12px;
- transition: border-color 0.2s ease;
- &:hover {
- border-color: #b9dbcb;
- transform: none;
- box-shadow: var(--mint-shadow);
- }
- .stats-icon {
- width: 44px;
- height: 44px;
- border-radius: 10px;
- border: 1px solid rgba(16, 185, 129, 0.12);
- background: var(--mint-primary-bg);
- color: var(--mint-primary-dark);
- &.orange,
- &.red {
- background: #fffbeb;
- border-color: rgba(217, 119, 6, 0.2);
- color: #d97706;
- }
- &.blue,
- &.green,
- &.purple {
- background: var(--mint-primary-bg);
- border-color: rgba(16, 185, 129, 0.12);
- color: var(--mint-primary-dark);
- }
- }
- .stats-info {
- .stats-num {
- font-size: 22px;
- font-weight: 700;
- color: var(--mint-text);
- line-height: 1.2;
- letter-spacing: -0.02em;
- font-variant-numeric: tabular-nums;
- }
- .stats-label {
- font-size: 12px;
- color: var(--mint-text-muted);
- margin-top: 4px;
- }
- }
- }
- .todo-card {
- padding: 14px;
- border-left: 3px solid var(--mint-border);
- cursor: pointer;
- transition: 0.2s ease;
- &:hover {
- border-color: var(--mint-primary);
- transform: none;
- box-shadow: var(--mint-shadow);
- }
- &.warning {
- border-left-color: #d97706;
- }
- &.danger {
- border-left-color: #dc2626;
- }
- &.primary {
- border-left-color: var(--mint-primary-dark);
- }
- &.success {
- border-left-color: var(--mint-primary);
- }
- .todo-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12px;
- .todo-title {
- font-size: 14px;
- font-weight: 600;
- color: var(--mint-text);
- }
- .todo-count {
- background: var(--mint-primary-bg);
- color: var(--mint-primary-dark);
- padding: 2px 10px;
- border-radius: 6px;
- font-size: 12px;
- font-weight: 600;
- &.blue {
- background: var(--mint-primary-bg);
- color: var(--mint-primary-dark);
- }
- &.orange {
- background: #fffbeb;
- color: #d97706;
- }
- &.green {
- background: #d1fae5;
- color: #047857;
- }
- }
- }
- .todo-list {
- .todo-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 0;
- border-bottom: 1px solid #edf4f1;
- font-size: 13px;
- &:last-child {
- border-bottom: none;
- }
- .todo-item-text {
- color: var(--mint-text-muted);
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .todo-item-action {
- color: var(--mint-primary-dark);
- cursor: pointer;
- margin-left: 8px;
- &:hover {
- text-decoration: underline;
- }
- }
- }
- }
- }
- .quick-btn {
- border-radius: var(--mint-radius);
- padding: 16px;
- display: flex;
- align-items: center;
- gap: 12px;
- cursor: pointer;
- transition: border-color 0.2s ease;
- &:hover {
- border-color: #b9dbcb;
- transform: none;
- box-shadow: var(--mint-shadow);
- }
- .quick-icon {
- width: 40px;
- height: 40px;
- border-radius: 10px;
- display: flex;
- align-items: center;
- justify-content: center;
- &.blue {
- background: var(--mint-primary-bg);
- color: var(--mint-primary-dark);
- }
- &.orange {
- background: #fffbeb;
- color: #d97706;
- }
- &.green {
- background: #d1fae5;
- color: #047857;
- }
- &.red {
- background: #fef2f2;
- color: #dc2626;
- }
- }
- .quick-text {
- font-size: 14px;
- font-weight: 500;
- color: var(--mint-text);
- flex: 1;
- }
- .quick-arrow {
- color: var(--mint-text-muted);
- }
- }
- .orders-card {
- .list-body {
- border-top: 1px solid var(--mint-border);
- }
- :deep(.el-table__cell .cell) {
- white-space: nowrap;
- }
- .amount-text {
- color: var(--mint-primary-dark);
- font-weight: 600;
- }
- }
- </style>
|