123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- <template>
- <view class="container">
- <!-- 搜索栏 -->
- <view class="search-box">
- <view class="search-header" @tap="toggleSearch">
- <text class="search-title">搜索条件</text>
- <text class="search-icon">{{ isSearchExpanded ? '收起' : '展开' }}</text>
- </view>
-
- <view class="search-content" v-if="isSearchExpanded">
- <view class="search-item">
- <text>项目编号</text>
- <input v-model="queryParams.projectNumber" placeholder="请输入项目编号" @confirm="handleQuery" />
- </view>
- <view class="search-item">
- <text>项目名称</text>
- <input v-model="queryParams.projectName" placeholder="请输入项目名称" @confirm="handleQuery" />
- </view>
- <view class="search-item">
- <text>项目负责人</text>
- <picker mode="selector" :range="userList" range-key="text" @change="handleLeaderChange">
- <view class="picker">
- <text>{{ selectedLeader || '请选择项目负责人' }}</text>
- </view>
- </picker>
- </view>
- <view class="search-item">
- <text>承担部门</text>
- <picker mode="selector" :range="deptList" range-key="text" @change="handleDeptChange">
- <view class="picker">
- <text>{{ selectedDept || '请选择承担部门' }}</text>
- </view>
- </picker>
- </view>
- <view class="search-item">
- <text>其他关键字</text>
- <input v-model="queryParams.queryString" placeholder="请输入关键字" @confirm="handleQuery" />
- </view>
- <view class="search-buttons">
- <button type="primary" size="mini" @tap="handleQuery">搜索</button>
- <button type="default" size="mini" @tap="resetQuery">重置</button>
- </view>
- </view>
- </view>
-
- <!-- 项目列表 -->
- <view class="list-box">
- <view class="list-header">
- <text class="title">测绘项目列表</text>
- <!-- <view class="header-btns">
- <button type="primary" size="mini" @tap="handleRegister">登记项目</button>
- <button type="default" size="mini" @tap="handleExport">导出项目</button>
- </view> -->
- </view>
-
- <scroll-view scroll-y class="list-scroll" id="projectList" :scroll-into-view="scrollToId"
- :scroll-with-animation="true" @scrolltolower="loadMore" :refresher-enabled="true"
- :refresher-triggered="isRefreshing" @refresherrefresh="onRefresh" @scroll="onScroll">
- <view class="list-content">
- <view id="top"></view>
- <view class="project-card" v-for="(item, index) in projectList" :key="index">
- <!-- 卡片头部 -->
- <view class="card-header">
- <view class="header-left">
- <text class="project-name">{{ item.projectName }}</text>
- <text class="status-tag" :class="item.isFinished === '0' ? 'success' : 'warning'">
- {{ item.isFinished === '0' ? '进行中' : '已结束' }}
- </text>
- </view>
- <text class="level-tag" :class="item.projectLevel === '0' ? 'info' : 'error'">
- {{ item.projectLevel === '0' ? '一般项目' : '重大项目' }}
- </text>
- </view>
-
- <!-- 卡片内容 -->
- <view class="card-content">
- <view class="info-row">
- <text class="label">项目编号:</text>
- <text class="value">{{ item.projectNumber }}</text>
- </view>
- <view class="info-row">
- <text class="label">合同编码:</text>
- <text class="value">{{ item.contract ? item.contract.contractCode : '未关联' }}</text>
- </view>
- <view class="info-row">
- <text class="label">承担部门:</text>
- <text class="value">{{ item.undertakingDeptName }}</text>
- </view>
- <view class="info-row">
- <text class="label">项目负责人:</text>
- <text class="value">{{ item.projectLeaderUser ? item.projectLeaderUser.nickName : '' }}</text>
- </view>
- <view class="info-row">
- <text class="label">项目类型:</text>
- <text class="value">{{ item.projectType }}</text>
- </view>
- <view class="info-row">
- <text class="label">项目进度:</text>
- <view class="progress-box">
- <progress :percent="item.percentage" :activeColor="getProgressColor(item.percentage)" />
- <text class="progress-text">{{ item.percentage }}%</text>
- </view>
- </view>
- </view>
-
- <!-- 卡片底部 -->
- <view class="card-footer">
- <button type="primary" size="mini" @tap="handleView(item)">查看</button>
- </view>
- </view>
- </view>
- </scroll-view>
-
- <!-- 加载更多 -->
- <view class="load-more" v-if="projectList.length > 0">
- <text v-if="hasMore">上拉加载更多</text>
- <text v-else>没有更多数据了</text>
- </view>
-
- <!-- 回到顶部按钮 -->
- <view class="back-to-top" v-if="showBackToTop" @tap="scrollToTop">
- <text class="iconfont">
- <uv-icon size="28" name="arrow-upward" color="#fcfcfc"></uv-icon>
- </text>
- </view>
- </view>
- </view>
- </template>
-
- <script>
- import { listProject, listProjectFuzzy, delProject } from "@/api/oa/project/project";
- import { getProjectProgress } from "@/api/oa/project/projectProgress";
- import { listDept } from '@/api/system/dept';
- import { listUser } from '@/api/system/user';
- import { checkPermi } from '@/utils/permission';
-
- export default {
- data() {
- return {
- isSearchExpanded: false,
- queryParams: {
- pageNum: 1,
- pageSize: 10,
- projectNumber: '',
- projectLeader: '',
- projectName: '',
- queryString: '',
- undertakingDept: ''
- },
- projectList: [],
- total: 0,
- loading: false,
- userList: [],
- deptList: [],
- selectedLeader: '',
- selectedDept: '',
- hasMore: true,
- isRefreshing: false,
- showBackToTop: false,
- scrollTop: 0,
- scrollToId: ''
- }
- },
- onLoad() {
- this.getList();
- this.getDeptList();
- this.getUserList();
- },
- methods: {
- toggleSearch() {
- this.isSearchExpanded = !this.isSearchExpanded;
- },
-
- resetQuery() {
- this.queryParams = {
- pageNum: 1,
- pageSize: 10,
- projectNumber: '',
- projectLeader: '',
- projectName: '',
- queryString: '',
- undertakingDept: ''
- };
- this.selectedLeader = '';
- this.selectedDept = '';
- this.handleQuery();
- },
-
- async getList() {
- this.loading = true;
- try {
- const deptId = this.$store.getters.deptId;
- if (deptId > 106 && deptId != 109) {
- let response = await checkPermi(['oa:allproject:query'])
- if (response == false) {
- this.queryParams.undertakingDept = deptId
- }
- }
- const response = this.queryParams.queryString ?
- await listProjectFuzzy(this.queryParams) :
- await listProject(this.queryParams);
-
- if (this.queryParams.pageNum === 1) {
- this.projectList = response.rows;
- } else {
- this.projectList = [...this.projectList, ...response.rows];
- }
-
- this.total = response.total;
- this.hasMore = this.projectList.length < this.total;
-
- for (let project of this.projectList) {
- const res = await getProjectProgress(project.projectId);
- if (res.data && res.data.length > 0) {
- this.$set(project, 'percentage', Number(res.data[res.data.length - 1].percentage));
- } else {
- this.$set(project, 'percentage', 0);
- }
- if (project.isFinished === '1') {
- this.$set(project, 'percentage', 100);
- }
- }
- } catch (error) {
- uni.showToast({
- title: '获取数据失败',
- icon: 'none'
- });
- } finally {
- this.loading = false;
- this.isRefreshing = false;
- }
- },
-
- async getDeptList() {
- try {
- const res = await listDept({});
- this.deptList = res.data.map(item => ({
- value: item.deptId,
- text: item.deptName
- }));
- } catch (error) {
- uni.showToast({
- title: '获取部门列表失败',
- icon: 'none'
- });
- }
- },
-
- async getUserList() {
- try {
- const res = await listUser({ pageNum: 1, pageSize: 9999 });
- this.userList = res.rows.map(item => ({
- value: item.userId,
- text: item.nickName
- }));
- } catch (error) {
- uni.showToast({
- title: '获取用户列表失败',
- icon: 'none'
- });
- }
- },
-
- handleQuery() {
- this.queryParams.pageNum = 1;
- this.getList();
- },
-
- handleLeaderChange(e) {
- const index = e.detail.value;
- this.queryParams.projectLeader = this.userList[index].value;
- this.selectedLeader = this.userList[index].text;
- this.handleQuery();
- },
-
- handleDeptChange(e) {
- const index = e.detail.value;
- this.queryParams.undertakingDept = this.deptList[index].value;
- this.selectedDept = this.deptList[index].text;
- this.handleQuery();
- },
-
- handleView(row) {
- uni.navigateTo({
- url: `/pages/project/projectInfo?projectId=${row.projectId}`
- });
- },
-
- handleRegister() {
- uni.navigateTo({
- url: '/pages/project/register'
- });
- },
-
- handleExport() {
- uni.showToast({
- title: '导出功能开发中',
- icon: 'none'
- });
- },
-
- loadMore() {
- if (this.hasMore) {
- this.queryParams.pageNum++;
- this.getList();
- }
- },
-
- onRefresh() {
- this.isRefreshing = true;
- this.queryParams.pageNum = 1;
- this.getList();
- },
-
- getProgressColor(percentage) {
- if (!percentage) return '#ff4d4f';
- if (percentage <= 20) return '#ff4d4f';
- if (percentage <= 50) return '#faad14';
- if (percentage <= 80) return '#1890ff';
- return '#52c41a';
- },
-
- onScroll(e) {
- this.scrollTop = e.detail.scrollTop;
- this.showBackToTop = this.scrollTop > 300;
- },
-
- scrollToTop() {
- this.scrollToId = 'top';
- setTimeout(() => {
- this.scrollToId = '';
- }, 300);
- }
- }
- }
- </script>
-
- <style>
- .container {
- padding: 20rpx;
- background-color: #f5f5f5;
- height: 100vh;
- display: flex;
- flex-direction: column;
- }
-
- .search-box {
- background-color: #fff;
- border-radius: 8rpx;
- margin-bottom: 20rpx;
- overflow: hidden;
- }
-
- .search-header {
- padding: 20rpx;
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #ebeef5;
- }
-
- .search-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
-
- .search-icon {
- font-size: 28rpx;
- color: #909399;
- }
-
- .search-content {
- padding: 20rpx;
- }
-
- .search-item {
- margin-bottom: 20rpx;
- }
-
- .search-item text {
- display: block;
- margin-bottom: 10rpx;
- font-size: 28rpx;
- color: #333;
- }
-
- .search-item input,
- .search-item .picker {
- width: 100%;
- height: 80rpx;
- padding: 0 20rpx;
- border: 1px solid #dcdfe6;
- border-radius: 4rpx;
- box-sizing: border-box;
- }
-
- .search-item .picker {
- line-height: 80rpx;
- color: #606266;
- }
-
- .search-buttons {
- display: flex;
- gap: 20rpx;
- margin-top: 20rpx;
- }
-
- .search-buttons button {
- flex: 1;
- }
-
- .list-box {
- flex: 1;
- background-color: #fff;
- border-radius: 8rpx;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
-
- .list-header {
- padding: 20rpx;
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #ebeef5;
- }
-
- .title {
- font-size: 32rpx;
- font-weight: bold;
- }
-
- .header-btns {
- display: flex;
- gap: 20rpx;
- }
-
- .list-scroll {
- flex: 1;
- height: 0;
- }
-
- .list-content {
- padding: 20rpx;
- }
-
- .project-card {
- background-color: #fff;
- border-radius: 8rpx;
- margin-bottom: 20rpx;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
- overflow: hidden;
- }
-
- .card-header {
- padding: 20rpx;
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #ebeef5;
- background-color: #fafafa;
- }
-
- .header-left {
- display: flex;
- align-items: center;
- gap: 20rpx;
- }
-
- .project-name {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
-
- .status-tag,
- .level-tag {
- padding: 4rpx 12rpx;
- border-radius: 4rpx;
- font-size: 24rpx;
- }
-
- .success {
- background-color: #f0f9eb;
- color: #67c23a;
- }
-
- .warning {
- background-color: #fdf6ec;
- color: #e6a23c;
- }
-
- .info {
- background-color: #f4f4f5;
- color: #909399;
- }
-
- .error {
- background-color: #fef0f0;
- color: #f56c6c;
- }
-
- .card-content {
- padding: 20rpx;
- }
-
- .info-row {
- display: flex;
- align-items: center;
- margin-bottom: 16rpx;
- }
-
- .info-row:last-child {
- margin-bottom: 0;
- }
-
- .info-row .label {
- width: 170rpx;
- color: #909399;
- font-size: 28rpx;
- }
-
- .info-row .value {
- flex: 1;
- color: #333;
- font-size: 28rpx;
- }
-
- .progress-box {
- flex: 1;
- display: flex;
- align-items: center;
- gap: 20rpx;
- }
-
- .progress-box progress {
- flex: 1;
- }
-
- .progress-text {
- width: 80rpx;
- text-align: right;
- color: #333;
- font-size: 28rpx;
- }
-
- .card-footer {
- display: flex;
- justify-content: flex-end;
- gap: 20rpx;
- padding: 20rpx;
- border-top: 1px solid #ebeef5;
- background-color: #fafafa;
- }
-
- .card-footer button {
- margin: 0;
- padding: 0 30rpx;
- }
-
- .load-more {
- text-align: center;
- padding: 20rpx;
- color: #909399;
- font-size: 28rpx;
- }
-
- .back-to-top {
- position: fixed;
- right: 30rpx;
- bottom: 100rpx;
- width: 80rpx;
- height: 80rpx;
- background-color: rgba(0, 0, 0, 0.5);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 999;
- }
-
- .back-to-top .iconfont {
- color: #fff;
- font-size: 40rpx;
- }
-
- .icon-top:before {
- content: "↑";
- }
- </style>
|