925 lines
33 KiB
Vue
Raw Permalink Normal View History

2025-06-26 11:24:11 +08:00
<template>
<div class="space-y-6">
<!-- 页面头部 -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-6">
<h1 class="text-2xl font-bold text-gray-900">订单管理</h1>
<div class="mt-4 sm:mt-0">
<button
@click="openCreateModal"
class="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
</svg>
创建订单
</button>
</div>
</div>
<!-- 统计卡片 -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">总订单</dt>
<dd class="text-lg font-medium text-gray-900">{{ stats.total }}</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">进行中</dt>
<dd class="text-lg font-medium text-gray-900">{{ stats.inProgress }}</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">已完成</dt>
<dd class="text-lg font-medium text-gray-900">{{ stats.completed }}</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">已取消</dt>
<dd class="text-lg font-medium text-gray-900">{{ stats.cancelled }}</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
<!-- 筛选和搜索 -->
<div class="bg-white shadow rounded-lg">
<div class="p-6">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label for="search" class="block text-sm font-medium text-gray-700 mb-1">搜索订单</label>
<input
id="search"
v-model="searchQuery"
type="text"
placeholder="订单号、客户姓名、项目标题"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label for="status-filter" class="block text-sm font-medium text-gray-700 mb-1">订单状态</label>
<select
id="status-filter"
v-model="statusFilter"
class="px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
>
<option value="">全部状态</option>
<option value="pending">待处理</option>
<option value="confirmed">已确认</option>
<option value="in_progress">进行中</option>
<option value="completed">已完成</option>
<option value="cancelled">已取消</option>
</select>
</div>
<div>
<label for="service-filter" class="block text-sm font-medium text-gray-700 mb-1">服务类型</label>
<select
id="service-filter"
v-model="serviceFilter"
class="px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
>
<option value="">全部服务</option>
<option value="voice">语音通话</option>
<option value="video">视频通话</option>
<option value="document">文档翻译</option>
<option value="interpretation">口译服务</option>
<option value="localization">本地化</option>
<option value="proofreading">校对服务</option>
</select>
</div>
<div>
<label for="date-range" class="block text-sm font-medium text-gray-700 mb-1">创建时间</label>
<select
id="date-range"
v-model="dateRange"
class="px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
>
<option value="">全部时间</option>
<option value="today">今天</option>
<option value="week">本周</option>
<option value="month">本月</option>
</select>
</div>
</div>
</div>
</div>
<!-- 订单列表 -->
<div class="bg-white shadow rounded-lg overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">订单列表</h3>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
订单信息
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
客户信息
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
项目信息
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
服务详情
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
状态
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
预估费用
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
口译员
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">操作</span>
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="order in filteredOrders" :key="order.id" class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">{{ order.order_number }}</div>
<div class="text-sm text-gray-500">{{ formatDate(order.created_at) }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div>
<div class="text-sm font-medium text-gray-900">{{ order.client_name }}</div>
<div class="text-sm text-gray-500">{{ order.client_email }}</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div>
<div class="text-sm font-medium text-gray-900">{{ order.project_name }}</div>
<div class="text-sm text-gray-500">{{ getUrgencyText(order.urgency) }}</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div>
<div class="text-sm font-medium text-gray-900">{{ getServiceTypeText(order.service_type) }}</div>
<div class="text-sm text-gray-500">{{ getLanguageName(order.source_language) }} {{ getLanguageName(order.target_language) }}</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span :class="getStatusClass(order.status)" class="inline-flex px-2 py-1 text-xs font-semibold rounded-full">
{{ getStatusText(order.status) }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
¥{{ order.estimated_cost || 0 }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ order.interpreter_name || '未分配' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
@click="viewOrder(order)"
class="text-blue-600 hover:text-blue-900 mr-3"
>
查看
</button>
<button
@click="editOrder(order)"
class="text-green-600 hover:text-green-900 mr-3"
>
编辑
</button>
<button
@click="handleDeleteOrder(order)"
class="text-red-600 hover:text-red-900"
>
删除
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 空状态 -->
<div v-if="filteredOrders.length === 0" class="text-center py-12">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">暂无订单</h3>
<p class="mt-1 text-sm text-gray-500">开始创建您的第一个翻译订单</p>
<div class="mt-6">
<button
@click="openCreateModal"
class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700"
>
创建订单
</button>
</div>
</div>
</div>
</div>
<!-- 创建订单模态框 -->
<div v-if="showCreateModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-10 mx-auto p-5 border w-11/12 md:w-2/3 lg:w-1/2 shadow-lg rounded-md bg-white max-h-[90vh] overflow-y-auto">
<div class="mt-3">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900">创建新订单</h3>
<button @click="showCreateModal = false" class="text-gray-400 hover:text-gray-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<form @submit.prevent="submitCreateOrder" class="space-y-6">
<!-- 基本信息 -->
<div class="border-b border-gray-200 pb-4">
<h4 class="text-md font-medium text-gray-900 mb-3">基本信息</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">客户姓名 *</label>
<input
v-model="newOrder.client_name"
type="text"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="请输入客户姓名"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">客户邮箱 *</label>
<input
v-model="newOrder.client_email"
type="email"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="请输入客户邮箱"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">客户电话</label>
<input
v-model="newOrder.client_phone"
type="tel"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="请输入客户电话"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">项目标题 *</label>
<input
v-model="newOrder.project_name"
type="text"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="请输入项目标题"
/>
</div>
</div>
</div>
<!-- 服务详情 -->
<div class="border-b border-gray-200 pb-4">
<h4 class="text-md font-medium text-gray-900 mb-3">服务详情</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">服务类型 *</label>
<select
v-model="newOrder.service_type"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">请选择服务类型</option>
<option value="voice">语音通话</option>
<option value="video">视频通话</option>
<option value="document">文档翻译</option>
<option value="interpretation">口译服务</option>
<option value="localization">本地化</option>
<option value="proofreading">校对服务</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">源语言 *</label>
<select
v-model="newOrder.source_language"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">请选择源语言</option>
<option value="zh">中文</option>
<option value="en">英文</option>
<option value="ja">日文</option>
<option value="ko">韩文</option>
<option value="fr">法文</option>
<option value="de">德文</option>
<option value="es">西班牙文</option>
<option value="ru">俄文</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">目标语言 *</label>
<select
v-model="newOrder.target_language"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">请选择目标语言</option>
<option value="zh">中文</option>
<option value="en">英文</option>
<option value="ja">日文</option>
<option value="ko">韩文</option>
<option value="fr">法文</option>
<option value="de">德文</option>
<option value="es">西班牙文</option>
<option value="ru">俄文</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">紧急程度 *</label>
<select
v-model="newOrder.urgency"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="normal">普通</option>
<option value="urgent">紧急</option>
<option value="emergency">特急</option>
</select>
</div>
</div>
</div>
<!-- 项目描述 -->
<div class="border-b border-gray-200 pb-4">
<h4 class="text-md font-medium text-gray-900 mb-3">项目描述</h4>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">详细描述</label>
<textarea
v-model="newOrder.project_description"
rows="4"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="请详细描述项目需求、要求等..."
></textarea>
</div>
</div>
<!-- 预算信息 -->
<div class="border-b border-gray-200 pb-4">
<h4 class="text-md font-medium text-gray-900 mb-3">预算信息</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">预估费用</label>
<input
v-model.number="newOrder.estimated_cost"
type="number"
min="0"
step="0.01"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="0.00"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">预计完成时间</label>
<input
v-model="newOrder.scheduled_date"
type="date"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
</div>
<!-- 客户公司和预计时长 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">客户公司</label>
<input
v-model="newOrder.client_company"
type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="可选"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">预计时长分钟</label>
<input
v-model.number="newOrder.expected_duration"
type="number"
min="1"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="60"
/>
</div>
</div>
<!-- 预约时间 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">预约时间</label>
<input
v-model="newOrder.scheduled_time"
type="time"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">备注</label>
<textarea
v-model="newOrder.notes"
rows="3"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="其他说明..."
/>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div id="estimated-cost" class="text-lg font-semibold text-gray-900">
预估费用: ¥{{ calculateOrderCost(
newOrder.service_type,
newOrder.source_language,
newOrder.target_language,
newOrder.urgency,
newOrder.expected_duration || 60
) }}
</div>
<div class="text-sm text-gray-600 mt-1">
费用会根据服务类型语言对紧急程度和预计时长自动计算
</div>
</div>
<!-- 按钮 -->
<div class="flex justify-end space-x-3 pt-4">
<button
type="button"
@click="showCreateModal = false"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
>
取消
</button>
<button
type="submit"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700"
>
创建订单
</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup>
import { useSupabaseData } from '~/composables/useSupabaseData'
import { useToast } from '~/composables/useToast'
// 定义页面meta
definePageMeta({
middleware: 'auth'
})
// 使用Supabase数据操作
const {
getOrders,
createOrder,
updateOrder,
deleteOrder,
getOrderStats,
calculateOrderCost
} = useSupabaseData()
const { showToast } = useToast()
// 响应式数据
const loading = ref(false)
const error = ref('')
const searchQuery = ref('')
const statusFilter = ref('')
const serviceFilter = ref('')
const dateRange = ref('')
const showCreateModal = ref(false)
// 订单数据
const allOrders = ref([])
const stats = ref({
total: 0,
pending: 0,
inProgress: 0,
completed: 0,
totalRevenue: 0
})
// 新订单表单数据
const newOrder = ref({
client_name: '',
client_email: '',
client_phone: '',
client_company: '',
project_name: '',
project_description: '',
source_language: 'zh',
target_language: 'en',
service_type: 'audio',
urgency: 'normal',
expected_duration: 60,
scheduled_date: '',
scheduled_time: '',
notes: ''
})
// 计算属性
const filteredOrders = computed(() => {
let filtered = allOrders.value
// 搜索过滤
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
filtered = filtered.filter(order =>
order.client_name?.toLowerCase().includes(query) ||
order.client_email?.toLowerCase().includes(query) ||
order.project_name?.toLowerCase().includes(query) ||
order.order_number?.toLowerCase().includes(query)
)
}
// 状态过滤
if (statusFilter.value) {
filtered = filtered.filter(order => order.status === statusFilter.value)
}
// 服务类型过滤
if (serviceFilter.value) {
filtered = filtered.filter(order => order.service_type === serviceFilter.value)
}
// 日期范围过滤
if (dateRange.value) {
const today = new Date()
const filterDate = new Date()
switch (dateRange.value) {
case 'today':
filterDate.setHours(0, 0, 0, 0)
filtered = filtered.filter(order =>
new Date(order.created_at) >= filterDate
)
break
case 'week':
filterDate.setDate(today.getDate() - 7)
filtered = filtered.filter(order =>
new Date(order.created_at) >= filterDate
)
break
case 'month':
filterDate.setMonth(today.getMonth() - 1)
filtered = filtered.filter(order =>
new Date(order.created_at) >= filterDate
)
break
}
}
return filtered
})
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
pending: '待确认',
confirmed: '已确认',
in_progress: '进行中',
completed: '已完成',
cancelled: '已取消'
}
return statusMap[status] || status
}
// 获取状态样式类
const getStatusClass = (status) => {
const classMap = {
pending: 'bg-yellow-100 text-yellow-800',
confirmed: 'bg-blue-100 text-blue-800',
in_progress: 'bg-green-100 text-green-800',
completed: 'bg-green-100 text-green-800',
cancelled: 'bg-red-100 text-red-800'
}
return classMap[status] || 'bg-gray-100 text-gray-800'
}
// 获取服务类型文本
const getServiceTypeText = (type) => {
const typeMap = {
voice: '语音通话',
video: '视频通话',
document: '文档翻译',
interpretation: '口译服务',
localization: '本地化',
proofreading: '校对服务',
// 向后兼容
audio: '语音翻译',
text: '文本翻译'
}
return typeMap[type] || type
}
// 获取紧急程度文本
const getUrgencyText = (urgency) => {
const urgencyMap = {
normal: '普通',
urgent: '紧急',
emergency: '特急'
}
return urgencyMap[urgency] || urgency
}
// 获取语言名称
const getLanguageName = (code) => {
const languages = {
zh: '中文',
en: '英文',
ja: '日文',
ko: '韩文',
fr: '法文',
de: '德文',
es: '西班牙文',
ru: '俄文'
}
return languages[code] || code
}
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return '未知时间'
const date = new Date(dateString)
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
// 查看订单详情
const viewOrder = (order) => {
// 可以跳转到订单详情页面
console.log('查看订单:', order)
showToast('订单详情功能开发中', 'info')
}
// 编辑订单
const editOrder = (order) => {
// 可以打开编辑模态框或跳转到编辑页面
console.log('编辑订单:', order)
showToast('编辑订单功能开发中', 'info')
}
// 删除订单
const handleDeleteOrder = async (order) => {
if (confirm(`确定要删除订单 ${order.order_number} 吗?`)) {
try {
const success = await deleteOrder(order.id)
if (success) {
// 从列表中移除
const index = allOrders.value.findIndex(o => o.id === order.id)
if (index > -1) {
allOrders.value.splice(index, 1)
}
// 更新统计
stats.value.total--
if (order.status === 'pending') stats.value.pending--
else if (order.status === 'in_progress') stats.value.inProgress--
else if (order.status === 'completed') stats.value.completed--
showToast('订单删除成功', 'success')
} else {
throw new Error('删除失败')
}
} catch (error) {
console.error('删除订单失败:', error)
showToast('删除订单失败', 'error')
}
}
}
// 方法
const loadOrders = async () => {
try {
loading.value = true
// 从数据库加载真实数据
const [ordersData, statsData] = await Promise.all([
getOrders(),
getOrderStats()
])
allOrders.value = ordersData || []
stats.value = statsData || {
total: 0,
pending: 0,
inProgress: 0,
completed: 0,
cancelled: 0,
totalRevenue: 0
}
console.log('成功加载订单数据:', ordersData?.length || 0, '条订单')
} catch (err) {
console.error('加载订单数据失败:', err)
error.value = '加载订单数据失败'
showToast('加载订单数据失败,请稍后重试', 'error')
// 初始化空数据
allOrders.value = []
stats.value = {
total: 0,
pending: 0,
inProgress: 0,
completed: 0,
cancelled: 0,
totalRevenue: 0
}
} finally {
loading.value = false
}
}
const openCreateModal = () => {
// 重置表单
newOrder.value = {
client_name: '',
client_email: '',
client_phone: '',
client_company: '',
project_name: '',
project_description: '',
source_language: 'zh',
target_language: 'en',
service_type: 'audio',
urgency: 'normal',
expected_duration: 60,
scheduled_date: '',
scheduled_time: '',
notes: ''
}
showCreateModal.value = true
}
const closeCreateModal = () => {
showCreateModal.value = false
}
const submitCreateOrder = async () => {
try {
loading.value = true
// 表单验证
if (!newOrder.value.client_name || !newOrder.value.client_email || !newOrder.value.project_name) {
throw new Error('请填写必填信息')
}
// 计算预估费用
const estimatedCost = calculateOrderCost(
newOrder.value.service_type,
newOrder.value.source_language,
newOrder.value.target_language,
newOrder.value.urgency,
newOrder.value.expected_duration
)
// 创建订单
const orderData = {
...newOrder.value,
estimated_cost: estimatedCost
}
const createdOrder = await createOrder(orderData)
if (createdOrder) {
// 添加到订单列表
allOrders.value.unshift(createdOrder)
// 更新统计
stats.value.total++
stats.value.pending++
showToast('订单创建成功', 'success')
closeCreateModal()
} else {
throw new Error('创建订单失败')
}
} catch (err) {
console.error('创建订单失败:', err)
showToast(err.message || '创建订单失败', 'error')
} finally {
loading.value = false
}
}
// 重新计算费用
const recalculateCost = () => {
if (newOrder.value.service_type && newOrder.value.source_language &&
newOrder.value.target_language && newOrder.value.urgency) {
const cost = calculateOrderCost(
newOrder.value.service_type,
newOrder.value.source_language,
newOrder.value.target_language,
newOrder.value.urgency,
newOrder.value.expected_duration || 60
)
// 显示计算出的费用
nextTick(() => {
const costElement = document.querySelector('#estimated-cost')
if (costElement) {
costElement.textContent = `预估费用: ¥${cost}`
}
})
}
}
// 监听表单字段变化,自动重新计算费用
watch([
() => newOrder.value.service_type,
() => newOrder.value.source_language,
() => newOrder.value.target_language,
() => newOrder.value.urgency,
() => newOrder.value.expected_duration
], recalculateCost)
// 页面挂载时加载数据
onMounted(() => {
loadOrders()
})
</script>