Files
im-uniapp/pages/workbench/profit/project.vue
砂糖 307b46b213 feat: 新增客户管理、项目进度和财务中心功能模块
新增客户管理、项目进度和财务中心相关页面及API接口
添加项目明细页面和启动图资源
重构请求基础URL和更新逻辑
引入uni-badge和uni-list组件
优化工作台首页功能入口布局
更新版本号至5.0.0并修改启动图配置
2025-11-06 16:56:35 +08:00

446 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="project-page">
<!-- 新增按钮 -->
<view class="add-btn-row">
<u-search placeholder="根据项目名称搜索" v-model="queryParams.projectName" class="search-input" :show-action="false"
@search="handleSearch" />
<view class="button-group">
</input>
<uni-icons type="gift" size="30" @click="toggleQualityFilter" :color="qualityFilter ? '#ffcc00' : '#333'" />
<uni-icons type="search" size="30" @click="openSettingsPopup" color="#333"></uni-icons>
<uni-icons
type="plus"
size="30"
@click="handleAdd"
color="#333"
/>
<uni-drawer ref="filterDrawer" mode="right" :width="300">
<view class="popup-content">
<view class="filter-title">更多筛选条件</view>
<view class="filter-item">
<text class="filter-label">项目类型</text>
<oa-dict-select v-model="queryParams.projectType" dict-type="sys_project_type" placeholder="请选择项目类型"
@change="handleQuery" clearable />
</view>
<view class="filter-item">
<text class="filter-label">贸易类型</text>
<oa-dict-select v-model="queryParams.tradeType" dict-type="sys_trade_type" placeholder="请选择贸易类型"
@change="handleQuery" clearable />
</view>
<view class="filter-item">
<text class="filter-label">项目代号</text>
<oa-dict-select v-model="queryParams.projectCode" dict-type="sys_project_code" placeholder="请选择代号类型"
@change="handleQuery" clearable filterable />
</view>
<view class="filter-item">
<text class="filter-label">日期范围</text>
<uni-datetime-picker v-model="searchTime" type="daterange" rangeSeparator="至" start-placeholder="开始日期"
end-placeholder="结束日期" @change="handleQuery" />
</view>
<view class="filter-actions">
<button class="primary" @click="handleQuery">查询</button>
<button @click="resetQuery">重置</button>
</view>
</view>
</uni-drawer>
<!-- <uni-icons type="plus" size="30" @click="handleAdd" color="#333" /> -->
</view>
</view>
<!-- 项目列表 -->
<scroll-view scroll-y @scrolltolower="loadMore" :style="{ height: scrollHeight + 'px' }">
<view class="project-grid">
<view class="project-card" v-for="item in projectList" @click="handleDetail(item)">
<view class="project-title">
{{ item.projectName }}
<uni-tag v-if="item.projectStatus == 1" size="mini" type="success" text="进度完成"></uni-tag>
<uni-tag v-else-if="item.projectStatus == 0" size="mini" type="warning" text="进行中"></uni-tag>
</view>
<view class="project-info">
<text>{{ item.functionary }}</text>
<!-- <text :style="getRemainTimeStyle(item.remainTime)" v-if="item.projectStatus == 0">
剩余时间{{ item.remainTime }}
</text> -->
<text>{{ item.projectNum }}</text>
<text>项目总金额{{ item.funds }}</text>
<text v-if="item.prePay > 0" style="color: #ffcc00;">预付款{{ item.prePay }}</text>
<view
style="display: flex; justify-content: flex-start; gap: 10rpx; position: absolute; bottom: 20rpx; margin-top: 10rpx;">
</view>
</view>
</view>
</view>
<!-- 加载更多组件 -->
<uni-load-more :status="loadMoreStatus"></uni-load-more>
</scroll-view>
</view>
</template>
<script>
import {
listProject,
addProject,
updateProject,
delProject,
getProject
} from '@/api/oa/project.js'
export default {
data() {
return {
projectList: [],
form: {},
editType: '', // add/edit
page: 1, // 当前页码
pageSize: 10, // 每页项目数量
selectedProject: {}, // 选中的项目
loadMoreStatus: 'more', // 加载更多状态
scrollHeight: 0, // 滚动区域高度
qualityFilter: false, // 优质筛选开关
searchTime: [], // 日期范围
queryParams: {
projectName: '',
projectType: '',
tradeType: '',
projectCode: '',
projectStatus: ''
},
dict: {
type: {
sys_project_type: [],
sys_trade_type: [],
sys_project_code: [],
sys_project_status: []
}
}
}
},
onShow() {
this.getList();
this.calculateScrollHeight();
// TODO: 这里应请求字典数据填充dict.type
},
methods: {
getStatusLabel(val) {
const arr = this.dict.type.sys_project_status;
const found = arr.find(i => i.value === val);
return found ? found.label : '';
},
openSettingsPopup() {
this.$nextTick(() => {
if (this.$refs.filterDrawer) this.$refs.filterDrawer.open();
});
},
handleSearch() {
this.page = 1;
this.projectList = [];
this.getList();
},
handleQuery() {
this.page = 1;
this.projectList = [];
if (this.$refs.filterDrawer) this.$refs.filterDrawer.close();
// 日期范围处理
if (this.searchTime && this.searchTime.length === 2) {
this.queryParams.beginDate = this.searchTime[0];
this.queryParams.endDate = this.searchTime[1];
} else {
this.queryParams.beginDate = '';
this.queryParams.endDate = '';
}
this.getList();
},
resetQuery() {
this.queryParams = {
projectName: '',
projectType: '',
tradeType: '',
projectCode: '',
projectStatus: ''
};
this.searchTime = [];
this.handleQuery();
},
filterCode(val) {
// 可根据需要自定义筛选逻辑
return true;
},
calculateScrollHeight() {
const systemInfo = uni.getSystemInfoSync();
const tabHeight = 75; // tab高度
const containerPadding = 40; // 容器padding
this.scrollHeight = systemInfo.windowHeight - tabHeight - containerPadding + 80;
},
getList() {
const params = {
pageNum: this.page,
pageSize: this.pageSize,
...this.queryParams
};
if (this.qualityFilter) {
params.prePay = 0.1; // 添加优质筛选参数
}
listProject(params).then(res => {
const newProjects = (res.rows || []).map(row => ({
...row,
projectTypeLabel: row.projectType,
projectStatusLabel: row.projectStatus,
}));
this.projectList = [...this.projectList, ...newProjects];
if (newProjects.length < this.pageSize) {
this.loadMoreStatus = 'noMore'; // 没有更多数据
} else {
this.loadMoreStatus = 'more'; // 还有更多数据
}
});
},
loadMore() {
if (this.loadMoreStatus === 'more') {
console.log('加载更多');
this.page++;
this.getList();
}
},
handleAdd() {
uni.navigateTo({
url: '/pages/workbench/profit/cost?id=' + '0'
})
},
handleDetail(item) {
uni.navigateTo({
url: '/pages/workbench/profit/cost?id=' + item.projectId
})
},
toggleQualityFilter() {
this.qualityFilter = !this.qualityFilter; // 切换优质筛选状态
this.page = 1; // 重置页码
this.projectList = []; // 清空当前项目列表
this.getList(); // 重新获取项目列表
},
openEditPopup(item) {
this.editType = 'edit';
getProject(item.projectId).then(res => {
this.form = {
...res.data
};
this.$refs.editPopup.open();
});
},
closePopup() {
this.$refs.editPopup.close();
},
submitForm() {
if (this.editType === 'add') {
addProject(this.form).then(() => {
this.getList();
this.closePopup();
});
} else {
updateProject(this.form).then(() => {
this.getList();
this.closePopup();
});
}
},
handleClosure(item) {
const update = {
...item,
projectStatus: '1'
};
updateProject(update).then(() => {
this.getList();
});
},
handleDelete(item) {
delProject(item.projectId).then(() => {
this.getList();
});
},
openDetailPopup(item) {
this.selectedProject = item;
this.$refs.detailPopup.open();
},
closeDetailPopup() {
this.$refs.detailPopup.close();
},
getRemainTimeStyle(remainTime) {
if (remainTime > 10) {
return {
color: '#3c763d'
}; // 绿色
} else if (remainTime > 5) {
return {
color: '#8a6d3b'
}; // 黄色
} else {
return {
color: '#a94442'
}; // 红色
}
}
}
}
</script>
<style scoped>
.add-btn-row {
display: flex;
justify-content: flex-end;
/* 右对齐 */
align-items: center;
margin: 20rpx;
gap: 10rpx;
/* 按钮之间的间距 */
}
.button-group {
display: flex;
gap: 10px;
/* 按钮之间的间距 */
}
.popup-content {
background-color: #fff;
/* 设置纯白背景 */
padding: 24px 18px 18px 18px;
/* 上右下左内边距 */
border-radius: 14px;
/* 圆角 */
min-height: 100vh;
box-sizing: border-box;
}
.filter-title {
font-size: 20px;
font-weight: bold;
color: #333;
margin-bottom: 18px;
text-align: left;
}
.filter-item {
display: flex;
align-items: center;
margin-bottom: 18px;
}
.filter-label {
min-width: 80px;
font-size: 16px;
color: #666;
margin-right: 10px;
}
.filter-item oa-dict-select,
.filter-item picker,
.filter-item uni-datetime-picker {
flex: 1;
}
.filter-item .uni-input {
background: #f7f7f7;
border-radius: 6px;
padding: 8px 12px;
font-size: 15px;
color: #333;
}
.filter-actions {
display: flex;
justify-content: center;
gap: 18px;
margin-top: 10px;
}
.filter-actions button {
min-width: 90px;
padding: 8px 0;
border-radius: 6px;
font-size: 16px;
background: #2979ff;
color: #fff;
border: none;
}
.filter-actions button:not(.primary) {
background: #f5f5f5;
color: #333;
border: 1px solid #e0e0e0;
}
.close-button {
display: flex;
justify-content: flex-end;
/* 右对齐 */
margin-bottom: 10px;
/* 底部间距 */
}
.project-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.project-card {
border: 1px solid #ccc;
/* 边框 */
border-radius: 10px;
/* 圆角 */
background-color: #fff;
/* 背景色 */
margin: 10px;
box-sizing: border-box;
width: calc(50% - 20px);
/* 每行两个卡片 */
padding: 20px 15px;
/* 添加内边距 */
position: relative;
}
.project-title {
font-size: 20px;
/* 项目名称字号 */
font-weight: bold;
/* 加粗 */
color: #333;
/* 主字体颜色 */
margin-bottom: 5px;
/* 下边距 */
}
.project-info text {
display: block;
/* 每个信息占一行 */
margin-top: 5px;
/* 上边距 */
color: #666;
/* 较淡的字体颜色 */
font-size: 16px;
/* 信息字号 */
line-height: 1.5;
/* 行间距 */
}
.project-info {
margin-top: 10px;
/* 项目信息与标题之间的间距 */
}
/* 新增的样式 */
.horizontal-actions {
display: flex;
justify-content: space-between;
/* 在一行内均匀分布 */
margin-top: 10px;
/* 添加顶部间距 */
}
.horizontal-actions button {
flex: 1;
/* 按钮均分宽度 */
margin: 0 5px;
/* 按钮之间的间距 */
}
</style>