|
|
@@ -0,0 +1,1388 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <!-- 数据统计卡片 -->
|
|
|
+ <div class="statistics-cards">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card" shadow="hover">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon pending">
|
|
|
+ <i class="el-icon-time"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">{{ statistics.pending }}</div>
|
|
|
+ <div class="stat-label">待审核</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card" shadow="hover">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon approved">
|
|
|
+ <i class="el-icon-check"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">{{ statistics.approved }}</div>
|
|
|
+ <div class="stat-label">已通过</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card" shadow="hover">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon rejected">
|
|
|
+ <i class="el-icon-close"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">{{ statistics.rejected }}</div>
|
|
|
+ <div class="stat-label">已拒绝</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card" shadow="hover">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon total">
|
|
|
+ <i class="el-icon-data-line"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">{{ statistics.total }}</div>
|
|
|
+ <div class="stat-label">总申请数</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 搜索表单 -->
|
|
|
+ <el-card class="search-card" shadow="hover">
|
|
|
+ <div class="search-header">
|
|
|
+ <div class="search-title">
|
|
|
+ <i class="el-icon-search"></i>
|
|
|
+ <span>筛选条件</span>
|
|
|
+ </div>
|
|
|
+ <el-button type="text" @click="toggleSearch">
|
|
|
+ {{ showSearch ? '收起' : '展开' }}
|
|
|
+ <i :class="showSearch ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-collapse-transition>
|
|
|
+ <el-form v-show="showSearch" :model="queryParams" ref="queryForm" :inline="true" label-width="80px"
|
|
|
+ class="search-form">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="公司名称" prop="companyName">
|
|
|
+ <el-input v-model="queryParams.companyName" placeholder="请输入公司名称" clearable
|
|
|
+ prefix-icon="el-icon-office-building" class="search-input" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="提交单号" prop="applicationNumber">
|
|
|
+ <el-input v-model="queryParams.applicationNumber" placeholder="请输入提交单号" clearable
|
|
|
+ prefix-icon="el-icon-document" class="search-input" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="申请数量" prop="applyQuantity">
|
|
|
+ <el-input v-model="queryParams.applyQuantity" placeholder="请输入申请数量" clearable
|
|
|
+ prefix-icon="el-icon-s-data" class="search-input" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="审核状态" prop="auditStatus">
|
|
|
+ <el-select v-model="queryParams.auditStatus" placeholder="请选择审核状态" clearable class="search-select">
|
|
|
+ <el-option label="待审核" value="0" />
|
|
|
+ <el-option label="审核通过" value="1" />
|
|
|
+ <el-option label="审核拒绝" value="2" />
|
|
|
+ <el-option label="部分分配" value="3" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="提交时间" prop="submitTime">
|
|
|
+ <el-date-picker clearable v-model="queryParams.submitTime" type="datetime"
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择提交时间" class="search-date">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="审核时间" prop="reviewTime">
|
|
|
+ <el-date-picker clearable v-model="queryParams.reviewTime" type="datetime"
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择审核时间" class="search-date">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="实际分配" prop="allocatedQuantity">
|
|
|
+ <el-input v-model="queryParams.allocatedQuantity" placeholder="请输入实际分配数量" clearable
|
|
|
+ prefix-icon="el-icon-s-marketing" class="search-input" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="el-icon-search" @click="handleQuery" class="search-btn">
|
|
|
+ 搜索
|
|
|
+ </el-button>
|
|
|
+ <el-button icon="el-icon-refresh" @click="resetQuery" class="reset-btn">
|
|
|
+ 重置
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </el-collapse-transition>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 批量操作工具栏 -->
|
|
|
+ <div class="batch-toolbar" v-show="selectedRows.length > 0">
|
|
|
+ <el-card class="batch-card" shadow="hover">
|
|
|
+ <div class="batch-content">
|
|
|
+ <div class="batch-info">
|
|
|
+ <i class="el-icon-warning-outline"></i>
|
|
|
+ <span>已选择 {{ selectedRows.length }} 项</span>
|
|
|
+ </div>
|
|
|
+ <div class="batch-actions">
|
|
|
+ <el-button size="small" @click="batchApprove" v-if="canBatchApprove" type="success">
|
|
|
+ <i class="el-icon-check"></i> 批量通过
|
|
|
+ </el-button>
|
|
|
+ <el-button size="small" @click="batchReject" v-if="canBatchReject" type="danger">
|
|
|
+ <i class="el-icon-close"></i> 批量拒绝
|
|
|
+ </el-button>
|
|
|
+ <el-button size="small" @click="handleBatchSync" type="warning">
|
|
|
+ <i class="el-icon-refresh"></i> 批量同步
|
|
|
+ </el-button>
|
|
|
+ <el-button size="small" @click="clearSelection">
|
|
|
+ <i class="el-icon-close"></i> 清空选择
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 操作按钮区域 -->
|
|
|
+ <el-card class="action-card" shadow="never">
|
|
|
+ <div class="action-bar">
|
|
|
+ <div class="action-group">
|
|
|
+ <el-button type="primary" icon="el-icon-plus" @click="handleAdd" class="action-btn"
|
|
|
+ >
|
|
|
+ 申请iPad
|
|
|
+ </el-button>
|
|
|
+
|
|
|
+ <el-button type="success" icon="el-icon-edit" @click="handleUpdate" :disabled="single" class="action-btn"
|
|
|
+ >
|
|
|
+ 修改
|
|
|
+ </el-button>
|
|
|
+
|
|
|
+ <el-dropdown @command="handleBatchCommand" class="action-dropdown">
|
|
|
+ <el-button type="warning" class="action-btn">
|
|
|
+ 批量操作<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </el-button>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <el-dropdown-item command="sync">
|
|
|
+ 批量同步
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item command="export">
|
|
|
+ 导出数据
|
|
|
+ </el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </el-dropdown>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="action-right">
|
|
|
+ <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 数据表格 -->
|
|
|
+ <el-card class="table-card" shadow="hover">
|
|
|
+ <el-table border v-loading="loading" :data="recordsList" @selection-change="handleSelectionChange"
|
|
|
+ class="modern-table" :header-cell-class-name="headerCellClass" :cell-class-name="cellClass"
|
|
|
+ @row-click="handleRowClick">
|
|
|
+
|
|
|
+ <el-table-column type="selection" width="55" align="center" />
|
|
|
+ <el-table-column label="公司名称" align="center" prop="companyName" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="company-name">
|
|
|
+ <i class="el-icon-office-building"></i>
|
|
|
+ {{ scope.row.companyName }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="申请数量" align="center" prop="applyQuantity" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="info" effect="plain">{{ scope.row.applyQuantity }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="实际分配数量" align="center" prop="allocatedQuantity" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="success" effect="plain">{{ scope.row.allocatedQuantity || 0 }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="提交时间" align="center" prop="submitTime" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="time-info">
|
|
|
+ <i class="el-icon-time"></i>
|
|
|
+ <span>{{ parseTime(scope.row.submitTime) }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="审核时间" align="center" prop="reviewTime" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="time-info" v-if="scope.row.reviewTime">
|
|
|
+ <i class="el-icon-check"></i>
|
|
|
+ <span>{{ parseTime(scope.row.reviewTime) }}</span>
|
|
|
+ </div>
|
|
|
+ <span v-else class="no-data">-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="审核状态" align="center" prop="auditStatus" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="status-wrapper">
|
|
|
+ <el-tag :class="getStatusClass(scope.row.auditStatus)"
|
|
|
+ :effect="scope.row.auditStatus === 1 ? 'dark' : 'light'">
|
|
|
+ <i :class="getStatusIcon(scope.row.auditStatus)"></i>
|
|
|
+ {{ getStatusText(scope.row.auditStatus) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="拒绝原因" align="center" prop="rejectionReason" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tooltip :content="scope.row.rejectionReason" placement="top" v-if="scope.row.rejectionReason">
|
|
|
+ <span class="rejection-reason">{{ scope.row.rejectionReason }}</span>
|
|
|
+ </el-tooltip>
|
|
|
+ <span v-else class="no-data">-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="操作" align="center" width="180" fixed="right">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="action-buttons">
|
|
|
+ <el-tooltip content="同步数据" placement="top">
|
|
|
+ <el-button type="text" icon="el-icon-refresh" class="action-icon-btn sync-btn"
|
|
|
+ @click="handleSingleSync(scope.row)" />
|
|
|
+ </el-tooltip>
|
|
|
+
|
|
|
+ <el-tooltip content="释放管理" placement="top"
|
|
|
+ v-if="scope.row.auditStatus == 1 || scope.row.auditStatus == 3">
|
|
|
+ <el-button type="text" icon="el-icon-unlock" class="action-icon-btn release-btn"
|
|
|
+ @click="handleRelease(scope.row)" />
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页 -->
|
|
|
+ <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
|
|
+ @pagination="getList" class="pagination-wrapper" />
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 申请iPad对话框 -->
|
|
|
+ <el-dialog :title="title" :visible.sync="open" width="600px" :close-on-click-modal="false"
|
|
|
+ custom-class="modern-dialog" append-to-body>
|
|
|
+ <div class="dialog-content">
|
|
|
+ <div class="form-header">
|
|
|
+ <i class="el-icon-mobile-phone"></i>
|
|
|
+ <span>请填写申请信息</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="modern-form">
|
|
|
+ <el-form-item label="申请地区" prop="area">
|
|
|
+ <el-input v-model="form.area" placeholder="请输入申请地区,如:广州、上海、重庆、北京等"
|
|
|
+ prefix-icon="el-icon-location" class="area-input" />
|
|
|
+ <div class="form-tip">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 请填写申请iPad使用的地区
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="申请数量" prop="applyQuantity">
|
|
|
+ <el-input-number v-model="form.applyQuantity" :min="1" :max="100" controls-position="right"
|
|
|
+ class="number-input" placeholder="请输入申请数量" />
|
|
|
+ <div class="form-tip">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 建议根据实际需求申请合理数量
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="cancel" class="cancel-btn">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="submitForm" :loading="submitting" class="confirm-btn">
|
|
|
+ {{ submitting ? '提交中...' : '确定申请' }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 释放信息抽屉 -->
|
|
|
+ <el-drawer title="服务器分配信息" :visible.sync="releaseOpen" direction="rtl" size="70%" :before-close="handleDrawerClose"
|
|
|
+ custom-class="modern-drawer">
|
|
|
+ <div class="drawer-content">
|
|
|
+ <el-table :data="serverInfoList" border v-loading="releaseLoading" class="server-table">
|
|
|
+ <el-table-column label="访问地址" align="center" prop="accessUrl" min-width="200">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <a :href="scope.row.accessUrl" target="_blank" class="access-url">
|
|
|
+ <i class="el-icon-link"></i>
|
|
|
+ {{ scope.row.accessUrl }}
|
|
|
+ </a>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="已分配座位数" align="center" prop="allocatedSeats" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="primary">{{ scope.row.allocatedSeats }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="分配时间" align="center" prop="allocatedTime" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ parseTime(scope.row.allocatedTime) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="分配状态" align="center" prop="allocationStatus" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag :type="scope.row.allocationStatus === 1 ? 'success' : 'info'">
|
|
|
+ {{ scope.row.allocationStatus === 1 ? '有效' : '已释放' }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="IP地址" align="center" prop="ipAddress" width="120" />
|
|
|
+ <el-table-column label="端口" align="center" prop="port" width="80" />
|
|
|
+ <el-table-column label="地区" align="center" prop="region" width="100" />
|
|
|
+ <el-table-column label="创建时间" align="center" prop="createdTime" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ parseTime(scope.row.createdTime) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" align="center" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button size="mini" type="primary" @click="handleReleaseSeats(scope.row)"
|
|
|
+ :disabled="scope.row.allocationStatus !== 1" class="release-seats-btn">
|
|
|
+ 释放座位
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
+ <!-- 释放座位数量输入弹窗 -->
|
|
|
+ <el-dialog title="释放座位" :visible.sync="releaseSeatsOpen" width="400px" custom-class="release-dialog" append-to-body>
|
|
|
+ <el-form :model="releaseForm" :rules="releaseRules" ref="releaseForm" label-width="100px">
|
|
|
+ <el-form-item label="释放数量" prop="releaseCount">
|
|
|
+ <el-input-number v-model="releaseForm.releaseCount" :min="1" :max="releaseForm.maxSeats" placeholder="请输入释放数量"
|
|
|
+ controls-position="right" style="width: 100%" />
|
|
|
+ <div class="form-tip">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 最大可释放数量:{{ releaseForm.maxSeats }}
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="releaseSeatsOpen = false">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="submitReleaseSeats()">确 定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { listRecords, getServerInfo, getRecords, delRecords, release, updateRecords, exportRecords, apply, batchUpdate } from "@/api/qw/applyIpad";
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: "Records",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ // 遮罩层
|
|
|
+ loading: true,
|
|
|
+ // 导出遮罩层
|
|
|
+ exportLoading: false,
|
|
|
+ // 提交状态
|
|
|
+ submitting: false,
|
|
|
+ // 选中数组
|
|
|
+ ids: [],
|
|
|
+ // 选中的行数据
|
|
|
+ selectedRows: [],
|
|
|
+ // 非单个禁用
|
|
|
+ single: true,
|
|
|
+ // 非多个禁用
|
|
|
+ multiple: true,
|
|
|
+ // 显示搜索条件
|
|
|
+ showSearch: true,
|
|
|
+ // 总条数
|
|
|
+ total: 0,
|
|
|
+ // 分配记录表格数据
|
|
|
+ recordsList: [],
|
|
|
+ // 弹出层标题
|
|
|
+ title: "",
|
|
|
+ // 是否显示弹出层
|
|
|
+ open: false,
|
|
|
+ // 查询参数
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ companyId: null,
|
|
|
+ companyName: null,
|
|
|
+ applicationNumber: null,
|
|
|
+ applyQuantity: null,
|
|
|
+ allocatedQuantity: null,
|
|
|
+ submitTime: null,
|
|
|
+ reviewTime: null,
|
|
|
+ auditStatus: null,
|
|
|
+ rejectionReason: null,
|
|
|
+ createdTime: null,
|
|
|
+ updatedTime: null
|
|
|
+ },
|
|
|
+ // 表单参数
|
|
|
+ form: {},
|
|
|
+ // 表单校验
|
|
|
+ rules: {
|
|
|
+ area: [
|
|
|
+ { required: true, message: "申请地区不能为空", trigger: "blur" }
|
|
|
+ ],
|
|
|
+ applyQuantity: [
|
|
|
+ { required: true, message: "申请数量不能为空", trigger: "blur" },
|
|
|
+ { pattern: /^[1-9]\d*$/, message: "申请数量必须为正整数", trigger: "blur" }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ // 释放抽屉显示状态
|
|
|
+ releaseOpen: false,
|
|
|
+ // 释放信息加载状态
|
|
|
+ releaseLoading: false,
|
|
|
+ // 服务器信息列表
|
|
|
+ serverInfoList: [],
|
|
|
+ // 释放座位弹窗显示状态
|
|
|
+ releaseSeatsOpen: false,
|
|
|
+ // 释放座位表单
|
|
|
+ releaseForm: {
|
|
|
+ releaseCount: null,
|
|
|
+ maxSeats: 0,
|
|
|
+ currentServer: null
|
|
|
+ },
|
|
|
+ // 释放座位表单校验规则
|
|
|
+ releaseRules: {
|
|
|
+ releaseCount: [
|
|
|
+ { required: true, message: "释放数量不能为空", trigger: "blur" },
|
|
|
+ { type: 'number', min: 1, message: "释放数量必须大于0", trigger: "blur" },
|
|
|
+ {
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ if (value > this.releaseForm.maxSeats) {
|
|
|
+ callback(new Error(`释放数量不能超过${this.releaseForm.maxSeats}`));
|
|
|
+ } else {
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ // 当前选中的申请记录ID
|
|
|
+ currentRecordId: null,
|
|
|
+ // 统计数据
|
|
|
+ statistics: {
|
|
|
+ pending: 0,
|
|
|
+ approved: 0,
|
|
|
+ rejected: 0,
|
|
|
+ total: 0
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ // 是否可以批量审核通过
|
|
|
+ canBatchApprove() {
|
|
|
+ return this.selectedRows.some(row => row.auditStatus === 0);
|
|
|
+ },
|
|
|
+ // 是否可以批量审核拒绝
|
|
|
+ canBatchReject() {
|
|
|
+ return this.selectedRows.some(row => row.auditStatus === 0);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.getList();
|
|
|
+ this.calculateStatistics();
|
|
|
+ // 添加键盘事件监听
|
|
|
+ document.addEventListener('keydown', this.handleKeydown);
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ document.removeEventListener('keydown', this.handleKeydown);
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /** 查询分配记录列表 */
|
|
|
+ getList() {
|
|
|
+ this.loading = true;
|
|
|
+ listRecords(this.queryParams).then(response => {
|
|
|
+ this.recordsList = response.rows;
|
|
|
+ this.total = response.total;
|
|
|
+ this.loading = false;
|
|
|
+ this.calculateStatistics();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 计算统计数据 */
|
|
|
+ calculateStatistics() {
|
|
|
+ this.statistics = {
|
|
|
+ pending: this.recordsList.filter(item => item.auditStatus === 0).length,
|
|
|
+ approved: this.recordsList.filter(item => item.auditStatus === 1).length,
|
|
|
+ rejected: this.recordsList.filter(item => item.auditStatus === 2).length,
|
|
|
+ total: this.recordsList.length
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 键盘事件处理 */
|
|
|
+ handleKeydown(e) {
|
|
|
+ // Ctrl + N: 新建申请
|
|
|
+ if (e.ctrlKey && e.key === 'n') {
|
|
|
+ e.preventDefault();
|
|
|
+ this.handleAdd();
|
|
|
+ }
|
|
|
+ // Ctrl + E: 导出数据
|
|
|
+ if (e.ctrlKey && e.key === 'e') {
|
|
|
+ e.preventDefault();
|
|
|
+ this.handleExport();
|
|
|
+ }
|
|
|
+ // F5: 刷新数据
|
|
|
+ if (e.key === 'F5') {
|
|
|
+ e.preventDefault();
|
|
|
+ this.getList();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 切换搜索显示 */
|
|
|
+ toggleSearch() {
|
|
|
+ this.showSearch = !this.showSearch;
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 表格头部样式 */
|
|
|
+ headerCellClass({ row, column, rowIndex, columnIndex }) {
|
|
|
+ return 'modern-header';
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 表格单元格样式 */
|
|
|
+ cellClass({ row, column, rowIndex, columnIndex }) {
|
|
|
+ return 'modern-cell';
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 行点击事件 */
|
|
|
+ handleRowClick(row, column, event) {
|
|
|
+ // 可以添加行点击后的处理逻辑
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 获取状态样式类 */
|
|
|
+ getStatusClass(status) {
|
|
|
+ const statusMap = {
|
|
|
+ 0: 'status-pending',
|
|
|
+ 1: 'status-approved',
|
|
|
+ 2: 'status-rejected',
|
|
|
+ 3: 'status-partial'
|
|
|
+ };
|
|
|
+ return statusMap[status] || '';
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 获取状态图标 */
|
|
|
+ getStatusIcon(status) {
|
|
|
+ const iconMap = {
|
|
|
+ 0: 'el-icon-time',
|
|
|
+ 1: 'el-icon-check',
|
|
|
+ 2: 'el-icon-close',
|
|
|
+ 3: 'el-icon-warning'
|
|
|
+ };
|
|
|
+ return iconMap[status] || 'el-icon-question';
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 获取状态文本 */
|
|
|
+ getStatusText(status) {
|
|
|
+ const textMap = {
|
|
|
+ 0: '待审核',
|
|
|
+ 1: '审核通过',
|
|
|
+ 2: '审核拒绝',
|
|
|
+ 3: '部分分配'
|
|
|
+ };
|
|
|
+ return textMap[status] || '-';
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 批量命令处理 */
|
|
|
+ handleBatchCommand(command) {
|
|
|
+ if (command === 'sync') {
|
|
|
+ this.handleBatchSync();
|
|
|
+ } else if (command === 'export') {
|
|
|
+ this.handleExport();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 批量审核通过 */
|
|
|
+ batchApprove() {
|
|
|
+ const pendingRows = this.selectedRows.filter(row => row.auditStatus === 0);
|
|
|
+ if (pendingRows.length === 0) {
|
|
|
+ this.msgWarning('没有可审核通过的记录');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$confirm(`确认审核通过选中的 ${pendingRows.length} 条记录?`, '批量审核', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ // 这里调用批量审核通过的API
|
|
|
+ this.msgSuccess('批量审核通过成功');
|
|
|
+ this.getList();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 批量审核拒绝 */
|
|
|
+ batchReject() {
|
|
|
+ const pendingRows = this.selectedRows.filter(row => row.auditStatus === 0);
|
|
|
+ if (pendingRows.length === 0) {
|
|
|
+ this.msgWarning('没有可审核拒绝的记录');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$confirm(`确认审核拒绝选中的 ${pendingRows.length} 条记录?`, '批量审核', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ // 这里调用批量审核拒绝的API
|
|
|
+ this.msgSuccess('批量审核拒绝成功');
|
|
|
+ this.getList();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 清空选择 */
|
|
|
+ clearSelection() {
|
|
|
+ this.$refs.multipleTable && this.$refs.multipleTable.clearSelection();
|
|
|
+ this.selectedRows = [];
|
|
|
+ this.ids = [];
|
|
|
+ },
|
|
|
+
|
|
|
+ // 取消按钮
|
|
|
+ cancel() {
|
|
|
+ this.open = false;
|
|
|
+ this.reset();
|
|
|
+ },
|
|
|
+ // 表单重置
|
|
|
+ reset() {
|
|
|
+ this.form = {
|
|
|
+ area: null,
|
|
|
+ applyQuantity: null
|
|
|
+ };
|
|
|
+ this.resetForm("form");
|
|
|
+ },
|
|
|
+ /** 搜索按钮操作 */
|
|
|
+ handleQuery() {
|
|
|
+ this.queryParams.pageNum = 1;
|
|
|
+ this.getList();
|
|
|
+ },
|
|
|
+ /** 重置按钮操作 */
|
|
|
+ resetQuery() {
|
|
|
+ this.resetForm("queryForm");
|
|
|
+ this.handleQuery();
|
|
|
+ },
|
|
|
+ // 多选框选中数据
|
|
|
+ handleSelectionChange(selection) {
|
|
|
+ this.ids = selection.map(item => item.id)
|
|
|
+ this.selectedRows = selection;
|
|
|
+ this.single = selection.length !== 1
|
|
|
+ this.multiple = !selection.length
|
|
|
+ },
|
|
|
+ /** 新增按钮操作 */
|
|
|
+ handleAdd() {
|
|
|
+ this.reset();
|
|
|
+ this.open = true;
|
|
|
+ this.title = "申请iPad";
|
|
|
+ },
|
|
|
+ /** 修改按钮操作 */
|
|
|
+ handleUpdate(row) {
|
|
|
+ this.reset();
|
|
|
+ const id = row.id || this.ids
|
|
|
+ getRecords(id).then(response => {
|
|
|
+ this.form = response.data;
|
|
|
+ this.open = true;
|
|
|
+ this.title = "修改分配记录";
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** 提交按钮 */
|
|
|
+ submitForm() {
|
|
|
+ this.$refs["form"].validate(valid => {
|
|
|
+ if (valid) {
|
|
|
+ this.submitting = true;
|
|
|
+ // 调用apply接口,传递申请数量和地区作为参数对象
|
|
|
+ apply({
|
|
|
+ applyCount: this.form.applyQuantity,
|
|
|
+ area: this.form.area
|
|
|
+ }).then(response => {
|
|
|
+ this.msgSuccess("申请成功");
|
|
|
+ this.open = false;
|
|
|
+ this.getList();
|
|
|
+ }).catch(() => {
|
|
|
+ // this.msgError("申请失败");
|
|
|
+ }).finally(() => {
|
|
|
+ this.submitting = false;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** 删除按钮操作 */
|
|
|
+ handleDelete(row) {
|
|
|
+ const ids = row.id || this.ids;
|
|
|
+ this.$confirm('是否确认删除分配记录编号为"' + ids + '"的数据项?', "警告", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning"
|
|
|
+ }).then(function () {
|
|
|
+ return delRecords(ids);
|
|
|
+ }).then(() => {
|
|
|
+ this.getList();
|
|
|
+ this.msgSuccess("删除成功");
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ /** 导出按钮操作 */
|
|
|
+ handleExport() {
|
|
|
+ const queryParams = this.queryParams;
|
|
|
+ this.$confirm('是否确认导出所有分配记录数据项?', "警告", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning"
|
|
|
+ }).then(() => {
|
|
|
+ this.exportLoading = true;
|
|
|
+ return exportRecords(queryParams);
|
|
|
+ }).then(response => {
|
|
|
+ this.download(response.msg);
|
|
|
+ this.exportLoading = false;
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ /** 单个同步按钮操作 */
|
|
|
+ handleSingleSync(row) {
|
|
|
+ const id = row.id;
|
|
|
+ this.$confirm('是否确认同步编号为"' + id + '"的数据项?', "警告", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning"
|
|
|
+ }).then(() => {
|
|
|
+ return batchUpdate([id]);
|
|
|
+ }).then(() => {
|
|
|
+ this.getList();
|
|
|
+ this.msgSuccess("同步成功");
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ /** 批量同步按钮操作 */
|
|
|
+ handleBatchSync() {
|
|
|
+ const ids = this.ids;
|
|
|
+ this.$confirm('是否确认同步选中的' + ids.length + '条数据项?', "警告", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning"
|
|
|
+ }).then(() => {
|
|
|
+ return batchUpdate(ids);
|
|
|
+ }).then(() => {
|
|
|
+ this.getList();
|
|
|
+ this.msgSuccess("批量同步成功");
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ /** 释放按钮操作 */
|
|
|
+ handleRelease(row) {
|
|
|
+ this.releaseLoading = true;
|
|
|
+ this.releaseOpen = true;
|
|
|
+ this.serverInfoList = [];
|
|
|
+ this.currentRecordId = row.id;
|
|
|
+
|
|
|
+ // 调用getServerInfo接口获取服务器信息
|
|
|
+ getServerInfo(row.id).then(response => {
|
|
|
+ this.serverInfoList = response.data || [];
|
|
|
+ this.releaseLoading = false;
|
|
|
+ }).catch(error => {
|
|
|
+ this.releaseLoading = false;
|
|
|
+ this.msgError("获取服务器信息失败");
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 抽屉关闭处理 */
|
|
|
+ handleDrawerClose(done) {
|
|
|
+ this.releaseOpen = false;
|
|
|
+ done();
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 释放座位按钮操作 */
|
|
|
+ handleReleaseSeats(server) {
|
|
|
+ this.releaseForm = {
|
|
|
+ releaseCount: null,
|
|
|
+ maxSeats: server.allocatedSeats || 0,
|
|
|
+ currentServer: server
|
|
|
+ };
|
|
|
+
|
|
|
+ this.releaseSeatsOpen = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.releaseForm && this.$refs.releaseForm.clearValidate();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 提交释放座位 */
|
|
|
+ submitReleaseSeats() {
|
|
|
+ this.$refs.releaseForm.validate(valid => {
|
|
|
+ if (valid) {
|
|
|
+ // 根据后端ServerParam类结构组装参数
|
|
|
+ const params = {
|
|
|
+ ipadServerId: this.releaseForm.currentServer.ipadRecordId, // 服务器ID
|
|
|
+ recordId: this.currentRecordId, // 记录ID
|
|
|
+ releaseCount: this.releaseForm.releaseCount, // 释放数量
|
|
|
+ ipAddress: this.releaseForm.currentServer.ipAddress, // iPad地址
|
|
|
+ port: this.releaseForm.currentServer.port // 端口号
|
|
|
+ };
|
|
|
+
|
|
|
+ // 调用release接口
|
|
|
+ release(params).then(response => {
|
|
|
+ this.msgSuccess("释放成功");
|
|
|
+ this.releaseSeatsOpen = false;
|
|
|
+ // 重新加载服务器信息
|
|
|
+ this.handleRelease({ id: this.currentRecordId });
|
|
|
+ }).catch(error => {
|
|
|
+ this.msgError("释放失败");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 重置释放座位表单 */
|
|
|
+ resetReleaseForm() {
|
|
|
+ this.releaseForm = {
|
|
|
+ releaseCount: null,
|
|
|
+ maxSeats: 0,
|
|
|
+ currentServer: null
|
|
|
+ };
|
|
|
+ this.$refs.releaseForm && this.$refs.releaseForm.clearValidate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.app-container {
|
|
|
+ padding: 20px;
|
|
|
+ background: #f0f2f5;
|
|
|
+ min-height: calc(100vh - 84px);
|
|
|
+}
|
|
|
+
|
|
|
+// 统计卡片样式
|
|
|
+.statistics-cards {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .stat-card {
|
|
|
+ border-radius: 12px;
|
|
|
+ border: none;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ transform: translateY(-5px);
|
|
|
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 10px;
|
|
|
+
|
|
|
+ .stat-icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-right: 15px;
|
|
|
+ font-size: 24px;
|
|
|
+ color: white;
|
|
|
+
|
|
|
+ &.pending {
|
|
|
+ background: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.approved {
|
|
|
+ background: #67c23a;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.rejected {
|
|
|
+ background: #f56c6c;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.total {
|
|
|
+ background: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-info {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .stat-value {
|
|
|
+ font-size: 28px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #2c3e50;
|
|
|
+ line-height: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #7f8c8d;
|
|
|
+ margin-top: 5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 搜索卡片样式
|
|
|
+.search-card {
|
|
|
+ border-radius: 12px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ border: none;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding-bottom: 15px;
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .search-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-form {
|
|
|
+
|
|
|
+ .search-input,
|
|
|
+ .search-select,
|
|
|
+ .search-date {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-btn,
|
|
|
+ .reset-btn {
|
|
|
+ border-radius: 8px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-btn:hover {
|
|
|
+ background-color: #66b1ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .reset-btn:hover {
|
|
|
+ color: #409eff;
|
|
|
+ border-color: #c6e2ff;
|
|
|
+ background-color: #ecf5ff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 批量操作工具栏
|
|
|
+.batch-toolbar {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .batch-card {
|
|
|
+ border-radius: 12px;
|
|
|
+ border: none;
|
|
|
+ background: #fdf6ec;
|
|
|
+
|
|
|
+ .batch-content {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .batch-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ color: #f39c12;
|
|
|
+ font-weight: 500;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 8px;
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .batch-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ border-radius: 6px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 操作按钮区域
|
|
|
+.action-card {
|
|
|
+ border-radius: 12px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ border: none;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+
|
|
|
+ .action-bar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .action-group {
|
|
|
+ display: flex;
|
|
|
+ gap: 15px;
|
|
|
+
|
|
|
+ .action-btn {
|
|
|
+ border-radius: 4px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ opacity: 0.8;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 表格卡片样式
|
|
|
+.table-card {
|
|
|
+ border-radius: 12px;
|
|
|
+ border: none;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
|
|
|
+ }
|
|
|
+
|
|
|
+ .modern-table {
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ ::v-deep .modern-header {
|
|
|
+ background: #f5f7fa;
|
|
|
+ color: #606266;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .modern-cell {
|
|
|
+ .company-name {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 5px;
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .time-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 12px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 5px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .no-data {
|
|
|
+ color: #c0c4cc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .rejection-reason {
|
|
|
+ max-width: 150px;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ display: inline-block;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-table__row {
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 10px;
|
|
|
+
|
|
|
+ .action-icon-btn {
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border-radius: 6px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ transform: scale(1.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.sync-btn {
|
|
|
+ color: #409eff;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(64, 158, 255, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.release-btn {
|
|
|
+ color: #e6a23c;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(230, 162, 60, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .pagination-wrapper {
|
|
|
+ margin-top: 20px;
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 状态标签样式
|
|
|
+.status-wrapper {
|
|
|
+ .el-tag {
|
|
|
+ border-radius: 20px;
|
|
|
+ padding: 4px 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ border: none;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-pending {
|
|
|
+ background-color: #e1f3d8;
|
|
|
+ color: #67c23a;
|
|
|
+ border-color: #e1f3d8;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-approved {
|
|
|
+ background-color: #f0f9ff;
|
|
|
+ color: #409eff;
|
|
|
+ border-color: #f0f9ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-rejected {
|
|
|
+ background-color: #fef0f0;
|
|
|
+ color: #f56c6c;
|
|
|
+ border-color: #fef0f0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.status-partial {
|
|
|
+ background-color: #fdf6ec;
|
|
|
+ color: #e6a23c;
|
|
|
+ border-color: #fdf6ec;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 对话框样式
|
|
|
+::v-deep .modern-dialog {
|
|
|
+ border-radius: 12px;
|
|
|
+
|
|
|
+ .el-dialog__header {
|
|
|
+ background: #f5f7fa;
|
|
|
+ color: #606266;
|
|
|
+ border-radius: 12px 12px 0 0;
|
|
|
+ padding: 20px;
|
|
|
+
|
|
|
+ .el-dialog__title {
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 30px 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .dialog-content {
|
|
|
+ .form-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding-bottom: 15px;
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 10px;
|
|
|
+ color: #409eff;
|
|
|
+ font-size: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ span {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .modern-form {
|
|
|
+ .area-input {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .number-input {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-tip {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ color: #909399;
|
|
|
+ font-size: 12px;
|
|
|
+ margin-top: 5px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .dialog-footer {
|
|
|
+ text-align: right;
|
|
|
+ padding: 15px 20px;
|
|
|
+ border-top: 1px solid #ebeef5;
|
|
|
+
|
|
|
+ .cancel-btn,
|
|
|
+ .confirm-btn {
|
|
|
+ border-radius: 8px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .confirm-btn:hover {
|
|
|
+ background-color: #66b1ff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 抽屉样式
|
|
|
+::v-deep .modern-drawer {
|
|
|
+ .el-drawer__header {
|
|
|
+ background: #f5f7fa;
|
|
|
+ color: #606266;
|
|
|
+ padding: 20px;
|
|
|
+ margin-bottom: 0;
|
|
|
+
|
|
|
+ span {
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .drawer-content {
|
|
|
+ padding: 20px;
|
|
|
+
|
|
|
+ .server-table {
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .access-url {
|
|
|
+ color: #409eff;
|
|
|
+ text-decoration: none;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ text-decoration: underline;
|
|
|
+ }
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .release-seats-btn {
|
|
|
+ border-radius: 6px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 释放对话框样式
|
|
|
+::v-deep .release-dialog {
|
|
|
+ border-radius: 12px;
|
|
|
+
|
|
|
+ .el-dialog__header {
|
|
|
+ background: #f5f7fa;
|
|
|
+ color: #606266;
|
|
|
+ border-radius: 12px 12px 0 0;
|
|
|
+
|
|
|
+ .el-dialog__title {
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 响应式设计
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .app-container {
|
|
|
+ padding: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statistics-cards {
|
|
|
+ .el-col {
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-form {
|
|
|
+ .el-col {
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-bar {
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: stretch;
|
|
|
+
|
|
|
+ .action-group {
|
|
|
+ margin-bottom: 10px;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .batch-content {
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+
|
|
|
+ .batch-actions {
|
|
|
+ margin-top: 10px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|