1. 移除了vis-network依赖包 2. 重构两个合同选择页面的搜索与列表逻辑,移除本地合并合同数据的操作,改为直接使用接口返回数据 3. 为所有合同tab添加分页组件,支持分页查询、搜索重置页码 4. 调整表格高度优化布局,更新搜索触发事件为失焦、回车和清空按钮 5. 新增搜索查询按钮,优化搜索交互体验
360 lines
11 KiB
Vue
360 lines
11 KiB
Vue
<template>
|
|
<div class="select-config-page">
|
|
<div class="page-header">
|
|
<h3>可选合同配置</h3>
|
|
<div class="header-stats">
|
|
<span class="stat-item">
|
|
<span class="stat-label">已配置</span>
|
|
<span class="stat-value">{{ contractList.length }}</span>
|
|
</span>
|
|
<span class="stat-item">
|
|
<span class="stat-label">手动添加</span>
|
|
<span class="stat-value manual">{{contractList.filter(i => i.isManual).length}}</span>
|
|
</span>
|
|
<span class="stat-item">
|
|
<span class="stat-label">接口同步</span>
|
|
<span class="stat-value api">{{contractList.filter(i => !i.isManual).length}}</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="content-card">
|
|
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
<el-tab-pane label="可选合同" name="configured">
|
|
<div v-if="contractList.length === 0" class="empty-state">
|
|
<i class="el-icon-document"></i>
|
|
<p>暂无已配置合同</p>
|
|
<span>切换至"所有合同"标签页添加合同</span>
|
|
</div>
|
|
<el-table v-else v-loading="configLoading" :data="contractList" style="width: 100%" size="small" stripe
|
|
max-height="calc(100vh - 280px)">
|
|
<el-table-column prop="contractCode" label="合同编号" width="160" />
|
|
<el-table-column prop="contractName" label="合同名称" min-width="180" show-overflow-tooltip />
|
|
<el-table-column prop="customer" label="客户" width="140" />
|
|
<el-table-column prop="salesman" label="销售人员" width="100" />
|
|
<el-table-column prop="deliveryDate" label="交付日期" width="110">
|
|
<template slot-scope="scope">
|
|
<span :class="{ 'text-muted': !scope.row.deliveryDate }">
|
|
{{ scope.row.deliveryDate || '-' }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip>
|
|
<template slot-scope="scope">
|
|
<span :class="{ 'text-muted': !scope.row.remark }">
|
|
{{ scope.row.remark || '-' }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="添加方式" width="90" align="center">
|
|
<template slot-scope="scope">
|
|
<el-tag v-if="scope.row.isManual" type="success" size="small">手动</el-tag>
|
|
<el-tag v-else type="info" size="small">接口</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<el-button type="text" size="small" @click="removeContract(scope.row.orderId)" style="color: #f56c6c;">
|
|
移除
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-tab-pane>
|
|
|
|
<el-tab-pane label="所有合同" v-loading="allLoading" name="all">
|
|
<div class="search-bar">
|
|
<el-input v-model="searchKeyword" placeholder="搜索合同编号 / 名称 / 客户" @blur="handleSearch"
|
|
@keyup.enter.native="handleSearch" @clear="handleSearch" clearable
|
|
size="small" class="search-input">
|
|
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
|
</el-input>
|
|
<el-button type="primary" size="small" @click="handleSearch" style="margin-left: 8px;">查询</el-button>
|
|
</div>
|
|
|
|
<div v-if="allContracts.length === 0" class="empty-state">
|
|
<i class="el-icon-folder-opened"></i>
|
|
<p>暂无合同数据</p>
|
|
</div>
|
|
<template v-else>
|
|
<el-table :data="allContracts" size="small" stripe height="calc(100vh - 400px)">
|
|
<el-table-column prop="contractCode" label="合同编号" width="160" />
|
|
<el-table-column prop="contractName" label="合同名称" min-width="180" show-overflow-tooltip />
|
|
<el-table-column prop="customer" label="客户" width="140" />
|
|
<el-table-column prop="salesman" label="销售人员" width="100" />
|
|
<el-table-column prop="deliveryDate" label="交付日期" width="110">
|
|
<template slot-scope="scope">
|
|
<span :class="{ 'text-muted': !scope.row.deliveryDate }">
|
|
{{ scope.row.deliveryDate || '-' }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip>
|
|
<template slot-scope="scope">
|
|
<span :class="{ 'text-muted': !scope.row.remark }">
|
|
{{ scope.row.remark || '-' }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="状态" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<el-tag v-if="isContractInList(scope.row.orderId)" type="success" size="small">已添加</el-tag>
|
|
<el-tag v-else type="info" size="small">未添加</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<el-button v-if="!isContractInList(scope.row.orderId)" type="primary" size="mini"
|
|
@click="addContract(scope.row)" plain>
|
|
添加
|
|
</el-button>
|
|
<el-button v-else type="text" size="small" @click="removeContract(scope.row.orderId)"
|
|
style="color: #f56c6c;">
|
|
移除
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<div class="pagination-wrapper">
|
|
<el-pagination background layout="total, prev, pager, next, jumper" :total="total"
|
|
:page-size="pageSize" :current-page.sync="pageNum" @current-change="handlePageChange" />
|
|
</div>
|
|
</template>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { listOrder, listTodayOrder } from '@/api/crm/order';
|
|
|
|
export default {
|
|
name: "SelectConfig",
|
|
data() {
|
|
return {
|
|
contractList: [],
|
|
configLoading: false,
|
|
searchKeyword: '',
|
|
allContracts: [],
|
|
allLoading: false,
|
|
activeTab: 'configured',
|
|
pageNum: 1,
|
|
pageSize: 20,
|
|
total: 0,
|
|
}
|
|
},
|
|
mounted() {
|
|
this.loadFromLocalStorage();
|
|
},
|
|
methods: {
|
|
handleTabClick(tab) {
|
|
if (tab.name === 'all' && this.allContracts.length === 0) {
|
|
this.loadAllContracts();
|
|
}
|
|
},
|
|
loadFromLocalStorage() {
|
|
try {
|
|
const storedContracts = localStorage.getItem('todayContracts');
|
|
if (storedContracts) {
|
|
this.contractList = JSON.parse(storedContracts);
|
|
}
|
|
this.loadContractList();
|
|
} catch (error) {
|
|
console.error('Failed to load contracts from localStorage:', error);
|
|
this.loadContractList();
|
|
}
|
|
},
|
|
|
|
saveToLocalStorage() {
|
|
try {
|
|
localStorage.setItem('todayContracts', JSON.stringify(this.contractList));
|
|
} catch (error) {
|
|
console.error('Failed to save contracts to localStorage:', error);
|
|
}
|
|
},
|
|
|
|
async loadContractList() {
|
|
this.configLoading = true;
|
|
try {
|
|
const res = await listTodayOrder();
|
|
const existingManualContracts = this.contractList.filter(item => item.isManual);
|
|
const apiContracts = (res.data || []).map(item => ({
|
|
...item,
|
|
isManual: false
|
|
}));
|
|
this.contractList = [...apiContracts, ...existingManualContracts];
|
|
this.contractList = this.contractList.filter((item, index, self) =>
|
|
index === self.findIndex(t => t.orderId === item.orderId)
|
|
);
|
|
this.saveToLocalStorage();
|
|
} finally {
|
|
this.configLoading = false;
|
|
}
|
|
},
|
|
|
|
async loadAllContracts() {
|
|
this.allLoading = true;
|
|
try {
|
|
const res = await listOrder({
|
|
pageNum: this.pageNum,
|
|
pageSize: this.pageSize,
|
|
keyword: this.searchKeyword || undefined,
|
|
});
|
|
this.total = res.total || 0;
|
|
this.allContracts = res.rows || [];
|
|
} catch (error) {
|
|
console.error('Failed to load all contracts:', error);
|
|
this.allContracts = [];
|
|
} finally {
|
|
this.allLoading = false;
|
|
}
|
|
},
|
|
|
|
handleSearch() {
|
|
this.pageNum = 1;
|
|
this.loadAllContracts();
|
|
},
|
|
|
|
handlePageChange(page) {
|
|
this.pageNum = page;
|
|
this.loadAllContracts();
|
|
},
|
|
|
|
isContractInList(orderId) {
|
|
return this.contractList.some(item => item.orderId === orderId);
|
|
},
|
|
|
|
addContract(contract) {
|
|
if (!this.isContractInList(contract.orderId)) {
|
|
this.contractList.push({
|
|
...contract,
|
|
isManual: true
|
|
});
|
|
this.saveToLocalStorage();
|
|
}
|
|
},
|
|
|
|
removeContract(orderId) {
|
|
this.contractList = this.contractList.filter(item => item.orderId !== orderId);
|
|
this.saveToLocalStorage();
|
|
},
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.select-config-page {
|
|
padding: 16px;
|
|
min-height: 100%;
|
|
background: #f5f7fa;
|
|
}
|
|
|
|
.page-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 16px;
|
|
padding: 16px 20px;
|
|
background: #fff;
|
|
border-radius: 6px;
|
|
border: 1px solid #ebeef5;
|
|
}
|
|
|
|
.page-header h3 {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
color: #303133;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.header-stats {
|
|
display: flex;
|
|
gap: 24px;
|
|
}
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 13px;
|
|
color: #909399;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #303133;
|
|
}
|
|
|
|
.stat-value.manual {
|
|
color: #67c23a;
|
|
}
|
|
|
|
.stat-value.api {
|
|
color: #909399;
|
|
}
|
|
|
|
.content-card {
|
|
background: #fff;
|
|
border-radius: 6px;
|
|
border: 1px solid #ebeef5;
|
|
padding: 0 16px 16px;
|
|
}
|
|
|
|
.content-card ::v-deep .el-tabs__header {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.search-bar {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.search-input {
|
|
width: 320px;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 60px 20px;
|
|
color: #c0c4cc;
|
|
}
|
|
|
|
.empty-state i {
|
|
font-size: 48px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.empty-state p {
|
|
margin: 0 0 8px;
|
|
font-size: 14px;
|
|
color: #909399;
|
|
}
|
|
|
|
.empty-state span {
|
|
font-size: 12px;
|
|
color: #c0c4cc;
|
|
}
|
|
|
|
.text-muted {
|
|
color: #c0c4cc;
|
|
}
|
|
|
|
.table-footer {
|
|
text-align: center;
|
|
padding: 12px;
|
|
font-size: 13px;
|
|
color: #909399;
|
|
}
|
|
|
|
.pagination-wrapper {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 12px 0;
|
|
}
|
|
</style>
|