feat: 新增订单列表交互

This commit is contained in:
duanshuwen
2025-07-27 18:08:06 +08:00
parent 87bdac8c57
commit 4cd0f59966
31 changed files with 3535 additions and 2559 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,100 @@
<template>
<view class="order-card" @click="handleCardClick">
<!-- 卡片头部 -->
<view class="card-header">
<view class="status-info">
<image class="status-icon" src="./images/service.png"></image>
<view class="order-title">{{ orderData.title }}</view>
</view>
<view
v-if="orderData.status !== 'pending'"
:class="['status-tag', `tag-${orderData.status}`]"
>
{{ getStatusText(orderData.status) }}
</view>
</view>
<!-- 分割线 -->
<Divider />
<!-- 卡片内容 -->
<view class="card-content">
<view class="info-row">
<text class="label">创建时间</text>
<text class="value">{{ orderData.createTime }}</text>
</view>
<view class="info-row">
<text class="label">联系房客</text>
<text class="value">{{ orderData.contactName }}</text>
</view>
<view class="info-row">
<text class="label">联系电话</text>
<text class="value">{{ orderData.contactPhone }}</text>
</view>
</view>
<!-- 操作区域 -->
<view v-if="orderData.status === 'pending'" class="action-area">
<button class="action-btn" @click.stop="handleCall">立即呼叫</button>
</view>
</view>
</template>
<script setup>
import Divider from "@/components/Divider/index.vue";
import { defineProps } from "vue";
// Props
const props = defineProps({
orderData: {
type: Object,
required: true,
default: () => ({
id: "",
title: "",
createTime: "",
contactName: "",
contactPhone: "",
status: "pending", // pending-待处理, completed-已完成, cancelled-已取消
}),
},
});
// Emits
const emit = defineEmits(["click", "call", "complete"]);
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
pending: "待处理",
completed: "已完成",
cancelled: "已取消",
processing: "处理中",
};
return statusMap[status] || "未知状态";
};
// 处理卡片点击
const handleCardClick = () => {
emit("click", props.orderData);
};
// 处理呼叫
const handleCall = () => {
emit("call", props.orderData);
};
// 处理完成
const handleComplete = () => {
emit("complete", props.orderData);
};
// 暴露方法
defineExpose({
getStatusText,
});
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>

View File

