Browse Source

feat: 库存调整

master
wangqi 2 months ago
parent
commit
c85187b3f8
  1. 14
      src/api/module/eam/device/storage.ts
  2. 6
      src/pages/deviceInfo/components/DeliveryInfo.vue
  3. 190
      src/pages/deviceStorage/components/data-filter.vue
  4. 124
      src/pages/deviceStorage/components/pie-chart.vue
  5. 178
      src/pages/deviceStorage/index.vue
  6. 14
      src/pages/deviceStorage/utils.ts

14
src/api/module/eam/device/storage.ts

@ -16,16 +16,22 @@ export const storageOutDevice = (params: IStorageOutParam) => @@ -16,16 +16,22 @@ export const storageOutDevice = (params: IStorageOutParam) =>
data: params,
})
export const getStorageOutByDeviceId = (deviceId: number) =>
export const getStorageByDeviceId = (deviceId: number) =>
eamServer({
url: `/out-storage/get-by-device-id`,
url: `/storage/get-by-device-id`,
method: 'get',
params: { deviceId },
})
export const getStorageOutPage = (params: PageParam) =>
export const getStoragePage = (params: PageParam) =>
eamServer({
url: `/out-storage/page`,
url: `/storage/page`,
method: 'get',
params,
})
export const getStorageStatics = () =>
eamServer({
url: `/storage/statics`,
method: 'get',
})

6
src/pages/deviceInfo/components/DeliveryInfo.vue

