16 changed files with 1821 additions and 905 deletions
@ -1,114 +1,133 @@ |
|||
// api.js
|
|||
|
|||
// 导入qs库
|
|||
import qs from "qs"; |
|||
|
|||
// 基础URL,可以根据环境来设置
|
|||
// uEnvDev
|
|||
const accountInfo = wx.getAccountInfoSync(); |
|||
// env类型 develop:开发版、trial:体验版、release:正式版
|
|||
export const env = accountInfo.miniProgram.envVersion; |
|||
if (!env) { |
|||
console.error("获取运行环境失败!"); |
|||
console.error("获取运行环境失败!"); |
|||
} |
|||
const baseApi = { |
|||
// 开发版
|
|||
develop: "http://110.40.156.216:30005/api", |
|||
// 体验版
|
|||
trial: "http://110.40.156.216:30005/api", |
|||
// 正式版
|
|||
release: "http://110.40.156.216:30005/api" |
|||
// 开发版
|
|||
develop: "http://110.40.156.216:30005/api", |
|||
// 体验版
|
|||
trial: "http://110.40.156.216:30005/api", |
|||
// 正式版
|
|||
release: "http://110.40.156.216:30005/api", |
|||
}; |
|||
console.log(env, 'env') |
|||
console.log(env, "env"); |
|||
// request请求baseURL
|
|||
const BASE_URL = baseApi[env] || 'http://110.40.156.216:30005/api'; |
|||
|
|||
const BASE_URL = baseApi[env] || "http://110.40.156.216:30005/api"; |
|||
|
|||
function request(url, method, data = {}) { |
|||
const loginRes = uni.getStorageSync('loginRes') || {}; |
|||
const UTCOffset = new Date().getTimezoneOffset(); |
|||
return new Promise((resolve, reject) => { |
|||
uni.request({ |
|||
url: BASE_URL + url, // 完整的URL
|
|||
method: method, |
|||
data: data, |
|||
header: { |
|||
'AccessToken': loginRes.accessToken || '-1', // 请求头,可根据需要调整
|
|||
'UserId': loginRes.appUserId || '-1', // 请求头,可根据需要调整
|
|||
'LanguageType': 0, |
|||
}, |
|||
success: (res) => { |
|||
if (res.statusCode === 200) { |
|||
console.log(res) |
|||
if (res.data.code === 200 | res.data.code === 0) { |
|||
resolve(res.data) |
|||
} else { |
|||
reject(res.data) |
|||
} |
|||
} else { |
|||
reject(res); |
|||
} |
|||
|
|||
}, |
|||
fail: (err) => { |
|||
uni.showToast({ |
|||
title: '请求错误', // 提示的文本内容
|
|||
icon: 'none', // 提示的图标,可选值:'success' | 'loading' | 'none'
|
|||
duration: 2000 // 提示框的显示时间,单位为毫秒,默认1500ms
|
|||
}); |
|||
reject(err); // 请求失败
|
|||
} |
|||
}); |
|||
}); |
|||
const loginRes = uni.getStorageSync("loginRes") || {}; |
|||
const UTCOffset = new Date().getTimezoneOffset(); |
|||
return new Promise((resolve, reject) => { |
|||
uni.request({ |
|||
url: BASE_URL + url, // 完整的URL
|
|||
method: method, |
|||
data: data, |
|||
header: { |
|||
AccessToken: loginRes.accessToken || "-1", // 请求头,可根据需要调整
|
|||
UserId: loginRes.appUserId || "-1", // 请求头,可根据需要调整
|
|||
LanguageType: 0, |
|||
}, |
|||
success: (res) => { |
|||
if (res.statusCode === 200) { |
|||
console.log(res); |
|||
if ((res.data.code === 200) | (res.data.code === 0)) { |
|||
resolve(res.data); |
|||
} else { |
|||
reject(res.data); |
|||
} |
|||
} else { |
|||
reject(res); |
|||
} |
|||
}, |
|||
fail: (err) => { |
|||
uni.showToast({ |
|||
title: "请求错误", // 提示的文本内容
|
|||
icon: "none", // 提示的图标,可选值:'success' | 'loading' | 'none'
|
|||
duration: 2000, // 提示框的显示时间,单位为毫秒,默认1500ms
|
|||
}); |
|||
reject(err); // 请求失败
|
|||
}, |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
// 使用uni.addInterceptor拦截请求,处理GET请求中的数组参数
|
|||
uni.addInterceptor("request", { |
|||
invoke(args) { |
|||
// request 触发前拼接 url
|
|||
const { data, method } = args; |
|||
if (method === "GET") { |
|||
// 如果是get请求,且params是数组类型如arr=[1,2],则转换成arr=1&arr=2
|
|||
const newData = qs.stringify(data, { |
|||
arrayFormat: "repeat", |
|||
}); |
|||
delete args.data; |
|||
args.url = `${args.url}?${newData}`; |
|||
} |
|||
}, |
|||
success(args) {}, |
|||
fail(err) {}, |
|||
complete(res) {}, |
|||
}); |
|||
|
|||
// GET 请求
|
|||
export function get(url, params = {}) { |
|||
return request(url, 'GET', params); |
|||
return request(url, "GET", params); |
|||
} |
|||
|
|||
// POST 请求
|
|||
export function post(url, data = {}) { |
|||
return request(url, 'POST', data); |
|||
} |
|||
|
|||
export function uploadFile(url, filePath = '', data = {}) { |
|||
const loginRes = uni.getStorageSync('loginRes') || {}; |
|||
return new Promise((resolve, reject) => { |
|||
uni.uploadFile({ |
|||
url: BASE_URL + url, // 完整的URL
|
|||
filePath: filePath, |
|||
name: 'image', |
|||
header: { |
|||
'AccessToken': loginRes.accessToken || '-1', // 请求头,可根据需要调整
|
|||
'UserId': loginRes.appUserId || '-1', // 请求头,可根据需要调整
|
|||
LanguageType: 0, |
|||
// 其他需要的请求头信息(如Token)
|
|||
}, |
|||
formData: data, |
|||
success: (res) => { |
|||
if (res.statusCode === 200) { |
|||
if (res.data.code === 500) { |
|||
uni.showToast({ |
|||
title: '系统错误', // 提示的文本内容
|
|||
icon: 'none', // 提示的图标,可选值:'success' | 'loading' | 'none'
|
|||
duration: 2000 // 提示框的显示时间,单位为毫秒,默认1500ms
|
|||
}); |
|||
} |
|||
if (res.data.code === 20001) { |
|||
reject(res.data); |
|||
} |
|||
resolve(res.data); // 返回数据
|
|||
} else { |
|||
reject(res.data); // 返回错误信息
|
|||
} |
|||
}, |
|||
fail: (err) => { |
|||
uni.showToast({ |
|||
title: '请求错误', // 提示的文本内容
|
|||
icon: 'none', // 提示的图标,可选值:'success' | 'loading' | 'none'
|
|||
duration: 2000 // 提示框的显示时间,单位为毫秒,默认1500ms
|
|||
}); |
|||
reject(err); // 请求失败
|
|||
} |
|||
}); |
|||
}) |
|||
return request(url, "POST", data); |
|||
} |
|||
|
|||
export function uploadFile(url, filePath = "", data = {}) { |
|||
const loginRes = uni.getStorageSync("loginRes") || {}; |
|||
return new Promise((resolve, reject) => { |
|||
uni.uploadFile({ |
|||
url: BASE_URL + url, // 完整的URL
|
|||
filePath: filePath, |
|||
name: "image", |
|||
header: { |
|||
AccessToken: loginRes.accessToken || "-1", // 请求头,可根据需要调整
|
|||
UserId: loginRes.appUserId || "-1", // 请求头,可根据需要调整
|
|||
LanguageType: 0, |
|||
// 其他需要的请求头信息(如Token)
|
|||
}, |
|||
formData: data, |
|||
success: (res) => { |
|||
if (res.statusCode === 200) { |
|||
if (res.data.code === 500) { |
|||
uni.showToast({ |
|||
title: "系统错误", // 提示的文本内容
|
|||
icon: "none", // 提示的图标,可选值:'success' | 'loading' | 'none'
|
|||
duration: 2000, // 提示框的显示时间,单位为毫秒,默认1500ms
|
|||
}); |
|||
} |
|||
if (res.data.code === 20001) { |
|||
reject(res.data); |
|||
} |
|||
resolve(res.data); // 返回数据
|
|||
} else { |
|||
reject(res.data); // 返回错误信息
|
|||
} |
|||
}, |
|||
fail: (err) => { |
|||
uni.showToast({ |
|||
title: "请求错误", // 提示的文本内容
|
|||
icon: "none", // 提示的图标,可选值:'success' | 'loading' | 'none'
|
|||
duration: 2000, // 提示框的显示时间,单位为毫秒,默认1500ms
|
|||
}); |
|||
reject(err); // 请求失败
|
|||
}, |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
@ -1,278 +1,273 @@ |
|||
<template> |
|||
<view class="drawer-container"> |
|||
<!-- 蒙层 --> |
|||
<!-- <view |
|||
class="mask" |
|||
:class="{ show: visibleInner && isFullScreen }" |
|||
:style="{ top: isFullScreen ? '0' : halfY + 'px' }" |
|||
@click="close" |
|||
/> --> |
|||
|
|||
<!-- 抽屉 --> |
|||
<view |
|||
class="drawer" |
|||
:class="{ |
|||
dragging: isDragging |
|||
}" |
|||
:style="{ transform: `translateY(${translateY}px)` }" |
|||
@touchstart="onStart" |
|||
@touchmove.stop.prevent="onMove" |
|||
@touchend="onEnd" |
|||
> |
|||
<!-- 顶部拖拽条 --> |
|||
<view class="header" @touchstart.stop="onStart"> |
|||
<view class="bar"></view> |
|||
</view> |
|||
|
|||
<!-- 内容 --> |
|||
<view class="content"> |
|||
<slot></slot> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "PopupDrawer", |
|||
props: { |
|||
visible: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
visibleInner: false, |
|||
|
|||
// 拖动状态 |
|||
isDragging: false, |
|||
startY: 0, |
|||
currentY: 0, |
|||
|
|||
// 位置 |
|||
windowHeight: 0, |
|||
halfY: 0, |
|||
fullY: 0, |
|||
translateY: 0, |
|||
|
|||
startTranslateY: 0, |
|||
|
|||
// 显示状态 |
|||
isFullScreen: false |
|||
}; |
|||
}, |
|||
watch: { |
|||
visible: { |
|||
immediate: true, |
|||
handler(v) { |
|||
if (v) { |
|||
this.open(); |
|||
} else { |
|||
this.close(); |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
created() { |
|||
const res = uni.getSystemInfoSync(); |
|||
this.windowHeight = res.windowHeight; |
|||
|
|||
this.halfY = this.windowHeight * 0.5; |
|||
this.fullY = 0; |
|||
|
|||
this.translateY = this.halfY; |
|||
}, |
|||
methods: { |
|||
/** 打开 */ |
|||
open() { |
|||
this.visibleInner = true; |
|||
this.isFullScreen = false; // 默认打开为半屏 |
|||
this.$nextTick(() => { |
|||
this.animateTo(this.halfY); |
|||
}); |
|||
}, |
|||
|
|||
/** 关闭 */ |
|||
close() { |
|||
this.isFullScreen = false; |
|||
this.animateTo(this.windowHeight, () => { |
|||
this.visibleInner = false; |
|||
this.$emit("update:visible", false); |
|||
this.$emit("close"); |
|||
}); |
|||
}, |
|||
|
|||
/** 手势开始 */ |
|||
onStart(e) { |
|||
// 阻止默认行为和事件冒泡 |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
|
|||
this.isDragging = true; |
|||
this.startY = e.touches[0].clientY; |
|||
this.startTranslateY = this.translateY; |
|||
}, |
|||
|
|||
/** 手势移动 */ |
|||
onMove(e) { |
|||
// 阻止默认行为和事件冒泡 |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
|
|||
if (!this.isDragging) return; |
|||
|
|||
const moveY = e.touches[0].clientY - this.startY; |
|||
let newY = this.startTranslateY + moveY; |
|||
|
|||
// 限制范围 |
|||
if (newY < 0) newY = 0; |
|||
if (newY > this.windowHeight) newY = this.windowHeight; |
|||
|
|||
this.translateY = newY; |
|||
}, |
|||
|
|||
/** 手势结束(吸附逻辑) */ |
|||
onEnd(e) { |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
|
|||
this.isDragging = false; |
|||
|
|||
const moveY = e.changedTouches[0].clientY - this.startY; |
|||
|
|||
// --------------------------- |
|||
// 1. 在全屏状态 |
|||
// --------------------------- |
|||
if (this.translateY === this.fullY) { // translateY = 0 |
|||
// 往下拉超过 80 → 吸附到半屏,而不是关闭 |
|||
if (moveY > 80) { |
|||
this.isFullScreen = false; |
|||
return this.animateTo(this.halfY); |
|||
} |
|||
|
|||
// 往上拉(基本不会)→ 保持全屏 |
|||
if (moveY < -50) { |
|||
this.isFullScreen = true; |
|||
return this.animateTo(this.fullY); |
|||
} |
|||
} |
|||
|
|||
// --------------------------- |
|||
// 2. 在半屏状态 |
|||
// --------------------------- |
|||
if (this.translateY >= this.halfY - 10 && this.translateY <= this.halfY + 10) { |
|||
// 上拉 → 全屏 |
|||
if (moveY < -80) { |
|||
this.isFullScreen = true; |
|||
return this.animateTo(this.fullY); |
|||
} |
|||
// 下拉轻微 → 保持半屏 |
|||
if (moveY > 0 && moveY < 120) { |
|||
this.isFullScreen = false; |
|||
return this.animateTo(this.halfY); |
|||
} |
|||
} |
|||
|
|||
// --------------------------- |
|||
// 3. 如果下拉超过阈值(且不是全屏)→ 关闭 |
|||
// --------------------------- |
|||
if (moveY > 120) { |
|||
this.isFullScreen = false; |
|||
return this.close(); |
|||
} |
|||
|
|||
// --------------------------- |
|||
// 4. 自动吸附最近位置 |
|||
// --------------------------- |
|||
const points = [this.fullY, this.halfY]; |
|||
let nearest = points.reduce((prev, curr) => |
|||
Math.abs(curr - this.translateY) < Math.abs(prev - this.translateY) ? curr : prev |
|||
); |
|||
|
|||
this.isFullScreen = (nearest === this.fullY); |
|||
this.animateTo(nearest); |
|||
}, |
|||
/** 动画过渡(使用 RAF,不卡顿) */ |
|||
animateTo(targetY, callback) { |
|||
// 小程序动画对象 |
|||
const animation = uni.createAnimation({ |
|||
duration: 250, |
|||
timingFunction: "cubic-bezier(0.25, 0.1, 0.25, 1)" |
|||
}); |
|||
|
|||
animation.translateY(targetY).step(); |
|||
|
|||
this.animationData = animation.export(); |
|||
this.translateY = targetY; |
|||
|
|||
if (callback) { |
|||
setTimeout(callback, 260); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.drawer-container { |
|||
position: fixed; |
|||
left: 0; |
|||
top: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
pointer-events: none; /* 默认不阻挡交互 */ |
|||
z-index: 9999; |
|||
|
|||
.mask { |
|||
position: absolute; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.45); |
|||
opacity: 0; |
|||
transition: opacity 0.25s; |
|||
pointer-events: auto; /* 蒙层接收交互 */ |
|||
|
|||
&.show { |
|||
opacity: 1; |
|||
} |
|||
} |
|||
|
|||
.drawer { |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background: #fff; |
|||
border-radius: 24rpx 24rpx 0 0; |
|||
transition: transform 0.25s ease-out; |
|||
pointer-events: auto; /* 抽屉部分接收交互 */ |
|||
|
|||
&.dragging { |
|||
transition: none !important; |
|||
} |
|||
|
|||
.header { |
|||
width: 100%; |
|||
height: 80rpx; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
|
|||
.bar { |
|||
width: 120rpx; |
|||
height: 12rpx; |
|||
border-radius: 6rpx; |
|||
background: #ccc; |
|||
} |
|||
} |
|||
|
|||
.content { |
|||
height: calc(100% - 80rpx); |
|||
overflow-y: auto; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
<template> |
|||
<view class="drawer-container"> |
|||
<!-- 蒙层 --> |
|||
<!-- <view |
|||
class="mask" |
|||
:class="{ show: visibleInner && isFullScreen }" |
|||
:style="{ top: isFullScreen ? '0' : halfY + 'px' }" |
|||
@click="close" |
|||
/> --> |
|||
|
|||
<!-- 抽屉 --> |
|||
<view class="drawer" :class="{ |
|||
dragging: isDragging |
|||
}" :style="{ transform: `translateY(${translateY}px)` }"> |
|||
<!-- 顶部拖拽条 --> |
|||
<view class="header" @touchstart.stop="onStart" @touchmove.stop.prevent="onMove" @touchend="onEnd"> |
|||
<view class="bar"></view> |
|||
</view> |
|||
|
|||
<!-- 内容 --> |
|||
<view class="content"> |
|||
<slot></slot> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "PopupDrawer", |
|||
props: { |
|||
visible: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
visibleInner: false, |
|||
|
|||
// 拖动状态 |
|||
isDragging: false, |
|||
startY: 0, |
|||
currentY: 0, |
|||
|
|||
// 位置 |
|||
windowHeight: 0, |
|||
halfY: 0, |
|||
fullY: 0, |
|||
translateY: 0, |
|||
|
|||
startTranslateY: 0, |
|||
|
|||
// 显示状态 |
|||
isFullScreen: false |
|||
}; |
|||
}, |
|||
watch: { |
|||
visible: { |
|||
immediate: true, |
|||
handler(v) { |
|||
if (v) { |
|||
this.open(); |
|||
} else { |
|||
this.close(); |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
created() { |
|||
const res = uni.getSystemInfoSync(); |
|||
this.windowHeight = res.windowHeight; |
|||
|
|||
this.halfY = this.windowHeight * 0.5; |
|||
this.fullY = 0; |
|||
|
|||
// 初始位置设置为完全隐藏 |
|||
this.translateY = this.windowHeight; |
|||
}, |
|||
methods: { |
|||
/** 打开 */ |
|||
open() { |
|||
this.visibleInner = true; |
|||
this.isFullScreen = false; // 默认打开为半屏 |
|||
this.$nextTick(() => { |
|||
this.animateTo(this.halfY); |
|||
}); |
|||
}, |
|||
|
|||
/** 关闭 */ |
|||
close() { |
|||
this.isFullScreen = false; |
|||
this.animateTo(this.windowHeight, () => { |
|||
this.visibleInner = false; |
|||
this.$emit("update:visible", false); |
|||
this.$emit("close"); |
|||
}); |
|||
}, |
|||
|
|||
/** 手势开始 */ |
|||
onStart(e) { |
|||
// 阻止默认行为和事件冒泡 |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
|
|||
this.isDragging = true; |
|||
this.startY = e.touches[0].clientY; |
|||
this.startTranslateY = this.translateY; |
|||
}, |
|||
|
|||
/** 手势移动 */ |
|||
onMove(e) { |
|||
// 阻止默认行为和事件冒泡 |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
|
|||
if (!this.isDragging) return; |
|||
|
|||
const moveY = e.touches[0].clientY - this.startY; |
|||
let newY = this.startTranslateY + moveY; |
|||
|
|||
// 限制范围 |
|||
if (newY < 0) newY = 0; |
|||
if (newY > this.windowHeight) newY = this.windowHeight; |
|||
|
|||
this.translateY = newY; |
|||
}, |
|||
|
|||
/** 手势结束(吸附逻辑) */ |
|||
onEnd(e) { |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
|
|||
this.isDragging = false; |
|||
|
|||
const moveY = e.changedTouches[0].clientY - this.startY; |
|||
// 调试信息已移除 |
|||
// --------------------------- |
|||
// 1. 在全屏状态 |
|||
// --------------------------- |
|||
if (this.isFullScreen) { // translateY = 0 |
|||
if (moveY > 50) { |
|||
this.isFullScreen = false; |
|||
return this.animateTo(this.halfY); |
|||
} |
|||
|
|||
// 往上拉(基本不会)→ 保持全屏 |
|||
if (moveY < -50) { |
|||
this.isFullScreen = true; |
|||
return this.animateTo(this.fullY); |
|||
} |
|||
} |
|||
|
|||
// --------------------------- |
|||
// 2. 在半屏状态 |
|||
// --------------------------- |
|||
if (!this.isFullScreen) { |
|||
if (moveY < -80) { |
|||
this.isFullScreen = true; |
|||
return this.animateTo(this.fullY); |
|||
} |
|||
// 下拉轻微 → 保持半屏 |
|||
if (moveY > 0 && moveY < 50) { |
|||
this.isFullScreen = false; |
|||
return this.animateTo(this.halfY); |
|||
} |
|||
} |
|||
|
|||
// --------------------------- |
|||
// 3. 如果下拉超过阈值(且不是全屏)→ 关闭 |
|||
// --------------------------- |
|||
if (moveY > 50 && !this.isFullScreen) { |
|||
this.isFullScreen = false; |
|||
return this.close(); |
|||
} |
|||
|
|||
// --------------------------- |
|||
// 4. 自动吸附最近位置 |
|||
// --------------------------- |
|||
const points = [this.fullY, this.halfY]; |
|||
let nearest = points.reduce((prev, curr) => |
|||
Math.abs(curr - this.translateY) < Math.abs(prev - this.translateY) ? curr : prev |
|||
); |
|||
|
|||
this.isFullScreen = (nearest === this.fullY); |
|||
this.animateTo(nearest); |
|||
}, |
|||
/** 动画过渡(使用 RAF,不卡顿) */ |
|||
animateTo(targetY, callback) { |
|||
// 小程序动画对象 |
|||
const animation = uni.createAnimation({ |
|||
duration: 250, |
|||
timingFunction: "cubic-bezier(0.25, 0.1, 0.25, 1)" |
|||
}); |
|||
|
|||
animation.translateY(targetY).step(); |
|||
|
|||
this.animationData = animation.export(); |
|||
this.translateY = targetY; |
|||
|
|||
if (callback) { |
|||
setTimeout(callback, 260); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.drawer-container { |
|||
position: fixed; |
|||
left: 0; |
|||
top: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
pointer-events: none; |
|||
/* 默认不阻挡交互 */ |
|||
z-index: 9999; |
|||
|
|||
.mask { |
|||
position: absolute; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.45); |
|||
opacity: 0; |
|||
transition: opacity 0.25s; |
|||
pointer-events: auto; |
|||
/* 蒙层接收交互 */ |
|||
|
|||
&.show { |
|||
opacity: 1; |
|||
} |
|||
} |
|||
|
|||
.drawer { |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background: #fff; |
|||
border-radius: 24rpx 24rpx 0 0; |
|||
transition: transform 0.25s ease-out; |
|||
pointer-events: auto; |
|||
/* 抽屉部分接收交互 */ |
|||
|
|||
&.dragging { |
|||
transition: none !important; |
|||
} |
|||
|
|||
.header { |
|||
width: 100%; |
|||
height: 60rpx; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
|
|||
.bar { |
|||
width: 120rpx; |
|||
height: 12rpx; |
|||
border-radius: 6rpx; |
|||
background: #ccc; |
|||
} |
|||
} |
|||
|
|||
.content { |
|||
height: calc(100% - 80rpx); |
|||
overflow-y: auto; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,275 @@ |
|||
{ |
|||
"name": "laowumap", |
|||
"lockfileVersion": 3, |
|||
"requires": true, |
|||
"packages": { |
|||
"": { |
|||
"dependencies": { |
|||
"qs": "^6.14.0" |
|||
} |
|||
}, |
|||
"node_modules/call-bind-apply-helpers": { |
|||
"version": "1.0.2", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", |
|||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"es-errors": "^1.3.0", |
|||
"function-bind": "^1.1.2" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/call-bound": { |
|||
"version": "1.0.4", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/call-bound/-/call-bound-1.0.4.tgz", |
|||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"call-bind-apply-helpers": "^1.0.2", |
|||
"get-intrinsic": "^1.3.0" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/dunder-proto": { |
|||
"version": "1.0.1", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/dunder-proto/-/dunder-proto-1.0.1.tgz", |
|||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"call-bind-apply-helpers": "^1.0.1", |
|||
"es-errors": "^1.3.0", |
|||
"gopd": "^1.2.0" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/es-define-property": { |
|||
"version": "1.0.1", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/es-define-property/-/es-define-property-1.0.1.tgz", |
|||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", |
|||
"license": "MIT", |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/es-errors": { |
|||
"version": "1.3.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/es-errors/-/es-errors-1.3.0.tgz", |
|||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", |
|||
"license": "MIT", |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/es-object-atoms": { |
|||
"version": "1.1.1", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/es-object-atoms/-/es-object-atoms-1.1.1.tgz", |
|||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"es-errors": "^1.3.0" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/function-bind": { |
|||
"version": "1.1.2", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz", |
|||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", |
|||
"license": "MIT", |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/get-intrinsic": { |
|||
"version": "1.3.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/get-intrinsic/-/get-intrinsic-1.3.0.tgz", |
|||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"call-bind-apply-helpers": "^1.0.2", |
|||
"es-define-property": "^1.0.1", |
|||
"es-errors": "^1.3.0", |
|||
"es-object-atoms": "^1.1.1", |
|||
"function-bind": "^1.1.2", |
|||
"get-proto": "^1.0.1", |
|||
"gopd": "^1.2.0", |
|||
"has-symbols": "^1.1.0", |
|||
"hasown": "^2.0.2", |
|||
"math-intrinsics": "^1.1.0" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/get-proto": { |
|||
"version": "1.0.1", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/get-proto/-/get-proto-1.0.1.tgz", |
|||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"dunder-proto": "^1.0.1", |
|||
"es-object-atoms": "^1.0.0" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/gopd": { |
|||
"version": "1.2.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/gopd/-/gopd-1.2.0.tgz", |
|||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", |
|||
"license": "MIT", |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/has-symbols": { |
|||
"version": "1.1.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/has-symbols/-/has-symbols-1.1.0.tgz", |
|||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", |
|||
"license": "MIT", |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/hasown": { |
|||
"version": "2.0.2", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", |
|||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"function-bind": "^1.1.2" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/math-intrinsics": { |
|||
"version": "1.1.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/math-intrinsics/-/math-intrinsics-1.1.0.tgz", |
|||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", |
|||
"license": "MIT", |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
} |
|||
}, |
|||
"node_modules/object-inspect": { |
|||
"version": "1.13.4", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/object-inspect/-/object-inspect-1.13.4.tgz", |
|||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", |
|||
"license": "MIT", |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/qs": { |
|||
"version": "6.14.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/qs/-/qs-6.14.0.tgz", |
|||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", |
|||
"license": "BSD-3-Clause", |
|||
"dependencies": { |
|||
"side-channel": "^1.1.0" |
|||
}, |
|||
"engines": { |
|||
"node": ">=0.6" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/side-channel": { |
|||
"version": "1.1.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/side-channel/-/side-channel-1.1.0.tgz", |
|||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"es-errors": "^1.3.0", |
|||
"object-inspect": "^1.13.3", |
|||
"side-channel-list": "^1.0.0", |
|||
"side-channel-map": "^1.0.1", |
|||
"side-channel-weakmap": "^1.0.2" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/side-channel-list": { |
|||
"version": "1.0.0", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/side-channel-list/-/side-channel-list-1.0.0.tgz", |
|||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"es-errors": "^1.3.0", |
|||
"object-inspect": "^1.13.3" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/side-channel-map": { |
|||
"version": "1.0.1", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/side-channel-map/-/side-channel-map-1.0.1.tgz", |
|||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"call-bound": "^1.0.2", |
|||
"es-errors": "^1.3.0", |
|||
"get-intrinsic": "^1.2.5", |
|||
"object-inspect": "^1.13.3" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
}, |
|||
"node_modules/side-channel-weakmap": { |
|||
"version": "1.0.2", |
|||
"resolved": "https://repo.huaweicloud.com/repository/npm/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", |
|||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"call-bound": "^1.0.2", |
|||
"es-errors": "^1.3.0", |
|||
"get-intrinsic": "^1.2.5", |
|||
"object-inspect": "^1.13.3", |
|||
"side-channel-map": "^1.0.1" |
|||
}, |
|||
"engines": { |
|||
"node": ">= 0.4" |
|||
}, |
|||
"funding": { |
|||
"url": "https://github.com/sponsors/ljharb" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"dependencies": { |
|||
"qs": "^6.14.0" |
|||
} |
|||
} |
|||
@ -0,0 +1,154 @@ |
|||
<template> |
|||
<view class="favorites-page"> |
|||
<scroll-view class="search-list" scroll-y :refresher-enabled="true" :refresher-triggered="refreshing" |
|||
@refresherrefresh="onRefresh" @scrolltolower="onLoadMore"> |
|||
<template v-for="item in list" :key="item.jobId"> |
|||
<recruitment :dataSource="item"></recruitment> |
|||
<view class="driver"></view> |
|||
</template> |
|||
<view class="loading-more"> |
|||
<text v-if="loadingMore">加载中...</text> |
|||
<text v-else-if="noMoreData">没有更多数据了</text> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { |
|||
onLoad |
|||
} from '@dcloudio/uni-app' |
|||
import { |
|||
collectCancel, |
|||
collectGetCollectList, |
|||
getAppJobVO, tagGetListPage |
|||
} from '../../api'; |
|||
import { |
|||
ref |
|||
} from 'vue'; |
|||
import { |
|||
delay |
|||
} from '../../utils'; |
|||
let currentPage = 1 |
|||
const list = ref([]) |
|||
const refreshing = ref(false) |
|||
const loadingMore = ref(false) |
|||
const noMoreData = ref(false) |
|||
const pageSize = ref(10) |
|||
|
|||
const hanleGetList = async (params = {}) => { |
|||
const res = await collectGetCollectList({ |
|||
pageNum: currentPage, |
|||
pageSize: pageSize.value, |
|||
...params |
|||
}) |
|||
console.log(res) |
|||
res.data.records = res.data.records.map((item) => ({ |
|||
...item, |
|||
isCollect: true |
|||
})) |
|||
|
|||
if (currentPage === 1) { |
|||
// 刷新或首次加载 |
|||
list.value = res.data.records |
|||
} else { |
|||
// 加载更多 |
|||
|
|||
if (res.data.records.length > 0) { |
|||
list.value.records = [...list.value.records, ...res.data.records] |
|||
currentPage++ |
|||
} else { |
|||
noMoreData.value = true |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
onLoad(() => { |
|||
currentPage = 1 |
|||
hanleGetList() |
|||
}) |
|||
|
|||
|
|||
|
|||
const onRefresh = async () => { |
|||
console.log("刷新") |
|||
refreshing.value = true |
|||
loadingMore.value = false |
|||
noMoreData.value = false |
|||
try { |
|||
await delay(1000) |
|||
currentPage = 1 |
|||
await hanleGetList() |
|||
} catch (err) { |
|||
console.log(err) |
|||
uni.showToast({ |
|||
title: '刷新失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
refreshing.value = false |
|||
} |
|||
} |
|||
|
|||
const onLoadMore = async () => { |
|||
// 如果已经在加载或没有更多数据,则不执行 |
|||
console.log("dao dibule") |
|||
if (loadingMore.value || noMoreData.value) return |
|||
|
|||
loadingMore.value = true |
|||
currentPage++ |
|||
|
|||
try { |
|||
await delay(1000) |
|||
await hanleGetList() |
|||
} catch (error) { |
|||
uni.showToast({ |
|||
title: '加载失败', |
|||
icon: 'none' |
|||
}) |
|||
currentPage-- |
|||
} finally { |
|||
loadingMore.value = false |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.favorites-page { |
|||
// 页面容器设置 |
|||
height: 100vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
background-color: #f8f8f8; |
|||
// 列表样式 - 可滚动区域 |
|||
.search-list { |
|||
height: 100vh; |
|||
box-sizing: border-box; |
|||
padding: 4px; |
|||
// flex: 1; |
|||
// overflow-y: auto; |
|||
|
|||
// 隐藏滚动条但保留滚动功能 |
|||
::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
|
|||
-ms-overflow-style: none; |
|||
scrollbar-width: none; |
|||
} |
|||
|
|||
.driver { |
|||
width: 100%; |
|||
height: 1px; |
|||
background-color: #eee; |
|||
} |
|||
|
|||
.loading-more { |
|||
text-align: center; |
|||
padding: 16px 0; |
|||
font-size: 14px; |
|||
color: #999; |
|||
} |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
Loading…
Reference in new issue