@@ -0,0 +1,170 @@
# OrderCard 工单卡片组件
## 组件概述
OrderCard 是一个用于显示工单信息的卡片组件,支持多种工单状态展示、操作按钮和自定义内容。适用于工单管理、客服系统等场景。
## 功能特性
- ✅ 多种工单状态支持(待处理、处理中、已完成、已取消)
- ✅ 状态图标和标签显示
- ✅ 工单基本信息展示
- ✅ 可配置的操作按钮
- ✅ 自定义操作区域插槽
- ✅ 点击事件和操作事件
- ✅ 响应式设计
- ✅ 暗色模式支持
- ✅ 优雅的交互动画
## 组件属性 (Props)
| 属性名 | 类型 | 默认值 | 必填 | 说明 |
|--------|------|--------|------|------|
| orderData | Object | - | 是 | 工单数据对象 |
| showActions | Boolean | true | 否 | 是否显示操作按钮区域 |
### orderData 对象结构
```javascript
{
id: String, // 工单ID
title: String, // 工单标题
createTime: String, // 创建时间
contactName: String, // 联系人姓名
contactPhone: String, // 联系电话
status: String // 工单状态pending/processing/completed/cancelled
}
```
## 组件事件 (Events)
| 事件名 | 参数 | 说明 |
|--------|------|------|
| click | orderData | 卡片点击事件 |
| call | orderData | 呼叫按钮点击事件 |
| complete | orderData | 完成按钮点击事件 |
## 插槽 (Slots)
| 插槽名 | 说明 |
|--------|------|
| actions | 自定义操作按钮区域 |
## 工单状态说明
| 状态值 | 显示文本 | 图标颜色 | 标签样式 |
|--------|----------|----------|----------|
| pending | 待处理 | 橙色 | 橙色边框 |
| processing | 处理中 | 蓝色 | 蓝色边框 |
| completed | 已完成 | 绿色 | 绿色边框 |
| cancelled | 已取消 | 灰色 | 灰色边框 |
## 使用示例
### 基础用法
```vue
<template>
<OrderCard
:orderData="orderInfo"
@click="handleCardClick"
@call="handleCall"
@complete="handleComplete"
/>
</template>
<script setup>
import OrderCard from '@/components/OrderCard/index.vue'
const orderInfo = {
id: '001',
title: '空调不制冷,需要维修',
createTime: '2024-01-15 14:30',
contactName: '张先生',
contactPhone: '138****8888',
status: 'pending'
}
const handleCardClick = (orderData) => {
console.log('卡片点击', orderData)
}
const handleCall = (orderData) => {
console.log('呼叫', orderData.contactPhone)
}
const handleComplete = (orderData) => {
console.log('标记完成', orderData.id)
}
</script>
```
### 隐藏操作按钮
```vue
<template>
<OrderCard
:orderData="completedOrder"
:showActions="false"
@click="handleCardClick"
/>
</template>
```
### 自定义操作按钮
```vue
<template>
<OrderCard
:orderData="orderInfo"
@click="handleCardClick"
>
<template #actions>
<button class="custom-btn" @click.stop="handleEdit">
编辑
</button>
<button class="custom-btn" @click.stop="handleDelete">
删除
</button>
</template>
</OrderCard>
</template>
```
## 样式定制
组件支持通过 CSS 变量进行样式定制:
```css
.order-card {
--card-bg: #ffffff;
--card-radius: 12px;
--card-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
--primary-color: #007AFF;
--success-color: #52C41A;
--warning-color: #FF8C00;
--danger-color: #FF3B30;
}
```
## 响应式支持
- 在小屏设备≤375px上自动调整字体大小和间距
- 支持暗色模式自动适配
- 触摸设备优化的交互体验
## 注意事项
1. **数据格式**:确保传入的 `orderData` 包含所有必需字段
2. **状态值**`status` 字段必须是预定义的状态值之一
3. **事件处理**:使用 `@click.stop` 防止操作按钮事件冒泡
4. **性能优化**:大量卡片时建议使用虚拟滚动
## 更新日志
### v1.0.0 (2024-01-15)
- 初始版本发布
- 支持基础工单信息展示
- 支持多种状态和操作按钮
- 支持自定义插槽
- 响应式设计和暗色模式支持

View File

@@ -0,0 +1,130 @@
.order-card {
background-color: #fff;
border-radius: 6px 6px 12px 12px;
box-shadow: 0px 3px 8px 0 rgba(0,0,0,0.12);
margin: 12px;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
&.expired {
filter: grayscale(100%);
}
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 14px 12px 16px;
}
.status-info {
display: flex;
align-items: center;
flex: 1;
}
.status-icon {
width: 20px;
height: 20px;
margin-right: 8px;
flex-shrink: 0;
}
.order-title {
font-size: 14px;
font-weight: 500;
color: #333333;
line-height: 1.4;
flex: 1;
}
.status-tag {
box-sizing: border-box;
padding: 6px 16px;
border-radius: 20px ;
font-size: 12px;
font-weight: 500;
&.tag-pending {
background-color: #FFF7E6;
color: #FF8C00;
border: 1px solid #FFD591;
}
&.tag-completed {
background-color: #F6FFED;
color: #52C41A;
border: 1px solid #B7EB8F;
}
&.tag-cancelled {
background-color: #F5F5F5;
color: #999999;
border: 1px solid #D9D9D9;
}
&.tag-processing {
background-color: #E6F7FF;
color: #1890FF;
border: 1px solid #91D5FF;
}
}
.card-content {
padding: 16px;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
.label {
font-size: 12px;
color: #666666;
flex-shrink: 0;
margin-right: 8px;
}
.value {
font-size: 14px;
color: #333333;
flex: 1;
}
.action-area {
padding-bottom: 16px;
}
.action-btn {
width: 280px;
height: 42px;
border-radius: 50px;
border: 2px solid #FFCA70;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
background: linear-gradient( 179deg, #FFB100 0%, #FF7F19 100%);
color: #ffffff;
margin: 0 auto;
&:hover {
background: linear-gradient(135deg, #FF7A00 0%, #FF6600 100%);
}
&:active {
transform: scale(0.95);
}
}