123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- <!-- components/ProjectPicker.vue -->
- <template>
- <uni-popup ref="popup" type="bottom" @maskClick="close">
- <view class="modal-container">
- <!-- 搜索框 -->
- <view class="search-box">
- <uni-data-select v-model="keyword" :localdata="keywordData" :clear="false" style="width: 30rpx;"></uni-data-select>
- <uni-icons type="search" size="18" color="#999" />
- <input class="search-input" placeholder="输入项目编号/名称搜索" v-model="searchKeyword" @input="handleSearch" />
- </view>
-
- <!-- 项目列表 -->
- <scroll-view scroll-y class="list-container" @scrolltolower="loadMore" :scroll-top="scrollTop">
- <view v-for="(item,index) in projectList" :key="'p' + index" class="list-item"
- :class="{ selected: selectedProject && selectedProject.projectId === item.projectId }"
- @click="handleSelect(item)">
- <view class="item-content">
- <text class="project-id">{{ item.projectNumber }}</text>
- <text class="project-name">{{ item.projectName }}</text>
- </view>
- <uni-icons v-if="selectedProject && selectedProject.projectId === item.projectId" type="checkmarkempty"
- color="#007AFF" size="18" />
- </view>
-
- <!-- 加载状态 -->
- <view class="loading-status">
- <text v-if="loading">加载中...</text>
- <text v-else-if="!hasMore">没有更多了</text>
- </view>
- </scroll-view>
-
- <!-- 底部操作 -->
- <view class="footer">
- <button class="btn" @click="close">取消</button>
- <button class="btn confirm-btn" @click="confirm">确定</button>
- </view>
- </view>
- </uni-popup>
- </template>
-
- <script>
- import {
- debounce
- } from 'lodash-es';
- import {
- listProject,
- submitProject,
- modifyProject,
- delProject
- } from "@/api/oa/project/project";
- export default {
- name: 'ProjectPickerModal',
- props: {
- // 已选项目
- selected: {
- type: Object,
- default: null
- },
- // 是否显示弹窗
- visible: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
- searchKeyword: '', // 搜索关键词
- projectList: [], // 项目列表
- currentPage: 1, // 当前页码
- pageSize: 10, // 每页数量
- hasMore: true, // 是否还有更多
- loading: false, // 加载状态
- scrollTop: 0, // 滚动位置
- selectedProject: null, // 当前选中项目
- keyword: 'projectNumber',
- keywordData: [{
- text: '项目编号',
- value: 'projectNumber',
- },
- {
- text: '项目名称',
- value: 'projectName'
- }
- ]
- };
- },
- watch: {
- visible(newVal) {
- if (newVal) {
- this.$refs.popup.open();
- this.initData();
- } else {
- this.$refs.popup.close();
- }
- },
- selected: {
- immediate: true,
- handler(val) {
- this.selectedProject = val ? {
- ...val
- } : null;
- }
- }
- },
- created() {
- this.debouncedSearch = debounce(this.searchProjects, 300);
- },
- methods: {
- // 初始化数据
- initData() {
- this.currentPage = 1;
- this.hasMore = true;
- this.projectList = [];
- this.loadProjects();
- },
-
- // 获取项目列表
- async loadProjects() {
- if (this.loading || !this.hasMore) return;
-
- this.loading = true;
- try {
- const keyword = this.keyword
- let res;
- if (keyword == 'projectNumber') {
- res = await listProject({
- projectNumber: this.searchKeyword,
- pageNum: this.currentPage,
- pageSize: this.pageSize
- });
- }else{
- res = await listProject({
- projectName: this.searchKeyword,
- pageNum: this.currentPage,
- pageSize: this.pageSize
- });
- }
-
- console.log(res)
- this.projectList = this.currentPage === 1 ?
- res.rows : [...this.projectList, ...res.rows];
-
- this.hasMore = res.total > this.currentPage * this.pageSize;
- this.currentPage++;
- } catch (error) {
- console.error('获取项目列表失败:', error);
- } finally {
- this.loading = false;
- }
- },
-
- // 搜索项目
- searchProjects() {
- this.currentPage = 1;
- this.hasMore = true;
- this.projectList = [];
- this.loadProjects();
- },
-
- // 处理搜索输入
- handleSearch() {
- this.debouncedSearch();
- },
-
- // 加载更多
- loadMore() {
- if (!this.searchKeyword) this.loadProjects();
- },
-
- // 选择项目
- handleSelect(item) {
- this.selectedProject = this.selectedProject?.projectId === item.projectId ? null : item;
- },
-
- // 确认选择
- confirm() {
- if (this.selectedProject) {
- this.$emit('update:selected', this.selectedProject);
- this.$emit('confirm', this.selectedProject);
- }
- this.close();
- },
-
- // 关闭弹窗
- close() {
- this.$emit('update:visible', false);
- }
- }
- };
- </script>
-
- <style lang="scss" scoped>
- .modal-container {
- background: #fff;
- border-radius: 16px 16px 0 0;
- max-height: 70vh;
- padding: 20px;
- }
-
- .search-box {
- display: flex;
- align-items: center;
- padding: 10px;
- background: #f5f5f5;
- border-radius: 8px;
- margin-bottom: 15px;
-
- .search-input {
- flex: 1;
- margin-left: 8px;
- font-size: 14px;
- }
- }
-
- .list-container {
- max-height: 50vh;
- margin-bottom: 15px;
-
- .list-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 12px;
- border-bottom: 1px solid #eee;
-
- &.selected {
- background-color: #f8f8f8;
- }
-
- .item-content {
- flex: 1;
-
- .project-id {
- color: #666;
- font-size: 12px;
- margin-right: 8px;
- }
-
- .project-name {
- color: #333;
- font-size: 14px;
- }
- }
- }
-
- .loading-status {
- text-align: center;
- padding: 10px;
- color: #999;
- font-size: 12px;
- }
- }
-
- .footer {
- display: flex;
- gap: 15px;
-
- .btn {
- flex: 1;
- border-radius: 8px;
-
- &.confirm-btn {
- background-color: #007AFF;
- color: #fff;
- }
- }
- }
- </style>
|