Files
assistant-storefront/app/javascript/shared/components/Logistics.vue
Liang XJ 092fb2e083
Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
Initial commit: Add logistics and order_detail message types
- Add Logistics component with progress tracking
- Add OrderDetail component for order information
- Support data-driven steps and actions
- Add blue color scale to widget SCSS
- Fix node overflow and progress bar rendering issues
- Add English translations for dashboard components

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-26 11:16:56 +08:00

156 lines
5.5 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="max-w-sm mx-auto bg-white dark:bg-n-solid-1 rounded-xl shadow-md overflow-hidden border border-n-weak">
<!-- 头部状态与单号 -->
<div class="p-4 border-b border-n-weak">
<div class="flex justify-between items-start">
<div>
<h3 class="text-lg font-bold text-n-slate-12 leading-none">{{ currentStatusText }}</h3>
<p class="text-xs text-n-slate-11 mt-2 flex items-center">
{{ logisticsName }}: {{ trackingNumber }}
<button @click="copyTrackingNumber" class="ml-2 text-n-blue-10 hover:text-n-blue-9">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</button>
</p>
</div>
</div>
</div>
<!-- 中间步骤条 -->
<div class="px-6 py-8 relative">
<!-- 进度背景线 -->
<div class="absolute top-[42px] left-10 right-10 h-0.5 bg-n-slate-4 dark:bg-n-slate-6 z-0"></div>
<!-- 激活进度线 -->
<div
v-if="currentStep < steps.length - 1"
class="absolute top-[42px] left-10 h-0.5 bg-n-blue-9 z-0"
:style="{ width: progressWidth }"
></div>
<div class="relative z-10 flex justify-between">
<div
v-for="(step, index) in steps"
:key="index"
class="flex flex-col items-center w-16"
>
<!-- 节点圆圈 -->
<div
class="w-5 h-5 rounded-full flex items-center justify-center flex-shrink-0"
:class="index <= currentStep ? 'bg-n-blue-9 ring-4 ring-n-blue-2' : 'bg-n-slate-4 dark:bg-n-slate-6 ring-4 ring-transparent'"
>
<svg v-if="index < currentStep" class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
<div v-else-if="index === currentStep" class="w-2 h-2 bg-white rounded-full"></div>
</div>
<!-- 节点文字 -->
<span
class="text-[11px] mt-3 text-center w-full truncate block"
:class="index <= currentStep ? 'text-n-blue-10 font-medium' : 'text-n-slate-11'"
>
{{ step.label }}
</span>
</div>
</div>
</div>
<!-- 详情描述框 -->
<div v-if="latestLog" class="mx-4 mb-4 p-3 bg-n-slate-2 dark:bg-n-solid-2 rounded-lg border-l-4 border-n-blue-9">
<p class="text-xs text-n-slate-11 leading-relaxed">
{{ latestLog }}
</p>
<p v-if="latestTime" class="text-[10px] text-n-slate-11 mt-1">{{ latestTime }}</p>
</div>
<!-- 底部操作栏 -->
<div class="flex border-t border-n-weak" style="border-style: solid;">
<button
v-for="(action, index) in actions"
:key="index"
@click="onActionClick(action)"
class="flex-1 py-3 text-sm transition-colors"
:class="{
'text-n-slate-11 hover:bg-n-slate-2 dark:hover:bg-n-solid-2 border-r border-n-weak': action.style === 'default',
'text-n-blue-10 font-semibold hover:bg-n-blue-1 dark:hover:bg-n-solid-blue': action.style === 'primary'
}"
>
{{ action.text }}
</button>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const { sendMessage } = store;
const props = defineProps({
logisticsName: { type: String, default: '顺丰速运' },
trackingNumber: { type: String, default: 'SF154228901' },
currentStep: { type: Number, default: 2 },
isUrgent: { type: Boolean, default: false },
latestLog: { type: String, default: '上海市 | 包裹正在由派送员王大力13800000000派送中' },
latestTime: { type: String, default: '2023-10-24 14:20:05' },
steps: {
type: Array,
default: () => [
{ label: '已发货' },
{ label: '运输中' },
{ label: '派送中' },
{ label: '已签收' }
]
},
actions: { type: Array, default: () => [] }
});
const currentStatusText = computed(() => {
return props.steps[props.currentStep]?.label || '状态未知';
});
const progressWidth = computed(() => {
// 计算进度条长度:(当前步骤 / 总步数间隔) * 100%
return `${(props.currentStep / (props.steps.length - 1)) * 100}%`;
});
const statusBadgeClass = computed(() => {
return props.isUrgent
? 'bg-n-ruby-2 text-n-ruby-11'
: 'bg-n-teal-2 text-n-teal-11';
});
const copyTrackingNumber = () => {
navigator.clipboard.writeText(props.trackingNumber);
alert('Tracking number copied');
};
const onActionClick = (action) => {
// 如果有 URL在父窗口中打开
if (action && action.url) {
if (window.parent !== window) {
// 在 iframe 中,直接设置父窗口 location
window.parent.location.href = action.url;
} else {
// 不在 iframe 中,直接使用当前窗口
window.location.href = action.url;
}
return;
}
// 否则发送消息
if (action && action.reply) {
sendMessage({
type: 'outgoing',
content: action.reply,
});
}
};
</script>
<style scoped>
/* 如果需要微调 Tailwind 未覆盖的样式,可写在这里 */
</style>