优化ui,修复固定资产借用问题
This commit is contained in:
parent
b0e010f80e
commit
38a551da22
@ -13,6 +13,15 @@
|
|||||||
<!--preload-links-->
|
<!--preload-links-->
|
||||||
<!--app-context-->
|
<!--app-context-->
|
||||||
</head>
|
</head>
|
||||||
|
<style>
|
||||||
|
body::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
body::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"><!--app-html--></div>
|
<div id="app"><!--app-html--></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
|||||||
@ -137,6 +137,12 @@
|
|||||||
"navigationBarTitleText": "申请详情"
|
"navigationBarTitleText": "申请详情"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/consumables/temp-request-edit",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "编辑申请"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/consumables/temp-approve",
|
"path": "pages/consumables/temp-approve",
|
||||||
"style": {
|
"style": {
|
||||||
@ -160,6 +166,13 @@
|
|||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "扫码查看信息"
|
"navigationBarTitleText": "扫码查看信息"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/fixed-assets/lent-management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "出借资产管理",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { formatTime, formatDate, formatDateTime, getHeaders } from './index';
|
import { formatTime, formatDate, formatDateTime, getHeaders } from './index';
|
||||||
|
|
||||||
const BASE_URL = 'http://localhost:3000' // 替换为你的实际后端地址
|
const BASE_URL = 'http://localhost:8090' // 替换为你的实际后端地址
|
||||||
// 请求封装
|
// 请求封装
|
||||||
const request = (url: string, options: any = {}) => {
|
const request = (url: string, options: any = {}) => {
|
||||||
const token = uni.getStorageSync('token')
|
const token = uni.getStorageSync('token')
|
||||||
|
|||||||
@ -178,6 +178,8 @@ export function borrowAssetById(addDTO) {
|
|||||||
//作用:前端 “个人借用记录页” 加载数据时调用(如显示当前用户借了哪些资产)。
|
//作用:前端 “个人借用记录页” 加载数据时调用(如显示当前用户借了哪些资产)。
|
||||||
export function borrowInfo(borrowerId) {
|
export function borrowInfo(borrowerId) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log(1);
|
||||||
|
|
||||||
uni.request({
|
uni.request({
|
||||||
url: `${BASE_URL}/api/asset/borrow/list`,// 接口路径:个人借用记录列表
|
url: `${BASE_URL}/api/asset/borrow/list`,// 接口路径:个人借用记录列表
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -222,6 +224,30 @@ export function returnAsset(id) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//查询所有借出资产列表
|
||||||
|
//作用:前端"出借资产管理"页面加载数据时调用(管理员查看所有借出的资产,包括已归还和未归还)。
|
||||||
|
export function fetchBorrowList() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: `${BASE_URL}/api/asset/borrow/list`, // 接口路径:借出资产列表接口
|
||||||
|
method: 'POST', // 请求方法:POST
|
||||||
|
header: getHeaders(),
|
||||||
|
data: {}, // 请求体:空(获取所有借出记录,不过滤条件)
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
resolve(res.data); // 返回借出资产列表数据
|
||||||
|
} else {
|
||||||
|
reject(res);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//————————————————————————————————————————————————业务接口函数(易耗品相关)——————————————————————————————————————————
|
//————————————————————————————————————————————————业务接口函数(易耗品相关)——————————————————————————————————————————
|
||||||
//易耗品领用
|
//易耗品领用
|
||||||
|
|
||||||
|
|||||||
@ -60,7 +60,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="info-row">
|
<view class="info-row">
|
||||||
<text class="label">需要时间:</text>
|
<text class="label">需要时间:</text>
|
||||||
<text class="value">{{ detail.neededAt || '-' }}</text>
|
<text class="value">{{ detail.neededAt ? new Date(detail.neededAt).toLocaleDateString() : '-' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-row" v-if="detail.approverId">
|
<view class="info-row" v-if="detail.approverId">
|
||||||
<text class="label">审批人ID:</text>
|
<text class="label">审批人ID:</text>
|
||||||
@ -459,47 +459,51 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status-card {
|
.status-card {
|
||||||
background: #3a5ddd;
|
background: #fff;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge {
|
.status-badge {
|
||||||
padding: 12rpx 32rpx;
|
padding: 8rpx 24rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 8rpx;
|
||||||
font-size: 32rpx;
|
font-size: 28rpx;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-pending {
|
.status-pending {
|
||||||
|
background: #fff3e0;
|
||||||
color: #f57c00;
|
color: #f57c00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-approved {
|
.status-approved {
|
||||||
|
background: #e8f5e9;
|
||||||
color: #2e7d32;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-rejected {
|
.status-rejected {
|
||||||
|
background: #ffebee;
|
||||||
color: #c62828;
|
color: #c62828;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-cancelled {
|
.status-cancelled {
|
||||||
|
background: #f5f5f5;
|
||||||
color: #757575;
|
color: #757575;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-completed {
|
.status-completed {
|
||||||
|
background: #e3f2fd;
|
||||||
color: #1976d2;
|
color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-time {
|
.status-time {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: rgba(255, 255, 255, 0.9);
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
@ -527,16 +531,17 @@ onMounted(() => {
|
|||||||
width: 100rpx;
|
width: 100rpx;
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #3a5ddd;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
border: 4rpx solid #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-text {
|
.avatar-text {
|
||||||
font-size: 40rpx;
|
font-size: 36rpx;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,25 +685,26 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.total-card {
|
.total-card {
|
||||||
background: #f9a825;
|
background: #fff;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: 0 8rpx 24rpx rgba(249, 168, 37, 0.2);
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
|
border-left: 6rpx solid #ff9800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-label {
|
.total-label {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #fff;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-value {
|
.total-value {
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #fff;
|
color: #ff6b00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-actions {
|
.footer-actions {
|
||||||
@ -726,12 +732,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-reject {
|
.btn-reject {
|
||||||
background: #ef5350;
|
background: #fff;
|
||||||
color: #fff;
|
color: #f44336;
|
||||||
|
border: 2rpx solid #f44336;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-approve {
|
.btn-approve {
|
||||||
background: #3a5ddd;
|
background: #007aff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -837,12 +844,12 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-confirm {
|
.btn-confirm {
|
||||||
background: #3a5ddd;
|
background: #007aff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-confirm.btn-danger {
|
.btn-confirm.btn-danger {
|
||||||
background: #ef5350;
|
background: #f44336;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-confirm[disabled] {
|
.btn-confirm[disabled] {
|
||||||
|
|||||||
@ -177,7 +177,7 @@ async function loadList(reset = false) {
|
|||||||
|
|
||||||
// 根据开关过滤数据:默认不显示 COMPLETED
|
// 根据开关过滤数据:默认不显示 COMPLETED
|
||||||
if (!showCompleted.value) {
|
if (!showCompleted.value) {
|
||||||
newData = newData.filter((item: ConsumableRequest) => item.status !== 'COMPLETED')
|
newData = newData.filter((item: ConsumableRequest) => item.status !== 'COMPLETED' && item.status !== 'REJECTED')
|
||||||
}
|
}
|
||||||
|
|
||||||
list.value = reset ? newData : [...list.value, ...newData]
|
list.value = reset ? newData : [...list.value, ...newData]
|
||||||
|
|||||||
@ -247,47 +247,51 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status-card {
|
.status-card {
|
||||||
background: #3a5ddd;
|
background: #fff;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge {
|
.status-badge {
|
||||||
padding: 12rpx 32rpx;
|
padding: 8rpx 24rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 8rpx;
|
||||||
font-size: 32rpx;
|
font-size: 28rpx;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-pending {
|
.status-pending {
|
||||||
|
background: #fff3e0;
|
||||||
color: #f57c00;
|
color: #f57c00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-approved {
|
.status-approved {
|
||||||
|
background: #e8f5e9;
|
||||||
color: #2e7d32;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-rejected {
|
.status-rejected {
|
||||||
|
background: #ffebee;
|
||||||
color: #c62828;
|
color: #c62828;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-cancelled {
|
.status-cancelled {
|
||||||
|
background: #f5f5f5;
|
||||||
color: #757575;
|
color: #757575;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-completed {
|
.status-completed {
|
||||||
|
background: #e3f2fd;
|
||||||
color: #1976d2;
|
color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-time {
|
.status-time {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: rgba(255, 255, 255, 0.9);
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
@ -400,25 +404,26 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.total-card {
|
.total-card {
|
||||||
background: #f9a825;
|
background: #fff;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: 0 8rpx 24rpx rgba(249, 168, 37, 0.2);
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
|
border-left: 6rpx solid #ff9800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-label {
|
.total-label {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #fff;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-value {
|
.total-value {
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #fff;
|
color: #ff6b00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-actions {
|
.footer-actions {
|
||||||
@ -445,12 +450,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
background: #ef5350;
|
background: #fff;
|
||||||
color: #fff;
|
color: #f44336;
|
||||||
|
border: 2rpx solid #f44336;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background: #3a5ddd;
|
background: #007aff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
672
src/pages/consumables/temp-request-edit.vue
Normal file
672
src/pages/consumables/temp-request-edit.vue
Normal file
@ -0,0 +1,672 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<scroll-view scroll-y class="content" v-if="!loading">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">
|
||||||
|
<text>基本信息</text>
|
||||||
|
</view>
|
||||||
|
<view class="form-card">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label required">申请人姓名</text>
|
||||||
|
<input class="form-input" v-model="formData.applicantName" placeholder="请输入申请人姓名" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label required">联系电话</text>
|
||||||
|
<input class="form-input" v-model="formData.applicantPhone" type="number" placeholder="请输入联系电话" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">部门</text>
|
||||||
|
<input class="form-input" v-model="formData.department" placeholder="请输入部门" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">申请原因</text>
|
||||||
|
<textarea class="form-textarea" v-model="formData.reason" placeholder="请输入申请原因" maxlength="200" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">需要时间</text>
|
||||||
|
<picker mode="date" :value="formData.neededAt" @change="onDateChange">
|
||||||
|
<view class="form-picker">
|
||||||
|
<text>{{ formData.neededAt || '请选择需要时间' }}</text>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 物料明细 -->
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">
|
||||||
|
<text>物料明细</text>
|
||||||
|
<button class="btn-add" @tap="addItem">+ 添加物料</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="formData.items.length === 0" class="empty-items">
|
||||||
|
<text>暂无物料,请点击上方按钮添加</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-for="(item, index) in formData.items" :key="index" class="item-card">
|
||||||
|
<view class="item-header">
|
||||||
|
<text class="item-number">#{{ index + 1 }}</text>
|
||||||
|
<button class="btn-delete" @tap="deleteItem(index)">删除</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label required">物料名称</text>
|
||||||
|
<view class="form-row">
|
||||||
|
<input class="form-input flex-1" v-model="item.name" placeholder="请输入物料名称" />
|
||||||
|
<button class="btn-catalog" @tap="selectFromCatalog(index)">从目录选择</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">规格</text>
|
||||||
|
<input class="form-input" v-model="item.spec" placeholder="请输入规格" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-row-group">
|
||||||
|
<view class="form-item flex-1">
|
||||||
|
<text class="form-label required">数量</text>
|
||||||
|
<input class="form-input" v-model.number="item.quantity" type="digit" placeholder="数量" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item flex-1">
|
||||||
|
<text class="form-label">单位</text>
|
||||||
|
<input class="form-input" v-model="item.unit" placeholder="单位" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">预估单价</text>
|
||||||
|
<input class="form-input" v-model.number="item.estimatedUnitCost" type="digit" placeholder="请输入预估单价" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">备注</text>
|
||||||
|
<textarea class="form-textarea" v-model="item.remark" placeholder="请输入备注" maxlength="100" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<view v-if="loading" class="loading-container">
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作按钮 -->
|
||||||
|
<view class="footer-actions" v-if="!loading">
|
||||||
|
<button class="btn-secondary" @tap="goBack">取消</button>
|
||||||
|
<button class="btn-primary" @tap="submitForm" :disabled="submitting">
|
||||||
|
{{ submitting ? '保存中...' : '保存修改' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 目录选择弹窗 -->
|
||||||
|
<view v-if="showCatalogModal" class="modal-mask" @tap="closeCatalog">
|
||||||
|
<view class="modal-content" @tap.stop>
|
||||||
|
<view class="modal-header">
|
||||||
|
<text class="modal-title">选择物料</text>
|
||||||
|
<text class="modal-close" @tap="closeCatalog">✕</text>
|
||||||
|
</view>
|
||||||
|
<view class="modal-search">
|
||||||
|
<input class="search-input" v-model="catalogSearch" placeholder="搜索物料名称" />
|
||||||
|
</view>
|
||||||
|
<scroll-view scroll-y class="modal-list">
|
||||||
|
<view v-for="catalog in filteredCatalog" :key="catalog.id"
|
||||||
|
class="catalog-item"
|
||||||
|
@tap="selectCatalog(catalog)">
|
||||||
|
<view class="catalog-name">{{ catalog.name }}</view>
|
||||||
|
<view class="catalog-info">
|
||||||
|
<text v-if="catalog.spec">{{ catalog.spec }}</text>
|
||||||
|
<text v-if="catalog.unit">{{ catalog.unit }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="filteredCatalog.length === 0" class="empty">
|
||||||
|
<text>暂无数据</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import consumableTempAPI, { type RequestItem, type CatalogItem } from '@/pages/api/consumable-temp'
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({
|
||||||
|
applicantName: '',
|
||||||
|
applicantPhone: '',
|
||||||
|
department: '',
|
||||||
|
reason: '',
|
||||||
|
neededAt: '',
|
||||||
|
items: [] as RequestItem[]
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
|
const submitting = ref(false)
|
||||||
|
const requestId = ref('')
|
||||||
|
const currentItemIndex = ref(-1)
|
||||||
|
const showCatalogModal = ref(false)
|
||||||
|
const catalogList = ref<CatalogItem[]>([])
|
||||||
|
const catalogSearch = ref('')
|
||||||
|
|
||||||
|
// 过滤后的目录
|
||||||
|
const filteredCatalog = computed(() => {
|
||||||
|
if (!catalogSearch.value) return catalogList.value
|
||||||
|
const keyword = catalogSearch.value.toLowerCase()
|
||||||
|
return catalogList.value.filter(item =>
|
||||||
|
item.name.toLowerCase().includes(keyword) ||
|
||||||
|
item.spec?.toLowerCase().includes(keyword)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载详情
|
||||||
|
async function loadDetail() {
|
||||||
|
if (!requestId.value) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await consumableTempAPI.getRequestDetail(requestId.value)
|
||||||
|
|
||||||
|
if (res.statusCode === 200 && (res.data as any)?.data) {
|
||||||
|
const detail = (res.data as any).data
|
||||||
|
|
||||||
|
// 填充表单数据
|
||||||
|
formData.value.applicantName = detail.applicantName || ''
|
||||||
|
formData.value.applicantPhone = detail.applicantPhone || ''
|
||||||
|
formData.value.department = detail.department || ''
|
||||||
|
formData.value.reason = detail.reason || ''
|
||||||
|
formData.value.neededAt = detail.neededAt ? detail.neededAt.split('T')[0] : ''
|
||||||
|
|
||||||
|
// 填充物料明细
|
||||||
|
formData.value.items = detail.items?.map((item: any) => ({
|
||||||
|
name: item.name || '',
|
||||||
|
spec: item.spec || '',
|
||||||
|
quantity: item.quantity || 1,
|
||||||
|
unit: item.unit || '',
|
||||||
|
estimatedUnitCost: item.estimatedUnitCost || 0,
|
||||||
|
remark: item.remark || '',
|
||||||
|
catalogId: item.catalogId
|
||||||
|
})) || []
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||||
|
setTimeout(() => uni.navigateBack(), 1500)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载详情失败:', err)
|
||||||
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||||
|
setTimeout(() => uni.navigateBack(), 1500)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载目录
|
||||||
|
async function loadCatalog() {
|
||||||
|
try {
|
||||||
|
const res = await consumableTempAPI.getCatalogList({
|
||||||
|
isActive: true,
|
||||||
|
pageSize: 99
|
||||||
|
})
|
||||||
|
if (res.statusCode === 200 && (res.data as any)?.data) {
|
||||||
|
catalogList.value = (res.data as any).data
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载目录失败:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日期改变
|
||||||
|
function onDateChange(e: any) {
|
||||||
|
formData.value.neededAt = e.detail.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加物料
|
||||||
|
function addItem() {
|
||||||
|
formData.value.items.push({
|
||||||
|
name: '',
|
||||||
|
spec: '',
|
||||||
|
quantity: 1,
|
||||||
|
unit: '',
|
||||||
|
estimatedUnitCost: 0,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除物料
|
||||||
|
function deleteItem(index: number) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '确定要删除这个物料吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
formData.value.items.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从目录选择
|
||||||
|
function selectFromCatalog(index: number) {
|
||||||
|
currentItemIndex.value = index
|
||||||
|
showCatalogModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择目录项
|
||||||
|
function selectCatalog(catalog: CatalogItem) {
|
||||||
|
if (currentItemIndex.value >= 0) {
|
||||||
|
const item = formData.value.items[currentItemIndex.value]
|
||||||
|
item.name = catalog.name
|
||||||
|
item.spec = catalog.spec || ''
|
||||||
|
item.unit = catalog.unit || ''
|
||||||
|
item.catalogId = catalog.id
|
||||||
|
}
|
||||||
|
closeCatalog()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭目录弹窗
|
||||||
|
function closeCatalog() {
|
||||||
|
showCatalogModal.value = false
|
||||||
|
catalogSearch.value = ''
|
||||||
|
currentItemIndex.value = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
function validateForm() {
|
||||||
|
if (!formData.value.applicantPhone) {
|
||||||
|
uni.showToast({ title: '请输入联系电话', icon: 'none' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.value.applicantPhone.length < 3) {
|
||||||
|
uni.showToast({ title: '联系电话格式不正确', icon: 'none' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.value.items.length === 0) {
|
||||||
|
uni.showToast({ title: '请至少添加一项物料', icon: 'none' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < formData.value.items.length; i++) {
|
||||||
|
const item = formData.value.items[i]
|
||||||
|
if (!item.name) {
|
||||||
|
uni.showToast({ title: `第${i + 1}项物料名称不能为空`, icon: 'none' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!item.quantity || item.quantity <= 0) {
|
||||||
|
uni.showToast({ title: `第${i + 1}项物料数量必须大于0`, icon: 'none' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
async function submitForm() {
|
||||||
|
if (!validateForm()) return
|
||||||
|
|
||||||
|
if (submitting.value) return
|
||||||
|
|
||||||
|
submitting.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await consumableTempAPI.updateRequest(requestId.value, formData.value)
|
||||||
|
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success',
|
||||||
|
success: () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// 通知列表页需要刷新
|
||||||
|
uni.$emit('tempRequest:changed')
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '保存失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('保存失败:', err)
|
||||||
|
uni.showToast({ title: '保存失败', icon: 'none' })
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
function goBack() {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要放弃当前修改吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
const currentPage = pages[pages.length - 1] as any
|
||||||
|
const options = currentPage.options || {}
|
||||||
|
|
||||||
|
if (options.id) {
|
||||||
|
requestId.value = options.id
|
||||||
|
loadDetail()
|
||||||
|
loadCatalog()
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '参数错误', icon: 'none' })
|
||||||
|
setTimeout(() => uni.navigateBack(), 1500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f7fb;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24rpx 32rpx 140rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 12rpx 16rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add {
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
margin: 0;
|
||||||
|
height: 56rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-card,
|
||||||
|
.item-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-card {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-bottom: 16rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-number {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete {
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
height: 48rpx;
|
||||||
|
line-height: 32rpx;
|
||||||
|
background: #fff;
|
||||||
|
color: #f44336;
|
||||||
|
border: 2rpx solid #f44336;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label.required::before {
|
||||||
|
content: '*';
|
||||||
|
color: #ff4d4f;
|
||||||
|
margin-right: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input,
|
||||||
|
.form-picker {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
background: #f5f7fb;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
background: #f5f7fb;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-picker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12rpx;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row .flex-1 {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-catalog {
|
||||||
|
padding: 0 20rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
background: #e3f2fd;
|
||||||
|
color: #1976d2;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row-group .form-item {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-items {
|
||||||
|
padding: 80rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-actions {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary,
|
||||||
|
.btn-primary {
|
||||||
|
flex: 1;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary[disabled] {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗样式 */
|
||||||
|
.modal-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 80vh;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 32rpx 32rpx 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-close {
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: #999;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-search {
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 72rpx;
|
||||||
|
line-height: 72rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
background: #f5f7fb;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-list {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-item {
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-info {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
padding: 80rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -241,7 +241,7 @@ onUnload(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline {
|
.btn-outline {
|
||||||
padding: 12rpx 24rpx;
|
padding: 0rpx 24rpx;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #4b7aff;
|
color: #4b7aff;
|
||||||
border: 2rpx solid #4b7aff;
|
border: 2rpx solid #4b7aff;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -237,7 +237,7 @@ function previewImage(imageBase64: string) {
|
|||||||
|
|
||||||
.meta-value {
|
.meta-value {
|
||||||
color: #333;
|
color: #333;
|
||||||
font-size: 28rpx;
|
font-size: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail {
|
.detail {
|
||||||
|
|||||||
547
src/pages/fixed-assets/lent-management.vue
Normal file
547
src/pages/fixed-assets/lent-management.vue
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<!-- 顶部统一卡片 -->
|
||||||
|
<view class="top-card card">
|
||||||
|
<!-- 统计信息 -->
|
||||||
|
<view class="stats-row">
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-value">{{ totalCount }}</text>
|
||||||
|
<text class="stat-label">借出总数</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-value highlight">{{ unreturned }}</text>
|
||||||
|
<text class="stat-label">未归还</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-value success">{{ returned }}</text>
|
||||||
|
<text class="stat-label">已归还</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 筛选和搜索 -->
|
||||||
|
<view class="filter-search-row">
|
||||||
|
<view class="filter-tabs">
|
||||||
|
<view
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: filterStatus === null }"
|
||||||
|
@tap="changeFilter(null)"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: filterStatus === 0 }"
|
||||||
|
@tap="changeFilter(0)"
|
||||||
|
>
|
||||||
|
未归还
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: filterStatus === 1 }"
|
||||||
|
@tap="changeFilter(1)"
|
||||||
|
>
|
||||||
|
已归还
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="search-input-wrap">
|
||||||
|
<text class="search-icon">🔍</text>
|
||||||
|
<input
|
||||||
|
class="search-input"
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="手机号"
|
||||||
|
placeholder-class="search-placeholder"
|
||||||
|
type="number"
|
||||||
|
maxlength="11"
|
||||||
|
/>
|
||||||
|
<text v-if="searchKeyword" class="clear-icon" @tap="clearSearch">✕</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<scroll-view
|
||||||
|
class="list-container"
|
||||||
|
scroll-y
|
||||||
|
refresher-enabled
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresherrefresh="onRefresh"
|
||||||
|
>
|
||||||
|
<view v-if="loading && list.length === 0" class="loading-wrap">
|
||||||
|
<text class="loading-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="list.length === 0" class="empty-wrap">
|
||||||
|
<text class="empty-icon">📦</text>
|
||||||
|
<text class="empty-text">暂无借出记录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="list">
|
||||||
|
<view
|
||||||
|
v-for="item in filteredList"
|
||||||
|
:key="item.borrowId"
|
||||||
|
class="list-item card"
|
||||||
|
>
|
||||||
|
<!-- 顶部:资产名称 + 状态标签 -->
|
||||||
|
<view class="item-header">
|
||||||
|
<view class="asset-name-wrap">
|
||||||
|
<text class="asset-name">{{ item.assetName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="status-badge" :class="item.isReturn === 1 ? 'returned' : 'unreturned'">
|
||||||
|
{{ item.isReturn === 1 ? '已归还' : '未归还' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 信息行 -->
|
||||||
|
<view class="item-info">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">借用人:</text>
|
||||||
|
<text class="info-value">{{ item.borrowerName || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">联系方式:</text>
|
||||||
|
<text class="info-value">{{ item.borrowerMobile || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">借用日期:</text>
|
||||||
|
<text class="info-value">{{ item.borrowDate || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">应还日期:</text>
|
||||||
|
<text class="info-value">{{ item.returnDate || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.actualReturnDate" class="info-row">
|
||||||
|
<text class="info-label">实际归还:</text>
|
||||||
|
<text class="info-value success-text">{{ item.actualReturnDate }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.note" class="info-row">
|
||||||
|
<text class="info-label">备注:</text>
|
||||||
|
<text class="info-value">{{ item.note }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<view v-if="item.isReturn === 0" class="item-actions">
|
||||||
|
<button class="action-btn return-btn" @tap="handleReturn(item)">
|
||||||
|
确认归还
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
|
// @ts-ignore
|
||||||
|
import { fetchBorrowList, returnAsset } from '@/pages/api/fixedAssets.js'
|
||||||
|
|
||||||
|
interface BorrowRecord {
|
||||||
|
borrowId: number
|
||||||
|
assetId: number
|
||||||
|
assetName: string
|
||||||
|
borrowDate: string
|
||||||
|
returnDate: string
|
||||||
|
actualReturnDate: string | null
|
||||||
|
borrowerId: string
|
||||||
|
borrowerName: string | null
|
||||||
|
borrowerMobile: string | null
|
||||||
|
borrowDepartmentId: string | null
|
||||||
|
borrowDepartmentName: string | null
|
||||||
|
location: string | null
|
||||||
|
lenderId: string
|
||||||
|
lenderName: string | null
|
||||||
|
lenderMobile: string | null
|
||||||
|
approverId: string | null
|
||||||
|
approverName: string | null
|
||||||
|
approverMobile: string | null
|
||||||
|
registrantId: string
|
||||||
|
registrantName: string | null
|
||||||
|
registrantMobile: string | null
|
||||||
|
registrantDate: string
|
||||||
|
isReturn: number // 0-未归还 1-已归还
|
||||||
|
note: string
|
||||||
|
deleteFlag: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = ref<BorrowRecord[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const refreshing = ref(false)
|
||||||
|
const filterStatus = ref<number | null>(null)
|
||||||
|
const searchKeyword = ref('')
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
const totalCount = computed(() => list.value.length)
|
||||||
|
const unreturned = computed(() => list.value.filter(item => item.isReturn === 0).length)
|
||||||
|
const returned = computed(() => list.value.filter(item => item.isReturn === 1).length)
|
||||||
|
|
||||||
|
// 筛选后的列表
|
||||||
|
const filteredList = computed(() => {
|
||||||
|
let result = list.value
|
||||||
|
|
||||||
|
// 按状态筛选
|
||||||
|
if (filterStatus.value !== null) {
|
||||||
|
result = result.filter(item => item.isReturn === filterStatus.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按手机号搜索
|
||||||
|
if (searchKeyword.value.trim()) {
|
||||||
|
const keyword = searchKeyword.value.trim()
|
||||||
|
result = result.filter(item =>
|
||||||
|
item.borrowerMobile && item.borrowerMobile.includes(keyword)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
async function loadData() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const result = await fetchBorrowList()
|
||||||
|
if (result.code === 0 && Array.isArray(result.data)) {
|
||||||
|
list.value = result.data
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: result.msg || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载借出记录失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
refreshing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
function onRefresh() {
|
||||||
|
refreshing.value = true
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换筛选
|
||||||
|
function changeFilter(status: number | null) {
|
||||||
|
filterStatus.value = status
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除搜索
|
||||||
|
function clearSearch() {
|
||||||
|
searchKeyword.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 归还资产
|
||||||
|
function handleReturn(item: BorrowRecord) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认归还',
|
||||||
|
content: `确认归还资产:${item.assetName}?`,
|
||||||
|
success: async (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
await doReturnAsset(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行归还操作
|
||||||
|
async function doReturnAsset(item: BorrowRecord) {
|
||||||
|
uni.showLoading({ title: '处理中...' })
|
||||||
|
try {
|
||||||
|
const result = await returnAsset(item.assetId)
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (result.code === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '归还成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
// 刷新列表
|
||||||
|
loadData()
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: result.msg || '归还失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('归还资产失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f7fb;
|
||||||
|
padding-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部统一卡片 */
|
||||||
|
.top-card {
|
||||||
|
margin: 24rpx 24rpx 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 统计信息行 */
|
||||||
|
.stats-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
border-bottom: 2rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value.highlight {
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value.success {
|
||||||
|
color: #51cf66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 筛选和搜索行 */
|
||||||
|
.filter-search-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8rpx;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tab {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 12rpx 8rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
background: #f8f9fa;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tab.active {
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索框 */
|
||||||
|
.search-input-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 10rpx 16rpx;
|
||||||
|
gap: 8rpx;
|
||||||
|
width: 200rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
font-size: 28rpx;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #333;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-icon {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
padding: 0 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表容器 */
|
||||||
|
.list-container {
|
||||||
|
height: calc(100vh - 280rpx);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrap,
|
||||||
|
.empty-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 120rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项 */
|
||||||
|
.list {
|
||||||
|
padding: 0 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
border-bottom: 2rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-name-wrap {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.unreturned {
|
||||||
|
background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 100%);
|
||||||
|
color: #d32f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.returned {
|
||||||
|
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
|
||||||
|
color: #2e7d32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 信息行 */
|
||||||
|
.item-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
color: #999;
|
||||||
|
min-width: 160rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-text {
|
||||||
|
color: #51cf66;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮 */
|
||||||
|
.item-actions {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
padding-top: 24rpx;
|
||||||
|
border-top: 2rpx solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
padding: 0rpx 40rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-btn {
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-btn:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,73 +1,212 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="borrow-list">
|
<!-- 顶部统计卡片 -->
|
||||||
<view v-for="item in borrowList.filter(item => !item.isReturn)" :key="item.borrowId || item.id"
|
<view class="stats-card card">
|
||||||
class="borrow-item card">
|
<view class="stat-item">
|
||||||
|
<text class="stat-value">{{ totalCount }}</text>
|
||||||
|
<text class="stat-label">借用总数</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-divider"></view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-value highlight">{{ borrowingCount }}</text>
|
||||||
|
<text class="stat-label">借用中</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-divider"></view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-value success">{{ returnedCount }}</text>
|
||||||
|
<text class="stat-label">已归还</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 筛选栏 -->
|
||||||
|
<view class="filter-bar card">
|
||||||
|
<view class="filter-tabs">
|
||||||
|
<view
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: filterStatus === null }"
|
||||||
|
@tap="changeFilter(null)"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: filterStatus === 0 }"
|
||||||
|
@tap="changeFilter(0)"
|
||||||
|
>
|
||||||
|
借用中
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: filterStatus === 1 }"
|
||||||
|
@tap="changeFilter(1)"
|
||||||
|
>
|
||||||
|
已归还
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<scroll-view
|
||||||
|
class="list-container"
|
||||||
|
scroll-y
|
||||||
|
refresher-enabled
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresherrefresh="onRefresh"
|
||||||
|
>
|
||||||
|
<view v-if="loading && myBorrowList.length === 0" class="loading-wrap">
|
||||||
|
<text class="loading-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="myBorrowList.length === 0" class="empty-wrap">
|
||||||
|
<text class="empty-icon">📦</text>
|
||||||
|
<text class="empty-text">暂无借用记录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="list">
|
||||||
|
<view
|
||||||
|
v-for="item in filteredList"
|
||||||
|
:key="item.borrowId"
|
||||||
|
class="list-item card"
|
||||||
|
>
|
||||||
|
<!-- 顶部:资产名称 + 状态标签 -->
|
||||||
<view class="item-header">
|
<view class="item-header">
|
||||||
|
<view class="asset-name-wrap">
|
||||||
<text class="asset-name">{{ item.assetName }}</text>
|
<text class="asset-name">{{ item.assetName }}</text>
|
||||||
<!-- <button class="btn btn-primary outline" @tap="button_returnAsset(item)">归还</button> -->
|
|
||||||
</view>
|
</view>
|
||||||
<view class="detail">借用日期:{{ item.borrowDate }}</view>
|
<view class="status-badge" :class="item.isReturn === 1 ? 'returned' : 'borrowing'">
|
||||||
<view class="detail">预计归还日期:{{ item.returnDate }}</view>
|
{{ item.isReturn === 1 ? '已归还' : '借用中' }}
|
||||||
<view class="detail">借用人:{{ item.borrowerName }}({{ item.borrowerMobile }})</view>
|
|
||||||
<view class="detail">出借人:{{ item.lenderName }}({{ item.lenderMobile }})</view>
|
|
||||||
<view class="detail">备注:{{ item.note || '无' }}</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty" v-if="borrowList.length === 0">暂无更多数据</view>
|
|
||||||
|
<!-- 信息行 -->
|
||||||
|
<view class="item-info">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">借用日期:</text>
|
||||||
|
<text class="info-value">{{ item.borrowDate || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">应还日期:</text>
|
||||||
|
<text class="info-value">{{ item.returnDate || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.actualReturnDate" class="info-row">
|
||||||
|
<text class="info-label">实际归还:</text>
|
||||||
|
<text class="info-value success-text">{{ item.actualReturnDate }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">出借人:</text>
|
||||||
|
<text class="info-value">{{ item.lenderName || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.lenderMobile" class="info-row">
|
||||||
|
<text class="info-label">联系方式:</text>
|
||||||
|
<text class="info-value">{{ item.lenderMobile }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.note" class="info-row">
|
||||||
|
<text class="info-label">备注:</text>
|
||||||
|
<text class="info-value">{{ item.note }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
// @ts-ignore JS模块无类型声明
|
// @ts-ignore
|
||||||
import { borrowInfo, returnAsset } from '@/pages/api/fixedAssets.js'
|
import { fetchBorrowList } from '@/pages/api/fixedAssets.js'
|
||||||
|
|
||||||
const borrowList = ref<any[]>([])
|
interface BorrowRecord {
|
||||||
const userInfo = uni.getStorageSync('staff') || {}
|
borrowId: number
|
||||||
|
assetId: number
|
||||||
onLoad(() => { loadBorrowList() })
|
assetName: string
|
||||||
|
borrowDate: string
|
||||||
onShow(() => { loadBorrowList() })
|
returnDate: string
|
||||||
|
actualReturnDate: string | null
|
||||||
function loadBorrowList() {
|
borrowerId: string
|
||||||
if (!userInfo?.id) {
|
borrowerName: string | null
|
||||||
borrowList.value = []
|
borrowerMobile: string | null
|
||||||
return
|
lenderId: string
|
||||||
|
lenderName: string | null
|
||||||
|
lenderMobile: string | null
|
||||||
|
registrantDate: string
|
||||||
|
isReturn: number // 0-借用中 1-已归还
|
||||||
|
note: string
|
||||||
}
|
}
|
||||||
borrowInfo(userInfo.id)
|
|
||||||
.then((res: any) => { borrowList.value = res?.data || [] })
|
const myBorrowList = ref<BorrowRecord[]>([])
|
||||||
.catch((err: any) => {
|
const loading = ref(false)
|
||||||
console.error(err)
|
const refreshing = ref(false)
|
||||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
const filterStatus = ref<number | null>(null)
|
||||||
|
const userMobile = uni.getStorageSync('userName') || ''
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
const totalCount = computed(() => myBorrowList.value.length)
|
||||||
|
const borrowingCount = computed(() => myBorrowList.value.filter(item => item.isReturn === 0).length)
|
||||||
|
const returnedCount = computed(() => myBorrowList.value.filter(item => item.isReturn === 1).length)
|
||||||
|
|
||||||
|
// 筛选后的列表
|
||||||
|
const filteredList = computed(() => {
|
||||||
|
if (filterStatus.value === null) {
|
||||||
|
return myBorrowList.value
|
||||||
|
}
|
||||||
|
return myBorrowList.value.filter(item => item.isReturn === filterStatus.value)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
function button_returnAsset(item: any) {
|
onLoad(() => {
|
||||||
uni.showModal({
|
loadData()
|
||||||
content: '确认归还?',
|
})
|
||||||
success: async (r) => {
|
|
||||||
if (r.confirm) {
|
onShow(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
async function loadData() {
|
||||||
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
await returnAsset(item.assetId)
|
const result = await fetchBorrowList()
|
||||||
uni.showToast({ title: '归还成功', icon: 'success' })
|
if (result.code === 0 && Array.isArray(result.data)) {
|
||||||
loadBorrowList()
|
// 在前端筛选出当前用户的借用记录(按手机号匹配)
|
||||||
} catch (e) {
|
myBorrowList.value = result.data.filter((item: BorrowRecord) =>
|
||||||
uni.showToast({ title: '归还失败', icon: 'none' })
|
item.borrowerMobile === userMobile
|
||||||
}
|
)
|
||||||
}
|
} else {
|
||||||
}
|
uni.showToast({
|
||||||
|
title: result.msg || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载借用记录失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
refreshing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
function onRefresh() {
|
||||||
|
refreshing.value = true
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换筛选
|
||||||
|
function changeFilter(status: number | null) {
|
||||||
|
filterStatus.value = status
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page {
|
.page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f7fb;
|
background: #f5f7fb;
|
||||||
padding: 24rpx;
|
padding-bottom: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@ -76,58 +215,180 @@ function button_returnAsset(item: any) {
|
|||||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.borrow-list {
|
/* 统计卡片 */
|
||||||
|
.stats-card {
|
||||||
|
margin: 24rpx 24rpx 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
gap: 20rpx;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
.borrow-item {
|
.stat-item {
|
||||||
padding: 20rpx;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value.highlight {
|
||||||
|
color: #ff9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value.success {
|
||||||
|
color: #51cf66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-divider {
|
||||||
|
width: 2rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
background: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 筛选栏 */
|
||||||
|
.filter-bar {
|
||||||
|
margin: 0 24rpx 16rpx;
|
||||||
|
padding: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tab {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 12rpx 8rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
background: #f8f9fa;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tab.active {
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表容器 */
|
||||||
|
.list-container {
|
||||||
|
height: calc(100vh - 260rpx);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrap,
|
||||||
|
.empty-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 120rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项 */
|
||||||
|
.list {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-header {
|
.item-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
margin-bottom: 16rpx;
|
||||||
margin-bottom: 8rpx;
|
padding-bottom: 16rpx;
|
||||||
|
border-bottom: 2rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-name-wrap {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-name {
|
.asset-name {
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
color: #222;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
padding: 6rpx 16rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.borrowing {
|
||||||
|
background: #fff3e0;
|
||||||
|
color: #ff9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.returned {
|
||||||
|
background: #e8f5e9;
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 信息行 */
|
||||||
|
.item-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
color: #999;
|
||||||
|
min-width: 140rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: #333;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail {
|
.success-text {
|
||||||
font-size: 24rpx;
|
color: #51cf66;
|
||||||
color: #666;
|
font-weight: 500;
|
||||||
margin-top: 6rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
text-align: center;
|
|
||||||
color: #9a9aa0;
|
|
||||||
margin-top: 40rpx;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -83,6 +83,8 @@ import config, { AssetRole } from '@/config'
|
|||||||
import * as staffRole from '@/constant/staffRole.js'
|
import * as staffRole from '@/constant/staffRole.js'
|
||||||
// @ts-ignore JS模块无类型声明
|
// @ts-ignore JS模块无类型声明
|
||||||
import util from '@/utils/util.js'
|
import util from '@/utils/util.js'
|
||||||
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
|
import { fetchPendingCount } from '@/utils/messageManager'
|
||||||
// 资产管理展开状态
|
// 资产管理展开状态
|
||||||
const assetOpen = ref(true)
|
const assetOpen = ref(true)
|
||||||
|
|
||||||
@ -90,15 +92,15 @@ const assetOpen = ref(true)
|
|||||||
const assetItems = computed(() => {
|
const assetItems = computed(() => {
|
||||||
const role = assetsRole.value || 0
|
const role = assetsRole.value || 0
|
||||||
return [
|
return [
|
||||||
{ key: 'fixed', text: '固定资产', icon: '/static/icons/fixed-assets.png' },
|
{ key: 'fixed', text: '固定资产', icon: '/static/icons/fixed-assets.png', url: '/pages/fixed-assets/index' },
|
||||||
{ key: 'mine', text: '我的借用', icon: '/static/icons/my-loans.png' },
|
{ key: 'mine', text: '我的借用', icon: '/static/icons/my-loans.png', url: '/pages/fixed-assets/my-borrows' },
|
||||||
{ key: 'scan', text: '扫码查看信息', icon: '/static/icons/scan-to-view-info.png' },
|
{ key: 'scan', text: '扫码查看信息', icon: '/static/icons/scan-to-view-info.png', url: '/pages/fixed-assets/scan' },
|
||||||
{ key: 'inventory', text: '资产盘点', icon: '/static/icons/asset-inventory.png' },
|
{ key: 'inventory', text: '资产盘点', icon: '/static/icons/asset-inventory.png', url: '/pages/fixed-assets/inventory-plan' },
|
||||||
{ key: 'plan', text: '易耗品领用计划', icon: '/static/icons/consumables-plan.png' },
|
{ key: 'plan', text: '易耗品领用计划', icon: '/static/icons/consumables-plan.png', url: '/pages/consumables/plan' },
|
||||||
{ key: 'consumable', text: '易耗品盘点', icon: '/static/icons/consumables-inventory.png' },
|
{ key: 'consumable', text: '易耗品盘点', icon: '/static/icons/consumables-inventory.png', url: '/pages/consumables/inventory-plan' },
|
||||||
{ key: 'lent', text: '出借资产管理', icon: '/static/icons/lent-assets-management.png', show: !!(role & AssetRole.TEAM_LEAD) },
|
{ key: 'lent', text: '出借资产管理', icon: '/static/icons/lent-assets-management.png', url: '/pages/fixed-assets/lent-management', show: !!(role & AssetRole.DEVICE_MANAGER) },
|
||||||
{ key: 'request', text: '临时易耗品申请', icon: '/static/icons/consumables-temp-request.png', show: !!(role & AssetRole.TEAM_LEAD) },
|
{ key: 'request', text: '临时易耗品申请', icon: '/static/icons/consumables-temp-request.png', url: '/pages/consumables/temp-request', show: !!(role & AssetRole.TEAM_LEAD) },
|
||||||
{ key: 'approve', text: '临时易耗品申请审批', icon: '/static/icons/consumables-temp-approve.png', show: !!(role & AssetRole.CONSUMABLES_MANAGER) },
|
{ key: 'approve', text: '临时易耗品申请审批', icon: '/static/icons/consumables-temp-approve.png', url: '/pages/consumables/temp-approve', show: !!(role & AssetRole.CONSUMABLES_MANAGER) },
|
||||||
].filter(item => item.show !== false)
|
].filter(item => item.show !== false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -146,6 +148,10 @@ onMounted(() => {
|
|||||||
syncLoginState()
|
syncLoginState()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
syncLoginState()
|
||||||
|
})
|
||||||
|
|
||||||
// 登录与角色态
|
// 登录与角色态
|
||||||
const isLoggedIn = ref(false)
|
const isLoggedIn = ref(false)
|
||||||
const staff = ref<any | null>(null)
|
const staff = ref<any | null>(null)
|
||||||
@ -158,7 +164,7 @@ const isRider = ref(false)
|
|||||||
function syncLoginState() {
|
function syncLoginState() {
|
||||||
staff.value = uni.getStorageSync('staff') || null
|
staff.value = uni.getStorageSync('staff') || null
|
||||||
assetsRole.value = uni.getStorageSync('assetsRole') || null // 资产系统用户信息
|
assetsRole.value = uni.getStorageSync('assetsRole') || null // 资产系统用户信息
|
||||||
isLoggedIn.value = !!staff.value
|
isLoggedIn.value = (!!staff.value) || typeof assetsRole.value === 'number'
|
||||||
// 平台
|
// 平台
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
isWeixin.value = process?.env?.VUE_APP_PLATFORM === 'mp-weixin'
|
isWeixin.value = process?.env?.VUE_APP_PLATFORM === 'mp-weixin'
|
||||||
@ -182,6 +188,8 @@ function goLogin() {
|
|||||||
function logout() {
|
function logout() {
|
||||||
uni.setStorageSync('staff', null)
|
uni.setStorageSync('staff', null)
|
||||||
uni.setStorageSync('token', null)
|
uni.setStorageSync('token', null)
|
||||||
|
uni.setStorageSync('assetsRole', null)
|
||||||
|
fetchPendingCount()
|
||||||
isLoggedIn.value = false
|
isLoggedIn.value = false
|
||||||
staff.value = null
|
staff.value = null
|
||||||
isRider.value = false
|
isRider.value = false
|
||||||
@ -203,45 +211,13 @@ function toggleAsset() {
|
|||||||
function handleAsset(key: string) {
|
function handleAsset(key: string) {
|
||||||
const item = assetItems.value.find((i: any) => i.key === key)
|
const item = assetItems.value.find((i: any) => i.key === key)
|
||||||
if (!item) return
|
if (!item) return
|
||||||
if (key === 'fixed') {
|
if (item.url) {
|
||||||
uni.navigateTo({ url: '/pages/fixed-assets/index' })
|
uni.navigateTo({ url: item.url })
|
||||||
return
|
} else {
|
||||||
}
|
|
||||||
if (key === 'mine') {
|
|
||||||
uni.navigateTo({ url: '/pages/fixed-assets/my-borrows' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (key === 'inventory') {
|
|
||||||
uni.navigateTo({ url: '/pages/fixed-assets/inventory-plan' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (key === 'plan') {
|
|
||||||
uni.navigateTo({ url: '/pages/consumables/plan' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (key === 'consumable') {
|
|
||||||
uni.navigateTo({ url: '/pages/consumables/inventory-plan' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (key === 'scan') {
|
|
||||||
uni.navigateTo({ url: '/pages/fixed-assets/scan' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (key === 'lent') {
|
|
||||||
uni.showToast({ title: `打开:${item.text}`, icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (key === 'request') {
|
|
||||||
uni.navigateTo({ url: '/pages/consumables/temp-request' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (key === 'approve') {
|
|
||||||
uni.navigateTo({ url: '/pages/consumables/temp-approve' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 其他入口可在此对接具体页面
|
// 其他入口可在此对接具体页面
|
||||||
uni.showToast({ title: `打开:${item.text}`, icon: 'none' })
|
uni.showToast({ title: `打开:${item.text}`, icon: 'none' })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function previewCharge() {
|
function previewCharge() {
|
||||||
uni.previewImage({
|
uni.previewImage({
|
||||||
urls: ['/static/stretcher-charge-standard.png']
|
urls: ['/static/stretcher-charge-standard.png']
|
||||||
@ -251,7 +227,6 @@ function previewCharge() {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page {
|
.page {
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f7fb;
|
background: #f5f7fb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,11 +8,13 @@
|
|||||||
<view class="card form">
|
<view class="card form">
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<text class="label">手机号</text>
|
<text class="label">手机号</text>
|
||||||
<input class="input" v-model="loginRequest.username" placeholder="请输入用户名或电话" :focus="usernameFocus" />
|
<input class="input" v-model="loginRequest.username"
|
||||||
|
placeholder="请输入用户名或电话" :focus="usernameFocus" />
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<text class="label">密码</text>
|
<text class="label">密码</text>
|
||||||
<input class="input" v-model="loginRequest.password" placeholder="请输入密码" password />
|
<input class="input" v-model="loginRequest.password" placeholder="请输入密码"
|
||||||
|
password />
|
||||||
</view>
|
</view>
|
||||||
<view class="actions">
|
<view class="actions">
|
||||||
<button class="btn primary" @tap="onLogin">登录</button>
|
<button class="btn primary" @tap="onLogin">登录</button>
|
||||||
@ -26,6 +28,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, onMounted } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { login } from '../api/user'
|
import { login } from '../api/user'
|
||||||
|
import { fetchPendingCount } from '@/utils/messageManager'
|
||||||
|
|
||||||
const usernameFocus = ref(true)
|
const usernameFocus = ref(true)
|
||||||
const loginRequest = reactive({
|
const loginRequest = reactive({
|
||||||
@ -82,6 +85,7 @@ async function onLogin() {
|
|||||||
uni.setStorageSync('assetsRole', staff.assetsRole) // 保存用户信息到本地缓存
|
uni.setStorageSync('assetsRole', staff.assetsRole) // 保存用户信息到本地缓存
|
||||||
// 跳转到首页(tabBar)
|
// 跳转到首页(tabBar)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
fetchPendingCount() // 登录成功后,获取待办消息数
|
||||||
uni.switchTab({ url: '/pages/index/index' })
|
uni.switchTab({ url: '/pages/index/index' })
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="info" v-else>
|
<view class="info" v-else>
|
||||||
<text class="name">{{ staff?.user_name || staff?.name || '已登录' }}</text>
|
<text class="name">{{ staff?.user_name || staff?.name || '已登录' }}</text>
|
||||||
<text class="sub">类型:{{ userTypeText }}</text>
|
<!-- <text class="sub">类型:{{ userTypeText }}</text> -->
|
||||||
<!-- <text class="sub">角色:{{ staff?.role_name || '-' }}</text> -->
|
<!-- <text class="sub">角色:{{ staff?.role_name || '-' }}</text> -->
|
||||||
<text class="sub">角色:{{ [
|
<text class="sub">角色:{{ [
|
||||||
assetRole & AssetRole.CONSUMABLES_MANAGER ? '易耗品发放负责人' : undefined,
|
assetRole & AssetRole.CONSUMABLES_MANAGER ? '易耗品发放负责人' : undefined,
|
||||||
@ -175,6 +175,7 @@ import queryService from '@/service/queryService.js'
|
|||||||
import getService from '@/service/getService.js'
|
import getService from '@/service/getService.js'
|
||||||
// @ts-ignore JS模块无类型声明
|
// @ts-ignore JS模块无类型声明
|
||||||
import config, { AssetRole } from '@/config'
|
import config, { AssetRole } from '@/config'
|
||||||
|
import { fetchPendingCount } from '@/utils/messageManager'
|
||||||
|
|
||||||
type Task = Record<string, any>
|
type Task = Record<string, any>
|
||||||
|
|
||||||
@ -220,6 +221,8 @@ function goLogin() {
|
|||||||
function logout() {
|
function logout() {
|
||||||
uni.setStorageSync('staff', null)
|
uni.setStorageSync('staff', null)
|
||||||
uni.setStorageSync('token', null)
|
uni.setStorageSync('token', null)
|
||||||
|
uni.setStorageSync('assetsRole', null)
|
||||||
|
fetchPendingCount()
|
||||||
staff.value = null
|
staff.value = null
|
||||||
isLoggedIn.value = false
|
isLoggedIn.value = false
|
||||||
taskList.value = []
|
taskList.value = []
|
||||||
@ -371,6 +374,7 @@ onShow(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@ -378,6 +382,7 @@ onShow(async () => {
|
|||||||
.name {
|
.name {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub {
|
.sub {
|
||||||
@ -390,7 +395,7 @@ onShow(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
padding: 12rpx 20rpx;
|
padding: 0rpx 20rpx;
|
||||||
background: #3a5ddd;
|
background: #3a5ddd;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 10rpx;
|
border-radius: 10rpx;
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
* 用于管理待审批消息数量和 TabBar 小红点
|
* 用于管理待审批消息数量和 TabBar 小红点
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { AssetRole } from '@/config'
|
||||||
import consumableTempAPI from '@/pages/api/consumable-temp'
|
import consumableTempAPI from '@/pages/api/consumable-temp'
|
||||||
|
|
||||||
// 消息数量缓存
|
// 消息数量缓存
|
||||||
@ -14,11 +15,11 @@ let pollingTimer: number | null = null
|
|||||||
*/
|
*/
|
||||||
export async function fetchPendingCount(): Promise<number> {
|
export async function fetchPendingCount(): Promise<number> {
|
||||||
try {
|
try {
|
||||||
const res = await consumableTempAPI.getRequestList({
|
const res = (uni.getStorageSync('assetsRole') & AssetRole.CONSUMABLES_MANAGER) ? await consumableTempAPI.getRequestList({
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 100, // 获取足够多的数据来计算总数
|
pageSize: 100, // 获取足够多的数据来计算总数
|
||||||
status: 'PENDING'
|
status: 'PENDING'
|
||||||
})
|
}) : { statusCode: 200, data: { data: [] } }
|
||||||
|
|
||||||
if (res.statusCode === 200 && (res.data as any)?.data) {
|
if (res.statusCode === 200 && (res.data as any)?.data) {
|
||||||
const data = (res.data as any).data
|
const data = (res.data as any).data
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user