@ -49,7 +49,7 @@ import { getSimpleCustomerList, type CustomerVO } from '@/api/module/eam/custome @@ -49,7 +49,7 @@ import { getSimpleCustomerList, type CustomerVO } from '@/api/module/eam/custome
import { isResError } from '@/hooks/useMessage'
import type { IDevice } from '@/api/module/eam/device'
import {
getStorageOutByDeviceId,
getStorageByDeviceId,
storageOutDevice,
type IStorageOutParam,
} from '@/api/module/eam/device/storage'
@ -107,8 +107,8 @@ watch( @@ -107,8 +107,8 @@ watch(
async function loadDeviceStorageInfo() {
if (!props.deviceId) return
const res = await getStorageOutByDeviceId(props.deviceId)
if (isResError(res)) return
const res = await getStorageByDeviceId(props.deviceId)
if (isResError(res) || !res.data) return
for (const key of Object.keys(params.value)) {
params.value[key] = res.data[key] ?? ''
}

190
src/pages/deviceStorage/components/data-filter.vue

@ -0,0 +1,190 @@ @@ -0,0 +1,190 @@
<template>
<div class="storage-data-filter">
<div class="time-item">
<el-config-provider :locale="locale">
<span>开始时间:</span>
<div class="item item-time">
<el-date-picker
class="data-picker"
v-model="filterData.startDay"
type="date"
placeholder="请选择时间"
value-format="YYYY-MM-DD HH:mm:ss"
:clearable="false"
:editable="false"
/>
<el-checkbox v-model="checkbox.startDay" size="large" />
</div>
<span>终止时间:</span>
<div class="item item-time">
<el-date-picker
class="data-picker"
v-model="filterData.endDay"
type="date"
placeholder="请选择时间"
value-format="YYYY-MM-DD HH:mm:ss"
:clearable="false"
:editable="false"
:disabled-date="endDisabledDate"
/>
<el-checkbox v-model="checkbox.endDay" size="large" />
</div>
</el-config-provider>
</div>
<div class="item">
<el-input placeholder="设备SN" v-model="filterData.sn" />
<el-checkbox v-model="checkbox.sn" size="large" />
</div>
<div class="item">
<el-select v-model="filterData.customerId" placeholder="客户">
<el-option
v-for="item in prop.customerList"
:key="item.id"
:label="item.name"
:value="item.id as string"
/>
</el-select>
<el-checkbox v-model="checkbox.customerId" size="large" />
</div>
<div class="item">
<el-select v-model="filterData.status" placeholder="设备状态">
<el-option
v-for="item in statusList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-checkbox v-model="checkbox.status" size="large" disabled/>
</div>
<div class="item item-btn">
<EdfsButton
inner-text="搜索"
type="success"
style="width: 100%"
@click="onSearch"
/>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import { getIntDictOptions } from '@/utils/dict'
import { isResError } from '@/hooks/useMessage'
import type { CustomerVO } from '@/api/module/eam/customer'
import { statusList } from '../utils'
const locale = zhCn
const prop = defineProps<{
customerList: Array<Pick<CustomerVO, 'id' | 'name'>>
}>()
const endDisabledDate = (time: Date) => {
return (
time.getTime() <
dayjs(filterData.value.startDay).add(1, 'days').startOf('day').valueOf()
)
}
const emit = defineEmits(['search'])
type FilterKeys = keyof typeof filterData.value
const filterData = ref({
// startDay: dayjs().subtract(15, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
// endDay: dayjs().add(1, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
startDay: '',
endDay: '',
status: 3,
sn: '',
customerId: '',
})
const checkbox = ref<Record<FilterKeys, boolean>>({
startDay: false,
endDay: false,
status: true,
sn: false,
customerId: false,
})
function onSearch() {
emit('search', formatParams())
}
function formatParams() {
const params: Partial<Record<FilterKeys, string>> = {}
const keys = Object.keys(checkbox.value) as FilterKeys[]
for (const key of keys) {
if (checkbox.value[key]) {
if (key === 'startDay' || key === 'endDay') {
params['createTime'] = [filterData.value.startDay, filterData.value.endDay]
} else {
params[key] = filterData.value[key]
}
}
}
return params
}
onMounted(() => {
onSearch()
})
</script>
<style lang="scss" scoped>
.storage-data-filter {
padding: 16px;
box-sizing: border-box;
span {
font-size: 14px;
color: var(--label-text);
}
.time-item {
margin-bottom: 32px;
.item {
margin-top: 0;
margin-bottom: 16px;
}
}
.item {
margin-top: 16px;
height: 32px;
display: flex;
align-items: center;
:deep(.el-checkbox:last-of-type) {
margin-left: 8px;
}
}
.item-btn {
margin-top: 36px;
}
:deep(.data-picker) {
width: 100%;
}
:deep(.el-input__icon) {
// margin: 0;
}
:deep(.el-date-editor .el-input__wrapper) {
height: 100%;
line-height: 100%;
flex-direction: row-reverse;
box-sizing: border-box;
padding: 1px 0 1px 11px;
}
:deep(.el-input__icon),
:deep(.el-input__prefix-inner),
:deep(.el-input__prefix),
:deep(.perfix-icon) {
height: 100%;
}
}
</style>

124
src/pages/deviceStorage/components/pie-chart.vue

@ -0,0 +1,124 @@ @@ -0,0 +1,124 @@
<template>
<v-chart class="chart" :option="option" :autoresize="autoresize" />
</template>
<script setup lang="ts">
import VChart from 'vue-echarts'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
LegendComponent,
GraphicComponent,
} from 'echarts/components'
import type { EChartsOption } from 'echarts'
import { useTheme } from '@/utils/useTheme'
use([
CanvasRenderer,
PieChart,
TitleComponent,
TooltipComponent,
GraphicComponent,
LegendComponent,
])
interface Props {
data: any[]
}
const { chartGraphicTextColor } = useTheme()
const props = withDefaults(defineProps<Props>(), {
data: () => [],
})
const updateTrigger = ref(0)
const autoresize = {
throttle: 2500,
onResize: () => {
updateTrigger.value++
},
}
//
const total = computed(() => props.data.reduce((acc, cur) => acc + cur.value, 0))
const option = computed(() => {
const trigger = updateTrigger.value
const res: EChartsOption = {
tooltip: {
show: false,
},
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: '30%',
style: {
text: total.value.toString(),
fill: chartGraphicTextColor.value,
fontSize: 20,
fontWeight: 700,
},
},
{
type: 'text',
left: 'center',
top: '42%',
style: {
text: '库存总数',
fill: chartGraphicTextColor.value,
fontSize: 16,
},
},
],
},
legend: {
show: true,
bottom: '3%',
itemWidth: 20,
itemHeight: 15,
itemGap: 10,
textStyle: {
fontSize: 14,
},
},
color: ['#437BF8', '#F84343', '#D1D1D1 ', '#FBB852'],
series: [
{
type: 'pie',
radius: ['40%', '55%'],
center: ['50%', '40%'],
data: props.data,
avoidLabelOverlap: false,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 1,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
label: {
show: true,
position: 'outside',
// formatter: '{b}: {c} ({d}%)',
formatter: ' {c}',
},
labelLine: {
show: true,
},
},
],
}
return res
})
</script>
<style scoped lang="scss">
.chart {
width: 100%;
height: 100%;
}
</style>

