410 lines
11 KiB
JavaScript
410 lines
11 KiB
JavaScript
// pages/demand/list/index.js
|
|
import { fetchDemandList } from '../../../services/demand/fetchDemand';
|
|
import { fetchConfig } from '../../../services/home/home';
|
|
import { withPromoter } from '../../../utils/promoter';
|
|
|
|
Page({
|
|
|
|
/**
|
|
* 页面的初始数据
|
|
*/
|
|
data: {
|
|
page: 1,
|
|
opType: 0,
|
|
keywords: '',
|
|
demandList: [],
|
|
isEmpty: false,
|
|
status: -1,
|
|
freshPage: false,
|
|
fromPreview: false, // 预览锁
|
|
hasMore: true, // 新增字段,标记是否还有更多数据
|
|
refreshing: false, // 刷新按钮是否处于加载中
|
|
tabList: [
|
|
{ text: '全部', key: -1 },
|
|
{ text: '招标中', key: 1 },
|
|
{ text: '签约中', key: 2 },
|
|
{ text: '工作中', key: 3 },
|
|
{ text: '验收中', key: 4 },
|
|
{ text: '结算中', key: 5 },
|
|
{ text: '交易完成', key: 6 },
|
|
{ text: '交易关闭', key: 7 },
|
|
]
|
|
},
|
|
|
|
// 标签页切换事件处理
|
|
tabChangeHandle(e) {
|
|
const { value } = e.detail;
|
|
this.setData({
|
|
status: value,
|
|
page: 1,
|
|
hasMore: true
|
|
});
|
|
// 根据不同标签加载不同数据
|
|
this.loadDemandList(true);
|
|
},
|
|
//搜索
|
|
onChangeInput(e) {
|
|
const { value } = e.detail;
|
|
this.setData({
|
|
keywords: value
|
|
});
|
|
if (!value) {
|
|
this.loadDemandList(true);
|
|
}
|
|
},
|
|
onChangeSearch(e) {
|
|
const { value } = e.detail;
|
|
if (value !== undefined) {
|
|
this.setData({
|
|
keywords: value
|
|
});
|
|
}
|
|
this.loadDemandList(true);
|
|
},
|
|
// 点击右侧刷新按钮
|
|
async onRefreshTap() {
|
|
if (this.data.refreshing) return;
|
|
this.setData({ refreshing: true });
|
|
await this.loadDemandList(true);
|
|
this.setData({ refreshing: false });
|
|
},
|
|
//图片预览
|
|
onImageView(e) {
|
|
const { images, index } = e.currentTarget.dataset;
|
|
wx.previewImage({
|
|
current: images[index],
|
|
urls: images,
|
|
complete: () => {
|
|
this.setData({ fromPreview: true });
|
|
}
|
|
});
|
|
},
|
|
|
|
// 加载需求列表数据
|
|
async loadDemandList(fresh = false, options = {}) {
|
|
const { silent = false } = options;
|
|
// 上拉加载:请求未结束或没有下一页时直接返回,避免并发把同一页打两次
|
|
if (!fresh && (this._loading || !this.data.hasMore)) {
|
|
return;
|
|
}
|
|
const seq = (this._listSeq || 0) + 1;
|
|
this._listSeq = seq;
|
|
this._loading = true;
|
|
const page = fresh ? 1 : this.data.page;
|
|
const { status, keywords, opType } = this.data;
|
|
if (fresh) {
|
|
wx.pageScrollTo({ scrollTop: 0 });
|
|
}
|
|
if (!silent) {
|
|
wx.showLoading({ title: '加载中', mask: true });
|
|
}
|
|
try {
|
|
const res = await fetchDemandList({ page, status, keywords, opType });
|
|
if (seq !== this._listSeq) {
|
|
return;
|
|
}
|
|
if (!res || res.code !== 1 || !res.data) {
|
|
wx.showToast({ title: (res && res.msg) || '加载失败', icon: 'none' });
|
|
return;
|
|
}
|
|
const nextList = Array.isArray(res.data.data) ? res.data.data : [];
|
|
const hasMore = res.data.current_page < res.data.last_page;
|
|
const start = fresh ? 0 : this.data.demandList.length;
|
|
const patch = {
|
|
hasMore,
|
|
isEmpty: start + nextList.length === 0,
|
|
};
|
|
if (hasMore) {
|
|
patch.page = page + 1;
|
|
} else if (fresh) {
|
|
patch.page = 1;
|
|
}
|
|
if (fresh) {
|
|
patch.demandList = nextList;
|
|
} else {
|
|
nextList.forEach((item, i) => {
|
|
patch[`demandList[${start + i}]`] = item;
|
|
});
|
|
}
|
|
this.setData(patch, () => {
|
|
if (seq !== this._listSeq) {
|
|
return;
|
|
}
|
|
const indices = [];
|
|
for (let i = start; i < this.data.demandList.length; i++) {
|
|
indices.push(i);
|
|
}
|
|
this.checkDescOverflow(indices);
|
|
});
|
|
} catch (err) {
|
|
if (seq === this._listSeq) {
|
|
console.error('Failed to load demand list:', err);
|
|
wx.showToast({ title: '网络异常,请重试', icon: 'none' });
|
|
}
|
|
} finally {
|
|
if (seq === this._listSeq) {
|
|
this._loading = false;
|
|
wx.hideLoading();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 保持当前分页与滚动位置重新拉取列表
|
|
* 用于「详情页/签约页操作后返回」时刷新该条数据的状态与按钮:
|
|
* 一次拉回已加载的全部条目,避免像 loadDemandList(true) 那样回到第一页
|
|
*/
|
|
async refreshListKeepPosition() {
|
|
const { status, keywords, opType, demandList } = this.data;
|
|
const loadedCount = demandList.length;
|
|
if (!loadedCount) {
|
|
await this.loadDemandList(true);
|
|
return;
|
|
}
|
|
const prev = {};
|
|
demandList.forEach((item) => {
|
|
prev[item.id] = { expanded: !!item.expanded, showToggle: !!item.showToggle };
|
|
});
|
|
const seq = (this._listSeq || 0) + 1;
|
|
this._listSeq = seq;
|
|
this._loading = true;
|
|
try {
|
|
const res = await fetchDemandList({
|
|
page: 1,
|
|
pageSize: loadedCount,
|
|
status,
|
|
keywords,
|
|
opType
|
|
});
|
|
if (seq !== this._listSeq) {
|
|
return;
|
|
}
|
|
if (!res || res.code !== 1 || !res.data) {
|
|
return;
|
|
}
|
|
const raw = Array.isArray(res.data.data) ? res.data.data : [];
|
|
// 保留已展开状态,避免返回列表时把「查看全文」收回去
|
|
const nextList = raw.map((item) => {
|
|
const old = prev[item.id];
|
|
return {
|
|
...item,
|
|
expanded: old ? old.expanded : false,
|
|
showToggle: old ? old.showToggle : false,
|
|
};
|
|
});
|
|
this.setData({
|
|
demandList: nextList,
|
|
hasMore: res.data.current_page < res.data.last_page,
|
|
isEmpty: nextList.length === 0
|
|
}, () => {
|
|
if (seq !== this._listSeq) {
|
|
return;
|
|
}
|
|
const indices = [];
|
|
nextList.forEach((item, index) => {
|
|
if (!prev[item.id]) {
|
|
indices.push(index);
|
|
}
|
|
});
|
|
this.checkDescOverflow(indices);
|
|
});
|
|
} catch (err) {
|
|
if (seq === this._listSeq) {
|
|
console.error('Failed to refresh demand list:', err);
|
|
}
|
|
} finally {
|
|
if (seq === this._listSeq) {
|
|
this._loading = false;
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 只测量指定下标的描述高度,按路径更新 showToggle
|
|
* 不改 expanded,加载下一页时已展开的条目保持展开
|
|
* @param {number[]} indices
|
|
*/
|
|
checkDescOverflow(indices) {
|
|
if (!indices || !indices.length) {
|
|
return;
|
|
}
|
|
const seq = this._listSeq;
|
|
const query = this.createSelectorQuery();
|
|
const maxHeight = 60; // 120rpx ≈ 60px
|
|
indices.forEach((index) => {
|
|
query.select(`#desc-${index}`).boundingClientRect();
|
|
});
|
|
query.exec((rects) => {
|
|
if (seq !== this._listSeq || !rects) {
|
|
return;
|
|
}
|
|
const patch = {};
|
|
rects.forEach((rect, i) => {
|
|
const index = indices[i];
|
|
const item = this.data.demandList[index];
|
|
if (!item) {
|
|
return;
|
|
}
|
|
const showToggle = !!(rect && rect.height > maxHeight + 1);
|
|
if (!!item.showToggle !== showToggle) {
|
|
patch[`demandList[${index}].showToggle`] = showToggle;
|
|
}
|
|
});
|
|
if (Object.keys(patch).length) {
|
|
this.setData(patch);
|
|
}
|
|
});
|
|
},
|
|
|
|
// 切换展开/收起
|
|
toggleDesc(e) {
|
|
const index = e.currentTarget.dataset.index;
|
|
const key = `demandList[${index}].expanded`;
|
|
this.setData({ [key]: !this.data.demandList[index].expanded });
|
|
},
|
|
|
|
loadConfig() {
|
|
fetchConfig().then(
|
|
(res) => {
|
|
console.log(res);
|
|
if (res.code == 1) {
|
|
let resData = res.data;
|
|
this.setData({
|
|
tabList: resData.developTypes
|
|
});
|
|
}
|
|
}
|
|
);
|
|
},
|
|
|
|
demandListClickHandle(e) {
|
|
const { index } = e.currentTarget.dataset;
|
|
const item = this.data.demandList[index];
|
|
if (!item) {
|
|
return;
|
|
}
|
|
// 交易完成只允许发布者和接单开发者进入详情,其他人点击无反应
|
|
if (item.button && item.button.key === 'finished' && !item.isMy && !item.isMyWin) {
|
|
return;
|
|
}
|
|
// 参与投标仅开发者可进入报价;发布者不能给自己报名。未入驻先提示,确定后再去申请
|
|
if (item.button && item.button.key === 'bid' && !item.isMy) {
|
|
const role = (wx.getStorageSync('userInfo') || {}).role || '';
|
|
if (role !== 'dev') {
|
|
wx.showModal({
|
|
title: '提示',
|
|
content: '请先申请成为开发者',
|
|
confirmText: '确定',
|
|
cancelText: '取消',
|
|
success: (res) => {
|
|
if (!res.confirm) {
|
|
return;
|
|
}
|
|
wx.navigateTo({
|
|
url: '/pages/developer/join/index',
|
|
});
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
const { id } = item;
|
|
const type = this.data.opType;
|
|
// 大厅和我投标的默认是开发者视角;发布者看自己的需求走客户视角,避免进入报名
|
|
const opType = (type == 'all' || type == 'myQuote') && !item.isMy ? 'quote' : 'view';
|
|
//按后端返回的按钮动作标识判断(acceptPassed=验收),
|
|
//命中时带标记,详情页据此弹出「先到沟通群体验后再验收」的提示
|
|
const isAccept = !!item.button && item.button.key === 'acceptPassed';
|
|
wx.navigateTo({
|
|
url: `/pages/demand/detail/index?id=${id}&opType=${opType}${isAccept ? '&fromAccept=1' : ''}`,
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 生命周期函数--监听页面加载
|
|
*/
|
|
onLoad(options) {
|
|
// 分享卡片不带 opType,缺省进需求大厅;我的/我投标的仍由入口显式传入
|
|
const opType = options.opType || 'all';
|
|
this.setData({ opType });
|
|
wx.setNavigationBarTitle({
|
|
title: opType == 'all' ? '需求大厅' : (opType == 'myQuote' ? '我投标的需求' : '我发布的需求')
|
|
});
|
|
opType == 'all' && this.loadConfig();
|
|
this.loadDemandList();
|
|
},
|
|
|
|
/**
|
|
* 生命周期函数--监听页面初次渲染完成
|
|
*/
|
|
onReady() {
|
|
|
|
},
|
|
|
|
/**
|
|
* 生命周期函数--监听页面显示
|
|
*/
|
|
onShow() {
|
|
if (this.data.fromPreview) {
|
|
// 刚从预览回来,直接跳过
|
|
this.setData({ fromPreview: false });
|
|
return;
|
|
}
|
|
//详情页/签约页/发货页操作成功后会写入该标记,回到列表时以接口数据刷新状态与按钮
|
|
const app = getApp();
|
|
const refreshDemandId = app && app.globalData ? app.globalData.refreshDemandId : 0;
|
|
if (refreshDemandId) {
|
|
//同时清掉 backAndRefresh 打的 freshPage,避免下次返回时又整页重载回到顶部
|
|
app.globalData.refreshDemandId = 0;
|
|
this.setData({ freshPage: false });
|
|
this.refreshListKeepPosition();
|
|
return;
|
|
}
|
|
if (this.data.freshPage) {
|
|
this.setData({ freshPage: false });
|
|
this.loadDemandList(true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 生命周期函数--监听页面隐藏
|
|
*/
|
|
onHide() {
|
|
|
|
},
|
|
|
|
/**
|
|
* 生命周期函数--监听页面卸载
|
|
*/
|
|
onUnload() {
|
|
|
|
},
|
|
|
|
/**
|
|
* 页面相关事件处理函数--监听用户下拉动作
|
|
*/
|
|
async onPullDownRefresh() {
|
|
await this.loadDemandList(true, { silent: true });
|
|
wx.stopPullDownRefresh();
|
|
},
|
|
|
|
/**
|
|
* 页面上拉触底事件的处理函数
|
|
*/
|
|
onReachBottom() {
|
|
if (this._loading || !this.data.hasMore) {
|
|
return;
|
|
}
|
|
this.loadDemandList();
|
|
},
|
|
|
|
/**
|
|
* 用户点击右上角分享
|
|
*/
|
|
onShareAppMessage() {
|
|
return {
|
|
title: '需求大厅',
|
|
path: withPromoter('/pages/demand/list/index?opType=all'),
|
|
};
|
|
}
|
|
}) |