Files
ApiServer-Web-admin_dashboa…/src/views/order/OrderList.vue
T
lin 36271b8bd0
Build and Deploy Vue3 / build (push) Successful in 6m17s
Build and Deploy Vue3 / deploy (push) Successful in 1m25s
fix:将填写弹窗修改为选择弹窗
2026-01-19 17:02:26 +08:00

887 lines
27 KiB
Vue

<template>
<div class="order-list-container">
<!-- 主容器 -->
<el-card class="main-container" shadow="never">
<!-- 搜索和操作栏 -->
<div class="filter-section">
<div class="filter-content">
<el-form :inline="true" :model="queryParams" class="filter-form">
<el-form-item label="关键词">
<el-input v-model="queryParams.key" placeholder="订单名称/ID" clearable style="width: 150px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户ID">
<el-input v-model="queryParams.user_id" placeholder="用户ID" clearable style="width: 120px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户关键词">
<el-input v-model="queryParams.user_key" placeholder="用户名/手机号/邮箱" clearable style="width: 180px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="queryParams.state" placeholder="全部" clearable style="width: 120px">
<el-option label="待支付" value="0" />
<el-option label="已支付" value="1" />
<el-option label="已失效" value="2" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<el-icon><Search /></el-icon>搜索
</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<div class="action-bar">
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>新增订单
</el-button>
<el-button type="success" @click="fetchOrderList">
<el-icon><Refresh /></el-icon>刷新
</el-button>
</div>
</div>
</div>
<!-- 订单列表 -->
<div class="table-section">
<!-- 骨架屏 -->
<div v-if="loading" class="skeleton-container">
<div v-for="i in 5" :key="i" class="skeleton-row">
<div class="skeleton-cell skeleton-checkbox"></div>
<div class="skeleton-cell skeleton-id"></div>
<div class="skeleton-cell skeleton-name"></div>
<div class="skeleton-cell skeleton-user"></div>
<div class="skeleton-cell skeleton-price"></div>
<div class="skeleton-cell skeleton-status"></div>
<div class="skeleton-cell skeleton-time"></div>
<div class="skeleton-cell skeleton-action"></div>
</div>
</div>
<el-table
v-else
v-loading="loading"
:data="orderList"
@selection-change="handleSelectionChange"
style="width: 100%"
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="订单ID" width="100" />
<el-table-column prop="name" label="订单名称" min-width="180" />
<el-table-column prop="userId" label="用户ID" width="100" />
<el-table-column prop="commodityId" label="商品ID" width="100" />
<el-table-column label="表名" width="120">
<template #default="{ row }">
<el-tag size="small">{{ row.table || '未知' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="订单金额" width="120">
<template #default="{ row }">
<span class="amount">¥{{ (row.price / 100).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="续费价格" width="120">
<template #default="{ row }">
<span class="renew-price">¥{{ (row.renewPrice / 100).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="数量" width="80">
<template #default="{ row }">
<span>{{ row.payNum }}</span>
</template>
</el-table-column>
<el-table-column label="订单状态" width="100">
<template #default="{ row }">
<el-tag :type="getStatusType(row.state)">
{{ getStatusText(row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="支付方式" width="100">
<template #default="{ row }">
<span>{{ row.payType || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="过期时间" width="170">
<template #default="{ row }">
<span>{{ formatDate(row.expireTime) }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" width="170">
<template #default="{ row }">
<span>{{ formatDate(row.CreatedAt) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<div class="action-buttons">
<el-button type="primary" link @click="handleView(row)">查看</el-button>
<el-button type="warning" link @click="handleEdit(row)">编辑</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="queryParams.page"
v-model:page-size="queryParams.count"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
background
class="pagination"
/>
</div>
</el-card>
<!-- 订单详情对话框 -->
<el-dialog
v-model="detailDialogVisible"
title="订单详情"
width="800px"
append-to-body
>
<el-descriptions :column="2" border v-if="orderDetail">
<el-descriptions-item label="订单ID">{{ orderDetail.id }}</el-descriptions-item>
<el-descriptions-item label="订单名称">{{ orderDetail.name }}</el-descriptions-item>
<el-descriptions-item label="用户ID">{{ orderDetail.userId }}</el-descriptions-item>
<el-descriptions-item label="商品ID">{{ orderDetail.commodityId }}</el-descriptions-item>
<el-descriptions-item label="表名">{{ orderDetail.table }}</el-descriptions-item>
<el-descriptions-item label="数量">{{ orderDetail.payNum }}</el-descriptions-item>
<el-descriptions-item label="订单金额">¥{{ (orderDetail.price / 100).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="续费价格">¥{{ (orderDetail.renewPrice / 100).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="订单状态">
<el-tag :type="getStatusType(orderDetail.state)">
{{ getStatusText(orderDetail.state) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="支付方式">{{ orderDetail.payType || '-' }}</el-descriptions-item>
<el-descriptions-item label="过期时间">{{ formatDate(orderDetail.expireTime) }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ formatDate(orderDetail.CreatedAt) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ formatDate(orderDetail.UpdatedAt) }}</el-descriptions-item>
<el-descriptions-item label="参数信息">{{ orderDetail.args || '-' }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ orderDetail.note || '无' }}</el-descriptions-item>
</el-descriptions>
</el-dialog>
<!-- 订单表单对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增订单' : '编辑订单'"
width="700px"
append-to-body
>
<el-form
ref="orderFormRef"
:model="orderForm"
:rules="orderRules"
label-width="120px"
>
<el-form-item label="订单名称" prop="name">
<el-input v-model="orderForm.name" placeholder="请输入订单名称" />
</el-form-item>
<el-form-item label="所属表" prop="table">
<el-input v-model="orderForm.table" placeholder="请输入所属表" />
</el-form-item>
<el-form-item label="用户ID" prop="user_id">
<div class="selector-field">
<div class="selector-info" v-if="selectedUserInfo">
<el-tag type="primary" effect="plain">
ID: {{ orderForm.user_id }} - {{ selectedUserInfo.user_name }}
</el-tag>
</div>
<div class="selector-actions">
<el-button type="primary" @click="userSelectorVisible = true">
<el-icon><User /></el-icon>
{{ orderForm.user_id ? '更换用户' : '选择用户' }}
</el-button>
<el-button v-if="orderForm.user_id" @click="clearUser">清除</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="商品ID" prop="commodity_id">
<div class="selector-field">
<div class="selector-info" v-if="selectedProductInfo">
<el-tag type="success" effect="plain">
ID: {{ orderForm.commodity_id }} - {{ selectedProductInfo.name }}
</el-tag>
</div>
<div class="selector-actions">
<el-button type="success" @click="productSelectorVisible = true">
<el-icon><ShoppingCart /></el-icon>
{{ orderForm.commodity_id ? '更换商品' : '选择商品' }}
</el-button>
<el-button v-if="orderForm.commodity_id" @click="clearProduct">清除</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="购买数量" prop="pay_num">
<el-input-number v-model="orderForm.pay_num" :min="1" placeholder="请输入数量" style="width: 100%" />
</el-form-item>
<el-form-item label="价格(分)" prop="price">
<el-input-number v-model="orderForm.price" :min="0" placeholder="请输入价格(分)" style="width: 100%" />
</el-form-item>
<el-form-item label="续费价格(分)" prop="renew_price">
<el-input-number v-model="orderForm.renew_price" :min="0" placeholder="请输入续费价格(分)" style="width: 100%" />
</el-form-item>
<el-form-item label="过期时间" prop="expire_time">
<el-input-number v-model="orderForm.expire_time" :min="0" placeholder="请输入过期时间(时间戳)" style="width: 100%" />
</el-form-item>
<el-form-item label="优惠码ID" prop="discount_code_id">
<div class="selector-field">
<div class="selector-info" v-if="selectedDiscountCodeInfo">
<el-tag type="warning" effect="plain">
ID: {{ orderForm.discount_code_id }} - {{ selectedDiscountCodeInfo.name || selectedDiscountCodeInfo.code }}
</el-tag>
</div>
<div class="selector-actions">
<el-button type="warning" @click="discountCodeSelectorVisible = true">
<el-icon><Ticket /></el-icon>
{{ orderForm.discount_code_id ? '更换优惠码' : '选择优惠码' }}
</el-button>
<el-button v-if="orderForm.discount_code_id" @click="clearDiscountCode">清除</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="代金券ID" prop="coupon_id">
<div class="selector-field">
<div class="selector-info" v-if="selectedVoucherInfo">
<el-tag type="danger" effect="plain">
ID: {{ orderForm.coupon_id }} - {{ selectedVoucherInfo.name || selectedVoucherInfo.code }}
</el-tag>
</div>
<div class="selector-actions">
<el-button type="danger" @click="voucherSelectorVisible = true">
<el-icon><Money /></el-icon>
{{ orderForm.coupon_id ? '更换代金券' : '选择代金券' }}
</el-button>
<el-button v-if="orderForm.coupon_id" @click="clearVoucher">清除</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="订单状态" prop="state">
<el-radio-group v-model="orderForm.state">
<el-radio :label="0">待支付</el-radio>
<el-radio :label="1">已支付</el-radio>
<el-radio :label="2">已失效</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="支付方式" prop="pay_type">
<el-input v-model="orderForm.pay_type" placeholder="请输入支付类型" />
</el-form-item>
<el-form-item label="订单参数" prop="args">
<el-input v-model="orderForm.args" placeholder="请输入订单参数" />
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="orderForm.note" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 用户选择器 -->
<UserListSelector
v-model="userSelectorVisible"
:current-user-id="orderForm.user_id"
@confirm="handleUserSelect"
/>
<!-- 商品选择器 -->
<ProductSelector
v-model="productSelectorVisible"
:current-product-id="orderForm.commodity_id"
@confirm="handleProductSelect"
/>
<!-- 优惠码选择器 -->
<DiscountCodeSelector
v-model="discountCodeSelectorVisible"
:current-code-id="orderForm.discount_code_id"
@confirm="handleDiscountCodeSelect"
/>
<!-- 代金券选择器 -->
<VoucherSelector
v-model="voucherSelectorVisible"
:current-voucher-id="orderForm.coupon_id"
@confirm="handleVoucherSelect"
/>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Delete, Search, Download, Refresh, User, ShoppingCart, Ticket, Money } from '@element-plus/icons-vue'
import { getOrderList, getOrderDetail, createOrder, updateOrder, deleteOrder } from '@/api/admin/order'
import UserListSelector from '@/components/admin/UserListSelector.vue'
import ProductSelector from '@/components/admin/ProductSelector.vue'
import DiscountCodeSelector from '@/components/admin/DiscountCodeSelector.vue'
import VoucherSelector from '@/components/admin/VoucherSelector.vue'
// 查询参数
const queryParams = reactive({
page: 1,
count: 10,
key: '',
state: '',
user_id: '',
user_key: ''
})
// 订单表单
const orderForm = reactive({
order_id: undefined,
name: '',
table: '',
user_id: undefined,
commodity_id: 0,
pay_num: 1,
price: 0,
renew_price: 0,
expire_time: 0,
discount_code_id: 0,
coupon_id: 0,
state: 0,
pay_type: '',
args: '',
note: ''
})
const orderRules = {
name: [
{ required: true, message: '请输入订单名称', trigger: 'blur' }
],
table: [
{ required: true, message: '请输入所属表', trigger: 'blur' }
],
user_id: [
{ required: true, message: '请输入用户ID', trigger: 'blur' },
{ type: 'number', message: '用户ID必须是数字', trigger: 'blur' }
],
coupon_id: [
{ required: true, message: '请输入代金券ID', trigger: 'blur' },
{ type: 'number', message: '代金券ID必须是数字', trigger: 'blur' }
],
pay_num: [
{ required: true, message: '请输入购买数量', trigger: 'blur' }
],
price: [
{ required: true, message: '请输入价格', trigger: 'blur' }
]
}
// 状态数据
const loading = ref(false)
const orderList = ref([])
const orderDetail = ref(null)
const total = ref(0)
const selectedRows = ref([])
const dialogVisible = ref(false)
const detailDialogVisible = ref(false)
const dialogType = ref('add')
const orderFormRef = ref(null)
// 选择器弹窗状态
const userSelectorVisible = ref(false)
const productSelectorVisible = ref(false)
const discountCodeSelectorVisible = ref(false)
const voucherSelectorVisible = ref(false)
// 选择的显示信息
const selectedUserInfo = ref(null)
const selectedProductInfo = ref(null)
const selectedDiscountCodeInfo = ref(null)
const selectedVoucherInfo = ref(null)
// 获取订单列表
const fetchOrderList = async () => {
loading.value = true
try {
// 过滤空值参数
const params = {}
Object.keys(queryParams).forEach(key => {
if (queryParams[key] !== '' && queryParams[key] !== null && queryParams[key] !== undefined) {
params[key] = queryParams[key]
}
})
const res = await getOrderList(params)
console.log('订单列表数据:', res.data)
if (res.data.code === 200) {
orderList.value = res.data.data.list || []
total.value = res.data.data.all_count || 0
}
} catch (error) {
console.error('获取订单列表失败:', error)
ElMessage.error('获取订单列表失败')
} finally {
loading.value = false
}
}
// 格式化日期
const formatDate = (dateStr) => {
if (!dateStr) return '-'
const date = new Date(dateStr)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
}
// 获取订单状态类型
const getStatusType = (status) => {
const statusMap = {
0: 'warning', // 待支付
1: 'success', // 已支付
2: 'info', // 已失效
}
return statusMap[status] || 'info'
}
// 获取订单状态文本
// state 0:未支付 1:已支付 2:已失效
const getStatusText = (status) => {
const statusMap = {
0: '待支付',
1: '已支付',
2: '已失效'
}
return statusMap[status] || '未知'
}
// 查询
const handleQuery = () => {
queryParams.page = 1
fetchOrderList()
}
// 重置查询
const resetQuery = () => {
queryParams.key = ''
queryParams.state = ''
queryParams.user_id = ''
queryParams.user_key = ''
queryParams.page = 1
fetchOrderList()
}
// 选择项变化
const handleSelectionChange = (selection) => {
selectedRows.value = selection
}
// 分页
const handleSizeChange = (size) => {
queryParams.count = size
fetchOrderList()
}
const handleCurrentChange = (page) => {
queryParams.page = page
fetchOrderList()
}
// 新增订单
const handleAdd = () => {
dialogType.value = 'add'
dialogVisible.value = true
clearAllSelections()
Object.assign(orderForm, {
order_id: undefined,
name: '',
table: '',
user_id: undefined,
commodity_id: 0,
pay_num: 1,
price: 0,
renew_price: 0,
expire_time: 0,
discount_code_id: 0,
coupon_id: 0,
state: 0,
pay_type: '',
args: '',
note: ''
})
orderFormRef.value?.resetFields()
}
// 查看订单详情
const handleView = async (row) => {
try {
const res = await getOrderDetail({ order_id: row.id })
if (res.data.code === 200) {
orderDetail.value = res.data.data
detailDialogVisible.value = true
}
} catch (error) {
console.error('获取订单详情失败:', error)
ElMessage.error('获取订单详情失败')
}
}
// 编辑订单
const handleEdit = (row) => {
dialogType.value = 'edit'
dialogVisible.value = true
clearAllSelections()
Object.assign(orderForm, {
order_id: row.id,
name: row.name,
table: row.table,
user_id: row.userId,
commodity_id: row.commodityId,
pay_num: row.payNum,
price: row.price,
renew_price: row.renewPrice,
expire_time: row.expireTime ? new Date(row.expireTime).getTime() / 1000 : 0,
discount_code_id: 0, // 从详情接口获取
coupon_id: 0, // 从详情接口获取
state: row.state,
pay_type: row.payType || '',
args: row.args || '',
note: row.note || ''
})
// 设置显示信息(只显示ID,名称需要从选择器中获取)
if (row.userId) {
selectedUserInfo.value = { user_id: row.userId, user_name: `用户${row.userId}` }
}
if (row.commodityId) {
selectedProductInfo.value = { id: row.commodityId, name: `商品${row.commodityId}` }
}
}
// 删除订单
const handleDelete = (row) => {
ElMessageBox.confirm(`确认删除订单 ${row.name} 吗?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const res = await deleteOrder({ id: row.id })
if (res.data.code === 200) {
ElMessage.success('删除成功')
fetchOrderList()
}
} catch (error) {
console.error('删除失败:', error)
ElMessage.error(error.response?.data?.message || '删除失败')
}
}).catch(() => {})
}
// 批量删除
const handleBatchDelete = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请至少选择一条记录')
return
}
ElMessageBox.confirm(`确认删除选中的 ${selectedRows.value.length} 条记录吗?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
ElMessage.success('批量删除成功')
fetchOrderList()
}).catch(() => {})
}
// 提交表单
const submitForm = () => {
orderFormRef.value?.validate(async (valid) => {
if (valid) {
try {
// 准备提交的数据
const submitData = {
name: orderForm.name,
table: orderForm.table,
user_id: Number(orderForm.user_id),
commodity_id: Number(orderForm.commodity_id),
pay_num: Number(orderForm.pay_num),
price: Number(orderForm.price),
renew_price: Number(orderForm.renew_price),
expire_time: Number(orderForm.expire_time),
discount_code_id: Number(orderForm.discount_code_id),
coupon_id: Number(orderForm.coupon_id),
state: Number(orderForm.state),
pay_type: orderForm.pay_type || '',
args: orderForm.args || '',
note: orderForm.note || ''
}
// 如果是编辑,添加order_id
if (dialogType.value === 'edit') {
submitData.order_id = Number(orderForm.order_id)
}
console.log('提交订单数据:', submitData)
let res
if (dialogType.value === 'add') {
res = await createOrder(submitData)
} else {
res = await updateOrder(submitData)
}
if (res.data.code === 200) {
ElMessage.success(dialogType.value === 'add' ? '新增成功' : '修改成功')
dialogVisible.value = false
fetchOrderList()
}
} catch (error) {
console.error('操作失败:', error)
ElMessage.error(error.response?.data?.message || '操作失败')
}
}
})
}
// 用户选择处理
const handleUserSelect = (user) => {
orderForm.user_id = user.user_id
selectedUserInfo.value = user
}
const clearUser = () => {
orderForm.user_id = undefined
selectedUserInfo.value = null
}
// 商品选择处理
const handleProductSelect = (product) => {
orderForm.commodity_id = product.id
selectedProductInfo.value = product
// 自动填充表名
if (product.table) {
orderForm.table = product.table
}
}
const clearProduct = () => {
orderForm.commodity_id = 0
selectedProductInfo.value = null
}
// 优惠码选择处理
const handleDiscountCodeSelect = (code) => {
orderForm.discount_code_id = code.id
selectedDiscountCodeInfo.value = code
}
const clearDiscountCode = () => {
orderForm.discount_code_id = 0
selectedDiscountCodeInfo.value = null
}
// 代金券选择处理
const handleVoucherSelect = (voucher) => {
orderForm.coupon_id = voucher.id
selectedVoucherInfo.value = voucher
}
const clearVoucher = () => {
orderForm.coupon_id = 0
selectedVoucherInfo.value = null
}
// 清除所有选择信息
const clearAllSelections = () => {
selectedUserInfo.value = null
selectedProductInfo.value = null
selectedDiscountCodeInfo.value = null
selectedVoucherInfo.value = null
}
// 初始化
onMounted(() => {
fetchOrderList()
})
</script>
<style scoped>
.order-list-container {
padding: 0;
}
.main-container {
border: 1px solid #e1e8ed;
background: #ffffff;
}
.filter-section {
padding: 0;
border-bottom: 1px solid #e1e8ed;
background: #fafbfc;
}
.filter-content {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 16px 20px;
gap: 20px;
flex-wrap: wrap;
}
.filter-form {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.filter-form :deep(.el-form-item) {
margin-bottom: 0;
margin-right: 8px;
}
.filter-form :deep(.el-form-item__label) {
font-size: 13px;
}
.action-bar {
display: flex;
gap: 12px;
flex-shrink: 0;
}
.table-section {
padding: 0;
}
.action-buttons {
display: flex;
gap: 8px;
align-items: center;
}
.amount {
color: #f56c6c;
font-weight: bold;
font-size: 14px;
}
.renew-price {
color: #409eff;
font-weight: 500;
font-size: 14px;
}
.pagination {
margin-top: 20px;
padding: 16px 20px;
border-top: 1px solid #e1e8ed;
background: #fafbfc;
justify-content: flex-end;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
padding: 0;
}
/* 表格样式优化 */
:deep(.el-table) {
border: none;
color: #2c3e50;
}
:deep(.el-table__header) {
background: #f8f9fa;
}
:deep(.el-table th) {
background: #f8f9fa !important;
border-bottom: 2px solid #e1e8ed;
color: #2c3e50;
font-weight: 600;
font-size: 13px;
}
:deep(.el-table td) {
border-bottom: 1px solid #f0f2f5;
color: #34495e;
}
:deep(.el-table tr:hover > td) {
background-color: #f8f9fa !important;
}
:deep(.el-card__body) {
padding: 0;
}
/* 骨架屏样式 */
.skeleton-container {
padding: 20px;
}
.skeleton-row {
display: flex;
align-items: center;
padding: 16px 0;
border-bottom: 1px solid #f0f0f0;
gap: 16px;
}
.skeleton-row:last-child {
border-bottom: none;
}
.skeleton-cell {
height: 20px;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s ease-in-out infinite;
border-radius: 4px;
}
.skeleton-checkbox { width: 55px; }
.skeleton-id { width: 100px; }
.skeleton-name { width: 180px; }
.skeleton-user { width: 100px; }
.skeleton-price { width: 120px; }
.skeleton-status { width: 100px; }
.skeleton-time { width: 170px; }
.skeleton-action { width: 200px; height: 32px; }
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* 选择器字段样式 */
.selector-field {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.selector-info {
display: flex;
align-items: center;
}
.selector-actions {
display: flex;
gap: 8px;
align-items: center;
}
</style>