代码提交-对接接口

This commit is contained in:
lixiaobang 2026-01-13 17:58:05 +08:00
parent 6a7d5db119
commit a19f48034d
17 changed files with 13596 additions and 1449 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

12477
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

38
src/api/base/category.js Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询商品类别列表
export function listproductCategory(query) {
return request({
url: '/api/v1/product-category',
method: 'get',
params: query
})
}
// 新增商品类别
export function addProductCategory(data) {
return request({
url: '/api/v1/product-category',
method: 'post',
data: data
})
}
// 修改商品类别
export function updateProductCategory(data) {
return request({
url: '/api/v1/product-category/' + data.id,
method: 'put',
data: data
})
}
// 删除商品类别
export function delProductCategory(data) {
return request({
url: '/api/v1/product-category',
method: 'delete',
data: data
})
}

38
src/api/base/customer.js Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询客户列表
export function listCustomer(query) {
return request({
url: '/api/v1/customer',
method: 'get',
params: query
})
}
// 新增客户
export function addCustomer(data) {
return request({
url: '/api/v1/customer',
method: 'post',
data: data
})
}
// 修改客户
export function updateCustomer(data) {
return request({
url: '/api/v1/customer/' + data.id,
method: 'put',
data: data
})
}
// 删除客户
export function delCustomer(data) {
return request({
url: '/api/v1/customer',
method: 'delete',
data: data
})
}

38
src/api/base/price.js Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询价格管理列表
export function listPriceIndex(query) {
return request({
url: '/api/v1/price-index',
method: 'get',
params: query
})
}
// 新增价格管理
export function addPriceIndex(data) {
return request({
url: '/api/v1/price-index',
method: 'post',
data: data
})
}
// 修改价格管理
export function updatePriceIndex(data) {
return request({
url: '/api/v1/price-index/' + data.id,
method: 'put',
data: data
})
}
// 删除价格管理
export function delPriceIndex(data) {
return request({
url: '/api/v1/price-index',
method: 'delete',
data: data
})
}

52
src/api/base/product.js Normal file
View File

@ -0,0 +1,52 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询商品列表
export function listproduct(query) {
return request({
url: '/api/v1/product',
method: 'get',
params: query
})
}
// 新增商品
export function addProduct(data) {
return request({
url: '/api/v1/product',
method: 'post',
data: data
})
}
// 修改商品
export function updateProduct(data) {
return request({
url: '/api/v1/product/' + data.id,
method: 'put',
data: data
})
}
// 删除商品
export function delProduct(data) {
return request({
url: '/api/v1/product',
method: 'delete',
data: data
})
}
//上传文件
export function uploadFile(data) {
return request({
url: '/api/v1/public/uploadFile',
method: 'post',
data,
headers: {
'Content-Type': 'multipart/form-data',
// 关闭防重复提交,避免 FormData 被序列化
repeatSubmit: false
}
})
}

View File

@ -0,0 +1,74 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询仓库列表
export function listWarehouse(query) {
return request({
url: '/api/v1/warehouse',
method: 'get',
params: query
})
}
// 新增仓库
export function addWarehouse(data) {
return request({
url: '/api/v1/warehouse',
method: 'post',
data: data
})
}
// 修改仓库
export function updateWarehouse(data) {
return request({
url: '/api/v1/warehouse/' + data.id,
method: 'put',
data: data
})
}
// 删除仓库
export function delWarehouse(data) {
return request({
url: '/api/v1/warehouse',
method: 'delete',
data: data
})
}
// 查询仓库垛位列表
export function listWarehouseStock(query) {
return request({
url: '/api/v1/warehouse-stock',
method: 'get',
params: query
})
}
// 新增仓库垛位
export function addWarehouseStock(data) {
return request({
url: '/api/v1/warehouse-stock',
method: 'post',
data: data
})
}
// 修改仓库垛位
export function updateWarehouseStock(data) {
return request({
url: '/api/v1/warehouse-stock/' + data.id,
method: 'put',
data: data
})
}
// 删除仓库垛位
export function delWarehouseStock(data) {
return request({
url: '/api/v1/warehouse-stock',
method: 'delete',
data: data
})
}

38
src/api/base/supplier.js Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询供应商管理列表
export function listSupplier(query) {
return request({
url: '/api/v1/supplier',
method: 'get',
params: query
})
}
// 新增供应商
export function addSupplier(data) {
return request({
url: '/api/v1/supplier',
method: 'post',
data: data
})
}
// 修改供应商
export function updateSupplier(data) {
return request({
url: '/api/v1/supplier/' + data.id,
method: 'put',
data: data
})
}
// 删除供应商
export function delSupplier(data) {
return request({
url: '/api/v1/supplier',
method: 'delete',
data: data
})
}

View File

@ -0,0 +1,27 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询库存报警配置列表
export function listInventoryAlertConfig(query) {
return request({
url: '/api/v1/inventory-alert-config',
method: 'get',
params: query
})
}
// 修改库存报警配置
export function updateInventoryAlertConfig(data, id) {
return request({
url: '/api/v1/inventory-alert-config/' + id,
method: 'put',
data: data
})
}
// 删除库存报警配置
export function delInventoryAlertConfig(data) {
return request({
url: '/api/v1/inventory-alert-config/' + query.id,
method: 'delete',
data: data
})
}

View File