178
src/pages/deviceStorage/index.vue

@ -1,48 +1,14 @@ @@ -1,48 +1,14 @@
<template>
<div class="storage-info-wrap">
<!-- <EdfsWrap class="storage-info-from-wrap">
<div class="from">
<div class="from-input">
<div class="from-row">
<div class="label">设备序列:</div>
<el-input
v-model="queryParams.number"
placeholder="请输入设备序列号"
clearable
@keyup.enter="handleQuery"
/>
</div>
<div class="from-row">
<div class="label">设备名称:</div>
<el-input
v-model="queryParams.deviceName"
placeholder="请输入设备名称"
clearable
@keyup.enter="handleQuery"
/>
</div>
<div class="from-row">
<div class="label">注册时段:</div>
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
</div>
<div class="btn-group">
<el-button @click="addDevice"><Icon icon="ep:plus" />添加设备</el-button>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
</div>
<div class="left-wrap">
<EdfsWrap class="chart-box" title="设备库存概览">
<PieChart :data="chartData" />
</EdfsWrap>
<div class="filter">
<LeftFilter @search="onSearch" :customerList="customerList" />
</div>
</div>
</EdfsWrap> -->
<EdfsWrap title="出库设备列表" class="storage-info-table">
<EdfsWrap title="设备库存列表" class="storage-info-table">
<EdfsTable
class="table"
v-loading="loading"
@ -57,11 +23,23 @@ @@ -57,11 +23,23 @@
<template v-for="(col, idx) in tableCol" :key="idx">
<el-table-column
v-if="col.prop.endsWith('Time')"
:label="storageStatus + col.label"
:min-width="col.minWidth"
>
<template #default="scope">
{{ dayjs(scope.row[col.prop]).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column
v-if="col.prop === 'status'"
:label="col.label"
:min-width="col.minWidth"
>
<template #default="scope">
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
{{
statusList.find(item => item.value === scope.row[col.prop])?.label ?? ''
}}
</template>
</el-table-column>
<el-table-column
@ -72,14 +50,22 @@ @@ -72,14 +50,22 @@
/>
</template>
<el-table-column label="操作" width="190" align="center">
<el-table-column label="操作" width="120" align="center">
<template #default="scope">
<EdfsButton
link
type="success"
inner-text="添加维修信息"
v-if="scope.row.status === 3"
@click="addMaintain(scope.row)"
/>
<EdfsButton
link
type="success"
v-else-if="scope.row.status === 2"
inner-text="添加出库信息"
@click="addOutStorage(scope.row)"
/>
</template>
</el-table-column>
</EdfsTable>
@ -91,10 +77,13 @@ @@ -91,10 +77,13 @@
import dayjs from 'dayjs'
import EdfsWrap from '@/components/dashboard/Edfs-wrap.vue'
import EdfsTable from '@/components/dashboard/Edfs-table/index.vue'
import { tableCol } from './utils'
import { statusList, tableCol } from './utils'
import { isResError } from '@/hooks/useMessage'
import { getStorageOutPage } from '@/api/module/eam/device/storage'
import LeftFilter from './components/data-filter.vue'
import { getStoragePage, getStorageStatics } from '@/api/module/eam/device/storage'
import { useRouter } from 'vue-router'
import PieChart from './components/pie-chart.vue'
import { getSimpleCustomerList, type CustomerVO } from '@/api/module/eam/customer'
const router = useRouter()
const loading = ref(true)
@ -103,17 +92,12 @@ const list = ref<any>([]) @@ -103,17 +92,12 @@ const list = ref<any>([])
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
// number: undefined,
// deviceName: undefined,
// status: undefined,
// code: undefined,
// createTime: [],
})
const getList = async () => {
loading.value = true
const res = await getStorageOutPage(queryParams)
const res = await getStoragePage(Object.assign({}, queryParams, filter.value))
if (!isResError(res)) {
list.value = res.data.list
@ -123,10 +107,17 @@ const getList = async () => { @@ -123,10 +107,17 @@ const getList = async () => {
loading.value = false
}
const handleQuery = () => {
const storageStatus = computed(
() => statusList.find(item => item.value === filter.value.status)?.colName ?? ''
)
const filter = ref<any>({})
function onSearch(search: any) {
queryParams.pageNo = 1
filter.value = search
getList()
}
function handleJump(page: number) {
queryParams.pageNo = page
getList()
@ -138,8 +129,31 @@ function addMaintain(row: any) { @@ -138,8 +129,31 @@ function addMaintain(row: any) {
query: { action: 'update', type: 'maintain', id: row.deviceId },
})
}
function addOutStorage(row: any) {
router.push({
path: '/device/deviceOperation',
query: { action: 'update', type: 'delivery', id: row.deviceId },
})
}
const customerList = ref<Array<Pick<CustomerVO, 'id' | 'name'>>>([])
async function loadCustomerList() {
const res = await getSimpleCustomerList()
if (isResError(res)) return
customerList.value = res.data
}
const chartData = ref([]) as Ref<any>
async function loadChartData() {
const res = await getStorageStatics()
if (!isResError(res)) {
chartData.value = res.data
}
}
onMounted(() => {
getList()
loadCustomerList()
loadChartData()
})
</script>
@ -148,37 +162,47 @@ onMounted(() => { @@ -148,37 +162,47 @@ onMounted(() => {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
row-gap: 10px;
.storage-info-from-wrap {
height: auto;
width: 100%;
.from {
display: flex;
flex-direction: column;
justify-content: space-around;
column-gap: 12px;
box-sizing: border-box;
font-size: 16px;
.left-wrap {
width: 280px;
min-width: 100px;
height: 100%;
width: 100%;
.from-input {
display: flex;
width: 100%;
column-gap: 20px;
row-gap: 10px;
flex-wrap: wrap;
.from-row {
min-width: 270px;
background: var(--warp-bg);
display: flex;
align-items: center;
column-gap: 4px;
.label {
white-space: nowrap;
width: 70px;
color: var(--label-text);
flex-direction: column;
box-sizing: border-box;
.station {
position: relative;
height: 60px;
padding-left: 20px;
border-bottom: 1px solid var(--pagination-border-color);
:deep(.el-input__inner) {
color: #666;
}
:deep(.el-input__wrapper) {
padding: 0;
}
:deep(.el-input__suffix) {
display: none;
}
}
.chart-box {
height: 300px;
}
.filter {
height: calc(100% - 300px);
overflow: hidden;
}
:deep(.edfs-wrap) {
box-shadow: none;
}
}
.storage-info-table {
flex: 1;
.table {

14
src/pages/deviceStorage/utils.ts

@ -1,7 +1,13 @@ @@ -1,7 +1,13 @@
export const tableCol = [
{ label: '出库设备', prop: 'deviceName', minWidth: '12%' },
{ label: '客户', prop: 'customerName', minWidth: '10%' },
{ label: '设备名称', prop: 'deviceName', minWidth: '10%' },
{ label: '设备sn', prop: 'sn', minWidth: '10%' },
{ label: '库存状态', prop: 'status', minWidth: '10%' },
{ label: '客户', prop: 'customerName', minWidth: '14%' },
{ label: '出库价格', prop: 'price', minWidth: '10%' },
// { label: '出库单名称', prop: 'name', minWidth: '10%' },
{ label: '描述', prop: 'description', minWidth: '16%' },
{ label: '时间', prop: 'updateTime', minWidth: '18%' },
]
export const statusList = [
{ label: '已入库', value: 2, colName: '入库' },
{ label: '已出库', value: 3, colName: '出库' },
]

Loading…
Cancel
Save