597 lines
14 KiB
Vue
597 lines
14 KiB
Vue
<template>
|
||
<div class="service-page">
|
||
<el-row :gutter="20">
|
||
<!-- 服务入口 -->
|
||
<el-col :span="16">
|
||
<el-card class="service-grid">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>生活服务</span>
|
||
</div>
|
||
</template>
|
||
<div class="grid-container">
|
||
<div
|
||
v-for="service in services"
|
||
:key="service.id"
|
||
class="service-item"
|
||
@click="handleService(service)"
|
||
>
|
||
<el-icon :size="40" :color="service.color">
|
||
<component :is="service.icon" />
|
||
</el-icon>
|
||
<h3>{{ service.name }}</h3>
|
||
<p>{{ service.description }}</p>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 社区团购 -->
|
||
<el-card class="group-buy mt-20">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>社区团购</span>
|
||
<el-radio-group v-model="groupBuyFilter" size="small">
|
||
<el-radio-button label="ongoing">进行中</el-radio-button>
|
||
<el-radio-button label="upcoming">即将开始</el-radio-button>
|
||
<el-radio-button label="ended">已结束</el-radio-button>
|
||
</el-radio-group>
|
||
</div>
|
||
</template>
|
||
<div class="group-buy-list">
|
||
<div
|
||
v-for="item in filteredGroupBuyItems"
|
||
:key="item.id"
|
||
class="group-buy-item"
|
||
>
|
||
<el-image :src="item.image" fit="cover">
|
||
<template #error>
|
||
<div class="image-slot">
|
||
<el-icon><Picture /></el-icon>
|
||
</div>
|
||
</template>
|
||
</el-image>
|
||
<div class="item-info">
|
||
<h4>{{ item.title }}</h4>
|
||
<p class="description">{{ item.description }}</p>
|
||
<div class="price-info">
|
||
<div class="price">
|
||
<span class="current">¥{{ item.groupPrice }}</span>
|
||
<span class="original">¥{{ item.originalPrice }}</span>
|
||
</div>
|
||
<span class="participants">{{ item.participants }}人已参与</span>
|
||
</div>
|
||
<div class="progress-info">
|
||
<el-progress
|
||
:percentage="(item.participants / item.target) * 100"
|
||
:format="() => `${item.participants}/${item.target}人`"
|
||
/>
|
||
</div>
|
||
<el-button
|
||
type="primary"
|
||
:disabled="item.status === 'ended'"
|
||
@click="joinGroupBuy(item)"
|
||
>
|
||
{{ getGroupBuyButtonText(item.status) }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
|
||
<!-- 服务记录 -->
|
||
<el-col :span="8">
|
||
<!-- 进行中的服务 -->
|
||
<el-card class="active-services">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>进行中的服务</span>
|
||
</div>
|
||
</template>
|
||
<el-timeline>
|
||
<el-timeline-item
|
||
v-for="service in activeServices"
|
||
:key="service.id"
|
||
:type="service.status"
|
||
:timestamp="service.time"
|
||
>
|
||
<h4>{{ service.title }}</h4>
|
||
<p>{{ service.description }}</p>
|
||
<div class="service-actions">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
@click="contactService(service)"
|
||
>
|
||
联系服务人员
|
||
</el-button>
|
||
<el-button
|
||
type="danger"
|
||
link
|
||
@click="cancelService(service)"
|
||
>
|
||
取消服务
|
||
</el-button>
|
||
</div>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
</el-card>
|
||
|
||
<!-- 缴费提醒 -->
|
||
<el-card class="payment-reminder mt-20">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>缴费提醒</span>
|
||
<el-button type="primary" text @click="batchPayment">
|
||
一键缴费
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
<div class="payment-list">
|
||
<div
|
||
v-for="bill in unpaidBills"
|
||
:key="bill.id"
|
||
class="payment-item"
|
||
>
|
||
<div class="bill-info">
|
||
<el-icon :color="bill.color"><component :is="bill.icon" /></el-icon>
|
||
<div class="bill-details">
|
||
<h4>{{ bill.title }}</h4>
|
||
<p>{{ bill.period }}</p>
|
||
</div>
|
||
</div>
|
||
<div class="bill-amount">
|
||
<span class="amount">¥{{ bill.amount }}</span>
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
@click="payBill(bill)"
|
||
>
|
||
立即缴费
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 家政服务预约对话框 -->
|
||
<el-dialog
|
||
v-model="serviceDialogVisible"
|
||
:title="currentService?.name"
|
||
width="50%"
|
||
>
|
||
<el-form :model="serviceForm" label-width="100px">
|
||
<el-form-item label="服务类型">
|
||
<el-select v-model="serviceForm.type" placeholder="请选择服务类型">
|
||
<el-option
|
||
v-for="type in currentService?.types"
|
||
:key="type.value"
|
||
:label="type.label"
|
||
:value="type.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="预约时间">
|
||
<el-date-picker
|
||
v-model="serviceForm.date"
|
||
type="datetime"
|
||
placeholder="选择日期时间"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="服务地址">
|
||
<el-input v-model="serviceForm.address" />
|
||
</el-form-item>
|
||
<el-form-item label="备注说明">
|
||
<el-input
|
||
v-model="serviceForm.notes"
|
||
type="textarea"
|
||
:rows="3"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="serviceDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="submitService">确认预约</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
import {
|
||
Box,
|
||
Van,
|
||
Money,
|
||
House,
|
||
Tools,
|
||
ShoppingCart,
|
||
Picture,
|
||
WaterMeter,
|
||
Lightning,
|
||
Connection
|
||
} from '@element-plus/icons-vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
|
||
// 服务列表
|
||
const services = [
|
||
{
|
||
id: 1,
|
||
name: '家政服务',
|
||
description: '专业保洁、护理等服务',
|
||
icon: 'House',
|
||
color: '#409EFF',
|
||
types: [
|
||
{ label: '日常保洁', value: 'cleaning' },
|
||
{ label: '深度保洁', value: 'deep-cleaning' },
|
||
{ label: '护理服务', value: 'care' }
|
||
]
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '快递代收',
|
||
description: '快递代收和暂存服务',
|
||
icon: 'Box',
|
||
color: '#67C23A'
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '水电缴费',
|
||
description: '水电费在线缴纳',
|
||
icon: 'Money',
|
||
color: '#E6A23C'
|
||
},
|
||
{
|
||
id: 4,
|
||
name: '物业服务',
|
||
description: '物业报修、投诉建议',
|
||
icon: 'House',
|
||
color: '#F56C6C'
|
||
},
|
||
{
|
||
id: 5,
|
||
name: '维修服务',
|
||
description: '家电维修、管道疏通',
|
||
icon: 'Tools',
|
||
color: '#909399'
|
||
},
|
||
{
|
||
id: 6,
|
||
name: '社区团购',
|
||
description: '团购优惠、便民服务',
|
||
icon: 'ShoppingCart',
|
||
color: '#9C27B0'
|
||
}
|
||
]
|
||
|
||
// 团购商品
|
||
const groupBuyFilter = ref('ongoing')
|
||
const groupBuyItems = ref([
|
||
{
|
||
id: 1,
|
||
title: '新鲜水果礼盒',
|
||
description: '精选时令水果,营养美味',
|
||
image: 'https://example.com/fruits.jpg',
|
||
groupPrice: 99,
|
||
originalPrice: 199,
|
||
participants: 35,
|
||
target: 50,
|
||
status: 'ongoing'
|
||
},
|
||
// 添加更多团购商品...
|
||
])
|
||
|
||
// 进行中的服务
|
||
const activeServices = ref([
|
||
{
|
||
id: 1,
|
||
title: '家政保洁服务',
|
||
description: '预约时间:2024-03-21 14:00',
|
||
time: '2024-03-21 14:00',
|
||
status: 'primary'
|
||
},
|
||
// 添加更多服务记录...
|
||
])
|
||
|
||
// 未缴费账单
|
||
const unpaidBills = ref([
|
||
{
|
||
id: 1,
|
||
title: '水费',
|
||
period: '2024年2月',
|
||
amount: 85.5,
|
||
icon: 'WaterMeter',
|
||
color: '#409EFF'
|
||
},
|
||
{
|
||
id: 2,
|
||
title: '电费',
|
||
period: '2024年2月',
|
||
amount: 156.8,
|
||
icon: 'Lightning',
|
||
color: '#E6A23C'
|
||
},
|
||
{
|
||
id: 3,
|
||
title: '物业费',
|
||
period: '2024年第一季度',
|
||
amount: 450,
|
||
icon: 'House',
|
||
color: '#67C23A'
|
||
}
|
||
])
|
||
|
||
// 服务预约表单
|
||
const serviceDialogVisible = ref(false)
|
||
const currentService = ref<any>(null)
|
||
const serviceForm = ref({
|
||
type: '',
|
||
date: '',
|
||
address: '',
|
||
notes: ''
|
||
})
|
||
|
||
// 计算属性
|
||
const filteredGroupBuyItems = computed(() => {
|
||
return groupBuyItems.value.filter(item => item.status === groupBuyFilter.value)
|
||
})
|
||
|
||
// 方法
|
||
const handleService = (service: any) => {
|
||
currentService.value = service
|
||
serviceDialogVisible.value = true
|
||
}
|
||
|
||
const submitService = () => {
|
||
ElMessage.success('服务预约成功')
|
||
serviceDialogVisible.value = false
|
||
// 重置表单
|
||
serviceForm.value = {
|
||
type: '',
|
||
date: '',
|
||
address: '',
|
||
notes: ''
|
||
}
|
||
}
|
||
|
||
const joinGroupBuy = (item: any) => {
|
||
ElMessageBox.confirm(
|
||
'确定参与该团购活动吗?',
|
||
'提示',
|
||
{
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'info'
|
||
}
|
||
).then(() => {
|
||
ElMessage.success('参与成功')
|
||
item.participants++
|
||
})
|
||
}
|
||
|
||
const getGroupBuyButtonText = (status: string) => {
|
||
const texts: Record<string, string> = {
|
||
ongoing: '立即参与',
|
||
upcoming: '即将开始',
|
||
ended: '已结束'
|
||
}
|
||
return texts[status] || '立即参与'
|
||
}
|
||
|
||
const contactService = (service: any) => {
|
||
ElMessage.success(`正在连接服务人员...`)
|
||
}
|
||
|
||
const cancelService = (service: any) => {
|
||
ElMessageBox.confirm(
|
||
'确定要取消该服务吗?',
|
||
'提示',
|
||
{
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}
|
||
).then(() => {
|
||
ElMessage.success('服务已取消')
|
||
activeServices.value = activeServices.value.filter(s => s.id !== service.id)
|
||
})
|
||
}
|
||
|
||
const payBill = (bill: any) => {
|
||
ElMessageBox.confirm(
|
||
`确定支付${bill.title} ¥${bill.amount}吗?`,
|
||
'提示',
|
||
{
|
||
confirmButtonText: '确定支付',
|
||
cancelButtonText: '取消',
|
||
type: 'info'
|
||
}
|
||
).then(() => {
|
||
ElMessage.success('支付成功')
|
||
unpaidBills.value = unpaidBills.value.filter(b => b.id !== bill.id)
|
||
})
|
||
}
|
||
|
||
const batchPayment = () => {
|
||
const total = unpaidBills.value.reduce((sum, bill) => sum + bill.amount, 0)
|
||
ElMessageBox.confirm(
|
||
`确定一键支付所有账单 ¥${total.toFixed(2)}吗?`,
|
||
'提示',
|
||
{
|
||
confirmButtonText: '确定支付',
|
||
cancelButtonText: '取消',
|
||
type: 'info'
|
||
}
|
||
).then(() => {
|
||
ElMessage.success('批量支付成功')
|
||
unpaidBills.value = []
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.service-page {
|
||
padding: 20px;
|
||
}
|
||
|
||
.mt-20 {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
/* 服务网格样式 */
|
||
.grid-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 20px;
|
||
}
|
||
|
||
.service-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 20px;
|
||
background: #f5f7fa;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.service-item:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.service-item h3 {
|
||
margin: 15px 0 5px;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.service-item p {
|
||
margin: 0;
|
||
color: #666;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 团购列表样式 */
|
||
.group-buy-list {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 20px;
|
||
}
|
||
|
||
.group-buy-item {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.group-buy-item .el-image {
|
||
width: 100%;
|
||
height: 200px;
|
||
}
|
||
|
||
.item-info {
|
||
padding: 15px;
|
||
}
|
||
|
||
.item-info h4 {
|
||
margin: 0 0 10px 0;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.description {
|
||
color: #666;
|
||
margin: 0 0 10px 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.price-info {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.price .current {
|
||
color: #f56c6c;
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.price .original {
|
||
color: #999;
|
||
text-decoration: line-through;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.participants {
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.progress-info {
|
||
margin: 10px 0;
|
||
}
|
||
|
||
/* 服务记录样式 */
|
||
.service-actions {
|
||
margin-top: 10px;
|
||
}
|
||
|
||
/* 缴费列表样式 */
|
||
.payment-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
|
||
.payment-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 15px;
|
||
background: #f5f7fa;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.bill-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.bill-details h4 {
|
||
margin: 0 0 5px 0;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.bill-details p {
|
||
margin: 0;
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.bill-amount {
|
||
text-align: right;
|
||
}
|
||
|
||
.amount {
|
||
display: block;
|
||
color: #f56c6c;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
margin-bottom: 5px;
|
||
}
|
||
</style> |