@ -78,6 +78,7 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { parseTime } from '@/utils/ruoyi'
import Pagination from '@/components/Pagination'
import { listproductCategory, addProductCategory, updateProductCategory, delProductCategory } from '@/api/base/category'
const loading = ref(false)
const submitLoading = ref(false)
@ -114,41 +115,16 @@ const dialogTitle = computed(() => isEdit.value ? '编辑类别' : '新增类别
const getList = async () => {
loading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_categories') || '[]')
const start = (queryParams.pageNum - 1) * queryParams.pageSize
const end = start + queryParams.pageSize
categoryList.value = mockData.slice(start, end)
total.value = mockData.length
if (mockData.length === 0) {
generateMockData()
}
const response = await listproductCategory(queryParams)
categoryList.value = response.data.list || []
total.value = response.count || 0
} catch (error) {
ElMessage.error('获取类别列表失败:' + error.message)
ElMessage.error('获取类别列表失败:' + (error.message || '未知错误'))
} finally {
loading.value = false
}
}
const generateMockData = () => {
setTimeout(() => {
const categoryNames = ['动力煤', '焦煤', '无烟煤', '褐煤', '瘦煤', '肥煤', '气煤', '1/3焦煤', '贫煤', '长焰煤', '不粘煤', '弱粘煤', '1/2中粘煤', '中粘煤', '强粘煤', '特强粘煤', '贫瘦煤', '瘦焦煤', '主焦煤', '其他']
const newCategories = []
const now = Date.now()
for (let i = 1; i <= 20; i++) {
newCategories.push({
id: i,
categoryCode: 'CAT' + String(i).padStart(3, '0'),
categoryName: categoryNames[i - 1],
createTime: new Date(now - i * 30 * 86400000).toISOString(),
updateTime: new Date(now - i * 30 * 86400000).toISOString()
})
}
localStorage.setItem('mock_categories', JSON.stringify(newCategories))
getList()
}, 10)
}
const handleAdd = () => {
isEdit.value = false
resetForm()
@ -177,62 +153,24 @@ const resetForm = () => {
const handleSubmit = async () => {
if (!categoryFormRef.value) return
await categoryFormRef.value.validate((valid) => {
await categoryFormRef.value.validate(async (valid) => {
if (!valid) return
submitLoading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_categories') || '[]')
const now = new Date().toISOString()
if (isEdit.value) {
//
const index = mockData.findIndex(item => item.id === categoryForm.id)
if (index !== -1) {
//
const codeExists = mockData.find(item =>
item.id !== categoryForm.id &&
item.categoryCode === categoryForm.categoryCode
)
if (codeExists) {
ElMessage.warning('类别编码已存在')
submitLoading.value = false
return
}
mockData[index] = {
...mockData[index],
categoryCode: categoryForm.categoryCode,
categoryName: categoryForm.categoryName,
updateTime: now
}
}
await updateProductCategory(categoryForm)
ElMessage.success('编辑成功')
} else {
//
//
const codeExists = mockData.find(item => item.categoryCode === categoryForm.categoryCode)
if (codeExists) {
ElMessage.warning('类别编码已存在')
submitLoading.value = false
return
}
const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1
mockData.push({
id: newId,
categoryCode: categoryForm.categoryCode,
categoryName: categoryForm.categoryName,
createTime: now,
updateTime: now
})
await addProductCategory(categoryForm)
ElMessage.success('新增成功')
}
localStorage.setItem('mock_categories', JSON.stringify(mockData))
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
dialogVisible.value = false
getList()
} catch (error) {
ElMessage.error('操作失败:' + error.message)
ElMessage.error('操作失败:' + (error.message || '未知错误'))
} finally {
submitLoading.value = false
}
@ -240,16 +178,19 @@ const handleSubmit = async () => {
}
const handleDelete = (row) => {
const Ids = (row.id && [row.id])
ElMessageBox.confirm('确定要删除此类别吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const categories = JSON.parse(localStorage.getItem('mock_categories') || '[]')
const filtered = categories.filter(c => c.id != row.id)
localStorage.setItem('mock_categories', JSON.stringify(filtered))
ElMessage.success('删除成功')
getList()
}).then(async () => {
try {
await delProductCategory({ ids: Ids })
ElMessage.success('删除成功')
getList()
} catch (error) {
ElMessage.error('删除失败:' + (error.message || '未知错误'))
}
}).catch(() => {})
}

View File

@ -14,8 +14,8 @@
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 150px">
<el-option label="全部" value="" />
<el-option label="正常" value="1" />
<el-option label="停用" value="0" />
<el-option label="正常" value="正常" />
<el-option label="停用" value="停用" />
</el-select>
</el-form-item>
<el-form-item>
@ -27,41 +27,36 @@
</div>
<el-table v-loading="loading" :data="customerList" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="customerCode" label="客户ID" width="120" />
<!-- <el-table-column prop="customerCode" label="客户ID" width="120" /> -->
<el-table-column prop="customerName" label="客户名称" width="200" />
<el-table-column prop="customerType" label="客户类型" width="120">
<template #default="scope">
{{ getCustomerTypeName(scope.row.customerType) }}
</template>
</el-table-column>
<el-table-column prop="contact" label="联系人" width="120" />
<el-table-column prop="phone" label="手机号" width="130" />
<el-table-column prop="contactPerson" label="联系人" width="120" />
<el-table-column prop="mobilePhone" label="手机号" width="130" />
<el-table-column prop="email" label="邮箱" width="180" />
<el-table-column prop="address" label="详细地址" min-width="200" />
<el-table-column prop="detailAddress" label="详细地址" min-width="200" />
<el-table-column prop="bankName" label="开户银行" width="150" />
<el-table-column prop="bankAccount" label="银行账号" width="180" />
<el-table-column prop="taxNumber" label="税号" width="180" />
<el-table-column prop="status" label="客户状态" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'danger'">
{{ scope.row.status == 1 ? '正常' : '停用' }}
<el-tag :type="scope.row.status == '正常' ? 'success' : 'danger'">
{{ scope.row.status == '正常' ? '正常' : '停用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="qualificationInfo" label="资质信息" width="150">
<!-- <el-table-column prop="qualificationInfo" label="资质信息" width="150">
<template #default="scope">
<el-button
v-if="scope.row.qualificationInfo && scope.row.qualificationInfo.length > 0"
link
type="primary"
size="small"
@click="handleViewQualification(scope.row)"
>
<el-button v-if="scope.row.qualificationInfo && scope.row.qualificationInfo.length > 0" link type="primary"
size="small" @click="handleViewQualification(scope.row)">
查看资质({{ scope.row.qualificationInfo.length }})
</el-button>
<span v-else style="color: #999;">暂无资质</span>
</template>
</el-table-column>
</el-table-column> -->
<el-table-column label="操作" width="250" fixed="right" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleView(scope.row)">查看</el-button>
@ -71,46 +66,23 @@
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageIndex"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 新增/编辑客户对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="800px"
:close-on-click-modal="false"
@close="resetForm"
>
<el-form
ref="customerFormRef"
:model="customerForm"
:rules="customerFormRules"
label-width="120px"
>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="800px" :close-on-click-modal="false"
@close="resetForm">
<el-form ref="customerFormRef" :model="customerForm" :rules="customerFormRules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="客户名称" prop="customerName">
<el-input
v-model="customerForm.customerName"
placeholder="请输入客户名称"
maxlength="50"
/>
<el-input v-model="customerForm.customerName" placeholder="请输入客户名称" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户类型" prop="customerType">
<el-select
v-model="customerForm.customerType"
placeholder="请选择客户类型"
style="width: 100%"
>
<el-select v-model="customerForm.customerType" placeholder="请选择客户类型" style="width: 100%">
<el-option label="发电厂" value="power_plant" />
<el-option label="炼钢厂" value="steel_plant" />
<el-option label="经销商" value="dealer" />
@ -123,93 +95,56 @@
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="联系人" prop="contact">
<el-input
v-model="customerForm.contact"
placeholder="请输入联系人"
maxlength="20"
/>
<el-form-item label="联系人" prop="contactPerson">
<el-input v-model="customerForm.contactPerson" placeholder="请输入联系人" maxlength="20" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号" prop="phone">
<el-input
v-model="customerForm.phone"
placeholder="请输入手机号"
maxlength="11"
/>
<el-form-item label="手机号" prop="mobilePhone">
<el-input v-model="customerForm.mobilePhone" placeholder="请输入手机号" maxlength="11" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input
v-model="customerForm.email"
placeholder="请输入邮箱"
maxlength="50"
/>
<el-input v-model="customerForm.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="详细地址" prop="address">
<el-input
v-model="customerForm.address"
type="textarea"
:rows="2"
placeholder="请输入详细地址"
maxlength="200"
/>
<el-form-item label="详细地址" prop="detailAddress">
<el-input v-model="customerForm.detailAddress" type="textarea" :rows="2" placeholder="请输入详细地址" maxlength="200" />
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开户银行" prop="bankName">
<el-input
v-model="customerForm.bankName"
placeholder="请输入开户银行"
maxlength="50"
/>
<el-input v-model="customerForm.bankName" placeholder="请输入开户银行" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="银行账号" prop="bankAccount">
<el-input
v-model="customerForm.bankAccount"
placeholder="请输入银行账号"
maxlength="30"
/>
<el-input v-model="customerForm.bankAccount" placeholder="请输入银行账号" maxlength="30" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="税号" prop="taxNumber">
<el-input
v-model="customerForm.taxNumber"
placeholder="请输入税号"
maxlength="30"
/>
<el-input v-model="customerForm.taxNumber" placeholder="请输入税号" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户状态" prop="status">
<el-radio-group v-model="customerForm.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">停用</el-radio>
<el-radio :label="'正常'">正常</el-radio>
<el-radio :label="'停用'">停用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="资质信息" prop="qualificationInfo">
<el-upload
ref="uploadRef"
:file-list="fileList"
:auto-upload="false"
:on-change="handleFileChange"
:on-remove="handleFileRemove"
:multiple="true"
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png"
>
<!-- <el-form-item label="资质信息" prop="qualificationInfo">
<el-upload ref="uploadRef" :file-list="fileList" :auto-upload="false" :on-change="handleFileChange"
:on-remove="handleFileRemove" :multiple="true" accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png">
<el-button type="primary">选择文件</el-button>
<template #tip>
<div class="el-upload__tip">
@ -217,14 +152,15 @@
</div>
</template>
</el-upload>
<div v-if="customerForm.qualificationInfo && customerForm.qualificationInfo.length > 0" style="margin-top: 10px;">
<div v-if="customerForm.qualificationInfo && customerForm.qualificationInfo.length > 0"
style="margin-top: 10px;">
<div v-for="(file, index) in customerForm.qualificationInfo" :key="index" style="margin-bottom: 5px;">
<el-button link type="primary" size="small" @click="handleViewQualificationFile(file)">
{{ file.name }}
</el-button>
</div>
</div>
</el-form-item>
</el-form-item> -->
</el-form>
<template #footer>
<div class="dialog-footer">
@ -237,28 +173,25 @@
</el-dialog>
<!-- 查看客户对话框 -->
<el-dialog
v-model="viewDialogVisible"
title="查看客户"
width="800px"
>
<el-dialog v-model="viewDialogVisible" title="查看客户" width="800px">
<el-descriptions :column="2" border v-if="currentCustomer">
<el-descriptions-item label="客户ID">{{ currentCustomer.customerCode }}</el-descriptions-item>
<!-- <el-descriptions-item label="客户ID">{{ currentCustomer.customerCode }}</el-descriptions-item> -->
<el-descriptions-item label="客户名称">{{ currentCustomer.customerName }}</el-descriptions-item>
<el-descriptions-item label="客户类型">{{ getCustomerTypeName(currentCustomer.customerType) }}</el-descriptions-item>
<el-descriptions-item label="客户类型">{{ getCustomerTypeName(currentCustomer.customerType)
}}</el-descriptions-item>
<el-descriptions-item label="客户状态">
<el-tag :type="currentCustomer.status == 1 ? 'success' : 'danger'">
{{ currentCustomer.status == 1 ? '正常' : '停用' }}
<el-tag :type="currentCustomer.status == '正常' ? 'success' : 'danger'">
{{ currentCustomer.status == '正常' ? '正常' : '停用' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="联系人">{{ currentCustomer.contact }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ currentCustomer.phone }}</el-descriptions-item>
<el-descriptions-item label="联系人">{{ currentCustomer.contactPerson }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ currentCustomer.mobilePhone }}</el-descriptions-item>
<el-descriptions-item label="邮箱">{{ currentCustomer.email || '-' }}</el-descriptions-item>
<el-descriptions-item label="详细地址" :span="2">{{ currentCustomer.address }}</el-descriptions-item>
<el-descriptions-item label="详细地址" :span="2">{{ currentCustomer.detailAddress }}</el-descriptions-item>
<el-descriptions-item label="开户银行">{{ currentCustomer.bankName || '-' }}</el-descriptions-item>
<el-descriptions-item label="银行账号">{{ currentCustomer.bankAccount || '-' }}</el-descriptions-item>
<el-descriptions-item label="税号" :span="2">{{ currentCustomer.taxNumber || '-' }}</el-descriptions-item>
<el-descriptions-item label="资质信息" :span="2">
<!-- <el-descriptions-item label="资质信息" :span="2">
<div v-if="currentCustomer.qualificationInfo && currentCustomer.qualificationInfo.length > 0">
<div v-for="(file, index) in currentCustomer.qualificationInfo" :key="index" style="margin-bottom: 5px;">
<el-button link type="primary" size="small" @click="handleViewQualificationFile(file)">
@ -267,28 +200,17 @@
</div>
</div>
<span v-else style="color: #999;">暂无资质</span>
</el-descriptions-item>
</el-descriptions-item> -->
</el-descriptions>
</el-dialog>
<!-- 查看资质文件对话框 -->
<el-dialog
v-model="qualificationDialogVisible"
title="查看资质文件"
width="900px"
>
<el-dialog v-model="qualificationDialogVisible" title="查看资质文件" width="900px">
<div v-if="currentQualificationFile" style="text-align: center;">
<iframe
v-if="currentQualificationFile.type === 'pdf'"
:src="currentQualificationFile.url"
style="width: 100%; height: 600px; border: none;"
></iframe>
<img
v-else-if="currentQualificationFile.type === 'image'"
:src="currentQualificationFile.url"
style="max-width: 100%; max-height: 600px;"
alt="资质文件"
/>
<iframe v-if="currentQualificationFile.type === 'pdf'" :src="currentQualificationFile.url"
style="width: 100%; height: 600px; border: none;"></iframe>
<img v-else-if="currentQualificationFile.type === 'image'" :src="currentQualificationFile.url"
style="max-width: 100%; max-height: 600px;" alt="资质文件" />
<div v-else>
<el-button type="primary" @click="handleDownloadQualificationFile(currentQualificationFile)">
下载文件
@ -304,7 +226,7 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { formatMoney } from '@/utils/business'
import Pagination from '@/components/Pagination'
import { listCustomer, addCustomer, updateCustomer, delCustomer } from '@/api/base/customer'
const loading = ref(false)
const submitLoading = ref(false)
const customerList = ref([])
@ -320,7 +242,7 @@ const currentCustomer = ref(null)
const currentQualificationFile = ref(null)
const queryParams = reactive({
pageNum: 1,
pageIndex: 1,
pageSize: 10,
customerType: '',
status: ''
@ -331,15 +253,15 @@ const customerForm = reactive({
customerCode: '',
customerName: '',
customerType: '',
contact: '',
phone: '',
contactPerson: '',
mobilePhone: '',
email: '',
address: '',
detailAddress: '',
bankName: '',
bankAccount: '',
taxNumber: '',
qualificationInfo: [],
status: 1
status: '正常'
})
const customerFormRules = {
@ -350,17 +272,17 @@ const customerFormRules = {
customerType: [
{ required: true, message: '请选择客户类型', trigger: 'change' }
],
contact: [
contactPerson: [
{ required: true, message: '请输入联系人', trigger: 'blur' }
],
phone: [
mobilePhone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
email: [
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
address: [
detailAddress: [
{ required: true, message: '请输入详细地址', trigger: 'blur' }
],
bankName: [
@ -394,20 +316,15 @@ const getCustomerTypeName = (type) => {
const getList = async () => {
loading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_customers') || '[]')
let filtered = mockData.filter(item => {
if (queryParams.customerType && item.customerType !== queryParams.customerType) return false
if (queryParams.status !== '' && item.status != queryParams.status) return false
return true
const res = await listCustomer({
pageIndex: queryParams.pageIndex,
pageSize: queryParams.pageSize,
customerType: queryParams.customerType,
status: queryParams.status
})
const start = (queryParams.pageNum - 1) * queryParams.pageSize
const end = start + queryParams.pageSize
customerList.value = filtered.slice(start, end)
total.value = filtered.length
if (mockData.length === 0) {
generateMockData()
}
//
customerList.value =res.data.list || []
total.value = res.count || customerList.value.length
} catch (error) {
ElMessage.error('获取客户列表失败:' + error.message)
} finally {
@ -446,10 +363,10 @@ const generateMockData = () => {
customerCode: 'C' + String(i).padStart(3, '0'),
customerName: (customerTypeNames[customerType] || '客户') + (i <= 6 ? String.fromCharCode(64 + i) : String(i)),
customerType: customerType,
contact: surnames[Math.floor(Math.random() * surnames.length)] + names[Math.floor(Math.random() * names.length)],
phone: '139' + String(Math.floor(Math.random() * 100000000)).padStart(8, '0'),
contactPerson: surnames[Math.floor(Math.random() * surnames.length)] + names[Math.floor(Math.random() * names.length)],
mobilePhone: '139' + String(Math.floor(Math.random() * 100000000)).padStart(8, '0'),
email: `customer${i}@example.com`,
address: city + district + 'xxx路xxx号',
detailAddress: city + district + 'xxx路xxx号',
taxNumber: '91140000' + String(Math.floor(Math.random() * 100000000)).padStart(8, '0') + String.fromCharCode(65 + Math.floor(Math.random() * 26)),
bankName: banks[Math.floor(Math.random() * banks.length)],
bankAccount: '622' + String(Math.floor(Math.random() * 10000000000000000)).padStart(16, '0'),
@ -465,7 +382,7 @@ const generateMockData = () => {
}
const handleQuery = () => {
queryParams.pageNum = 1
queryParams.pageIndex = 1
getList()
}
@ -510,17 +427,17 @@ const handleEdit = (row) => {
customerCode: row.customerCode,
customerName: row.customerName,
customerType: row.customerType,
contact: row.contact,
phone: row.phone,
contactPerson: row.contactPerson,
mobilePhone: row.mobilePhone,
email: row.email || '',
address: row.address,
detailAddress: row.detailAddress,
taxNumber: row.taxNumber || '',
bankName: row.bankName || '',
bankAccount: row.bankAccount || '',
qualificationInfo: row.qualificationInfo || [],
status: row.status
})
//
if (row.qualificationInfo && row.qualificationInfo.length > 0) {
fileList.value = row.qualificationInfo.map(file => ({
@ -530,21 +447,25 @@ const handleEdit = (row) => {
} else {
fileList.value = []
}
dialogVisible.value = true
}
const handleDelete = (row) => {
const Ids = (row.id && [row.id])
ElMessageBox.confirm('确定要删除此客户吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const customers = JSON.parse(localStorage.getItem('mock_customers') || '[]')
const filtered = customers.filter(c => c.id != row.id)
localStorage.setItem('mock_customers', JSON.stringify(filtered))
ElMessage.success('删除成功')
getList()
}).then(async () => {
try {
// id id
await delCustomer({ ids: Ids })
ElMessage.success('删除成功')
getList()
} catch (error) {
ElMessage.error('删除失败:' + error.message)
}
}).catch(() => {})
}
@ -554,15 +475,15 @@ const resetForm = () => {
customerCode: '',
customerName: '',
customerType: '',
contact: '',
phone: '',
contactPerson: '',
mobilePhone: '',
email: '',
address: '',
detailAddress: '',
taxNumber: '',
bankName: '',
bankAccount: '',
qualificationInfo: [],
status: 1
status: '正常'
})
fileList.value = []
customerFormRef.value?.clearValidate()
@ -577,7 +498,7 @@ const handleFileChange = (file, fileList) => {
uploadRef.value?.handleRemove(file)
return
}
// base64
if (file.raw) {
const reader = new FileReader()
@ -589,14 +510,14 @@ const handleFileChange = (file, fileList) => {
} else if (fileType.includes('image')) {
type = 'image'
}
const newFile = {
name: file.name,
url: e.target.result,
type: type,
size: file.raw.size
}
//
const exists = customerForm.qualificationInfo.find(f => f.name === file.name)
if (!exists) {
@ -634,82 +555,20 @@ const handleDownloadQualificationFile = (file) => {
const handleSubmit = async () => {
if (!customerFormRef.value) return
await customerFormRef.value.validate((valid) => {
await customerFormRef.value.validate(async (valid) => {
if (!valid) return
submitLoading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_customers') || '[]')
const now = new Date().toISOString()
let res
if (isEdit.value) {
//
const index = mockData.findIndex(item => item.id === customerForm.id)
if (index !== -1) {
// ID
const codeExists = mockData.find(item =>
item.id !== customerForm.id &&
item.customerCode === customerForm.customerCode
)
if (codeExists) {
ElMessage.warning('客户ID已存在')
submitLoading.value = false
return
}
mockData[index] = {
...mockData[index],
customerCode: customerForm.customerCode,
customerName: customerForm.customerName,
customerType: customerForm.customerType,
contact: customerForm.contact,
phone: customerForm.phone,
email: customerForm.email,
address: customerForm.address,
taxNumber: customerForm.taxNumber,
bankName: customerForm.bankName,
bankAccount: customerForm.bankAccount,
qualificationInfo: customerForm.qualificationInfo,
status: customerForm.status,
updateTime: now
}
}
res = await updateCustomer(customerForm)
} else {
//
// ID
if (!customerForm.customerCode || !customerForm.customerCode.match(/^C\d{3}$/)) {
customerForm.customerCode = generateCustomerCode()
}
// ID
const codeExists = mockData.find(item => item.customerCode === customerForm.customerCode)
if (codeExists) {
//
customerForm.customerCode = generateCustomerCode()
}
const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1
mockData.push({
id: newId,
customerCode: customerForm.customerCode,
customerName: customerForm.customerName,
customerType: customerForm.customerType,
contact: customerForm.contact,
phone: customerForm.phone,
email: customerForm.email,
address: customerForm.address,
creditCode: customerForm.creditCode,
bankName: customerForm.bankName,
bankAccount: customerForm.bankAccount,
qualificationInfo: customerForm.qualificationInfo,
status: customerForm.status,
createTime: now,
updateTime: now
})
res = await addCustomer(customerForm)
}
localStorage.setItem('mock_customers', JSON.stringify(mockData))
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
dialogVisible.value = false
getList()

View File

@ -1,29 +1,59 @@
<template>
<div class="app-container">
<el-card>
<div class="filter-toolbar">
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="价格类型" prop="indexType">
<el-select
v-model="queryParams.indexType"
placeholder="全部"
clearable
style="width: 180px"
>
<el-option label="全部" value="" />
<el-option label="CCI价" value="CCI价" />
<el-option label="神华外购石炭价" value="神华外购石炭价" />
<el-option label="神华外购价" value="神华外购价" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="handleAdd">新增价格配置</el-button>
</div>
<el-table v-loading="loading" :data="priceList" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="name" label="价格类型" />
<el-table-column prop="type" label="类型" width="150">
<!-- <el-table-column prop="name" label="价格类型" /> -->
<el-table-column prop="indexType" label="价格类型" width="150">
<template #default="scope">
<span v-if="scope.row.type">{{ scope.row.type }}</span>
<span v-if="scope.row.indexType">{{ scope.row.indexType }}</span>
<span v-else style="color: #999;">-</span>
</template>
</el-table-column>
<el-table-column prop="heatValue" label="热值" />
<el-table-column prop="price" label="价格" align="right">
<template #default="scope">
<span v-if="scope.row.price !== null && scope.row.price !== undefined">{{ formatMoney(scope.row.price) }}</span>
<span v-if="scope.row.price !== null && scope.row.price !== undefined">{{ formatMoney(scope.row.price)
}}</span>
<span v-else style="color: #999;">-</span>
</template>
</el-table-column>
<el-table-column prop="berthDayCCI" label="靠泊日CCI" align="right">
<el-table-column prop="publishDate" label="发布日期">
<template #default="scope">
<span v-if="scope.row.berthDayCCI !== null && scope.row.berthDayCCI !== undefined">{{ formatMoney(scope.row.berthDayCCI) }}</span>
<span>{{ formatDate(scope.row.publishDate) }}</span>
</template>
</el-table-column> <!-- <el-table-column prop="berthDayCCI" label="CCI" align="right">
<template #default="scope">
<span v-if="scope.row.berthDayCCI !== null && scope.row.berthDayCCI !== undefined">{{
formatMoney(scope.row.berthDayCCI) }}</span>
<span v-else style="color: #999;">-</span>
</template>
</el-table-column>
<el-table-column prop="weeklyCCIAvg" label="靠泊日当周CCI平均价" align="right">
<template #default="scope">
<span v-if="scope.row.weeklyCCIAvg !== null && scope.row.weeklyCCIAvg !== undefined">{{ formatMoney(scope.row.weeklyCCIAvg) }}</span>
<span v-if="scope.row.weeklyCCIAvg !== null && scope.row.weeklyCCIAvg !== undefined">{{
formatMoney(scope.row.weeklyCCIAvg) }}</span>
<span v-else style="color: #999;">-</span>
</template>
</el-table-column>
@ -35,153 +65,79 @@
</el-table-column>
<el-table-column prop="downRange" label="下浮范围(%)" width="150" align="right">
<template #default="scope">
<span v-if="scope.row.downRange !== null && scope.row.downRange !== undefined">{{ scope.row.downRange }}%</span>
<span v-if="scope.row.downRange !== null && scope.row.downRange !== undefined">{{ scope.row.downRange
}}%</span>
<span v-else style="color: #999;">-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right" align="center">
</el-table-column> -->
<el-table-column label="操作" width="220" fixed="right" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 编辑价格对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="700px"
:close-on-click-modal="false"
@close="resetForm"
>
<el-form
ref="priceFormRef"
:model="priceForm"
:rules="dynamicRules"
label-width="160px"
>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px" :close-on-click-modal="false"
@close="resetForm">
<el-form ref="priceFormRef" :model="priceForm" :rules="dynamicRules" label-width="160px">
<el-form-item label="价格类型">
<el-input v-model="priceForm.name" disabled />
<!-- <el-input v-model="priceForm.name" disabled /> -->
<el-select v-model="priceForm.indexType" placeholder="请选择价格类型" style="width: 100%">
<el-option label="CCI价" value="CCI价" />
<el-option label="神华外购石炭价" value="神华外购石炭价" />
<el-option label="神华外购价" value="神华外购价" />
</el-select>
</el-form-item>
<!-- CCI 类型 -->
<template v-if="priceForm.priceType === 'cci'">
<el-form-item label="靠泊日CCI" prop="berthDayCCI">
<el-input-number
v-model="priceForm.berthDayCCI"
:min="0"
:precision="2"
placeholder="请输入靠泊日CCI"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="靠泊日当周CCI平均价" prop="weeklyCCIAvg">
<el-input-number
v-model="priceForm.weeklyCCIAvg"
:min="0"
:precision="2"
placeholder="请输入靠泊日当周CCI平均价"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="上浮范围(%)" prop="upRange">
<el-input-number
v-model="priceForm.upRange"
:min="0"
:max="100"
:precision="2"
placeholder="请输入上浮范围"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="下浮范围(%)" prop="downRange">
<el-input-number
v-model="priceForm.downRange"
:min="0"
:max="100"
:precision="2"
placeholder="请输入下浮范围"
style="width: 100%"
/>
</el-form-item>
</template>
<el-form-item label="热值" prop="heatValue">
<el-input-number v-model="priceForm.heatValue" :min="0" :precision="2" placeholder="请输入热值"
style="width: 100%" />
</el-form-item>
<el-form-item label="发布日期" prop="publishDate">
<el-date-picker v-model="priceForm.publishDate" type="date" placeholder="请选择发布日期" style="width: 100%" />
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input-number v-model="priceForm.price" :min="0" :precision="2" placeholder="请输入价格" style="width: 100%" />
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model="priceForm.unit" disabled style="width: 100%" />
</el-form-item>
<!-- 神华外购石炭价 -->
<template v-else-if="priceForm.priceType === 'shenhua_stone'">
<el-form-item label="类型">
<el-input v-model="priceForm.type" disabled />
</el-form-item>
<!-- 神华外购石炭价价 -->
<!-- <template v-else-if="priceForm.indexType === '神华外购石炭价'">
<el-form-item label="价格" prop="price">
<el-input-number
v-model="priceForm.price"
:min="0"
:precision="2"
placeholder="请输入价格"
style="width: 100%"
/>
<el-input-number v-model="priceForm.price" :min="0" :precision="2" placeholder="请输入价格"
style="width: 100%" />
</el-form-item>
<el-form-item label="上浮范围(%)" prop="upRange">
<el-input-number
v-model="priceForm.upRange"
:min="0"
:max="100"
:precision="2"
placeholder="请输入上浮范围"
style="width: 100%"
/>
<el-input-number v-model="priceForm.upRange" :min="0" :max="100" :precision="2" placeholder="请输入上浮范围"
style="width: 100%" />
</el-form-item>
<el-form-item label="下浮范围(%)" prop="downRange">
<el-input-number
v-model="priceForm.downRange"
:min="0"
:max="100"
:precision="2"
placeholder="请输入下浮范围"
style="width: 100%"
/>
<el-input-number v-model="priceForm.downRange" :min="0" :max="100" :precision="2" placeholder="请输入下浮范围"
style="width: 100%" />
</el-form-item>
</template>
</template> -->
<!-- 神华外购价 -->
<template v-else-if="priceForm.priceType === 'shenhua_purchase'">
<el-form-item label="类型" prop="type">
<el-input
v-model="priceForm.type"
placeholder="请输入类型"
maxlength="50"
/>
</el-form-item>
<!-- 神华外购价价 -->
<!-- <template v-else-if="priceForm.indexType === '神华外购价'">
<el-form-item label="价格" prop="price">
<el-input-number
v-model="priceForm.price"
:min="0"
:precision="2"
placeholder="请输入价格"
style="width: 100%"
/>
<el-input-number v-model="priceForm.price" :min="0" :precision="2" placeholder="请输入价格"
style="width: 100%" />
</el-form-item>
<el-form-item label="上浮范围(%)" prop="upRange">
<el-input-number
v-model="priceForm.upRange"
:min="0"
:max="100"
:precision="2"
placeholder="请输入上浮范围"
style="width: 100%"
/>
<el-input-number v-model="priceForm.upRange" :min="0" :max="100" :precision="2" placeholder="请输入上浮范围"
style="width: 100%" />
</el-form-item>
<el-form-item label="下浮范围(%)" prop="downRange">
<el-input-number
v-model="priceForm.downRange"
:min="0"
:max="100"
:precision="2"
placeholder="请输入下浮范围"
style="width: 100%"
/>
<el-input-number v-model="priceForm.downRange" :min="0" :max="100" :precision="2" placeholder="请输入下浮范围"
style="width: 100%" />
</el-form-item>
</template>
</template> -->
</el-form>
<template #footer>
<div class="dialog-footer">
@ -197,28 +153,45 @@
<script setup name="Price">
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { formatMoney } from '@/utils/business'
import { listPriceIndex, addPriceIndex, updatePriceIndex, delPriceIndex } from '@/api/base/price'
const loading = ref(false)
const submitLoading = ref(false)
const priceList = ref([])
const dialogVisible = ref(false)
const priceFormRef = ref(null)
const isEdit = ref(false)
//
const queryParams = reactive({
indexType: ''
})
const priceForm = reactive({
id: null,
priceType: '',
indexType: '',
name: '',
type: '',
heatValue: null,
publishDate: null,
price: null,
berthDayCCI: null,
weeklyCCIAvg: null,
upRange: null,
downRange: null
downRange: null,
unit: '吨'
})
const baseRules = {
heatValue: [
{ required: true, message: '请输入热值', trigger: 'blur' },
{ type: 'number', min: 0, message: '热值必须大于等于0', trigger: 'blur' }
],
publishDate: [
{ required: true, message: '请选择发布日期', trigger: 'change' }
],
berthDayCCI: [
{ required: true, message: '请输入靠泊日CCI', trigger: 'blur' },
{ type: 'number', min: 0, message: 'CCI必须大于等于0', trigger: 'blur' }
@ -246,42 +219,39 @@ const baseRules = {
const dynamicRules = computed(() => {
const rules = {}
if (priceForm.priceType === 'cci') {
// CCICCICCI
rules.berthDayCCI = baseRules.berthDayCCI
rules.weeklyCCIAvg = baseRules.weeklyCCIAvg
rules.upRange = baseRules.upRange
rules.downRange = baseRules.downRange
} else if (priceForm.priceType === 'shenhua_stone') {
//
if (priceForm.indexType === 'CCI价') {
// CCI
rules.type = baseRules.type
rules.heatValue = baseRules.heatValue
rules.publishDate = baseRules.publishDate
rules.price = baseRules.price
} else if (priceForm.indexType === 'shenhua_stone') {
//
rules.price = baseRules.price
rules.upRange = baseRules.upRange
rules.downRange = baseRules.downRange
} else if (priceForm.priceType === 'shenhua_purchase') {
//
} else if (priceForm.indexType === 'shenhua_purchase') {
//
rules.type = baseRules.type
rules.price = baseRules.price
rules.upRange = baseRules.upRange
rules.downRange = baseRules.downRange
}
return rules
})
const dialogTitle = computed(() => '编辑价格配置')
const dialogTitle = computed(() => (isEdit.value ? '编辑价格配置' : '新增价格配置'))
const getList = async () => {
loading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_price_configs') || '[]')
if (mockData.length === 0) {
generateMockData()
return
}
priceList.value = mockData
const res = await listPriceIndex({
indexType: queryParams.indexType || undefined
})
//
priceList.value = (res && (res.data?.list || res.data || res.list)) || []
} catch (error) {
ElMessage.error('获取价格配置失败:' + error.message)
} finally {
@ -289,56 +259,35 @@ const getList = async () => {
}
}
const generateMockData = () => {
setTimeout(() => {
const defaultConfigs = [
{
id: 1,
priceType: 'cci',
name: 'CCI煤炭价格指数网',
type: null,
price: null,
berthDayCCI: 550.00,
weeklyCCIAvg: 545.50,
upRange: 5.00,
downRange: 3.00
},
{
id: 2,
priceType: 'shenhua_stone',
name: '神华外购石炭价',
type: '石炭',
price: 570.00,
berthDayCCI: null,
weeklyCCIAvg: null,
upRange: 8.00,
downRange: 5.00
},
{
id: 3,
priceType: 'shenhua_purchase',
name: '神华外购价',
type: '标准煤',
price: 580.00,
berthDayCCI: null,
weeklyCCIAvg: null,
upRange: 10.00,
downRange: 6.00
}
]
localStorage.setItem('mock_price_configs', JSON.stringify(defaultConfigs))
priceList.value = defaultConfigs
loading.value = false
}, 10)
//
const handleQuery = () => {
getList()
}
//
const resetQuery = () => {
queryParams.indexType = ''
getList()
}
const handleAdd = () => {
isEdit.value = false
resetForm()
// CCI
priceForm.indexType = 'CCI价'
priceForm.name = 'CCI煤炭价格指数网'
dialogVisible.value = true
}
const handleEdit = (row) => {
isEdit.value = true
Object.assign(priceForm, {
id: row.id,
priceType: row.priceType,
indexType: row.indexType,
name: row.name,
type: row.priceType === 'shenhua_stone' ? '石炭' : (row.type || ''),
type: row.indexType === 'shenhua_stone' ? '石炭' : (row.type || ''),
heatValue: row.heatValue,
publishDate: row.publishDate,
price: row.price,
berthDayCCI: row.berthDayCCI,
weeklyCCIAvg: row.weeklyCCIAvg,
@ -348,12 +297,33 @@ const handleEdit = (row) => {
dialogVisible.value = true
}
const handleDelete = (row) => {
const ids = row.id ? [row.id] : []
if (!ids.length) return
ElMessageBox.confirm('确定要删除该价格配置吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
await delPriceIndex({ ids })
ElMessage.success('删除成功')
getList()
} catch (error) {
ElMessage.error('删除失败:' + error.message)
}
}).catch(() => { })
}
const resetForm = () => {
Object.assign(priceForm, {
id: null,
priceType: '',
indexType: '',
name: '',
type: '',
heatValue: null,
publishDate: null,
price: null,
berthDayCCI: null,
weeklyCCIAvg: null,
@ -365,30 +335,18 @@ const resetForm = () => {
const handleSubmit = async () => {
if (!priceFormRef.value) return
await priceFormRef.value.validate((valid) => {
await priceFormRef.value.validate(async (valid) => {
if (!valid) return
submitLoading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_price_configs') || '[]')
const index = mockData.findIndex(item => item.id === priceForm.id)
if (index !== -1) {
mockData[index] = {
...mockData[index],
type: priceForm.priceType === 'shenhua_stone' ? '石炭' : (priceForm.type || null),
price: priceForm.price,
berthDayCCI: priceForm.berthDayCCI,
weeklyCCIAvg: priceForm.weeklyCCIAvg,
upRange: priceForm.upRange,
downRange: priceForm.downRange,
updateTime: new Date().toISOString()
}
if (isEdit.value) {
await updatePriceIndex(priceForm)
} else {
await addPriceIndex(priceForm)
}
localStorage.setItem('mock_price_configs', JSON.stringify(mockData))
ElMessage.success('编辑成功')
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
dialogVisible.value = false
getList()
} catch (error) {
@ -398,6 +356,15 @@ const handleSubmit = async () => {
}
})
}
const formatDate = (value) => {
if (!value) return '-'
const d = new Date(value)
if (Number.isNaN(d.getTime())) return value
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${y}-${m}-${day}`
}
onMounted(() => getList())
</script>
@ -419,4 +386,3 @@ onMounted(() => getList())
padding-top: 20px;
}
</style>

View File

@ -3,19 +3,18 @@
<el-card>
<div class="filter-toolbar">
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="商品类别" prop="category">
<el-select v-model="queryParams.category" placeholder="全部" clearable style="width: 150px">
<el-form-item label="商品类别" prop="categoryId">
<el-select v-model="queryParams.categoryId" placeholder="全部" clearable style="width: 150px">
<el-option label="全部" value="" />
<el-option label="动力煤" value="动力煤" />
<el-option label="焦煤" value="焦煤" />
<el-option label="无烟煤" value="无烟煤" />
<el-option v-for="category in categoryOptions" :key="category.id" :label="category.categoryName"
:value="category.id" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 150px">
<el-option label="全部" value="" />
<el-option label="启用" value="1" />
<el-option label="禁用" value="0" />
<el-option label="启用" value="启用" />
<el-option label="禁用" value="禁用" />
</el-select>
</el-form-item>
<el-form-item>
@ -27,46 +26,45 @@
</div>
<el-table v-loading="loading" :data="productList" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="productCode" label="商品编码" width="120" />
<el-table-column prop="productCode" label="商品编码" width="200" />
<el-table-column prop="productName" label="商品名称" width="200" />
<el-table-column prop="specification" label="规格型号" width="150" />
<el-table-column prop="categoryName" label="商品类别" width="120" />
<el-table-column prop="purchaseUnit" label="采购单位" width="120" />
<el-table-column prop="salesUnit" label="销售单位" width="120" />
<el-table-column prop="stockUnit" label="库存单位" width="120" />
<el-table-column prop="defaultCostPrice" label="默认成本价(元/吨)" width="160" align="right">
<el-table-column prop="specification" label="规格型号" width="200" />
<el-table-column prop="categoryName" label="商品类别" width="200">
<template #default="scope">
{{ scope.row.categoryName || scope.row.categoryId || scope.row.categoryId || '-' }}
</template>
</el-table-column>
<el-table-column prop="unit" label="计量单位" width="200" />
<!-- <el-table-column prop="salesUnit" label="销售单位" width="120" />
<el-table-column prop="stockUnit" label="库存单位" width="120" /> -->
<!-- <el-table-column prop="defaultCostPrice" label="默认成本价(元/吨)" width="160" align="right">
<template #default="scope">
{{ formatMoney(scope.row.defaultCostPrice) }}
</template>
</el-table-column>
<el-table-column prop="defaultSalesPrice" label="默认销售价(元/吨)" width="160" align="right">
<template #default="scope">
</el-table-column>
<el-table-column prop="defaultSalesPrice" label="默认销售价(元/吨)" width="160" align="right">
<template #default="scope">
{{ formatMoney(scope.row.defaultSalesPrice) }}
</template>
</el-table-column>
<el-table-column prop="calorificValue" label="热值(kcal/kg)" width="150" align="right" />
<el-table-column prop="qualityReport" label="质检报告" width="150">
</el-table-column> -->
<el-table-column prop="standardHeatValue" label="热值(kcal/kg)" width="200" />
<!-- <el-table-column prop="qualityReport" label="质检报告" width="">
<template #default="scope">
<el-button
v-if="scope.row.qualityReport"
link
type="primary"
size="small"
@click="handleViewReport(scope.row)"
>
<el-button v-if="scope.row.qualityReport" link type="primary" size="small"
@click="handleViewReport(scope.row)">
查看报告
</el-button>
<span v-else style="color: #999;">暂无报告</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
</el-table-column> -->
<el-table-column prop="status" label="状态" width="200">
<template #default="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'danger'">
{{ scope.row.status == 1 ? '启用' : '禁用' }}
<el-tag :type="scope.row.status == '启用' ? 'success' : 'danger'">
{{ scope.row.status == '启用' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right" align="center">
<el-table-column label="操作" width="" fixed="right" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
@ -74,162 +72,91 @@
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageIndex"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 新增/编辑商品对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="800px"
:close-on-click-modal="false"
@close="resetForm"
>
<el-form
ref="productFormRef"
:model="productForm"
:rules="productFormRules"
label-width="140px"
>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="800px" :close-on-click-modal="false"
@close="resetForm">
<el-form ref="productFormRef" :model="productForm" :rules="productFormRules" label-width="140px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="商品编码" prop="productCode">
<el-input
v-model="productForm.productCode"
placeholder="请输入商品编码"
:disabled="isEdit"
maxlength="30"
/>
<el-input v-model="productForm.productCode" placeholder="请输入商品编码" :disabled="isEdit" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品名称" prop="productName">
<el-input
v-model="productForm.productName"
placeholder="请输入商品名称"
maxlength="50"
/>
<el-input v-model="productForm.productName" placeholder="请输入商品名称" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="规格型号" prop="specification">
<el-input
v-model="productForm.specification"
placeholder="请输入规格型号"
maxlength="50"
/>
<el-input v-model="productForm.specification" placeholder="请输入规格型号" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品类别" prop="categoryName">
<el-select
v-model="productForm.categoryName"
placeholder="请选择商品类别"
filterable
style="width: 100%"
>
<el-option
v-for="category in categoryOptions"
:key="category"
:label="category"
:value="category"
/>
<el-form-item label="商品类别" prop="categoryId">
<el-select v-model="productForm.categoryId" placeholder="请选择商品类别" filterable style="width: 100%">
<el-option v-for="category in categoryOptions" :key="category.id" :label="category.categoryName"
:value="category.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="采购单位" prop="purchaseUnit">
<el-input
v-model="productForm.purchaseUnit"
placeholder="请输入采购单位"
maxlength="10"
/>
<el-form-item label="计量单位" prop="unit">
<el-input v-model="productForm.purchaseUnit" disabled placeholder="请输入计量单位" maxlength="10" />
</el-form-item>
</el-col>
<el-col :span="12">
<!-- <el-col :span="12">
<el-form-item label="销售单位" prop="salesUnit">
<el-input
v-model="productForm.salesUnit"
placeholder="请输入销售单位"
maxlength="10"
/>
<el-input v-model="productForm.salesUnit" placeholder="请输入销售单位" maxlength="10" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="库存单位" prop="stockUnit">
<el-input
v-model="productForm.stockUnit"
placeholder="请输入库存单位"
maxlength="10"
/>
<el-input v-model="productForm.stockUnit" placeholder="请输入库存单位" maxlength="10" />
</el-form-item>
</el-col>
</el-col> -->
</el-row>
<el-row :gutter="20">
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item label="默认成本价(元/吨)" prop="defaultCostPrice">
<el-input-number
v-model="productForm.defaultCostPrice"
:min="0"
:precision="2"
placeholder="请输入默认成本价"
style="width: 100%"
/>
<el-input-number v-model="productForm.defaultCostPrice" :min="0" :precision="2" placeholder="请输入默认成本价"
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="默认销售价(元/吨)" prop="defaultSalesPrice">
<el-input-number
v-model="productForm.defaultSalesPrice"
:min="0"
:precision="2"
placeholder="请输入默认销售价"
style="width: 100%"
/>
<el-input-number v-model="productForm.defaultSalesPrice" :min="0" :precision="2" placeholder="请输入默认销售价"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-row> -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="热值(kcal/kg)" prop="calorificValue">
<el-input-number
v-model="productForm.calorificValue"
:min="0"
:precision="0"
placeholder="请输入热值"
style="width: 100%"
/>
<el-form-item label="热值(kcal/kg)" prop="standardHeatValue">
<el-input-number v-model="productForm.standardHeatValue" :min="0" :precision="0" placeholder="请输入热值"
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-radio-group v-model="productForm.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
<el-radio :label="'启用'">启用</el-radio>
<el-radio :label="'禁用'">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="质检报告" prop="qualityReport">
<el-upload
ref="uploadRef"
:file-list="fileList"
:auto-upload="false"
:on-change="handleFileChange"
:on-remove="handleFileRemove"
:limit="1"
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png"
>
<!-- <el-form-item label="质检报告" prop="qualityReport">
<el-upload ref="uploadRef" :file-list="fileList" :auto-upload="false" :on-change="handleFileChange"
:on-remove="handleFileRemove" :limit="1" accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png">
<el-button type="primary">选择文件</el-button>
<template #tip>
<div class="el-upload__tip">
@ -242,7 +169,7 @@
查看当前报告
</el-button>
</div>
</el-form-item>
</el-form-item> -->
</el-form>
<template #footer>
<div class="dialog-footer">
@ -255,23 +182,12 @@
</el-dialog>
<!-- 查看质检报告对话框 -->
<el-dialog
v-model="reportDialogVisible"
title="质检报告"
width="900px"
>
<el-dialog v-model="reportDialogVisible" title="质检报告" width="900px">
<div v-if="currentReport" style="text-align: center;">
<iframe
v-if="currentReport.type === 'pdf'"
:src="currentReport.url"
style="width: 100%; height: 600px; border: none;"
></iframe>
<img
v-else-if="currentReport.type === 'image'"
:src="currentReport.url"
style="max-width: 100%; max-height: 600px;"
alt="质检报告"
/>
<iframe v-if="currentReport.type === 'pdf'" :src="currentReport.url"
style="width: 100%; height: 600px; border: none;"></iframe>
<img v-else-if="currentReport.type === 'image'" :src="currentReport.url"
style="max-width: 100%; max-height: 600px;" alt="质检报告" />
<div v-else>
<el-button type="primary" @click="handleDownloadReport(currentReport)">
下载报告
@ -287,6 +203,8 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { formatMoney } from '@/utils/business'
import Pagination from '@/components/Pagination'
import { listproductCategory } from '@/api/base/category'
import { listproduct, addProduct, updateProduct, delProduct } from '@/api/base/product'
const loading = ref(false)
const submitLoading = ref(false)
@ -301,28 +219,33 @@ const fileList = ref([])
const currentReport = ref(null)
const queryParams = reactive({
pageNum: 1,
pageIndex: 1,
pageSize: 10,
category: '',
status: ''
})
const categoryOptions = ref(['动力煤', '焦煤', '无烟煤', '褐煤', '瘦煤', '肥煤', '气煤', '1/3焦煤', '贫煤', '长焰煤', '不粘煤', '弱粘煤', '1/2中粘煤', '中粘煤', '强粘煤', '特强粘煤', '贫瘦煤', '瘦焦煤', '主焦煤', '其他'])
const categoryOptions = ref([])
const categoryMap = computed(() => {
const map = {}
categoryOptions.value.forEach((item) => {
map[item.id] = item.categoryName
})
return map
})
const productForm = reactive({
id: null,
productCode: '',
productName: '',
specification: '',
categoryName: '',
purchaseUnit: '吨',
salesUnit: '吨',
stockUnit: '吨',
categoryId: '',
unit: '吨',
defaultCostPrice: 0,
defaultSalesPrice: 0,
calorificValue: 0,
standardHeatValue: 0,
qualityReport: null,
status: 1
status: '启用'
})
const productFormRules = {
@ -334,13 +257,13 @@ const productFormRules = {
{ required: true, message: '请输入商品名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
categoryName: [
categoryId: [
{ required: true, message: '请选择商品类别', trigger: 'change' }
],
purchaseUnit: [
{ required: true, message: '请输入采购单位', trigger: 'blur' }
],
salesUnit: [
unit: [
{ required: true, message: '请输入销售单位', trigger: 'blur' }
],
stockUnit: [
@ -354,7 +277,7 @@ const productFormRules = {
{ required: true, message: '请输入默认销售价', trigger: 'blur' },
{ type: 'number', min: 0, message: '销售价必须大于等于0', trigger: 'blur' }
],
calorificValue: [
standardHeatValue: [
{ required: true, message: '请输入热值', trigger: 'blur' },
{ type: 'number', min: 0, message: '热值必须大于等于0', trigger: 'blur' }
],
@ -365,25 +288,37 @@ const productFormRules = {
const dialogTitle = computed(() => isEdit.value ? '编辑商品' : '新增商品')
//
const appendCategoryName = (list = []) => {
return list.map((item) => ({
...item,
categoryName: categoryMap.value[item.categoryId] || categoryMap.value[item.categoryId] || item.categoryName || ''
}))
}
const getCategoryList = async () => {
try {
const response = await listproductCategory({ pageIndex: 1, pageSize: 1000 })
categoryOptions.value = response.data?.list || []
//
productList.value = appendCategoryName(productList.value)
// categoryOptions.value = categories.map(item => item.categoryName).filter(Boolean)
console.log(categoryOptions.value, ' categoryOptions.value ');
} catch (error) {
ElMessage.error('获取商品类别列表失败:' + (error.message || '未知错误'))
}
}
const getList = async () => {
loading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_products') || '[]')
let filtered = mockData.filter(item => {
if (queryParams.category && item.categoryName !== queryParams.category) return false
if (queryParams.status !== '' && item.status != queryParams.status) return false
return true
})
const start = (queryParams.pageNum - 1) * queryParams.pageSize
const end = start + queryParams.pageSize
productList.value = filtered.slice(start, end)
total.value = filtered.length
if (mockData.length === 0) {
generateMockData()
}
const response = await listproduct(queryParams)
const list = response.data?.list || response.rows || []
productList.value = appendCategoryName(list)
total.value = response.data?.count || response.total || 0
} catch (error) {
ElMessage.error('获取商品列表失败:' + error.message)
ElMessage.error('获取商品列表失败:' + (error.message || '未知错误'))
} finally {
loading.value = false
}
@ -399,7 +334,7 @@ const generateMockData = () => {
for (let i = 1; i <= 20; i++) {
const costPrice = Math.floor(Math.random() * 300) + 400
const salesPrice = costPrice + Math.floor(Math.random() * 100) + 50
const calorificValue = Math.floor(Math.random() * 2000) + 4000
const standardHeatValue = Math.floor(Math.random() * 2000) + 4000
newProducts.push({
id: i,
productCode: 'PM' + String(i).padStart(3, '0'),
@ -411,7 +346,7 @@ const generateMockData = () => {
stockUnit: '吨',
defaultCostPrice: costPrice,
defaultSalesPrice: salesPrice,
calorificValue: calorificValue,
standardHeatValue: standardHeatValue,
qualityReport: i <= 5 ? { name: `质检报告_${productNames[i - 1]}.pdf`, url: '#', type: 'pdf' } : null,
status: Math.random() > 0.1 ? 1 : 0,
createTime: new Date(now - i * 30 * 86400000).toISOString(),
@ -424,12 +359,12 @@ const generateMockData = () => {
}
const handleQuery = () => {
queryParams.pageNum = 1
queryParams.pageIndex = 1
getList()
}
const resetQuery = () => {
queryParams.category = ''
queryParams.categoryId = ''
queryParams.status = ''
handleQuery()
}
@ -447,17 +382,17 @@ const handleEdit = (row) => {
productCode: row.productCode,
productName: row.productName,
specification: row.specification || '',
categoryName: row.categoryName,
categoryId: row.categoryId,
purchaseUnit: row.purchaseUnit,
salesUnit: row.salesUnit,
stockUnit: row.stockUnit,
defaultCostPrice: row.defaultCostPrice,
defaultSalesPrice: row.defaultSalesPrice,
calorificValue: row.calorificValue || 0,
standardHeatValue: row.standardHeatValue || 0,
qualityReport: row.qualityReport,
status: row.status
})
//
if (row.qualityReport) {
fileList.value = [{
@ -467,22 +402,25 @@ const handleEdit = (row) => {
} else {
fileList.value = []
}
dialogVisible.value = true
}
const handleDelete = (row) => {
const Ids = (row.id && [row.id])
ElMessageBox.confirm('确定要删除此商品吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const products = JSON.parse(localStorage.getItem('mock_products') || '[]')
const filtered = products.filter(p => p.id != row.id)
localStorage.setItem('mock_products', JSON.stringify(filtered))
ElMessage.success('删除成功')
getList()
}).catch(() => {})
}).then(async () => {
try {
await delProduct({ ids: Ids })
ElMessage.success('删除成功')
getList()
} catch (error) {
ElMessage.error('删除失败:' + (error.message || '未知错误'))
}
}).catch(() => { })
}
const resetForm = () => {
@ -497,9 +435,9 @@ const resetForm = () => {
stockUnit: '吨',
defaultCostPrice: 0,
defaultSalesPrice: 0,
calorificValue: 0,
standardHeatValue: 0,
qualityReport: null,
status: 1
status: '启用'
})
fileList.value = []
productFormRef.value?.clearValidate()
@ -514,7 +452,7 @@ const handleFileChange = (file) => {
uploadRef.value?.handleRemove(file)
return
}
// base64
if (file.raw) {
const reader = new FileReader()
@ -526,7 +464,7 @@ const handleFileChange = (file) => {
} else if (fileType.includes('image')) {
type = 'image'
}
productForm.qualityReport = {
name: file.name,
url: e.target.result,
@ -565,90 +503,35 @@ const handleDownloadReport = (report) => {
const handleSubmit = async () => {
if (!productFormRef.value) return
await productFormRef.value.validate((valid) => {
await productFormRef.value.validate(async (valid) => {
if (!valid) return
submitLoading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_products') || '[]')
const now = new Date().toISOString()
if (isEdit.value) {
//
const index = mockData.findIndex(item => item.id === productForm.id)
if (index !== -1) {
//
const codeExists = mockData.find(item =>
item.id !== productForm.id &&
item.productCode === productForm.productCode
)
if (codeExists) {
ElMessage.warning('商品编码已存在')
submitLoading.value = false
return
}
mockData[index] = {
...mockData[index],
productCode: productForm.productCode,
productName: productForm.productName,
specification: productForm.specification,
categoryName: productForm.categoryName,
purchaseUnit: productForm.purchaseUnit,
salesUnit: productForm.salesUnit,
stockUnit: productForm.stockUnit,
defaultCostPrice: productForm.defaultCostPrice,
defaultSalesPrice: productForm.defaultSalesPrice,
calorificValue: productForm.calorificValue,
qualityReport: productForm.qualityReport,
status: productForm.status,
updateTime: now
}
}
await updateProduct(productForm)
ElMessage.success('编辑成功')
} else {
//
//
const codeExists = mockData.find(item => item.productCode === productForm.productCode)
if (codeExists) {
ElMessage.warning('商品编码已存在')
submitLoading.value = false
return
}
const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1
mockData.push({
id: newId,
productCode: productForm.productCode,
productName: productForm.productName,
specification: productForm.specification,
categoryName: productForm.categoryName,
purchaseUnit: productForm.purchaseUnit,
salesUnit: productForm.salesUnit,
stockUnit: productForm.stockUnit,
defaultCostPrice: productForm.defaultCostPrice,
defaultSalesPrice: productForm.defaultSalesPrice,
calorificValue: productForm.calorificValue,
qualityReport: productForm.qualityReport,
status: productForm.status,
createTime: now,
updateTime: now
})
await addProduct(productForm)
ElMessage.success('新增成功')
}
localStorage.setItem('mock_products', JSON.stringify(mockData))
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
dialogVisible.value = false
getList()
} catch (error) {
ElMessage.error('操作失败:' + error.message)
ElMessage.error('操作失败:' + (error.message || '未知错误'))
} finally {
submitLoading.value = false
}
})
}
onMounted(() => getList())
onMounted(() => {
getCategoryList()
getList()
})
</script>
<style scoped>

View File

@ -7,13 +7,14 @@
<div class="filter-toolbar">
<el-form :model="warehouseQueryParams" ref="warehouseQueryForm" :inline="true">
<el-form-item label="仓库名称" prop="warehouseName">
<el-input v-model="warehouseQueryParams.warehouseName" placeholder="请输入仓库名称" clearable style="width: 200px" />
<el-input v-model="warehouseQueryParams.warehouseName" placeholder="请输入仓库名称" clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="warehouseQueryParams.status" placeholder="全部" clearable style="width: 150px">
<el-option label="全部" value="" />
<el-option label="启用" value="1" />
<el-option label="禁用" value="0" />
<el-option label="启用" value="启用" />
<el-option label="禁用" value="禁用" />
</el-select>
</el-form-item>
<el-form-item>
@ -27,19 +28,19 @@
<el-table v-loading="warehouseLoading" :data="warehouseList" border style="width: 100%">
<el-table-column prop="warehouseCode" label="仓库编码" width="150" />
<el-table-column prop="warehouseName" label="仓库名称" />
<el-table-column prop="address" label="地址" />
<el-table-column prop="manager" label="负责人" width="160" />
<el-table-column prop="phone" label="联系电话" width="180">
<el-table-column prop="detailAddress" label="地址" />
<el-table-column prop="contactPerson" label="联系人" width="160" />
<el-table-column prop="contactPhone" label="联系电话" width="180">
<template #default="scope">
{{ scope.row.phone || '-' }}
{{ scope.row.contactPhone || '-' }}
</template>
</el-table-column>
<el-table-column prop="capacity" label="容量(吨)" width="120" align="right" />
<el-table-column prop="currentStock" label="当前库存(吨)" width="150" align="right" />
<!-- <el-table-column prop="stockCapacity" label="容量(吨)" width="120" align="right" />
<el-table-column prop="stockInventory" label="当前库存(吨)" width="150" align="right" /> -->
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'danger'">
{{ scope.row.status == 1 ? '启用' : '禁用' }}
<el-tag :type="scope.row.status == '启用' ? 'success' : 'danger'">
{{ scope.row.status == '启用' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
@ -51,21 +52,16 @@
</el-table-column>
</el-table>
<pagination
v-show="warehouseTotal > 0"
:total="warehouseTotal"
v-model:page="warehouseQueryParams.pageNum"
v-model:limit="warehouseQueryParams.pageSize"
@pagination="getWarehouseList"
/>
<pagination v-show="warehouseTotal > 0" :total="warehouseTotal" v-model:page="warehouseQueryParams.pageIndex"
v-model:limit="warehouseQueryParams.pageSize" @pagination="getWarehouseList" />
</el-tab-pane>
<!-- 垛位管理 -->
<el-tab-pane label="垛位管理" name="stack">
<div class="filter-toolbar">
<el-form :model="stackQueryParams" ref="stackQueryForm" :inline="true">
<el-form-item label="垛位名称" prop="stackName">
<el-input v-model="stackQueryParams.stackName" placeholder="请输入垛位名称" clearable style="width: 200px" />
<el-form-item label="垛位名称" prop="stockName">
<el-input v-model="stackQueryParams.stockName" placeholder="请输入垛位名称" clearable style="width: 200px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleStackQuery">搜索</el-button>
@ -76,17 +72,21 @@
</div>
<el-table v-loading="stackLoading" :data="stackList" border style="width: 100%">
<el-table-column prop="stackCode" label="垛位编码" width="150" />
<el-table-column prop="stackName" label="垛位名称" width="150" />
<el-table-column prop="warehouseName" label="所属仓库" width="150" />
<el-table-column prop="capacity" label="容量(吨)" width="120" align="right" />
<el-table-column prop="currentStock" label="当前库存(吨)" width="150" align="right" />
<el-table-column prop="createTime" label="创建时间" width="180">
<el-table-column prop="stockCode" label="垛位编码" width="200" />
<el-table-column prop="stockName" label="垛位名称"/>
<el-table-column prop="warehouseId" label="所属仓库" width="200">
<template #default="scope">
{{ parseTime(scope.row.createTime) }}
{{ scope.row.warehouse.warehouseName }}
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<el-table-column prop="stockCapacity" label="容量(吨)" width="150" align="right" />
<el-table-column prop="stockInventory" label="当前库存(吨)" width="200" align="right" />
<el-table-column prop="createdAt" label="创建时间" >
<template #default="scope">
{{ parseTime(scope.row.createdAt) }}
</template>
</el-table-column>
<el-table-column label="操作" width="" fixed="right">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleStackEdit(scope.row)">编辑</el-button>
<el-button link type="danger" size="small" @click="handleStackDelete(scope.row)">删除</el-button>
@ -94,87 +94,53 @@
</el-table-column>
</el-table>
<pagination
v-show="stackTotal > 0"
:total="stackTotal"
v-model:page="stackQueryParams.pageNum"
v-model:limit="stackQueryParams.pageSize"
@pagination="getStackList"
/>
<pagination v-show="stackTotal > 0" :total="stackTotal" v-model:page="stackQueryParams.pageIndex"
v-model:limit="stackQueryParams.pageSize" @pagination="getStackList" />
</el-tab-pane>
</el-tabs>
</el-card>
<!-- 新增/编辑仓库对话框 -->
<el-dialog
v-model="warehouseDialogVisible"
:title="warehouseDialogTitle"
width="600px"
:close-on-click-modal="false"
>
<el-form
ref="warehouseFormRef"
:model="warehouseForm"
:rules="warehouseFormRules"
label-width="100px"
>
<el-dialog v-model="warehouseDialogVisible" :title="warehouseDialogTitle" width="600px"
:close-on-click-modal="false">
<el-form ref="warehouseFormRef" :model="warehouseForm" :rules="warehouseFormRules" label-width="100px">
<el-form-item label="仓库编码" prop="warehouseCode">
<el-input
v-model="warehouseForm.warehouseCode"
placeholder="系统自动生成"
disabled
/>
<el-input v-model="warehouseForm.warehouseCode" placeholder="系统自动生成" disabled />
</el-form-item>
<el-form-item label="仓库名称" prop="warehouseName">
<el-input
v-model="warehouseForm.warehouseName"
placeholder="请输入仓库名称"
maxlength="50"
/>
<el-input v-model="warehouseForm.warehouseName" placeholder="请输入仓库名称" maxlength="50" />
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input
v-model="warehouseForm.address"
placeholder="请输入地址"
maxlength="200"
/>
<el-form-item label="地址" prop="detailAddress">
<el-input v-model="warehouseForm.detailAddress" placeholder="请输入地址" maxlength="200" />
</el-form-item>
<el-form-item label="负责人" prop="manager">
<el-input
v-model="warehouseForm.manager"
placeholder="请输入负责人"
maxlength="20"
/>
<el-form-item label="联系人" prop="contactPerson">
<el-input v-model="warehouseForm.contactPerson" placeholder="请输入联系人" maxlength="20" />
</el-form-item>
<el-form-item label="联系电话" prop="phone">
<el-input
v-model="warehouseForm.phone"
placeholder="请输入联系电话"
maxlength="20"
/>
<el-form-item label="联系电话" prop="contactPhone">
<el-input v-model="warehouseForm.contactPhone" placeholder="请输入联系电话" maxlength="20" />
</el-form-item>
<el-form-item label="容量(吨)" prop="capacity">
<!-- <el-form-item label="容量(吨)" prop="stockCapacity">
<el-input-number
v-model="warehouseForm.capacity"
v-model="warehouseForm.stockCapacity"
:min="0"
:precision="0"
placeholder="请输入容量"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="当前库存(吨)" prop="currentStock">
</el-form-item> -->
<!-- <el-form-item label="当前库存(吨)" prop="stockInventory">
<el-input-number
v-model="warehouseForm.currentStock"
v-model="warehouseForm.stockInventory"
:min="0"
:precision="0"
placeholder="请输入当前库存"
style="width: 100%"
/>
</el-form-item>
</el-form-item> -->
<el-form-item label="状态" prop="status">
<el-radio-group v-model="warehouseForm.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
<el-radio :label="'启用'">启用</el-radio>
<el-radio :label="'禁用'">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
@ -189,64 +155,27 @@
</el-dialog>
<!-- 新增/编辑垛位对话框 -->
<el-dialog
v-model="stackDialogVisible"
:title="stackDialogTitle"
width="600px"
:close-on-click-modal="false"
>
<el-form
ref="stackFormRef"
:model="stackForm"
:rules="stackFormRules"
label-width="100px"
>
<el-form-item label="垛位编码" prop="stackCode">
<el-input
v-model="stackForm.stackCode"
placeholder="系统自动生成"
disabled
/>
<el-dialog v-model="stackDialogVisible" :title="stackDialogTitle" width="600px" :close-on-click-modal="false">
<el-form ref="stackFormRef" :model="stackForm" :rules="stackFormRules" label-width="100px">
<el-form-item label="垛位编码" prop="stockCode">
<el-input v-model="stackForm.stockCode" placeholder="系统自动生成" disabled />
</el-form-item>
<el-form-item label="垛位名称" prop="stackName">
<el-input
v-model="stackForm.stackName"
placeholder="请输入垛位名称"
maxlength="50"
/>
<el-form-item label="垛位名称" prop="stockName">
<el-input v-model="stackForm.stockName" placeholder="请输入垛位名称" maxlength="50" />
</el-form-item>
<el-form-item label="所属仓库" prop="warehouseName">
<el-select
v-model="stackForm.warehouseName"
placeholder="请选择所属仓库"
style="width: 100%"
filterable
>
<el-option
v-for="warehouse in warehouseOptions"
:key="warehouse.warehouseName"
:label="warehouse.warehouseName"
:value="warehouse.warehouseName"
/>
<el-form-item label="所属仓库" prop="warehouseId">
<el-select v-model="stackForm.warehouseId" placeholder="请选择所属仓库" style="width: 100%" filterable>
<el-option v-for="warehouse in warehouseOptions" :key="warehouse.id" :label="warehouse.warehouseName"
:value="warehouse.id" />
</el-select>
</el-form-item>
<el-form-item label="容量(吨)" prop="capacity">
<el-input-number
v-model="stackForm.capacity"
:min="0"
:precision="0"
placeholder="请输入容量"
style="width: 100%"
/>
<el-form-item label="容量(吨)" prop="stockCapacity">
<el-input-number v-model="stackForm.stockCapacity" :min="0" :precision="0" placeholder="请输入容量"
style="width: 100%" />
</el-form-item>
<el-form-item label="当前库存(吨)" prop="currentStock">
<el-input-number
v-model="stackForm.currentStock"
:min="0"
:precision="0"
placeholder="请输入当前库存"
style="width: 100%"
/>
<el-form-item label="当前库存(吨)" prop="stockInventory">
<el-input-number v-model="stackForm.stockInventory" :min="0" :precision="0" placeholder="请输入当前库存"
style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
@ -266,6 +195,16 @@ import { ref, reactive, computed, onMounted, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { parseTime } from '@/utils/ruoyi'
import Pagination from '@/components/Pagination'
import {
listWarehouse,
addWarehouse,
updateWarehouse,
delWarehouse,
listWarehouseStock,
addWarehouseStock,
updateWarehouseStock,
delWarehouseStock
} from '@/api/base/serviceProvider'
// Tab
const activeTab = ref('warehouse')
@ -279,46 +218,55 @@ const warehouseSubmitLoading = ref(false)
const warehouseIsEdit = ref(false)
const warehouseFormRef = ref(null)
// 使 pageIndex
const warehouseQueryParams = reactive({
pageNum: 1,
pageIndex: 1,
pageSize: 10,
warehouseName: '',
status: ''
})
const warehouseForm = reactive({
id: null,
warehouseCode: '',
warehouseName: '',
address: '',
manager: '',
phone: '',
capacity: null,
currentStock: null,
status: 1
detailAddress: '',
contactPerson: '',
contactPhone: '',
stockCapacity: null,
stockInventory: null,
status: '启用'
})
// CK + 6 + 3
const generateWarehouseCode = () => {
const prefix = 'CK'
const timePart = Date.now().toString().slice(-6)
const randomPart = Math.floor(Math.random() * 900 + 100) // 100-999
return `${prefix}${timePart}${randomPart}`
}
const warehouseFormRules = {
warehouseName: [
{ required: true, message: '请输入仓库名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
address: [
detailAddress: [
{ required: true, message: '请输入地址', trigger: 'blur' },
{ max: 200, message: '地址不能超过 200 个字符', trigger: 'blur' }
],
manager: [
{ required: true, message: '请输入负责人', trigger: 'blur' },
{ max: 20, message: '负责人姓名不能超过 20 个字符', trigger: 'blur' }
contactPerson: [
{ required: true, message: '请输入联系人', trigger: 'blur' },
{ max: 20, message: '联系人姓名不能超过 20 个字符', trigger: 'blur' }
],
phone: [
{ pattern: /^1[3-9]\d{9}$|^$/, message: '请输入正确的手机号码', trigger: 'blur' }
contactPhone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
capacity: [
stockCapacity: [
{ required: true, message: '请输入容量', trigger: 'blur' },
{ type: 'number', min: 0, message: '容量必须大于等于0', trigger: 'blur' }
],
currentStock: [
stockInventory: [
{ required: true, message: '请输入当前库存', trigger: 'blur' },
{ type: 'number', min: 0, message: '当前库存必须大于等于0', trigger: 'blur' }
],
@ -332,57 +280,19 @@ const warehouseDialogTitle = computed(() => warehouseIsEdit.value ? '编辑仓
const getWarehouseList = async () => {
warehouseLoading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_warehouses') || '[]')
let filtered = mockData.filter(item => {
if (warehouseQueryParams.warehouseName && !item.warehouseName.includes(warehouseQueryParams.warehouseName)) return false
if (warehouseQueryParams.status !== '' && item.status != warehouseQueryParams.status) return false
return true
})
const start = (warehouseQueryParams.pageNum - 1) * warehouseQueryParams.pageSize
const end = start + warehouseQueryParams.pageSize
warehouseList.value = filtered.slice(start, end)
warehouseTotal.value = filtered.length
if (mockData.length === 0) {
generateWarehouseMockData()
}
const response = await listWarehouse(warehouseQueryParams)
const list = response.data?.list || response.rows || []
warehouseList.value = list
warehouseTotal.value = response.data?.count || response.total || 0
} catch (error) {
ElMessage.error('获取仓库列表失败:' + error.message)
ElMessage.error('获取仓库列表失败:' + (error.message || '未知错误'))
} finally {
warehouseLoading.value = false
}
}
const generateWarehouseMockData = () => {
setTimeout(() => {
const cities = ['太原市', '大同市', '阳泉市', '长治市', '晋城市', '朔州市', '晋中市', '运城市', '忻州市', '临汾市']
const districts = ['xxx区', 'xxx县', 'xxx市']
const surnames = ['张', '李', '王', '赵', '刘', '陈', '杨', '黄', '周', '吴']
const names = ['三', '四', '五', '六', '七', '明', '强', '伟', '芳', '娜']
const newWarehouses = []
const now = Date.now()
for (let i = 1; i <= 20; i++) {
const capacity = Math.floor(Math.random() * 200000) + 50000
newWarehouses.push({
id: i,
warehouseCode: 'WH' + String(i).padStart(3, '0'),
warehouseName: '仓库' + i,
address: cities[Math.floor(Math.random() * cities.length)] + districts[Math.floor(Math.random() * districts.length)] + 'xxx路xxx号',
manager: surnames[Math.floor(Math.random() * surnames.length)] + names[Math.floor(Math.random() * names.length)],
phone: '138' + String(Math.floor(Math.random() * 100000000)).padStart(8, '0'),
capacity: capacity,
currentStock: Math.floor(capacity * (0.3 + Math.random() * 0.4)),
status: Math.random() > 0.1 ? 1 : 0,
createTime: new Date(now - i * 30 * 86400000).toISOString()
})
}
localStorage.setItem('mock_warehouses', JSON.stringify(newWarehouses))
getWarehouseList()
}, 10)
}
const handleWarehouseQuery = () => {
warehouseQueryParams.pageNum = 1
warehouseQueryParams.pageIndex = 1
getWarehouseList()
}
@ -392,19 +302,10 @@ const resetWarehouseQuery = () => {
handleWarehouseQuery()
}
const generateWarehouseCode = () => {
const warehouses = JSON.parse(localStorage.getItem('mock_warehouses') || '[]')
if (warehouses.length === 0) return 'WH001'
const codes = warehouses.map(w => w.warehouseCode).filter(code => code.startsWith('WH'))
if (codes.length === 0) return 'WH001'
const numbers = codes.map(code => parseInt(code.replace('WH', '')) || 0)
const maxNum = Math.max(...numbers)
return 'WH' + String(maxNum + 1).padStart(3, '0')
}
const handleWarehouseAdd = () => {
warehouseIsEdit.value = false
resetWarehouseForm()
//
warehouseForm.warehouseCode = generateWarehouseCode()
warehouseDialogVisible.value = true
}
@ -415,11 +316,11 @@ const handleWarehouseEdit = (row) => {
id: row.id,
warehouseCode: row.warehouseCode,
warehouseName: row.warehouseName,
address: row.address,
manager: row.manager,
phone: row.phone || '',
capacity: row.capacity,
currentStock: row.currentStock || 0,
detailAddress: row.detailAddress,
contactPerson: row.contactPerson,
contactPhone: row.contactPhone || '',
stockCapacity: row.stockCapacity,
stockInventory: row.stockInventory || 0,
status: row.status
})
warehouseDialogVisible.value = true
@ -430,65 +331,35 @@ const resetWarehouseForm = () => {
id: null,
warehouseCode: '',
warehouseName: '',
address: '',
manager: '',
phone: '',
capacity: null,
currentStock: 0,
status: 1
detailAddress: '',
contactPerson: '',
contactPhone: '',
stockCapacity: null,
stockInventory: 0,
status: '启用'
})
warehouseFormRef.value?.clearValidate()
}
const handleWarehouseSubmit = async () => {
if (!warehouseFormRef.value) return
await warehouseFormRef.value.validate((valid) => {
await warehouseFormRef.value.validate(async (valid) => {
if (!valid) return
warehouseSubmitLoading.value = true
try {
const warehouses = JSON.parse(localStorage.getItem('mock_warehouses') || '[]')
if (warehouseIsEdit.value) {
//
const index = warehouses.findIndex(w => w.id === warehouseForm.id)
if (index !== -1) {
warehouses[index] = {
...warehouses[index],
warehouseName: warehouseForm.warehouseName,
address: warehouseForm.address,
manager: warehouseForm.manager,
phone: warehouseForm.phone,
capacity: warehouseForm.capacity,
currentStock: warehouseForm.currentStock,
status: warehouseForm.status,
updateTime: new Date().toISOString()
}
}
await updateWarehouse(warehouseForm)
ElMessage.success('编辑成功')
} else {
//
const newWarehouse = {
id: Date.now(),
warehouseCode: warehouseForm.warehouseCode,
warehouseName: warehouseForm.warehouseName,
address: warehouseForm.address,
manager: warehouseForm.manager,
phone: warehouseForm.phone,
capacity: warehouseForm.capacity,
currentStock: warehouseForm.currentStock || 0,
status: warehouseForm.status,
createTime: new Date().toISOString()
}
warehouses.push(newWarehouse)
await addWarehouse(warehouseForm)
ElMessage.success('新增成功')
}
localStorage.setItem('mock_warehouses', JSON.stringify(warehouses))
ElMessage.success(warehouseIsEdit.value ? '编辑成功' : '新增成功')
warehouseDialogVisible.value = false
getWarehouseList()
} catch (error) {
ElMessage.error('操作失败:' + error.message)
ElMessage.error('操作失败:' + (error.message || '未知错误'))
} finally {
warehouseSubmitLoading.value = false
}
@ -496,17 +367,20 @@ const handleWarehouseSubmit = async () => {
}
const handleWarehouseDelete = (row) => {
const ids = row.id ? [row.id] : []
ElMessageBox.confirm('确定要删除此仓库吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const warehouses = JSON.parse(localStorage.getItem('mock_warehouses') || '[]')
const filtered = warehouses.filter(w => w.id != row.id)
localStorage.setItem('mock_warehouses', JSON.stringify(filtered))
ElMessage.success('删除成功')
getWarehouseList()
}).catch(() => {})
}).then(async () => {
try {
await delWarehouse({ ids })
ElMessage.success('删除成功')
getWarehouseList()
} catch (error) {
ElMessage.error('删除失败:' + (error.message || '未知错误'))
}
}).catch(() => { })
}
// ==================== ====================
@ -520,33 +394,33 @@ const stackFormRef = ref(null)
const warehouseOptions = ref([])
const stackQueryParams = reactive({
pageNum: 1,
pageIndex: 1,
pageSize: 10,
stackName: ''
stockName: ''
})
const stackForm = reactive({
id: null,
stackCode: '',
stackName: '',
warehouseId: null,
stockCode: '',
stockName: '',
warehouseName: '',
capacity: null,
currentStock: null
stockCapacity: null,
stockInventory: null
})
const stackFormRules = {
stackName: [
stockName: [
{ required: true, message: '请输入垛位名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
warehouseName: [
{ required: true, message: '请选择所属仓库', trigger: 'change' }
],
capacity: [
stockCapacity: [
{ required: true, message: '请输入容量', trigger: 'blur' },
{ type: 'number', min: 0, message: '容量必须大于等于0', trigger: 'blur' }
],
currentStock: [
stockInventory: [
{ required: true, message: '请输入当前库存', trigger: 'blur' },
{ type: 'number', min: 0, message: '当前库存必须大于等于0', trigger: 'blur' }
]
@ -557,82 +431,42 @@ const stackDialogTitle = computed(() => stackIsEdit.value ? '编辑垛位' : '
const getStackList = async () => {
stackLoading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_stacks') || '[]')
let filtered = mockData.filter(item => {
if (stackQueryParams.stackName && !item.stackName.includes(stackQueryParams.stackName)) return false
return true
})
const start = (stackQueryParams.pageNum - 1) * stackQueryParams.pageSize
const end = start + stackQueryParams.pageSize
stackList.value = filtered.slice(start, end)
stackTotal.value = filtered.length
if (mockData.length === 0) {
generateStackMockData()
}
const response = await listWarehouseStock(stackQueryParams)
const list = response.data?.list || response.rows || []
stackList.value = list
stackTotal.value = response.data?.count || response.total || 0
} catch (error) {
ElMessage.error('获取垛位列表失败:' + error.message)
ElMessage.error('获取垛位列表失败:' + (error.message || '未知错误'))
} finally {
stackLoading.value = false
}
}
const generateStackMockData = () => {
setTimeout(() => {
const warehouses = ['仓库1', '仓库2', '仓库3']
const newStacks = []
const now = Date.now()
for (let i = 1; i <= 20; i++) {
const capacity = Math.floor(Math.random() * 20000) + 5000
newStacks.push({
id: i,
stackCode: 'ST' + String(i).padStart(3, '0'),
stackName: i + '号垛位',
warehouseName: warehouses[Math.floor(Math.random() * warehouses.length)],
capacity: capacity,
currentStock: Math.floor(capacity * (0.2 + Math.random() * 0.6)),
createTime: new Date(now - i * 30 * 86400000).toISOString()
})
}
localStorage.setItem('mock_stacks', JSON.stringify(newStacks))
getStackList()
}, 10)
}
const loadWarehouseOptions = () => {
const warehouses = JSON.parse(localStorage.getItem('mock_warehouses') || '[]')
warehouseOptions.value = warehouses
.filter(w => w.status == 1)
.map(w => ({
warehouseName: w.warehouseName
}))
}
const generateStackCode = () => {
const stacks = JSON.parse(localStorage.getItem('mock_stacks') || '[]')
if (stacks.length === 0) return 'ST001'
const codes = stacks.map(s => s.stackCode).filter(code => code.startsWith('ST'))
if (codes.length === 0) return 'ST001'
const numbers = codes.map(code => parseInt(code.replace('ST', '')) || 0)
const maxNum = Math.max(...numbers)
return 'ST' + String(maxNum + 1).padStart(3, '0')
const loadWarehouseOptions = async () => {
try {
const response = await listWarehouse({ pageIndex: 1, pageSize: 1000, status: '启用' })
warehouseOptions.value = response.data?.list || response.rows || []
} catch (error) {
ElMessage.error('加载仓库选项失败:' + (error.message || '未知错误'))
}
}
//
const handleStackQuery = () => {
stackQueryParams.pageNum = 1
stackQueryParams.pageIndex = 1
getStackList()
}
const resetStackQuery = () => {
stackQueryParams.stackName = ''
stackQueryParams.stockName = ''
handleStackQuery()
}
const handleStackAdd = () => {
stackIsEdit.value = false
resetStackForm()
stackForm.stackCode = generateStackCode()
loadWarehouseOptions()
stackForm.stockCode = generateWarehouseCode()
stackDialogVisible.value = true
}
@ -640,11 +474,12 @@ const handleStackEdit = (row) => {
stackIsEdit.value = true
Object.assign(stackForm, {
id: row.id,
stackCode: row.stackCode,
stackName: row.stackName,
stockCode: row.stockCode,
stockName: row.stockName,
warehouseName: row.warehouseName,
capacity: row.capacity,
currentStock: row.currentStock || 0
stockCapacity: row.stockCapacity,
stockInventory: row.stockInventory || 0,
warehouseId: row.warehouseId
})
loadWarehouseOptions()
stackDialogVisible.value = true
@ -653,58 +488,34 @@ const handleStackEdit = (row) => {
const resetStackForm = () => {
Object.assign(stackForm, {
id: null,
stackCode: '',
stackName: '',
stockCode: '',
stockName: '',
warehouseName: '',
capacity: null,
currentStock: 0
stockCapacity: null,
stockInventory: 0
})
stackFormRef.value?.clearValidate()
}
const handleStackSubmit = async () => {
if (!stackFormRef.value) return
await stackFormRef.value.validate((valid) => {
await stackFormRef.value.validate(async (valid) => {
if (!valid) return
stackSubmitLoading.value = true
try {
const stacks = JSON.parse(localStorage.getItem('mock_stacks') || '[]')
if (stackIsEdit.value) {
//
const index = stacks.findIndex(s => s.id === stackForm.id)
if (index !== -1) {
stacks[index] = {
...stacks[index],
stackName: stackForm.stackName,
warehouseName: stackForm.warehouseName,
capacity: stackForm.capacity,
currentStock: stackForm.currentStock,
updateTime: new Date().toISOString()
}
}
await updateWarehouseStock(stackForm)
ElMessage.success('编辑成功')
} else {
//
const newStack = {
id: Date.now(),
stackCode: stackForm.stackCode,
stackName: stackForm.stackName,
warehouseName: stackForm.warehouseName,
capacity: stackForm.capacity,
currentStock: stackForm.currentStock || 0,
createTime: new Date().toISOString()
}
stacks.push(newStack)
await addWarehouseStock(stackForm)
ElMessage.success('新增成功')
}
localStorage.setItem('mock_stacks', JSON.stringify(stacks))
ElMessage.success(stackIsEdit.value ? '编辑成功' : '新增成功')
stackDialogVisible.value = false
getStackList()
} catch (error) {
ElMessage.error('操作失败:' + error.message)
ElMessage.error('操作失败:' + (error.message || '未知错误'))
} finally {
stackSubmitLoading.value = false
}
@ -712,31 +523,38 @@ const handleStackSubmit = async () => {
}
const handleStackDelete = (row) => {
const ids = row.id ? [row.id] : []
ElMessageBox.confirm('确定要删除此垛位吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const stacks = JSON.parse(localStorage.getItem('mock_stacks') || '[]')
const filtered = stacks.filter(s => s.id != row.id)
localStorage.setItem('mock_stacks', JSON.stringify(filtered))
ElMessage.success('删除成功')
getStackList()
}).catch(() => {})
}).then(async () => {
try {
await delWarehouseStock({ ids })
ElMessage.success('删除成功')
getStackList()
} catch (error) {
ElMessage.error('删除失败:' + (error.message || '未知错误'))
}
}).catch(() => { })
}
// tab
// tab
watch(activeTab, (newTab) => {
// tab warehouseOptions
loadWarehouseOptions()
if (newTab === 'warehouse') {
getWarehouseList()
} else if (newTab === 'stack') {
getStackList()
loadWarehouseOptions()
}
})
onMounted(() => {
// tab
getWarehouseList()
//
loadWarehouseOptions()
})
</script>
@ -763,4 +581,3 @@ onMounted(() => {
padding: 20px;
}
</style>

View File

@ -6,8 +6,8 @@
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 150px">
<el-option label="全部" value="" />
<el-option label="正常" value="1" />
<el-option label="停用" value="0" />
<el-option label="正常" value="正常" />
<el-option label="停用" value="停用" />
</el-select>
</el-form-item>
<el-form-item>
@ -19,19 +19,19 @@
</div>
<el-table v-loading="loading" :data="supplierList" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="supplierCode" label="供应商ID" width="120" />
<!-- <el-table-column prop="supplierCode" label="供应商ID" width="120" /> -->
<el-table-column prop="supplierName" label="供应商名称" width="200" />
<el-table-column prop="supplierType" label="供应商类别" width="150" />
<el-table-column prop="contact" label="联系人" width="120" />
<el-table-column prop="phone" label="手机号" width="130" />
<el-table-column prop="address" label="详细地址" min-width="200" />
<el-table-column prop="contactPerson" label="联系人" width="120" />
<el-table-column prop="mobilePhone" label="手机号" width="130" />
<el-table-column prop="detailAddress" label="详细地址" min-width="200" />
<el-table-column prop="bankName" label="开户银行" width="150" />
<el-table-column prop="bankAccount" label="银行账号" width="180" />
<el-table-column prop="taxNumber" label="税号" width="180" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'danger'">
{{ scope.row.status == 1 ? '正常' : '停用' }}
<el-tag :type="scope.row.status == '正常' ? 'success' : 'danger'">
{{ scope.row.status == '正常' ? '正常' : '停用' }}
</el-tag>
</template>
</el-table-column>
@ -47,7 +47,7 @@
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:page="queryParams.pageIndex"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
@ -94,27 +94,27 @@
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="联系人" prop="contact">
<el-form-item label="联系人" prop="contactPerson">
<el-input
v-model="supplierForm.contact"
v-model="supplierForm.contactPerson"
placeholder="请输入联系人"
maxlength="20"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号" prop="phone">
<el-form-item label="手机号" prop="mobilePhone">
<el-input
v-model="supplierForm.phone"
v-model="supplierForm.mobilePhone"
placeholder="请输入手机号"
maxlength="11"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="详细地址" prop="address">
<el-form-item label="详细地址" prop="detailAddress">
<el-input
v-model="supplierForm.address"
v-model="supplierForm.detailAddress"
type="textarea"
:rows="2"
placeholder="请输入详细地址"
@ -154,8 +154,8 @@
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-radio-group v-model="supplierForm.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">停用</el-radio>
<el-radio :label="'正常'">正常</el-radio>
<el-radio :label="'停用'">停用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@ -182,13 +182,13 @@
<el-descriptions-item label="供应商名称">{{ currentSupplier.supplierName }}</el-descriptions-item>
<el-descriptions-item label="供应商类别">{{ currentSupplier.supplierType }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="currentSupplier.status == 1 ? 'success' : 'danger'">
{{ currentSupplier.status == 1 ? '正常' : '停用' }}
<el-tag :type="currentSupplier.status == '正常' ? 'success' : 'danger'">
{{ currentSupplier.status == '正常' ? '正常' : '停用' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="联系人">{{ currentSupplier.contact }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ currentSupplier.phone }}</el-descriptions-item>
<el-descriptions-item label="详细地址" :span="2">{{ currentSupplier.address }}</el-descriptions-item>
<el-descriptions-item label="联系人">{{ currentSupplier.contactPerson }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ currentSupplier.mobilePhone }}</el-descriptions-item>
<el-descriptions-item label="详细地址" :span="2">{{ currentSupplier.detailAddress }}</el-descriptions-item>
<el-descriptions-item label="开户银行">{{ currentSupplier.bankName }}</el-descriptions-item>
<el-descriptions-item label="银行账号">{{ currentSupplier.bankAccount }}</el-descriptions-item>
<el-descriptions-item label="税号" :span="2">{{ currentSupplier.taxNumber }}</el-descriptions-item>
@ -201,6 +201,7 @@
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import Pagination from '@/components/Pagination'
import { listSupplier, addSupplier, updateSupplier, delSupplier } from '@/api/base/supplier'
const loading = ref(false)
const submitLoading = ref(false)
@ -213,7 +214,7 @@ const supplierFormRef = ref(null)
const currentSupplier = ref(null)
const queryParams = reactive({
pageNum: 1,
pageIndex: 1,
pageSize: 10,
status: ''
})
@ -223,13 +224,13 @@ const supplierForm = reactive({
supplierCode: '',
supplierName: '',
supplierType: '',
contact: '',
phone: '',
address: '',
contactPerson: '',
mobilePhone: '',
detailAddress: '',
bankName: '',
bankAccount: '',
taxNumber: '',
status: 1
status: '正常'
})
const supplierFormRules = {
@ -240,14 +241,14 @@ const supplierFormRules = {
supplierType: [
{ required: true, message: '请选择供应商类别', trigger: 'change' }
],
contact: [
contactPerson: [
{ required: true, message: '请输入联系人', trigger: 'blur' }
],
phone: [
mobilePhone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
address: [
detailAddress: [
{ required: true, message: '请输入详细地址', trigger: 'blur' }
],
bankName: [
@ -269,74 +270,18 @@ const dialogTitle = computed(() => isEdit.value ? '编辑供应商' : '新增供
const getList = async () => {
loading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_suppliers') || '[]')
let filtered = mockData.filter(item => {
if (queryParams.status !== '' && item.status != queryParams.status) return false
return true
})
const start = (queryParams.pageNum - 1) * queryParams.pageSize
const end = start + queryParams.pageSize
supplierList.value = filtered.slice(start, end)
total.value = filtered.length
if (mockData.length === 0) {
generateMockData()
}
const res = await listSupplier(queryParams)
supplierList.value = res.data?.list || res.rows || []
total.value = res.data?.count || res.total || supplierList.value.length
} catch (error) {
ElMessage.error('获取供应商列表失败:' + error.message)
ElMessage.error('获取供应商列表失败:' + (error.message || '未知错误'))
} finally {
loading.value = false
}
}
const generateMockData = () => {
setTimeout(() => {
const supplierTypes = ['煤炭供应商', '物流供应商', '设备供应商', '服务供应商']
const cities = ['太原市', '大同市', '阳泉市', '长治市', '晋城市', '朔州市', '晋中市', '运城市', '忻州市', '临汾市']
const banks = ['中国工商银行', '中国建设银行', '中国农业银行', '中国银行', '交通银行', '招商银行', '浦发银行']
const surnames = ['张', '李', '王', '赵', '刘', '陈', '杨', '黄', '周', '吴']
const names = ['三', '四', '五', '六', '七', '明', '强', '伟', '芳', '娜']
const newSuppliers = []
const now = Date.now()
for (let i = 1; i <= 20; i++) {
newSuppliers.push({
id: i,
supplierCode: 'S' + String(i).padStart(3, '0'),
supplierName: '煤贸商' + String.fromCharCode(64 + i),
supplierType: supplierTypes[Math.floor(Math.random() * supplierTypes.length)],
contact: surnames[Math.floor(Math.random() * surnames.length)] + names[Math.floor(Math.random() * names.length)],
phone: '138' + String(Math.floor(Math.random() * 100000000)).padStart(8, '0'),
address: cities[Math.floor(Math.random() * cities.length)] + 'xxx区xxx路xxx号',
bankName: banks[Math.floor(Math.random() * banks.length)],
bankAccount: '622' + String(Math.floor(Math.random() * 10000000000000000)).padStart(16, '0'),
taxNumber: '91140000' + String(Math.floor(Math.random() * 100000000)).padStart(8, '0') + String.fromCharCode(65 + Math.floor(Math.random() * 26)),
status: Math.random() > 0.2 ? 1 : 0,
createTime: new Date(now - i * 30 * 86400000).toISOString(),
updateTime: new Date(now - i * 30 * 86400000).toISOString()
})
}
localStorage.setItem('mock_suppliers', JSON.stringify(newSuppliers))
getList()
}, 10)
}
const generateSupplierCode = () => {
const mockData = JSON.parse(localStorage.getItem('mock_suppliers') || '[]')
let maxCode = 0
mockData.forEach(item => {
const match = item.supplierCode.match(/^S(\d+)$/)
if (match) {
const num = parseInt(match[1])
if (num > maxCode) {
maxCode = num
}
}
})
return 'S' + String(maxCode + 1).padStart(3, '0')
}
const handleQuery = () => {
queryParams.pageNum = 1
queryParams.pageIndex = 1
getList()
}
@ -348,8 +293,6 @@ const resetQuery = () => {
const handleAdd = () => {
isEdit.value = false
resetForm()
// ID
supplierForm.supplierCode = generateSupplierCode()
dialogVisible.value = true
}
@ -365,9 +308,9 @@ const handleEdit = (row) => {
supplierCode: row.supplierCode,
supplierName: row.supplierName,
supplierType: row.supplierType,
contact: row.contact,
phone: row.phone,
address: row.address,
contactPerson: row.contactPerson,
mobilePhone: row.mobilePhone,
detailAddress: row.detailAddress,
bankName: row.bankName,
bankAccount: row.bankAccount,
taxNumber: row.taxNumber,
@ -377,16 +320,19 @@ const handleEdit = (row) => {
}
const handleDelete = (row) => {
const Ids = row.id ? [row.id] : []
ElMessageBox.confirm('确定要删除此供应商吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const suppliers = JSON.parse(localStorage.getItem('mock_suppliers') || '[]')
const filtered = suppliers.filter(s => s.id != row.id)
localStorage.setItem('mock_suppliers', JSON.stringify(filtered))
ElMessage.success('删除成功')
getList()
}).then(async () => {
try {
await delSupplier({ ids: Ids })
ElMessage.success('删除成功')
getList()
} catch (error) {
ElMessage.error('删除失败:' + (error.message || '未知错误'))
}
}).catch(() => {})
}
@ -396,13 +342,13 @@ const resetForm = () => {
supplierCode: '',
supplierName: '',
supplierType: '',
contact: '',
phone: '',
address: '',
contactPerson: '',
mobilePhone: '',
detailAddress: '',
bankName: '',
bankAccount: '',
taxNumber: '',
status: 1
status: '正常'
})
supplierFormRef.value?.clearValidate()
}
@ -410,70 +356,22 @@ const resetForm = () => {
const handleSubmit = async () => {
if (!supplierFormRef.value) return
await supplierFormRef.value.validate((valid) => {
await supplierFormRef.value.validate(async (valid) => {
if (!valid) return
submitLoading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_suppliers') || '[]')
const now = new Date().toISOString()
if (isEdit.value) {
//
const index = mockData.findIndex(item => item.id === supplierForm.id)
if (index !== -1) {
mockData[index] = {
...mockData[index],
supplierName: supplierForm.supplierName,
supplierType: supplierForm.supplierType,
contact: supplierForm.contact,
phone: supplierForm.phone,
address: supplierForm.address,
bankName: supplierForm.bankName,
bankAccount: supplierForm.bankAccount,
taxNumber: supplierForm.taxNumber,
status: supplierForm.status,
updateTime: now
}
}
await updateSupplier(supplierForm)
ElMessage.success('编辑成功')
} else {
//
// ID
if (!supplierForm.supplierCode || !supplierForm.supplierCode.match(/^S\d{3}$/)) {
supplierForm.supplierCode = generateSupplierCode()
}
// ID
const codeExists = mockData.find(item => item.supplierCode === supplierForm.supplierCode)
if (codeExists) {
//
supplierForm.supplierCode = generateSupplierCode()
}
const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1
mockData.push({
id: newId,
supplierCode: supplierForm.supplierCode,
supplierName: supplierForm.supplierName,
supplierType: supplierForm.supplierType,
contact: supplierForm.contact,
phone: supplierForm.phone,
address: supplierForm.address,
bankName: supplierForm.bankName,
bankAccount: supplierForm.bankAccount,
taxNumber: supplierForm.taxNumber,
status: supplierForm.status,
createTime: now,
updateTime: now
})
await addSupplier(supplierForm)
ElMessage.success('新增成功')
}
localStorage.setItem('mock_suppliers', JSON.stringify(mockData))
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
dialogVisible.value = false
getList()
} catch (error) {
ElMessage.error('操作失败:' + error.message)
ElMessage.error('操作失败:' + (error.message || '未知错误'))
} finally {
submitLoading.value = false
}

View File

@ -2,46 +2,34 @@
<div class="app-container">
<el-card>
<el-table v-loading="loading" :data="alertConfigList" border style="width: 100%">
<el-table-column prop="category" label="报警类别" width="150" align="center">
<el-table-column prop="alertType" label="报警类别" width="150" align="center">
<template #default="scope">
<el-tag :type="scope.row.category === 'insufficient' ? 'danger' : 'warning'">
{{ scope.row.category === 'insufficient' ? '库存不足' : '库存积压' }}
<el-tag :type="scope.row.alertType === '库存不足' ? 'danger' : 'warning'">
<!-- {{ scope.row.alertType === 'insufficient' ? '库存不足' : '库存积压' }} -->
{{ scope.row.alertType }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="level" label="报警级别" width="120" align="center">
<el-table-column prop="alertLevel" label="报警级别" width="120" align="center">
<template #default="scope">
<el-tag :type="getLevelType(scope.row.level)">
{{ getLevelName(scope.row.level) }}
<el-tag :type="getLevelType(scope.row.alertLevel)">
{{ getLevelName(scope.row.alertLevel) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="threshold" label="阈值" width="200" align="center">
<template #default="scope">
<span v-if="!scope.row.editing">{{ scope.row.threshold }} {{ scope.row.unit }}</span>
<el-input-number
v-else
v-model="scope.row.threshold"
:min="0"
:precision="2"
:controls="false"
style="width: 120px"
/>
<el-input-number v-else v-model="scope.row.threshold" :min="0" :precision="2" :controls="false"
style="width: 120px" />
<span style="margin-left: 8px">{{ scope.row.unit }}</span>
</template>
</el-table-column>
<el-table-column prop="description" label="说明" min-width="200">
<template #default="scope">
<span v-if="!scope.row.editing">{{ scope.row.description || '-' }}</span>
<el-input
v-else
v-model="scope.row.description"
type="textarea"
:rows="2"
placeholder="请输入说明"
maxlength="200"
style="width: 100%"
/>
<el-input v-else v-model="scope.row.description" type="textarea" :rows="2" placeholder="请输入说明"
maxlength="200" style="width: 100%" />
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
@ -55,12 +43,8 @@
<template #default="scope">
<template v-if="!scope.row.editing">
<el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button
link
:type="scope.row.status === 1 ? 'warning' : 'success'"
size="small"
@click="handleToggleStatus(scope.row)"
>
<el-button link :type="scope.row.status === 1 ? 'warning' : 'success'" size="small"
@click="handleToggleStatus(scope.row)">
{{ scope.row.status === 1 ? '禁用' : '启用' }}
</el-button>
</template>
@ -78,7 +62,7 @@
<script setup name="InventoryAlertConfig">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { listInventoryAlertConfig, addInventoryAlertConfig, delInventoryAlertConfig } from "@/api/system/inventory-alert"
const loading = ref(false)
const alertConfigList = ref([])
@ -90,8 +74,15 @@ const getLevelName = (level) => {
}
return levelMap[level] || level
}
const queryParams = reactive({
pageIndex: 1,
pageSize: 10,
alertType: null,
alertLevel: null,
status: null
})
const getLevelType = (level) => {
const typeMap = {
'low': 'info',
'medium': 'warning',
@ -104,7 +95,7 @@ const getList = async () => {
loading.value = true
try {
const mockData = JSON.parse(localStorage.getItem('mock_inventory_alert_configs') || '[]')
//
if (mockData.length === 0 || !mockData.some(item => item.category === 'insufficient')) {
generateDefaultConfigs()
@ -132,71 +123,75 @@ const getList = async () => {
}
const generateDefaultConfigs = () => {
const defaultConfigs = [
// -
{
id: 1,
category: 'insufficient',
level: 'low',
threshold: 100,
unit: '吨',
description: '库存低于100吨时触发低级别报警',
status: 1
},
{
id: 2,
category: 'insufficient',
level: 'medium',
threshold: 50,
unit: '吨',
description: '库存低于50吨时触发中级别报警',
status: 1
},
{
id: 3,
category: 'insufficient',
level: 'high',
threshold: 20,
unit: '吨',
description: '库存低于20吨时触发高级别报警',
status: 1
},
// -
{
id: 4,
category: 'overstock',
level: 'low',
threshold: 5000,
unit: '吨',
description: '库存超过5000吨时触发低级别报警',
status: 1
},
{
id: 5,
category: 'overstock',
level: 'medium',
threshold: 8000,
unit: '吨',
description: '库存超过8000吨时触发中级别报警',
status: 1
},
{
id: 6,
category: 'overstock',
level: 'high',
threshold: 10000,
unit: '吨',
description: '库存超过10000吨时触发高级别报警',
status: 1
}
]
localStorage.setItem('mock_inventory_alert_configs', JSON.stringify(defaultConfigs))
alertConfigList.value = defaultConfigs.map(item => ({
...item,
editing: false,
originalData: null
}))
listInventoryAlertConfig(queryParams).then(response => {
alertConfigList.value = response.data.list
// const defaultConfigs = [
// // -
// {
// id: 1,
// category: 'insufficient',
// level: 'low',
// threshold: 100,
// unit: '',
// description: '100',
// status: 1
// },
// {
// id: 2,
// category: 'insufficient',
// level: 'medium',
// threshold: 50,
// unit: '',
// description: '50',
// status: 1
// },
// {
// id: 3,
// category: 'insufficient',
// level: 'high',
// threshold: 20,
// unit: '',
// description: '20',
// status: 1
// },
// // -
// {
// id: 4,
// category: 'overstock',
// level: 'low',
// threshold: 5000,
// unit: '',
// description: '5000',
// status: 1
// },
// {
// id: 5,
// category: 'overstock',
// level: 'medium',
// threshold: 8000,
// unit: '',
// description: '8000',
// status: 1
// },
// {
// id: 6,
// category: 'overstock',
// level: 'high',
// threshold: 10000,
// unit: '',
// description: '10000',
// status: 1
// }
// ]
// localStorage.setItem('mock_inventory_alert_configs', JSON.stringify(defaultConfigs))
// alertConfigList.value = defaultConfigs.map(item => ({
// ...item,
// editing: false,
// originalData: null
// }))
})
}
const handleEdit = (row) => {
@ -206,7 +201,7 @@ const handleEdit = (row) => {
handleCancel(item)
}
})
row.editing = true
row.originalData = { ...row }
}
@ -225,11 +220,11 @@ const handleSave = async (row) => {
ElMessage.error('请输入有效的阈值')
return
}
try {
const configs = JSON.parse(localStorage.getItem('mock_inventory_alert_configs') || '[]')
const index = configs.findIndex(c => c.id === row.id)
if (index !== -1) {
configs[index] = {
id: row.id,
@ -240,7 +235,7 @@ const handleSave = async (row) => {
description: row.description || '',
status: row.status
}
localStorage.setItem('mock_inventory_alert_configs', JSON.stringify(configs))
ElMessage.success('保存成功')
row.editing = false
@ -264,21 +259,26 @@ const handleToggleStatus = (row) => {
try {
const configs = JSON.parse(localStorage.getItem('mock_inventory_alert_configs') || '[]')
const index = configs.findIndex(c => c.id === row.id)
if (index !== -1) {
configs[index].status = configs[index].status === 1 ? 0 : 1
row.status = configs[index].status
localStorage.setItem('mock_inventory_alert_configs', JSON.stringify(configs))
ElMessage.success('操作成功')
}
} catch (error) {
ElMessage.error('操作失败:' + error.message)
}
}).catch(() => {})
}).catch(() => { })
}
onMounted(() => getList())
onMounted(() => {
getList()
generateDefaultConfigs()
})
</script>
<style scoped>
@ -294,4 +294,3 @@ onMounted(() => getList())
text-align: left;
}
</style>

View File

@ -7,7 +7,8 @@ import { defineConfig, loadEnv } from 'vite'
import path from 'path'
import createVitePlugins from './vite/plugins'
const baseUrl = 'http://172.16.1.116:8000/' // 后端接口
// const baseUrl = 'http://172.16.1.116:8000/' // 后端接口
const baseUrl = 'http://172.16.1.162:40980/' // 后端接口
// https://vitejs.dev/config/
export default defineConfig(({ mode, command }) => {