452 lines
13 KiB
Vue
452 lines
13 KiB
Vue
<template>
|
||
<view class="container">
|
||
<!-- 搜索栏 -->
|
||
<view class="search-bar card">
|
||
<input v-model="searchName" placeholder="请输入资产名" class="search-input" />
|
||
<button class="btn btn-primary" @tap="search">搜索</button>
|
||
</view>
|
||
|
||
<!-- 空状态/加载状态 -->
|
||
<view class="load-status" v-if="loading">加载中...</view>
|
||
<view class="load-status" v-else-if="!assetList.length">没有更多数据了</view>
|
||
|
||
<!-- 资产列表 -->
|
||
<view class="list">
|
||
<view class="asset-item card" v-for="item in assetList" :key="item.assetId || item.assetid">
|
||
<view class="asset-main" @tap="gotoDetail(item)">
|
||
<view class="asset-name">{{ item.assetName }}</view>
|
||
<view class="asset-detail">采购日期:{{ item.purchaseDate }}</view>
|
||
<view class="asset-detail">采购价格:{{ item.purchasePrice }}</view>
|
||
<view class="asset-detail">采购数量:{{ item.purchaseQuantity }}</view>
|
||
<view class="asset-detail">设备状态:
|
||
<text v-if="item.status === 1 || item.status === 0">使用中</text>
|
||
<text v-else-if="item.status === 2">报废</text>
|
||
<text v-else-if="item.status === 3">闲置</text>
|
||
<text v-else-if="item.status === 4">维修中</text>
|
||
<text v-else-if="item.status === 5">外借</text>
|
||
<text v-else>未知</text>
|
||
</view>
|
||
</view>
|
||
<view class="asset-actions">
|
||
<button class="btn btn-primary outline" v-if="item.status === 3"
|
||
@tap.stop="borrow(item)">借用</button>
|
||
<button class="btn" @tap="gotoShowMaintenanceInfo(item)">维修信息</button>
|
||
<button class="btn" @tap="gotoDetail(item)">详细信息</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 分页加载状态 -->
|
||
<view class="load-status">
|
||
<text v-if="loading && assetList.length">加载中...</text>
|
||
<text v-else-if="noMore && assetList.length">没有更多数据了</text>
|
||
</view>
|
||
|
||
<!-- 借用弹窗 -->
|
||
<view class="modal" v-if="showBorrowPopup">
|
||
<view class="modal-content">
|
||
<view class="modal-header">
|
||
<text class="modal-title">借用设备信息</text>
|
||
<text class="modal-close" @tap="cancelBorrow">×</text>
|
||
</view>
|
||
<view class="modal-body">
|
||
<view class="row"><text class="label">设备名称:</text><text class="value">{{ selectedAsset?.assetName
|
||
}}</text></view>
|
||
<view class="form-item">
|
||
<text class="label">借用人电话:</text>
|
||
<input v-model="borrowForm.lenderMobile" placeholder="请输入借用人电话" type="number"
|
||
@input="searchUserByMobile" />
|
||
<view class="user-suggestions" v-if="userSuggestions.length">
|
||
<view class="suggestion-item" v-for="u in userSuggestions" :key="u.userMobile"
|
||
@tap="selectUser(u)">
|
||
{{ u.userName }} ({{ u.userMobile }}) (id: {{ u.id }})
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- <view class="form-item">
|
||
<text class="label">借用人姓名:</text>
|
||
<input v-model="borrowForm.borrowerName" disabled placeholder="请输入手机号查询" />
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">借用人ID:</text>
|
||
<input v-model="borrowForm.borrowerId" disabled placeholder="请输入手机号查询" />
|
||
</view> -->
|
||
<view class="form-item">
|
||
<text class="label">借用日期:</text>
|
||
<picker mode="date" :value="borrowForm.borrowDate" @change="onBorrowDateChange">
|
||
<view class="picker-value">{{ borrowForm.borrowDate || '请选择借用日期' }}</view>
|
||
</picker>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">预计归还日期:</text>
|
||
<picker mode="date" @change="onReturnDateChange">
|
||
<view class="picker-value">{{ borrowForm.returnDate || '请选择归还日期' }}</view>
|
||
</picker>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">备注:</text>
|
||
<textarea v-model="borrowForm.note" placeholder="请输入备注" />
|
||
</view>
|
||
</view>
|
||
<view class="modal-footer">
|
||
<button class="btn" @tap="cancelBorrow">取消</button>
|
||
<button class="btn btn-primary" @tap="confirmBorrow">确认</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
|
||
// @ts-ignore JS模块无类型声明
|
||
import { searchFixedAssets, fetchFixedAssets, fetchMaintenanceInfoById, borrowAssetById, returnAsset, searchUserByMobile as apiSearchUserByMobile } from '@/pages/api/fixedAssets.js'
|
||
|
||
interface AssetItem { [k: string]: any }
|
||
|
||
const assetList = ref<AssetItem[]>([])
|
||
const page = ref(1)
|
||
const pageSize = ref(10)
|
||
const loading = ref(false)
|
||
const noMore = ref(false)
|
||
const searchName = ref('')
|
||
|
||
const showBorrowPopup = ref(false)
|
||
const selectedAsset = ref<AssetItem | null>(null)
|
||
const userInfo = uni.getStorageSync('staff') || {}
|
||
const borrowForm = ref({
|
||
lenderMobile: '',
|
||
borrowDate: '',
|
||
returnDate: '',
|
||
location: '',
|
||
borrowDepartmentId: '',
|
||
approverId: '',
|
||
approverMobile: '',
|
||
note: ''
|
||
})
|
||
const userSuggestions = ref<any[]>([])
|
||
let searchTimeout: any = null
|
||
const isSearching = ref(false)
|
||
|
||
function formatDate(d: Date) {
|
||
const y = d.getFullYear()
|
||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
return `${y}-${m}-${day}`
|
||
}
|
||
|
||
onLoad(() => {
|
||
getAssetList()
|
||
})
|
||
|
||
onReachBottom(() => {
|
||
if (!loading.value && !noMore.value) {
|
||
page.value++
|
||
getAssetList()
|
||
}
|
||
})
|
||
|
||
async function search() {
|
||
loading.value = true
|
||
try {
|
||
if (!searchName.value) {
|
||
assetList.value = []
|
||
page.value = 1
|
||
noMore.value = false
|
||
await getAssetList()
|
||
return
|
||
}
|
||
const data: any = await searchFixedAssets(searchName.value)
|
||
assetList.value = data?.data || []
|
||
noMore.value = true
|
||
} catch (e) {
|
||
console.error('获取数据失败', e)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
async function getAssetList() {
|
||
loading.value = true
|
||
try {
|
||
const data: any = await fetchFixedAssets(page.value, pageSize.value)
|
||
const list = data?.data?.list || []
|
||
if (list.length < pageSize.value) noMore.value = true
|
||
assetList.value = assetList.value.concat(list)
|
||
} catch (e) {
|
||
console.error('获取数据失败', e)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function gotoDetail(item: AssetItem) {
|
||
const sn = item.assetSn || item.assetSN || item.asset_sn
|
||
if (sn) {
|
||
uni.navigateTo({ url: `/pages/fixed-assets/detail?assetSn=${encodeURIComponent(sn)}` })
|
||
} else {
|
||
uni.navigateTo({ url: '/pages/fixed-assets/detail', success(res) { res.eventChannel.emit('sendData', { data: item }) } })
|
||
}
|
||
}
|
||
|
||
function gotoShowMaintenanceInfo(item: AssetItem) {
|
||
const id = item.assetId || item.assetid
|
||
if (id)
|
||
uni.navigateTo({ url: `/pages/fixed-assets/maintenance-info?assetId=${id}` })
|
||
else
|
||
uni.navigateTo({
|
||
url: '/pages/fixed-assets/maintenance-info',
|
||
success(res) { res.eventChannel.emit('sendData', { data: item }) }
|
||
})
|
||
}
|
||
|
||
function borrow(item: AssetItem) {
|
||
selectedAsset.value = item
|
||
showBorrowPopup.value = true
|
||
userSuggestions.value = []
|
||
borrowForm.value.lenderMobile = ''
|
||
// 默认借用日期为今天
|
||
borrowForm.value.borrowDate = formatDate(new Date())
|
||
}
|
||
|
||
function cancelBorrow() {
|
||
showBorrowPopup.value = false
|
||
userSuggestions.value = []
|
||
}
|
||
|
||
function onBorrowDateChange(e: any) { borrowForm.value.borrowDate = e.detail.value }
|
||
function onReturnDateChange(e: any) { borrowForm.value.returnDate = e.detail.value }
|
||
|
||
async function searchUserByMobile() {
|
||
const mobile = borrowForm.value.lenderMobile
|
||
if (searchTimeout) clearTimeout(searchTimeout)
|
||
if (!mobile || mobile.length < 3) { userSuggestions.value = []; return }
|
||
isSearching.value = true
|
||
searchTimeout = setTimeout(async () => {
|
||
try {
|
||
const resp: any = await apiSearchUserByMobile(mobile)
|
||
userSuggestions.value = resp.data ?? []
|
||
} catch (err) {
|
||
userSuggestions.value = []
|
||
uni.showToast({ title: '搜索失败', icon: 'none' })
|
||
} finally {
|
||
isSearching.value = false
|
||
}
|
||
}, 500)
|
||
}
|
||
|
||
function selectUser(user: any) {
|
||
borrowForm.value.lenderMobile = user.userMobile
|
||
}
|
||
|
||
async function confirmBorrow() {
|
||
const s = selectedAsset.value || {}
|
||
const payload = {
|
||
assetId: s.assetId || s.assetid,
|
||
assetName: s.assetName,
|
||
borrowDate: borrowForm.value.borrowDate,
|
||
borrowerMobile: borrowForm.value.lenderMobile,
|
||
lenderMobile: s.headerMobile,
|
||
note: borrowForm.value.note,
|
||
registrantDate: new Date().toISOString(),
|
||
registrantId: s.registrantId,
|
||
registrantMobile: s.registrantMobile,
|
||
returnDate: borrowForm.value.returnDate
|
||
}
|
||
try {
|
||
await borrowAssetById(payload)
|
||
uni.showModal({ content: '借用成功', showCancel: false })
|
||
showBorrowPopup.value = false
|
||
// 刷新当前页
|
||
assetList.value = []
|
||
page.value = 1
|
||
noMore.value = false
|
||
await getAssetList()
|
||
} catch (err) {
|
||
uni.showToast({ title: '借用失败', icon: 'none' })
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container {
|
||
padding: 24rpx;
|
||
}
|
||
|
||
.card {
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.search-bar {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
padding: 20rpx;
|
||
align-items: center;
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
background: #f5f7fb;
|
||
border-radius: 12rpx;
|
||
padding: 12rpx 16rpx;
|
||
}
|
||
|
||
.btn {
|
||
padding: 0 20rpx;
|
||
background: #e9efff;
|
||
color: #3a5ddd;
|
||
border-radius: 10rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #3a5ddd;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-primary.outline {
|
||
background: #fff;
|
||
color: #3a5ddd;
|
||
border: 2rpx solid #3a5ddd;
|
||
}
|
||
|
||
.list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.asset-item {
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.asset-main {
|
||
border-bottom: 1rpx solid #f0f1f5;
|
||
padding-bottom: 16rpx;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.asset-name {
|
||
font-size: 30rpx;
|
||
color: #222;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.asset-detail {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
margin-top: 6rpx;
|
||
}
|
||
|
||
.asset-actions {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.load-status {
|
||
text-align: center;
|
||
color: #9a9aa0;
|
||
margin: 20rpx 0;
|
||
}
|
||
|
||
/* 弹窗样式与项目统一 */
|
||
.modal {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.45);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 100;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 86%;
|
||
max-height: 76vh;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.modal-header {
|
||
padding: 20rpx 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-bottom: 1rpx solid #eee;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 30rpx;
|
||
color: #222;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.modal-close {
|
||
font-size: 40rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 16rpx 24rpx;
|
||
max-height: 60vh;
|
||
}
|
||
|
||
.modal-footer {
|
||
padding: 16rpx 24rpx;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 16rpx;
|
||
border-top: 1rpx solid #eee;
|
||
}
|
||
|
||
.modal-footer .btn {
|
||
flex: 1;
|
||
}
|
||
|
||
.label {
|
||
color: #666;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.value {
|
||
color: #333;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.form-item {
|
||
margin: 16rpx 0;
|
||
}
|
||
|
||
.picker-value {
|
||
padding: 10rpx 12rpx;
|
||
background: #f5f7fb;
|
||
border-radius: 8rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.user-suggestions {
|
||
background: #fff;
|
||
border: 1rpx solid #eee;
|
||
border-radius: 8rpx;
|
||
margin-top: 8rpx;
|
||
max-height: 240rpx;
|
||
overflow: auto;
|
||
}
|
||
|
||
.suggestion-item {
|
||
padding: 10rpx 12rpx;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
}
|
||
</style>
|