OMS_H5/src/views/Detail/index.vue

1105 lines
34 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="order-detail-page">
<!-- 导航栏 -->
<van-nav-bar
title="订单详情"
left-arrow
@click-left="goBack"
/>
<van-loading v-if="detailLoading" class="loading-container" size="24px">
加载中...
</van-loading>
<div v-else-if="currentOrderInfo" class="page-content">
<!-- 项目信息标题 -->
<div class="project-header">
<div class="project-title">
{{ currentOrderInfo.projectName }}REV.{{ currentOrderInfo.versionCode }}
</div>
<div class="project-status">
待审批
</div>
</div>
<!-- Tab标签页 -->
<van-tabs v-model:active="activeTab" sticky>
<!-- 订单信息 -->
<van-tab title="订单信息" name="order">
<div class="tab-content">
<div class="card">
<div class="card-header">
<span>基本信息</span>
</div>
<div class="card-body">
<div class="info-grid">
<div class="info-item">
<span class="label">项目名称</span>
<span class="value">{{ currentOrderInfo.projectName || '' }}</span>
</div>
<div class="info-item">
<span class="label">版本号</span>
<span class="value">{{ currentOrderInfo.versionCode || '' }}</span>
</div>
<div class="info-item">
<span class="label">项目编号</span>
<span class="value">{{ currentOrderInfo.projectCode || '' }}</span>
</div>
<div class="info-item">
<span class="label">最终客户</span>
<span class="value">{{ currentOrderInfo.customerName || '' }}</span>
</div>
<div class="info-item">
<span class="label">BG</span>
<span class="value">{{ currentOrderInfo.bgPropertyDesc || '' }}</span>
</div>
<div class="info-item">
<span class="label">行业</span>
<span class="value">{{ currentOrderInfo.industryTypeDesc || '' }}</span>
</div>
<div class="info-item">
<span class="label">代表处</span>
<span class="value">{{ currentOrderInfo.agentName || '' }}</span>
</div>
<div class="info-item">
<span class="label">进货商接口人</span>
<span class="value">{{ currentOrderInfo.businessPerson || '' }}</span>
</div>
<div class="info-item">
<span class="label">Email</span>
<span class="value">{{ currentOrderInfo.businessEmail || '' }}</span>
</div>
<div class="info-item">
<span class="label">联系方式</span>
<span class="value">{{ currentOrderInfo.businessPhone || '' }}</span>
</div>
<div class="info-item">
<span class="label">合同编号</span>
<span class="value">{{ currentOrderInfo.orderCode || '' }}</span>
</div>
<div class="info-item">
<span class="label">执行单有效截止时间</span>
<span class="value">{{ currentOrderInfo.orderEndTime? formatDate(currentOrderInfo.orderEndTime, 'YYYY-MM-DD') : '' }}</span>
</div>
<div class="info-item">
<span class="label">币种</span>
<span class="value">{{ currentOrderInfo.currencyTypeDesc || '' }}</span>
</div>
<div class="info-item">
<span class="label">总代进货金额</span>
<span class="value">{{ currentOrderInfo.actualPurchaseAmount ? formatAmount(currentOrderInfo.actualPurchaseAmount) : '' }}</span>
</div>
<div class="info-item">
<span class="label">总代出货金额</span>
<span class="value">{{ currentOrderInfo.shipmentAmount ? formatAmount(currentOrderInfo.shipmentAmount) : '' }}</span>
</div>
<div class="info-item">
<span class="label">要求到货时间</span>
<span class="value">{{ currentOrderInfo.deliveryTime ? formatDate(currentOrderInfo.deliveryTime) : '' }}</span>
</div>
<div class="info-item">
<span class="label">公司直发</span>
<span class="value">{{ currentOrderInfo.companyDeliveryDesc}}</span>
</div>
<div class="info-item">
<span class="label">下单通路</span>
<span class="value">{{ currentOrderInfo.orderChannelDesc}}</span>
</div><div class="info-item">
<span class="label">供货商</span>
<span class="value">{{ currentOrderInfo.supplier}}</span>
</div><div class="info-item">
<span class="label">进货商</span>
<span class="value">{{ currentOrderInfo.partnerName}}</span>
</div><div class="info-item">
<span class="label">进货商类型</span>
<span class="value">{{ currentOrderInfo.levelDesc}}</span>
</div><div class="info-item">
<span class="label">进货商联系人</span>
<span class="value">{{ currentOrderInfo.partnerUserName}}</span>
</div><div class="info-item">
<span class="label">Email</span>
<span class="value">{{ currentOrderInfo.partnerEmail}}</span>
</div><div class="info-item">
<span class="label">联系方式</span>
<span class="value">{{ currentOrderInfo.partnerPhone}}</span>
</div><div class="info-item">
<span class="label">收货地址</span>
<span class="value">{{ currentOrderInfo.notifierAddress}}</span>
</div><div class="info-item">
<span class="label">收货人</span>
<span class="value">{{ currentOrderInfo.notifier}}</span>
</div><div class="info-item">
<span class="label">Email</span>
<span class="value">{{ currentOrderInfo.notifierEmail}}</span>
</div><div class="info-item">
<span class="label">联系方式</span>
<span class="value">{{ currentOrderInfo.notifierPhone}}</span>
</div><div class="info-item">
<span class="label">其他特别说明</span>
<span class="value">{{ currentOrderInfo.remark}}</span>
</div>
</div>
</div>
</div>
</div>
</van-tab>
<!-- 产品信息 -->
<van-tab title="产品信息" name="product">
<div class="tab-content">
<div v-if="hasProductInfo">
<!-- 软件产品 -->
<div v-if="currentOrderInfo.softwareProjectProductInfoList?.length">
<div v-for="(product, index) in currentOrderInfo.softwareProjectProductInfoList" :key="product.id" class="product-card">
<div class="product-header">
<span class="product-index">{{ index + 1 }}</span>
<div class="product-main-info">
<div class="product-code-price">
<span class="product-code">{{ product.productBomCode }}</span>
<span class="product-total-price">{{ formatAmount(product.allPrice) }}</span>
</div>
</div>
</div>
<div class="product-details">
<div class="product-row">
<span class="product-label">产品型号</span>
<span class="product-value">{{ product.model }}</span>
</div>
<div class="product-row">
<span class="product-label">描述</span>
<span class="product-value">{{ product.productDesc }}</span>
</div>
<div class="product-row">
<span class="product-label">数量</span>
<span class="product-value">{{ product.quantity }}</span>
</div>
<div class="product-row">
<span class="product-label">单价</span>
<span class="product-value">{{ formatAmount(product.price) }}</span>
</div>
</div>
</div>
</div>
<!-- 硬件产品 -->
<div v-if="currentOrderInfo.hardwareProjectProductInfoList?.length">
<div v-for="(product, index) in currentOrderInfo.hardwareProjectProductInfoList" :key="product.id" class="product-card">
<div class="product-header">
<span class="product-index">{{ index + 1 + (currentOrderInfo.softwareProjectProductInfoList?.length || 0) }}</span>
<div class="product-main-info">
<div class="product-code-price">
<span class="product-code">{{ product.productBomCode }}</span>
<span class="product-total-price">{{ formatAmount(product.allPrice) }}</span>
</div>
</div>
</div>
<div class="product-details">
<div class="product-row">
<span class="product-label">产品型号</span>
<span class="product-value">{{ product.model }}</span>
</div>
<div class="product-row">
<span class="product-label">描述</span>
<span class="product-value">{{ product.productDesc }}</span>
</div>
<div class="product-row">
<span class="product-label">数量</span>
<span class="product-value">{{ product.quantity }}</span>
</div>
<div class="product-row">
<span class="product-label">单价</span>
<span class="product-value">{{ formatAmount(product.price) }}</span>
</div>
</div>
</div>
</div>
<!-- 维保产品 -->
<div v-if="currentOrderInfo.maintenanceProjectProductInfoList?.length">
<div v-for="(product, index) in currentOrderInfo.maintenanceProjectProductInfoList" :key="product.id" class="product-card">
<div class="product-header">
<span class="product-index">{{ index + 1 + (currentOrderInfo.softwareProjectProductInfoList?.length || 0) + (currentOrderInfo.hardwareProjectProductInfoList?.length || 0) }}</span>
<div class="product-main-info">
<div class="product-code-price">
<span class="product-code">{{ product.productBomCode }}</span>
<span class="product-total-price">{{ formatAmount(product.allPrice) }}</span>
</div>
</div>
</div>
<div class="product-details">
<div class="product-row">
<span class="product-label">产品型号</span>
<span class="product-value">{{ product.model }}</span>
</div>
<div class="product-row">
<span class="product-label">描述</span>
<span class="product-value">{{ product.productDesc }}</span>
</div>
<div class="product-row">
<span class="product-label">数量</span>
<span class="product-value">{{ product.quantity }}</span>
</div>
<div class="product-row">
<span class="product-label">单价</span>
<span class="product-value">{{ formatAmount(product.price) }}</span>
</div>
</div>
</div>
</div>
<!-- 配置组总计 -->
<div class="product-summary">
<div class="summary-row">
<span class="summary-label">配置组总计</span>
<span class="summary-value">{{ getTotalProductCount() }}</span>
</div>
<div class="summary-row">
<span class="summary-label">总价</span>
<span class="summary-value total-amount">{{ formatAmount(getTotalAmount()) }}</span>
</div>
<div class="summary-row">
<span class="summary-label">现金折扣率</span>
<span class="summary-value">{{ getDiscountRate() }}</span>
</div>
<div class="summary-row final-total">
<span class="summary-label">折后总价</span>
<span class="summary-value final-amount">{{ formatAmount(getFinalTotalAmount()) }}</span>
</div>
</div>
</div>
<!-- 无产品信息时的提示 -->
<div v-else class="empty-state">
<van-empty description="暂无产品信息"/>
</div>
</div>
</van-tab>
<!-- 合同附件 -->
<van-tab title="合同附件" name="attachment">
<div class="tab-content">
<div class="card" v-if="currentOrderInfo.contractFileList?.length">
<div class="card-body">
<div class="file-list">
<div v-for="file in currentOrderInfo.contractFileList" :key="file.id" class="file-item"
@click="previewFile(file)">
<van-icon name="description"/>
<div class="file-info">
<div class="file-name">{{ file.fileName }}</div>
<div class="file-meta">{{ file.uploadUserName }} · {{ formatDate(file.uploadTime) }}</div>
</div>
<van-icon name="arrow"/>
</div>
</div>
</div>
</div>
<!-- 无附件时的提示 -->
<div v-else class="empty-state">
<van-empty description="暂无合同附件"/>
</div>
</div>
</van-tab>
<!-- 审批历史 -->
<van-tab title="审批历史" name="approval">
<div class="tab-content">
<div class="card" v-if="approvalHistory.length">
<div class="card-body">
<van-steps direction="vertical" :active="approvalHistory.length">
<van-step v-for="(record, index) in approvalHistory" :key="record.todoId || index">
<template #inactive-icon>
<van-icon
:name="getStepIcon(record.approveStatus)"
:color="getApprovalStatusColor(record.approveStatus)"
/>
</template>
<template #active-icon>
<van-icon
:name="getStepIcon(record.approveStatus)"
:color="getApprovalStatusColor(record.approveStatus)"
/>
</template>
<div class="approval-item">
<div class="approval-user">提交人:{{ record.approveUserName }}</div>
<div v-if="record.nextAllApproveUserName" class="approval-next-user">
接受人:{{ record.nextAllApproveUserName }}
</div>
<div class="approval-time">{{ formatDate(record.approveTime, 'YYYY-MM-DD HH:mm') }}</div>
<div v-if="record.approveOpinion" class="approval-opinion">
审批意见:{{ record.approveOpinion }}
</div>
</div>
</van-step>
</van-steps>
</div>
</div>
<!-- 无审批历史时的提示 -->
<div v-else class="empty-state">
<van-empty description="暂无审批历史"/>
</div>
</div>
</van-tab>
</van-tabs>
</div>
<!-- 审批操作按钮 -->
<div v-if="showApprovalButtons" class="approval-actions">
<van-button
type="default"
size="large"
@click="showApprovalDialog(0)"
:loading="submitting"
>
驳回
</van-button>
<van-button
type="primary"
size="large"
@click="showApprovalDialog(1)"
:loading="submitting"
>
通过
</van-button>
</div>
<!-- 审批意见弹窗 -->
<van-popup
v-model:show="approvalDialogVisible"
position="bottom"
round
:style="{ height: '50%' }"
>
<div class="approval-dialog">
<div class="dialog-header">
<span>审批意见</span>
<van-icon name="cross" @click="approvalDialogVisible = false"/>
</div>
<div class="dialog-body">
<!-- 默认意见标签 -->
<div class="opinion-tags">
<div class="tags-title">常用意见</div>
<div class="tags-container">
<van-tag
v-for="tag in getOpinionTags()"
:key="tag"
:type="selectedTag === tag ? 'primary' : 'default'"
size="medium"
@click="selectTag(tag)"
class="opinion-tag"
>
{{ tag }}
</van-tag>
</div>
</div>
<!-- 意见输入框 -->
<div class="opinion-input">
<van-field
v-model="approvalOpinion"
type="textarea"
:placeholder="currentApprovalStatus === 0 ? '请输入驳回原因' : '请输入审批意见'"
rows="4"
autosize
maxlength="500"
show-word-limit
/>
</div>
</div>
<div class="dialog-footer">
<van-button
type="primary"
block
@click="submitApproval"
:loading="submitting"
>
确认{{ currentApprovalStatus === 0 ? '驳回' : '通过' }}
</van-button>
</div>
</div>
</van-popup>
</div>
</template>
<script setup lang="ts">
import {ref, computed, onMounted} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {storeToRefs} from 'pinia'
import {showToast, showSuccessToast} from 'vant'
import {useOrderStore} from '@/store/order'
import {submitApproval as submitApprovalApi} from '@/api/order'
import {
formatOrderStatus,
formatAmount,
formatDate,
formatApprovalStatus,
getApprovalStatusColor,
getFilePreviewUrl
} from '@/utils'
import type {OrderStatus, ApprovalStatus, AttachmentFile} from '@/types'
const route = useRoute()
const router = useRouter()
const orderStore = useOrderStore()
const {currentOrder, currentOrderInfo, approvalHistory, detailLoading} = storeToRefs(orderStore)
//
const approvalDialogVisible = ref(false)
const approvalOpinion = ref('')
const currentApprovalStatus = ref<ApprovalStatus>(3)
const submitting = ref(false)
const selectedTag = ref('')
// Tab页签
const activeTab = ref('order')
// 计算属性
const hasProductInfo = computed(() => {
if (!currentOrderInfo.value) return false
return (
currentOrderInfo.value.softwareProjectProductInfoList?.length ||
currentOrderInfo.value.hardwareProjectProductInfoList?.length ||
currentOrderInfo.value.maintenanceProjectProductInfoList?.length
)
})
const showApprovalButtons = computed(() => {
// 直接展示审批按钮,无需条件控制
return true
})
// 获取状态样式类
const getStatusClass = (status: OrderStatus) => {
const classMap = {
'0': 'pending',
'1': 'approved',
'2': 'rejected'
}
return classMap[status] || 'pending'
}
// 获取步骤图标
const getStepIcon = (status?: ApprovalStatus) => {
if (status === undefined || status === null) return 'clock'
const iconMap = {
1: 'clock',
2: 'close',
3: 'success'
}
return iconMap[status] || 'clock'
}
// 计算产品总数量
const getTotalProductCount = () => {
if (!currentOrderInfo.value) return 0
const softwareCount = currentOrderInfo.value.softwareProjectProductInfoList?.length || 0
const hardwareCount = currentOrderInfo.value.hardwareProjectProductInfoList?.length || 0
const maintenanceCount = currentOrderInfo.value.maintenanceProjectProductInfoList?.length || 0
return softwareCount + hardwareCount + maintenanceCount
}
// 计算总金额
const getTotalAmount = () => {
if (!currentOrderInfo.value) return 0
let total = 0
// 软件产品总金额
if (currentOrderInfo.value.softwareProjectProductInfoList) {
total += currentOrderInfo.value.softwareProjectProductInfoList.reduce((sum, product) => sum + (product.allPrice || 0), 0)
}
// 硬件产品总金额
if (currentOrderInfo.value.hardwareProjectProductInfoList) {
total += currentOrderInfo.value.hardwareProjectProductInfoList.reduce((sum, product) => sum + (product.allPrice || 0), 0)
}
// 维保产品总金额
if (currentOrderInfo.value.maintenanceProjectProductInfoList) {
total += currentOrderInfo.value.maintenanceProjectProductInfoList.reduce((sum, product) => sum + (product.allPrice || 0), 0)
}
return total
}
// 获取现金折扣率
const getDiscountRate = () => {
if (!currentOrderInfo.value || !currentOrderInfo.value.discountFold) {
return '100%'
}
return (currentOrderInfo.value.discountFold * 100).toFixed(1) + '%'
}
// 计算折后总价
const getFinalTotalAmount = () => {
const totalAmount = getTotalAmount()
const discount = currentOrderInfo.value?.discountFold || 1
return totalAmount * discount
}
// 返回上一页
const goBack = () => {
router.back()
}
// 预览文件
const previewFile = (file: AttachmentFile) => {
if (!file.filePath) {
showToast('文件路径不存在')
return
}
const url = getFilePreviewUrl(file.filePath)
window.open(url, '_blank')
}
// 显示审批弹窗
const showApprovalDialog = (status: ApprovalStatus) => {
currentApprovalStatus.value = status
approvalOpinion.value = ''
selectedTag.value = ''
approvalDialogVisible.value = true
}
// 获取默认意见标签
const getOpinionTags = () => {
if (currentApprovalStatus.value === 0) {
// 驳回常用意见
return [
'资料不齐全,请补充相关文件',
'订单金额需要重新核实',
'客户信息有误,请修正',
'产品配置不符合要求',
'需要提供更多技术细节',
'合同条款需要调整'
]
} else {
// 通过常用意见
return [
'审核通过,订单信息完整',
'符合公司政策,同意执行',
'客户资质良好,建议通过',
'产品配置合理,批准发货',
'风险可控,同意此订单',
'经审查无误,予以批准'
]
}
}
// 选择标签
const selectTag = (tag: string) => {
if (selectedTag.value === tag) {
// 如果点击的是已选中的标签,则取消选择并清空输入框
selectedTag.value = ''
approvalOpinion.value = ''
} else {
// 选择新标签并填入输入框
selectedTag.value = tag
approvalOpinion.value = tag
}
}
// 提交审批
const submitApproval = async () => {
if (!currentOrder.value || !currentOrder.value.todo) {
showToast('审批信息不完整')
return
}
const opinion = approvalOpinion.value.trim()
if (currentApprovalStatus.value === 0 && !opinion) {
showToast('请输入驳回原因')
return
}
submitting.value = true
try {
const params = {
...currentOrder.value.todo, // 展开todo中的所有参数
approveOpinion: opinion || undefined , // 添加审批意见
variables:{
approveBtn:currentApprovalStatus.value,
comment:opinion
}
}
console.log('提交审批参数:', params)
await submitApprovalApi(params)
showSuccessToast(currentApprovalStatus.value === 0 ? '驳回成功' : '审批通过')
approvalDialogVisible.value = false
// 如果是审批通过,跳转到列表页面
if (currentApprovalStatus.value !== 0) {
// 跳转到列表页面
router.push('/list')
} else {
// 驳回情况下重新加载详情
await orderStore.fetchOrderDetail(route.params.id as string)
}
} catch (error) {
console.error('提交审批失败:', error)
showToast('提交审批失败')
} finally {
submitting.value = false
}
}
onMounted(async () => {
const id = route.params.id as string
if (id) {
console.log('详情页面加载订单ID:', id)
try {
const result = await orderStore.fetchOrderDetail(id)
console.log('获取订单详情结果:', result)
console.log('当前订单信息:', currentOrder.value)
console.log('当前订单基本信息:', currentOrderInfo.value)
} catch (error) {
console.error('获取订单详情失败:', error)
showToast('获取订单详情失败')
}
}
})
</script>
<style lang="scss" scoped>
.order-detail-page {
min-height: 100vh;
background-color: var(--background-color-secondary);
padding-bottom: 80px; // 为底部按钮留出空间
}
.page-content {
// 移除原来的padding让tabs组件自己控制间距
}
.project-header {
background: var(--background-color-primary);
padding: var(--spacing-lg);
border-bottom: 1px solid var(--divider-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.project-title {
font-size: 18px;
font-weight: 600;
color: var(--text-color-primary);
flex: 1;
word-break: break-all;
}
.project-status {
background: #FFF7E6;
color: #D48806;
padding: 4px 12px;
border-radius: var(--border-radius-sm);
font-size: 12px;
font-weight: 500;
border: 1px solid #FFD591;
}
.tab-content {
padding: var(--spacing-lg);
}
.loading-container {
padding: 60px var(--spacing-lg);
text-align: center;
}
.empty-state {
padding: 60px var(--spacing-lg);
}
.info-grid {
display: grid;
gap: var(--spacing-md);
}
.info-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
.label {
color: var(--text-color-secondary);
font-size: 14px;
min-width: 100px;
flex-shrink: 0;
}
.value {
color: var(--text-color-primary);
font-size: 14px;
text-align: right;
word-break: break-all;
&.amount {
color: var(--error-color);
font-weight: 500;
}
}
}
.product-section {
&:not(:last-child) {
margin-bottom: var(--spacing-lg);
padding-bottom: var(--spacing-lg);
border-bottom: 1px solid var(--divider-color);
}
}
.section-title {
font-size: 16px;
font-weight: 500;
color: var(--text-color-primary);
margin-bottom: var(--spacing-md);
}
.product-item {
background: var(--background-color-tertiary);
padding: var(--spacing-md);
border-radius: var(--border-radius-sm);
margin-bottom: var(--spacing-md);
&:last-child {
margin-bottom: 0;
}
.product-name {
font-size: 14px;
font-weight: 500;
color: var(--text-color-primary);
margin-bottom: var(--spacing-xs);
}
.product-desc {
font-size: 12px;
color: var(--text-color-secondary);
margin-bottom: var(--spacing-sm);
line-height: 1.4;
}
.product-info {
display: flex;
gap: var(--spacing-md);
font-size: 12px;
color: var(--text-color-tertiary);
}
}
.file-list {
.file-item {
display: flex;
align-items: center;
padding: var(--spacing-md);
background: var(--background-color-tertiary);
border-radius: var(--border-radius-sm);
margin-bottom: var(--spacing-sm);
cursor: pointer;
&:last-child {
margin-bottom: 0;
}
&:active {
background: var(--border-color);
}
.van-icon:first-child {
color: var(--primary-color);
margin-right: var(--spacing-md);
}
.file-info {
flex: 1;
.file-name {
font-size: 14px;
color: var(--text-color-primary);
margin-bottom: var(--spacing-xs);
}
.file-meta {
font-size: 12px;
color: var(--text-color-tertiary);
}
}
.van-icon:last-child {
color: var(--text-color-tertiary);
}
}
}
.approval-item {
.approval-user,
.approval-time,
.approval-next-user {
font-size: 12px;
color: var(--text-color-secondary);
margin-bottom: var(--spacing-xs);
}
.approval-next-user {
color: var(--primary-color);
font-weight: 500;
}
.approval-opinion {
font-size: 12px;
color: var(--text-color-primary);
background: var(--background-color-tertiary);
padding: var(--spacing-sm);
border-radius: var(--border-radius-sm);
line-height: 1.4;
}
}
.approval-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: var(--spacing-lg);
background: var(--background-color-primary);
border-top: 1px solid var(--divider-color);
display: flex;
gap: var(--spacing-lg);
.van-button {
flex: 1;
}
}
.approval-dialog {
height: 100%;
display: flex;
flex-direction: column;
.dialog-header {
padding: var(--spacing-lg);
border-bottom: 1px solid var(--divider-color);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
font-weight: 500;
.van-icon {
cursor: pointer;
}
}
.dialog-body {
flex: 1;
padding: var(--spacing-lg);
overflow-y: auto;
}
.dialog-footer {
padding: var(--spacing-lg);
border-top: 1px solid var(--divider-color);
}
}
// 意见标签样式
.opinion-tags {
margin-bottom: var(--spacing-lg);
.tags-title {
font-size: 14px;
color: var(--text-color-secondary);
margin-bottom: var(--spacing-md);
font-weight: 500;
}
.tags-container {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-sm);
.opinion-tag {
cursor: pointer;
transition: all 0.2s ease;
&:hover {
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
}
}
}
.opinion-input {
.van-field {
border: 1px solid var(--divider-color);
border-radius: var(--border-radius-sm);
background: var(--background-color-secondary);
}
}
:deep(.van-steps--vertical) {
padding-left: 0;
}
:deep(.van-step__content) {
padding-bottom: var(--spacing-lg);
}
// 产品相关样式
.product-card {
background: var(--background-color-primary);
border-radius: var(--border-radius-md);
margin-bottom: var(--spacing-md);
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
&:last-child {
margin-bottom: 0;
}
}
.product-header {
background: var(--background-color-secondary);
padding: var(--spacing-md);
display: flex;
align-items: center;
gap: var(--spacing-md);
}
.product-index {
background: var(--primary-color);
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 500;
flex-shrink: 0;
}
.product-main-info {
flex: 1;
}
.product-code-price {
display: flex;
justify-content: space-between;
align-items: center;
.product-code {
font-size: 16px;
font-weight: 600;
color: var(--text-color-primary);
}
.product-total-price {
font-size: 18px;
font-weight: 700;
}
}
.product-details {
padding: var(--spacing-md);
}
.product-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: var(--spacing-sm);
&:last-child {
margin-bottom: 0;
}
}
.product-label {
color: var(--text-color-secondary);
font-size: 14px;
min-width: 80px;
flex-shrink: 0;
}
.product-value {
color: var(--text-color-primary);
font-size: 14px;
text-align: right;
flex: 1;
word-break: break-all;
}
// 产品总计样式
.product-summary {
background: var(--background-color-primary);
border-radius: var(--border-radius-md);
padding: var(--spacing-lg);
margin-top: var(--spacing-lg);
border: 2px solid var(--primary-color);
}
.summary-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-md);
&:last-child {
margin-bottom: 0;
}
}
.summary-label {
font-size: 16px;
font-weight: 500;
color: var(--text-color-primary);
}
.summary-value {
font-size: 16px;
font-weight: 600;
color: var(--text-color-primary);
&.total-amount {
font-size: 18px;
font-weight: 700;
}
&.final-amount {
font-size: 20px;
font-weight: 700;
color: var(--primary-color);
}
}
.final-total {
border-top: 2px solid var(--divider-color);
padding-top: var(--spacing-md);
margin-top: var(--spacing-md);
.summary-label {
font-size: 18px;
font-weight: 600;
color: var(--primary-color);
}
}
</style>