274 lines
6.6 KiB
Plaintext
274 lines
6.6 KiB
Plaintext
<template>
|
||
<hr-page page-bg="#F3F4F6" status-bg="#FFFFFF" nav-title="物资详情" :xloading="true" @right-click="onMore">
|
||
<template v-slot:right>
|
||
<x-icon name="more-fill" color="#6B7280" font-size="18"></x-icon>
|
||
</template>
|
||
|
||
<view class="hr-body">
|
||
|
||
<!-- ============ 概览 ============ -->
|
||
<hr-card>
|
||
<view class="hr-row">
|
||
<view class="gd-ring">
|
||
<x-circle-progress :size="76" :line-width="8" color="#F3F4F6" active-color="#F59E0B"
|
||
:model-value="ringPercent" :show-label="false" :duration="600"></x-circle-progress>
|
||
<view class="gd-ring-label">
|
||
<text class="gd-ring-b">{{ goods.stock }}</text>
|
||
<text class="gd-ring-u">{{ goods.unit }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="hr-flex1">
|
||
<text class="gd-name">{{ goods.name }}</text>
|
||
<text class="gd-code">编码 {{ goods.code }} · {{ goods.spec }}</text>
|
||
<view style="margin-top:7px">
|
||
<hr-pc :level="0" :text="goods.statusLabel"></hr-pc>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</hr-card>
|
||
|
||
<!-- ============ 库存信息 ============ -->
|
||
<hr-sec title="库存信息"></hr-sec>
|
||
<hr-card variant="list">
|
||
<hr-lrow icon="▦" title="当前库存" :sub="'预警线 ' + goods.warnLine + ' ' + goods.unit">
|
||
<template v-slot:right>
|
||
<text class="gd-v gd-v-red">{{ goods.stock }} {{ goods.unit }}</text>
|
||
</template>
|
||
</hr-lrow>
|
||
<hr-lrow icon="⇅" theme="pri" title="近 30 天出库" :sub="goods.dailyUse">
|
||
<template v-slot:right>
|
||
<text class="gd-v">{{ goods.out30 }} {{ goods.unit }}</text>
|
||
</template>
|
||
</hr-lrow>
|
||
<hr-lrow icon="▤" theme="grn" title="最近入库" :sub="goods.lastIn" :is-last="true">
|
||
<template v-slot:right>
|
||
<text class="gd-v gd-v-grn">{{ goods.lastInQty }}</text>
|
||
</template>
|
||
</hr-lrow>
|
||
</hr-card>
|
||
|
||
<!-- ============ 消耗趋势 ============ -->
|
||
<hr-sec title="30 天消耗趋势" more="查看明细 ›"></hr-sec>
|
||
<hr-card>
|
||
<view class="gd-chart">
|
||
<view v-for="(h, i) in barHeights" :key="i" class="gd-bar-wrap">
|
||
<view class="gd-bar" :style="barStyle(i, h)"></view>
|
||
</view>
|
||
</view>
|
||
<view class="gd-xaxis">
|
||
<text v-for="(x, i) in xLabels" :key="i" class="gd-x">{{ x }}</text>
|
||
</view>
|
||
<view class="gd-legend">
|
||
<view class="gd-lg">
|
||
<view class="gd-lg-d" style="background-color:#EFF6FF"></view>
|
||
<text class="gd-lg-t">正常区间</text>
|
||
</view>
|
||
<view class="gd-lg">
|
||
<view class="gd-lg-d" style="background-color:#F59E0B"></view>
|
||
<text class="gd-lg-t">接近预警</text>
|
||
</view>
|
||
<view class="gd-lg">
|
||
<view class="gd-lg-d" style="background-color:#DC2626"></view>
|
||
<text class="gd-lg-t">低于预警线</text>
|
||
</view>
|
||
</view>
|
||
</hr-card>
|
||
|
||
<hr-tip style="margin-top:14px" theme="warn" icon="⚠"
|
||
:text="'按当前消耗速度(' + goods.dailyUse + '),库存预计 ' + daysLeft + ' 天后耗尽,建议尽快补货。'"></hr-tip>
|
||
|
||
<view class="hr-btn hr-btn-primary hr-btn-lg" style="margin-top:12px" @click="onRestock">
|
||
<x-icon name="add-line" color="#FFFFFF" font-size="17"></x-icon>
|
||
<text class="gd-bt">发起补货申请</text>
|
||
</view>
|
||
|
||
<view style="height:14px"></view>
|
||
</view>
|
||
</hr-page>
|
||
</template>
|
||
|
||
<script lang="uts" setup>
|
||
import { ref, computed } from 'vue'
|
||
import { goodsDetail } from '@/mock/index.uts'
|
||
import { navTo } from '@/utils/nav.uts'
|
||
|
||
/**
|
||
* s25 物资详情
|
||
* 库存环 + 库存信息 + 30 天消耗趋势 + 补货入口。
|
||
*/
|
||
const goods = goodsDetail
|
||
const xLabels : string[] = ['08-24', '08-31', '09-07', '09-14', '09-21']
|
||
|
||
const ringPercent = computed(() : number => {
|
||
// 库存 / (预警线 × 2.5) —— 8 / 50 ≈ 16%,与设计稿 18% 量级一致
|
||
return Math.round(goods.stock * 100 / (goods.warnLine * 2.5))
|
||
})
|
||
|
||
const barHeights = computed(() : number[] => {
|
||
const arr : number[] = []
|
||
for (let i = 0; i < goods.trend.length; i++) {
|
||
arr.push(Math.round(176 * goods.trend[i] / 100))
|
||
}
|
||
return arr
|
||
})
|
||
|
||
const daysLeft = computed(() : number => {
|
||
const daily = goods.out30 / 30
|
||
if (daily <= 0) { return 0 }
|
||
return Math.floor(goods.stock / daily)
|
||
})
|
||
|
||
function barStyle(i : number, h : number) : string {
|
||
let color = '#EFF6FF'
|
||
if (i == goods.trend.length - 1) { color = '#DC2626' }
|
||
else if (i == goods.trend.length - 2) { color = '#F59E0B' }
|
||
return `height:${h}rpx;background-color:${color};`
|
||
}
|
||
|
||
function onMore() : void {
|
||
uni.showToast({ title: '出入库流水 / 编辑物资', icon: 'none' })
|
||
}
|
||
|
||
function onRestock() : void {
|
||
uni.showModal({
|
||
title: '发起补货申请',
|
||
content: `物资:${goods.name}\n建议补货量:40 ${goods.unit}(约 2 周用量)`,
|
||
cancelText: '取消',
|
||
confirmText: '提交申请',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.showToast({ title: '补货申请已提交', icon: 'success' })
|
||
setTimeout(() => {
|
||
navTo('/pages/message/index')
|
||
}, 700)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.gd-ring {
|
||
width: 152rpx;
|
||
height: 152rpx;
|
||
position: relative;
|
||
margin-right: 26rpx;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.gd-ring-label {
|
||
position: absolute;
|
||
left: 0rpx;
|
||
top: 0rpx;
|
||
width: 152rpx;
|
||
height: 152rpx;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.gd-ring-b {
|
||
font-size: 42.6rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.gd-ring-u {
|
||
font-size: 22.4rpx;
|
||
color: #9CA3AF;
|
||
margin-left: 2rpx;
|
||
}
|
||
|
||
.gd-name {
|
||
font-size: 33.6rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.gd-code {
|
||
font-size: 25.8rpx;
|
||
color: #6B7280;
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
.gd-v {
|
||
font-size: 33.6rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
margin-left: 16rpx;
|
||
}
|
||
|
||
.gd-v-red {
|
||
color: #DC2626;
|
||
}
|
||
|
||
.gd-v-grn {
|
||
color: #059669;
|
||
}
|
||
|
||
/* ---------- 趋势图 ---------- */
|
||
.gd-chart {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: flex-end;
|
||
height: 176rpx;
|
||
}
|
||
|
||
.gd-bar-wrap {
|
||
flex: 1;
|
||
justify-content: flex-end;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.gd-bar {
|
||
border-radius: 8rpx 8rpx 0rpx 0rpx;
|
||
}
|
||
|
||
.gd-xaxis {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
margin-top: 14rpx;
|
||
}
|
||
|
||
.gd-x {
|
||
font-size: 22.4rpx;
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
.gd-legend {
|
||
display: flex;
|
||
flex-direction: row;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.gd-lg {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
margin-right: 28rpx;
|
||
}
|
||
|
||
.gd-lg-d {
|
||
width: 20rpx;
|
||
height: 20rpx;
|
||
border-radius: 6rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.gd-lg-t {
|
||
font-size: 23.6rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.gd-bt {
|
||
font-size: 35.8rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
margin-left: 12rpx;
|
||
}
|
||
</style>
|