366 lines
9.2 KiB
TypeScript
366 lines
9.2 KiB
TypeScript
// 费率计算和管理的组合函数
|
|
import { ref, computed } from 'vue'
|
|
import { useSupabase } from './useSupabase'
|
|
|
|
// 服务类型费率配置
|
|
export interface ServiceRate {
|
|
id: string
|
|
service_type: string
|
|
service_name: string
|
|
base_price: number // 基础价格
|
|
price_per_minute?: number // 按分钟计费
|
|
price_per_word?: number // 按字数计费
|
|
price_per_page?: number // 按页数计费
|
|
urgency_multiplier: {
|
|
normal: number
|
|
urgent: number
|
|
very_urgent: number
|
|
}
|
|
language_pair_multiplier: {
|
|
[key: string]: number // 语言对费率倍数
|
|
}
|
|
currency: string
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// 订单费用计算结果
|
|
export interface CostCalculation {
|
|
baseCost: number
|
|
urgencyMultiplier: number
|
|
languageMultiplier: number
|
|
totalCost: number
|
|
breakdown: {
|
|
basePrice: number
|
|
urgencyFee: number
|
|
languageFee: number
|
|
estimatedDuration?: number
|
|
estimatedWords?: number
|
|
}
|
|
}
|
|
|
|
export const useRateCalculation = () => {
|
|
const { supabase } = useSupabase()
|
|
|
|
// 默认费率配置
|
|
const defaultRates = ref<ServiceRate[]>([
|
|
{
|
|
id: 'voice-call',
|
|
service_type: 'voice',
|
|
service_name: '语音通话',
|
|
base_price: 50,
|
|
price_per_minute: 2.5,
|
|
urgency_multiplier: {
|
|
normal: 1.0,
|
|
urgent: 1.5,
|
|
very_urgent: 2.0
|
|
},
|
|
language_pair_multiplier: {
|
|
'zh-en': 1.0,
|
|
'zh-ja': 1.2,
|
|
'zh-ko': 1.2,
|
|
'zh-fr': 1.3,
|
|
'zh-de': 1.3,
|
|
'zh-es': 1.2,
|
|
'zh-ru': 1.4,
|
|
'en-ja': 1.3,
|
|
'en-ko': 1.3,
|
|
'en-fr': 1.2,
|
|
'en-de': 1.2,
|
|
'en-es': 1.1,
|
|
'en-ru': 1.4
|
|
},
|
|
currency: 'CNY',
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
},
|
|
{
|
|
id: 'video-call',
|
|
service_type: 'video',
|
|
service_name: '视频通话',
|
|
base_price: 80,
|
|
price_per_minute: 4.0,
|
|
urgency_multiplier: {
|
|
normal: 1.0,
|
|
urgent: 1.5,
|
|
very_urgent: 2.0
|
|
},
|
|
language_pair_multiplier: {
|
|
'zh-en': 1.0,
|
|
'zh-ja': 1.2,
|
|
'zh-ko': 1.2,
|
|
'zh-fr': 1.3,
|
|
'zh-de': 1.3,
|
|
'zh-es': 1.2,
|
|
'zh-ru': 1.4
|
|
},
|
|
currency: 'CNY',
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
},
|
|
{
|
|
id: 'document-translation',
|
|
service_type: 'document',
|
|
service_name: '文档翻译',
|
|
base_price: 100,
|
|
price_per_word: 0.15,
|
|
urgency_multiplier: {
|
|
normal: 1.0,
|
|
urgent: 1.3,
|
|
very_urgent: 1.8
|
|
},
|
|
language_pair_multiplier: {
|
|
'zh-en': 1.0,
|
|
'zh-ja': 1.2,
|
|
'zh-ko': 1.2,
|
|
'zh-fr': 1.3,
|
|
'zh-de': 1.3,
|
|
'zh-es': 1.2,
|
|
'zh-ru': 1.4
|
|
},
|
|
currency: 'CNY',
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
},
|
|
{
|
|
id: 'interpretation',
|
|
service_type: 'interpretation',
|
|
service_name: '口译服务',
|
|
base_price: 200,
|
|
price_per_minute: 8.0,
|
|
urgency_multiplier: {
|
|
normal: 1.0,
|
|
urgent: 1.6,
|
|
very_urgent: 2.2
|
|
},
|
|
language_pair_multiplier: {
|
|
'zh-en': 1.0,
|
|
'zh-ja': 1.3,
|
|
'zh-ko': 1.3,
|
|
'zh-fr': 1.4,
|
|
'zh-de': 1.4,
|
|
'zh-es': 1.3,
|
|
'zh-ru': 1.5
|
|
},
|
|
currency: 'CNY',
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
},
|
|
{
|
|
id: 'localization',
|
|
service_type: 'localization',
|
|
service_name: '本地化',
|
|
base_price: 300,
|
|
price_per_word: 0.25,
|
|
urgency_multiplier: {
|
|
normal: 1.0,
|
|
urgent: 1.4,
|
|
very_urgent: 1.9
|
|
},
|
|
language_pair_multiplier: {
|
|
'zh-en': 1.0,
|
|
'zh-ja': 1.3,
|
|
'zh-ko': 1.3,
|
|
'zh-fr': 1.4,
|
|
'zh-de': 1.4,
|
|
'zh-es': 1.3,
|
|
'zh-ru': 1.5
|
|
},
|
|
currency: 'CNY',
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
},
|
|
{
|
|
id: 'proofreading',
|
|
service_type: 'proofreading',
|
|
service_name: '校对服务',
|
|
base_price: 80,
|
|
price_per_word: 0.08,
|
|
urgency_multiplier: {
|
|
normal: 1.0,
|
|
urgent: 1.3,
|
|
very_urgent: 1.7
|
|
},
|
|
language_pair_multiplier: {
|
|
'zh-en': 1.0,
|
|
'zh-ja': 1.1,
|
|
'zh-ko': 1.1,
|
|
'zh-fr': 1.2,
|
|
'zh-de': 1.2,
|
|
'zh-es': 1.1,
|
|
'zh-ru': 1.3
|
|
},
|
|
currency: 'CNY',
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
}
|
|
])
|
|
|
|
// 当前费率配置
|
|
const serviceRates = ref<ServiceRate[]>([])
|
|
const loading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
|
|
// 从数据库加载费率配置
|
|
const loadRates = async () => {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
const { data, error: dbError } = await supabase
|
|
.from('service_rates')
|
|
.select('*')
|
|
.order('service_type')
|
|
|
|
if (dbError) {
|
|
console.warn('从数据库加载费率失败,使用默认费率:', dbError.message)
|
|
serviceRates.value = [...defaultRates.value]
|
|
} else {
|
|
serviceRates.value = data || [...defaultRates.value]
|
|
}
|
|
} catch (err) {
|
|
console.warn('费率加载错误,使用默认费率:', err)
|
|
serviceRates.value = [...defaultRates.value]
|
|
error.value = '费率加载失败,使用默认配置'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 保存费率配置到数据库
|
|
const saveRates = async (rates: ServiceRate[]) => {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
// 先删除现有费率
|
|
await supabase.from('service_rates').delete().neq('id', '')
|
|
|
|
// 插入新费率
|
|
const { data, error: dbError } = await supabase
|
|
.from('service_rates')
|
|
.insert(rates.map(rate => ({
|
|
...rate,
|
|
updated_at: new Date().toISOString()
|
|
})))
|
|
|
|
if (dbError) throw dbError
|
|
|
|
serviceRates.value = [...rates]
|
|
return { success: true, data }
|
|
} catch (err) {
|
|
console.error('保存费率失败:', err)
|
|
error.value = '保存费率失败'
|
|
return { success: false, error: err }
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 获取指定服务类型的费率
|
|
const getRateByServiceType = (serviceType: string): ServiceRate | null => {
|
|
return serviceRates.value.find(rate => rate.service_type === serviceType) || null
|
|
}
|
|
|
|
// 计算订单费用
|
|
const calculateOrderCost = (
|
|
serviceType: string,
|
|
sourceLanguage: string,
|
|
targetLanguage: string,
|
|
urgency: 'normal' | 'urgent' | 'very_urgent' = 'normal',
|
|
estimatedDuration?: number, // 分钟
|
|
estimatedWords?: number, // 字数
|
|
estimatedPages?: number // 页数
|
|
): CostCalculation => {
|
|
const rate = getRateByServiceType(serviceType)
|
|
|
|
if (!rate) {
|
|
throw new Error(`未找到服务类型 ${serviceType} 的费率配置`)
|
|
}
|
|
|
|
// 基础价格
|
|
let baseCost = rate.base_price
|
|
|
|
// 根据服务类型计算额外费用
|
|
if (rate.price_per_minute && estimatedDuration) {
|
|
baseCost += rate.price_per_minute * estimatedDuration
|
|
}
|
|
|
|
if (rate.price_per_word && estimatedWords) {
|
|
baseCost += rate.price_per_word * estimatedWords
|
|
}
|
|
|
|
if (rate.price_per_page && estimatedPages) {
|
|
baseCost += rate.price_per_page * estimatedPages
|
|
}
|
|
|
|
// 紧急程度倍数
|
|
const urgencyMultiplier = rate.urgency_multiplier[urgency] || 1.0
|
|
|
|
// 语言对倍数
|
|
const languagePair = `${sourceLanguage}-${targetLanguage}`
|
|
const languageMultiplier = rate.language_pair_multiplier[languagePair] || 1.0
|
|
|
|
// 计算最终费用
|
|
const urgencyFee = baseCost * (urgencyMultiplier - 1)
|
|
const languageFee = baseCost * (languageMultiplier - 1)
|
|
const totalCost = baseCost * urgencyMultiplier * languageMultiplier
|
|
|
|
return {
|
|
baseCost,
|
|
urgencyMultiplier,
|
|
languageMultiplier,
|
|
totalCost: Math.round(totalCost * 100) / 100, // 保留两位小数
|
|
breakdown: {
|
|
basePrice: baseCost,
|
|
urgencyFee: Math.round(urgencyFee * 100) / 100,
|
|
languageFee: Math.round(languageFee * 100) / 100,
|
|
estimatedDuration,
|
|
estimatedWords
|
|
}
|
|
}
|
|
}
|
|
|
|
// 获取服务类型选项
|
|
const serviceTypeOptions = computed(() => {
|
|
return serviceRates.value.map(rate => ({
|
|
value: rate.service_type,
|
|
label: rate.service_name,
|
|
basePrice: rate.base_price
|
|
}))
|
|
})
|
|
|
|
// 获取所有语言对的费率倍数
|
|
const getLanguagePairMultipliers = (serviceType: string) => {
|
|
const rate = getRateByServiceType(serviceType)
|
|
return rate ? rate.language_pair_multiplier : {}
|
|
}
|
|
|
|
// 初始化费率配置
|
|
const initializeRates = async () => {
|
|
await loadRates()
|
|
|
|
// 如果数据库中没有费率配置,则保存默认配置
|
|
if (serviceRates.value.length === 0) {
|
|
await saveRates(defaultRates.value)
|
|
}
|
|
}
|
|
|
|
return {
|
|
// 数据
|
|
serviceRates: readonly(serviceRates),
|
|
loading: readonly(loading),
|
|
error: readonly(error),
|
|
|
|
// 计算属性
|
|
serviceTypeOptions,
|
|
|
|
// 方法
|
|
loadRates,
|
|
saveRates,
|
|
getRateByServiceType,
|
|
calculateOrderCost,
|
|
getLanguagePairMultipliers,
|
|
initializeRates
|
|
}
|
|
}
|