Mars Developer 51f8d95bf9 first commit
2025-06-26 11:24:11 +08:00

925 lines
33 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="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>