Compare commits

...

54 Commits

Author SHA1 Message Date
my_ong
246ae7ae2f sparkles: 处理人工盘点交互
All checks were successful
Release / lint (push) Successful in 43s
Release / Release (push) Successful in 4m0s
2025-03-29 22:48:28 +08:00
my_ong
c8b45e7477 sparkles: 修改人工盘点样式业务逻辑
All checks were successful
Release / lint (push) Successful in 1m11s
Release / Release (push) Successful in 4m22s
2025-03-29 21:49:22 +08:00
my_ong
208e58c1f6 bug: 🐛 导出
All checks were successful
Release / lint (push) Successful in 52s
Release / Release (push) Successful in 1m46s
2025-03-25 20:37:36 +08:00
my_ong
5a843344ee bug: 🐛 去掉无用的文件
All checks were successful
Release / lint (push) Successful in 53s
Release / Release (push) Successful in 1m53s
2025-03-23 12:11:21 +08:00
my_ong
3723414588 sparkles: 样式修改
Some checks failed
Release / lint (push) Successful in 50s
Release / Release (push) Failing after 49s
2025-03-23 12:00:29 +08:00
my_ong
d4447eaf55 sparkles: 出入库查询
Some checks failed
Release / lint (push) Successful in 45s
Release / Release (push) Failing after 54s
2025-03-23 10:38:55 +08:00
my_ong
04afb056c3 sparkles: 要加物料类型筛选
All checks were successful
Release / lint (push) Successful in 50s
Release / Release (push) Successful in 2m0s
2025-03-23 10:10:29 +08:00
my_ong
fab443b8a0 bug: 🐛 编码位数
All checks were successful
Release / lint (push) Successful in 51s
Release / Release (push) Successful in 1m52s
2025-03-19 16:39:32 +08:00
my_ong
cdaa6797fa bug: 🐛 校验条形码
Some checks failed
Release / lint (push) Successful in 42s
Release / Release (push) Has been cancelled
2025-03-19 16:36:50 +08:00
my_ong
88795fe559 bug: 🐛 打包报错
All checks were successful
Release / lint (push) Successful in 47s
Release / Release (push) Successful in 1m55s
2025-03-18 16:53:06 +08:00
my_ong
2f8937ae13 bug: 🐛 重复用户名校验
Some checks failed
Release / lint (push) Successful in 48s
Release / Release (push) Failing after 52s
2025-03-17 10:12:35 +08:00
my_ong
a2d24b9492 sparkles: 导出excel
All checks were successful
Release / lint (push) Successful in 42s
Release / Release (push) Successful in 2m7s
2025-03-15 19:03:06 +08:00
my_ong
994fd58bb0 sparkles: 物料库存导出excel
All checks were successful
Release / lint (push) Successful in 49s
Release / Release (push) Successful in 1m53s
2025-03-15 17:41:49 +08:00
my_ong
21f10b6e88 bug: 🐛 fixed build bug
All checks were successful
Release / lint (push) Successful in 59s
Release / Release (push) Successful in 1m54s
2025-03-15 13:04:40 +08:00
my_ong
df572949f3 sparkles: 报废出库 查询和审核
Some checks failed
Release / lint (push) Successful in 46s
Release / Release (push) Failing after 58s
2025-03-15 12:44:11 +08:00
my_ong
8e3c2ec88f sparkles: 去掉角色key
Some checks failed
Release / lint (push) Successful in 47s
Release / Release (push) Failing after 59s
2025-03-14 16:24:58 +08:00
my_ong
2ffb3ae55c bug: 🐛 用户名校验
Some checks failed
Release / lint (push) Successful in 44s
Release / Release (push) Failing after 56s
2025-03-14 16:20:47 +08:00
my_ong
05ac02f58a sparkles: 校验
All checks were successful
Release / lint (push) Successful in 45s
Release / Release (push) Successful in 2m0s
2025-03-12 14:00:44 +08:00
my_ong
0a39446c65 sparkles: 查看按钮的功能
All checks were successful
Release / lint (push) Successful in 46s
Release / Release (push) Successful in 1m55s
2025-03-12 11:20:47 +08:00
my_ong
2f798b6b48 sparkles: 查看人工盘点数据
All checks were successful
Release / lint (push) Successful in 44s
Release / Release (push) Successful in 1m54s
2025-03-12 10:13:04 +08:00
my_ong
0014b71888 sparkles: 提交人工盘点数据
All checks were successful
Release / lint (push) Successful in 37s
Release / Release (push) Successful in 1m56s
2025-03-09 20:17:35 +08:00
my_ong
15cbb02e50 sparkles: 人工盘点功能,待完善
All checks were successful
Release / lint (push) Successful in 52s
Release / Release (push) Successful in 1m56s
2025-03-09 15:55:23 +08:00
my_ong
90da064f25 bug: 🐛 界面逻辑优化
All checks were successful
Release / lint (push) Successful in 54s
Release / Release (push) Successful in 2m0s
2025-03-09 14:36:02 +08:00
my_ong
f3a2438066 sparkles: 条件判断
All checks were successful
Release / lint (push) Successful in 47s
Release / Release (push) Successful in 1m52s
2025-03-09 13:22:20 +08:00
my_ong
6d8de8016b bug: 🐛 出入库申请逻辑修改
All checks were successful
Release / lint (push) Successful in 55s
Release / Release (push) Successful in 2m5s
2025-03-08 21:35:08 +08:00
my_ong
bdd0d478df bug: 🐛 解决打包报错
All checks were successful
Release / lint (push) Successful in 53s
Release / Release (push) Successful in 1m56s
2025-03-08 20:38:12 +08:00
my_ong
c4c2a8bd67 bug: 🐛 根据类型选择物料
Some checks failed
Release / lint (push) Successful in 55s
Release / Release (push) Failing after 59s
2025-03-08 20:26:58 +08:00
my_ong
7ca2ebcf5d bug: 🐛 页面逻辑修复并完善
Some checks failed
Release / lint (push) Successful in 51s
Release / Release (push) Failing after 50s
2025-03-08 15:36:52 +08:00
my_ong
fd0e324bc9 tada: 🎉 新增盘点抽屉样式调整,待完善
All checks were successful
Release / lint (push) Successful in 52s
Release / Release (push) Successful in 2m9s
2025-03-05 23:45:19 +08:00
my_ong
056672e443 bug: 🐛 序号
All checks were successful
Release / lint (push) Successful in 54s
Release / Release (push) Successful in 2m3s
2025-03-05 17:33:57 +08:00
my_ong
e24e89c513 sparkles: 报废出库的申请界面
All checks were successful
Release / lint (push) Successful in 55s
Release / Release (push) Successful in 2m5s
2025-03-04 22:40:41 +08:00
my_ong
9085a77931 bug: 🐛 修复打包报错
All checks were successful
Release / lint (push) Successful in 48s
Release / Release (push) Successful in 1m52s
2025-03-04 12:25:23 +08:00
my_ong
d1d2840abd bug: 🐛 修复打包报错
Some checks failed
Release / lint (push) Successful in 49s
Release / Release (push) Failing after 57s
2025-03-04 12:20:45 +08:00
my_ong
7ce15ddf0e sparkles: 先选类型再选物料
Some checks failed
Release / lint (push) Successful in 48s
Release / Release (push) Failing after 54s
2025-03-04 12:09:58 +08:00
my_ong
541fc2ff8a sparkles: 提交申请
Some checks failed
Release / lint (push) Successful in 49s
Release / Release (push) Failing after 53s
2025-03-02 20:02:53 +08:00
my_ong
6f859ebaa0 sparkles: 物料修改
Some checks failed
Release / lint (push) Successful in 50s
Release / Release (push) Failing after 1m0s
2025-03-02 19:22:46 +08:00
my_ong
fbf3579fa8 sparkles: 物料界面修改
Some checks failed
Release / lint (push) Successful in 49s
Release / Release (push) Failing after 52s
2025-03-02 15:10:27 +08:00
my_ong
8953941362 sparkles: 物料类型
All checks were successful
Release / lint (push) Successful in 58s
Release / Release (push) Successful in 2m9s
2025-03-02 14:38:52 +08:00
my_ong
0b6c1e9000 art: 🎨 页面全局提示
All checks were successful
Release / lint (push) Successful in 29s
Release / Release (push) Successful in 1m21s
2025-01-10 10:23:42 +08:00
my_ong
6e4eb46da6 bug: 取fullname
All checks were successful
Release / lint (push) Successful in 27s
Release / Release (push) Successful in 1m21s
2024-12-19 16:55:50 +08:00
my_ong
4072e8b1c4 bug: 取fullname
All checks were successful
Release / lint (push) Successful in 29s
Release / Release (push) Successful in 1m20s
2024-12-19 16:35:58 +08:00
my_ong
1deb6e58ec bug: 修改逻辑
All checks were successful
Release / lint (push) Successful in 29s
Release / Release (push) Successful in 1m19s
2024-12-19 16:23:00 +08:00
my_ong
d96b8f4329 art: 修改控制打印条形码的条件和成功页面的跳转条件
All checks were successful
Release / lint (push) Successful in 30s
Release / Release (push) Successful in 1m20s
2024-12-19 15:50:24 +08:00
my_ong
1b6fb9968e art: 修改权限标识
All checks were successful
Release / lint (push) Successful in 29s
Release / Release (push) Successful in 1m17s
2024-12-19 14:57:49 +08:00
my_ong
edb74d7bf3 sparkles: 全部重扫功能 2024-12-19 14:49:54 +08:00
my_ong
c9537f5f04 bug: 关闭条形码窗口时清空数据
All checks were successful
Release / lint (push) Successful in 28s
Release / Release (push) Successful in 1m21s
2024-12-19 12:42:12 +08:00
my_ong
8ebf15cdc4 art: 盘点状态颜色 2024-12-19 12:11:13 +08:00
my_ong
9af5995d65 bug: 盘点功能的条件查询
All checks were successful
Release / lint (push) Successful in 28s
Release / Release (push) Successful in 1m18s
2024-12-19 11:04:59 +08:00
my_ong
e390000c9b bug: 下拉搜索框
All checks were successful
Release / lint (push) Successful in 28s
Release / Release (push) Successful in 1m18s
2024-12-18 17:17:53 +08:00
my_ong
cdf349aabb bug: 修复查询提示
All checks were successful
Release / lint (push) Successful in 28s
Release / Release (push) Successful in 1m19s
2024-12-18 16:57:14 +08:00
my_ong
8e35143058 bug: 去掉无用的入参
All checks were successful
Release / lint (push) Successful in 29s
Release / Release (push) Successful in 1m18s
2024-12-18 15:56:08 +08:00
my_ong
f190f0ebea sparkles: 登录拦截
Some checks failed
Release / lint (push) Successful in 28s
Release / Release (push) Failing after 34s
2024-12-18 15:47:56 +08:00
my_ong
d1356f167c bug: 首页不受权限控制
All checks were successful
Release / lint (push) Successful in 28s
Release / Release (push) Successful in 1m22s
2024-12-18 12:53:06 +08:00
my_ong
ee0525e62b sparkles: 引入菜单权限控制
All checks were successful
Release / lint (push) Successful in 27s
Release / Release (push) Successful in 1m21s
2024-12-18 12:43:51 +08:00
69 changed files with 3806 additions and 942 deletions

3
.env
View File

@@ -1 +1,2 @@
VITE_HTTP_PREFIX='/api'
VITE_HTTP_PREFIX='/api'
VITE_UPLOAD_PREFIX='http://localhost:5173/api'

2
.env.production Normal file
View File

@@ -0,0 +1,2 @@
VITE_HTTP_PREFIX='/api'
VITE_UPLOAD_PREFIX= 'https://ims.riemann.tech/api'

BIN
dist.zip Normal file

Binary file not shown.

View File

@@ -29,7 +29,7 @@ export default [
dictionary: true,
sensor: true,
station: true,
material: true,
material: true,
},
ecmaVersion: 2020,
parser: parserVue,

286
src/api/acl/api.d.ts vendored
View File

@@ -1,198 +1,198 @@
declare namespace acl {
/**
* 权限
*/
export interface Permission {
/** createdTime */
createdTime?: string;
/**
* 权限
*/
export interface Permission {
/** createdTime */
createdTime?: string
/** 权限描述 */
description?: string;
/** 权限描述 */
description?: string
/** id */
id?: number;
/** id */
id?: number
/** 权限key,英文 */
key?: string;
/** 权限key,英文 */
key?: string
/** 权限keyPath,用来做业务,(父级keyPath.key) */
keyPath?: string;
/** 权限keyPath,用来做业务,(父级keyPath.key) */
keyPath?: string
/** 权限名称,中文用来做标识 */
name?: string;
/** 权限名称,中文用来做标识 */
name?: string
/** 父权限key */
parentKey?: string;
/** 父权限key */
parentKey?: string
/** 权限类型 */
type?: 'MENU' | 'BUTTON' | 'OTHER';
/** 权限类型 */
type?: 'MENU' | 'BUTTON' | 'OTHER'
/** typeInfo */
typeInfo?: acl.Codebook;
/** typeInfo */
typeInfo?: acl.Codebook
/** updatedTime */
updatedTime?: string;
}
/** updatedTime */
updatedTime?: string
}
/**
* 权限信息,包含是否选中标识
*/
export interface PermissionInfo {
/** createdTime */
createdTime?: string;
/**
* 权限信息,包含是否选中标识
*/
export interface PermissionInfo {
/** createdTime */
createdTime?: string
/** 权限描述 */
description?: string;
/** 权限描述 */
description?: string
/** id */
id?: number;
/** id */
id?: number
/** 权限key,英文 */
key?: string;
/** 权限key,英文 */
key?: string
/** 权限keyPath,用来做业务,(父级keyPath.key) */
keyPath?: string;
/** 权限keyPath,用来做业务,(父级keyPath.key) */
keyPath?: string
/** 权限名称,中文用来做标识 */
name?: string;
/** 权限名称,中文用来做标识 */
name?: string
/** 父权限key */
parentKey?: string;
/** 父权限key */
parentKey?: string
/** 权限是否选中标识 */
selected: boolean;
/** 权限是否选中标识 */
selected: boolean
/** 权限类型 */
type?: 'MENU' | 'BUTTON' | 'OTHER';
/** 权限类型 */
type?: 'MENU' | 'BUTTON' | 'OTHER'
/** typeInfo */
typeInfo?: acl.Codebook;
/** typeInfo */
typeInfo?: acl.Codebook
/** updatedTime */
updatedTime?: string;
}
/** updatedTime */
updatedTime?: string
}
/**
* 角色
*/
export interface Role {
/** createdTime */
createdTime?: string;
/**
* 角色
*/
export interface Role {
/** createdTime */
createdTime?: string
/** 角色描述 */
description?: string;
/** 角色描述 */
description?: string
/** id */
id?: number;
/** id */
id?: number
/** 角色key,英文,用来做业务 */
key: string;
/** 角色key,英文,用来做业务 */
key?: string
/** 角色名称,中文用来做标识 */
name?: string;
/** 角色名称,中文用来做标识 */
name?: string
/** updatedTime */
updatedTime?: string;
}
/** updatedTime */
updatedTime?: string
}
/**
* 角色信息,包含是否选中标识
*/
export interface RoleInfo {
/** createdTime */
createdTime?: string;
/**
* 角色信息,包含是否选中标识
*/
export interface RoleInfo {
/** createdTime */
createdTime?: string
/** 角色描述 */
description?: string;
/** 角色描述 */
description?: string
/** id */
id?: number;
/** id */
id?: number
/** 角色key,英文,用来做业务 */
key: string;
/** 角色key,英文,用来做业务 */
key?: string
/** 角色名称,中文用来做标识 */
name?: string;
/** 角色名称,中文用来做标识 */
name?: string
/** 角色是否选中标识 */
selected: boolean;
/** 角色是否选中标识 */
selected: boolean
/** updatedTime */
updatedTime?: string;
}
/** updatedTime */
updatedTime?: string
}
/**
* 树
*/
export interface TreeString {
/** 下级列表 */
children?: Array<acl.TreeString>;
/**
* 树
*/
export interface TreeString {
/** 下级列表 */
children?: Array<acl.TreeString>
/** 描述 */
description?: string;
/** 描述 */
description?: string
/** ID */
key: string;
/** ID */
key: string
/** 名称 */
name: string;
/** 名称 */
name: string
/** originData */
originData: acl.TreeableString;
/** originData */
originData: acl.TreeableString
/** 父级 ID */
parentKey?: string;
}
/** 父级 ID */
parentKey?: string
}
/**
* 原始数据
*/
export interface TreeableString {
/** description */
description?: string;
/**
* 原始数据
*/
export interface TreeableString {
/** description */
description?: string
/** key */
key?: string;
/** key */
key?: string
/** name */
name?: string;
/** name */
name?: string
/** parentKey */
parentKey?: string;
}
/** parentKey */
parentKey?: string
}
/**
* 用户
*/
export interface User {
/** createdTime */
createdTime?: string;
/**
* 用户
*/
export interface User {
/** createdTime */
createdTime?: string
/** 邮箱 */
email?: string;
/** 邮箱 */
email?: string
/** 真实姓名 */
fullName?: string;
/** 真实姓名 */
fullName?: string
/** id */
id?: number;
/** id */
id?: number
/** 手机号 */
mobile?: string;
/** 手机号 */
mobile?: string
/** 用户名 */
name: string;
/** 用户名 */
name: string
/** 密码 */
password?: string;
/** 密码 */
password?: string
/** 性别 */
sex?: 'MALE' | 'FEMALE';
/** 性别 */
sex?: 'MALE' | 'FEMALE'
/** sexInfo */
sexInfo?: acl.Codebook;
/** sexInfo */
sexInfo?: acl.Codebook
/** updatedTime */
updatedTime?: string;
}
/** updatedTime */
updatedTime?: string
}
}

View File

@@ -0,0 +1,26 @@
/**
* @desc 检查用户名是否存在,true存在,false不存在
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export interface Params {
/** name */
name: string
}
export default async function (
params: Params,
success: (data: boolean) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/user/exist-name`,
params,
})
.then((data: AxiosResponse<boolean, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -2,30 +2,32 @@
* @description 用户
*
*/
import saveOrUpdateUser from './saveOrUpdateUser';
import all from './all';
import sexes from './sexes';
import detail from './detail';
import deleteUser from './deleteUser';
import resetPassword from './resetPassword';
import permissionInfos from './permissionInfos';
import permissions from './permissions';
import grant from './grant';
import roleInfos from './roleInfos';
import grantRole from './grantRole';
import users from './users';
import saveOrUpdateUser from './saveOrUpdateUser'
import all from './all'
import existName from './existName'
import sexes from './sexes'
import detail from './detail'
import deleteUser from './deleteUser'
import resetPassword from './resetPassword'
import permissionInfos from './permissionInfos'
import permissions from './permissions'
import grant from './grant'
import roleInfos from './roleInfos'
import grantRole from './grantRole'
import users from './users'
export default {
saveOrUpdateUser,
all,
sexes,
detail,
deleteUser,
resetPassword,
permissionInfos,
permissions,
grant,
roleInfos,
grantRole,
users,
};
saveOrUpdateUser,
all,
existName,
sexes,
detail,
deleteUser,
resetPassword,
permissionInfos,
permissions,
grant,
roleInfos,
grantRole,
users,
}

File diff suppressed because it is too large Load Diff

100
src/api/auth/api.d.ts vendored
View File

@@ -1,71 +1,71 @@
declare namespace auth {
/**
* AuthUser
*/
export interface AuthUser {
/** anonymous */
anonymous?: boolean;
/**
* AuthUser
*/
export interface AuthUser {
/** anonymous */
anonymous?: boolean
/** 用户头像 */
avatar?: string;
/** 用户头像 */
avatar?: string
/** 邮箱 */
email?: string;
/** 邮箱 */
email?: string
/** extInfo */
extInfo?: auth.NutMap;
/** extInfo */
extInfo?: auth.NutMap
/** 姓名 */
fullName: string;
/** 姓名 */
fullName: string
/** 电话 */
mobile?: string;
/** 电话 */
mobile?: string
/** 密码 */
password: string;
/** 密码 */
password: string
/** 权限列表 */
permissions: Array<string>;
/** 权限列表 */
permissions: Array<string>
/** jwt refreshToken */
refreshToken: string;
/** jwt refreshToken */
refreshToken: string
/** 角色列表 */
roles: Array<string>;
/** 角色列表 */
roles: Array<string>
/** 性别 */
sex?: 'MALE' | 'FEMALE';
/** 性别 */
sex?: 'MALE' | 'FEMALE'
/** sexInfo */
sexInfo?: auth.Codebook;
/** sexInfo */
sexInfo?: auth.Codebook
/** jwt Token */
token: string;
/** jwt Token */
token: string
/** 用户名 */
userName: string;
}
/** 用户名 */
userName: string
}
/**
* 登录实体
*/
export interface LoginDTO {
/** 验证码 */
captcha?: string;
/**
* 登录实体
*/
export interface LoginDTO {
/** 验证码 */
captcha?: string
/** 手机号 */
mobile?: string;
/** 手机号 */
mobile?: string
/** 密码 */
password?: string;
/** 密码 */
password?: string
/** 登录类型 */
type: 'ACCOUNT' | 'WECHAT';
/** 登录类型 */
type: 'ACCOUNT' | 'WECHAT'
/** 用户名 */
userName?: string;
/** 用户名 */
userName?: string
/** uuid */
uuid?: string;
}
/** uuid */
uuid?: string
}
}

View File

@@ -1,22 +1,22 @@
/**
* @desc 登录
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
import type { AxiosResponse } from 'axios';
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 请求体 */
requestBody: auth.LoginDTO,
/** 请求体 */
requestBody: auth.LoginDTO,
success: (data: auth.AuthUser) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
success: (data: auth.AuthUser) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'post',
url: `/auth/login`,
data: requestBody,
})
.then((data: AxiosResponse<auth.AuthUser, unknown>) => {
success(data.data);
})
.catch((error: { code: string; error?: string }) => fail(error));
return http({
method: 'post',
url: `/auth/login`,
data: requestBody,
})
.then((data: AxiosResponse<auth.AuthUser, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -9,15 +9,24 @@ declare namespace material {
/** 申请日期 */
applyDate?: string
/** 申请id */
applyId?: number
/** 申请数量 */
applyNum?: string
/** 申请类型 */
applyType?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT'
applyType?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT' | 'SCRAP_OUT'
/** applyTypeInfo */
applyTypeInfo?: material.Codebook
/** 物料编码 */
code?: string
/** 是否确认 */
confirm?: boolean
/** 确认数量 */
confirmNum?: string
@@ -27,11 +36,20 @@ declare namespace material {
/** 备注 */
remark?: string
/** 审核状态 */
reviewResult?: 'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT'
/** 审核人 */
reviewer?: string
/** 物料规格 */
spec?: string
/** 物料类型 */
type?: string
/** 类型名称 */
typeName?: string
}
/**
@@ -41,8 +59,8 @@ declare namespace material {
/** 申请单ID */
applyId?: number
/** assignRule */
assignRule?: 'LOW_VALUE' | 'HIGH_VALUE'
/** 是否需要赋码 */
assignRule?: boolean
/** 确认数量 */
confirmQuantity?: number
@@ -76,8 +94,8 @@ declare namespace material {
/** 申请日期 */
applyDate?: string
/** 盘点类型1: 全盘 2: 部分盘点 */
auditType?: 'ALL' | 'PARTIAL'
/** 盘点类型1: 扫码 2: 人工 */
auditType?: 'SCAN' | 'MANUAL'
/** 是否确认0: 未确认 1: 已确认) */
confirm?: boolean
@@ -101,7 +119,7 @@ declare namespace material {
taker?: string
/** 类型1: 采购入库申请 2: 归还入库申请 3: 出库外借申请 4: 盘点申请) */
type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT'
type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT' | 'SCRAP_OUT'
/** updatedTime */
updatedTime?: string
@@ -131,8 +149,8 @@ declare namespace material {
/** 申请日期 */
applyDate?: string
/** 盘点类型1: 全盘 2: 部分盘点 */
auditType?: 'ALL' | 'PARTIAL'
/** 盘点类型1: 扫码 2: 人工 */
auditType?: 'SCAN' | 'MANUAL'
/** 是否确认0: 未确认 1: 已确认) */
confirm?: boolean
@@ -159,7 +177,10 @@ declare namespace material {
taker?: string
/** 类型1: 采购入库申请 2: 归还入库申请 3: 出库外借申请 4: 盘点申请) */
type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT'
type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT' | 'SCRAP_OUT'
/** 物料类型 */
types?: Array<string>
/** updatedTime */
updatedTime?: string
@@ -176,12 +197,79 @@ declare namespace material {
exceptionalData?: Array<material.StocktakingScanExceptionalData>
}
/**
* ManualStockDTO
*/
export interface ManualStockDTO {
/** 申请id */
applyId?: number
/** 编码 */
code?: string
/** 手动核实库存数量 */
manualStock?: number
/** 物料id */
materialId?: number
/** 异常原因 */
msg?: string
/** 名称 */
name?: string
/** 价格 */
price?: number
/** 规格 */
spec?: string
/** 库存数量 */
stock?: number
/** 类型 */
type?: string
/** typeName */
typeName?: string
}
/**
* 人工手动核实物料库存明细
*/
export interface ManualStockDetail {
/** 申请id */
applyId?: number
/** createdTime */
createdTime?: string
/** id */
id?: number
/** 手动核实库存数量 */
manualStock?: number
/** 物料id */
materialId?: number
/** 异常原因 */
msg?: string
/** 库存数量 */
stock?: number
/** updatedTime */
updatedTime?: string
}
/**
* 物料信息
*/
export interface Material {
/** 赋码规则(2-低值易耗品(不参与赋码) 1-高价值工具类(参与唯一赋码 */
assignRule?: 'LOW_VALUE' | 'HIGH_VALUE'
/** 是否赋码 */
assignRule?: boolean
/** 编码 */
code?: string
@@ -198,6 +286,9 @@ declare namespace material {
/** 名称 */
name?: string
/** 价格 */
price?: number
/** 规格 */
spec?: string
@@ -207,6 +298,9 @@ declare namespace material {
/** 类型 */
type?: string
/** typeName */
typeName?: string
/** updatedTime */
updatedTime?: string
}
@@ -231,7 +325,7 @@ declare namespace material {
materialId?: number
/** 状态 */
status?: 'IN' | 'OUT' | 'OFF' | 'LOST'
status?: 'IN' | 'OUT' | 'OFF' | 'LOST' | 'SCRAP_OUT'
/** updatedTime */
updatedTime?: string
@@ -251,6 +345,38 @@ declare namespace material {
reviewResult?: 'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT'
}
/**
* ScrapOutApplyDTO
*/
export interface ScrapOutApplyDTO {
/** 申请人 */
applicant?: string
/** 条码集合 */
barcodeList?: string
/** 确认数量 */
confirmQuantity?: number
/** 物料名称 */
name?: string
/** 单价 */
price?: string
/** 报废原因 */
remark?: string
/** 物料规格 */
spec?: string
/** 类型 */
type?: string
/** 类型名称 */
typeName?: string
}
/**
* 库存明细
*/
@@ -332,6 +458,52 @@ declare namespace material {
updatedTime?: string
}
/**
* 类型
*/
export interface Type {
/** createdTime */
createdTime?: string
/** id */
id?: number
/** 类型级别 */
level: 'ONE' | 'TWO'
/** levelInfo */
levelInfo?: material.Codebook
/** 类型名称 */
name: string
/** 类型编码 */
no: string
/** 上级类型编码 */
parentNo?: string
/** updatedTime */
updatedTime?: string
}
/**
* TypeTree
*/
export interface TypeTree {
/** children */
children?: Array<material.TypeTree>
/** 类型名称 */
label: string
/** parentNo */
parentNo?: string
/** 类型编码 */
value: string
}
/**
* WaitScanInfo
*/

View File

@@ -0,0 +1,32 @@
/**
* @desc 导出盘点申请单列表
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export interface Params {
/** 盘点类型 */
auditType?: 'SCAN' | 'MANUAL'
/** 盘点人 */
taker?: string
/** 创建日期 */
createDate?: string
/** 审核状态 */
reviewResults?: Array<'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT'>
}
export default async function (
params: Params,
success: (data: void) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/apply/audit/download-excel`,
params,
})
.then((data: AxiosResponse<void, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,30 @@
/**
* @desc 导出申请单列表
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export interface Params {
/** 申请类型(1入库 3出库 4盘点) */
applyType: number
/** 物料类型 */
type?: string
/** 编码/名称 */
key?: string
}
export default async function (
params: Params,
success: (data: void) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/apply/download-excel`,
params,
})
.then((data: AxiosResponse<void, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,21 @@
/**
* @desc 查询人工盘点选中物料的待核查数据
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 申请单ID */
applyId: number,
success: (data: Array<material.Material>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/manual/check-data/${applyId}`,
})
.then((data: AxiosResponse<Array<material.Material>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,21 @@
/**
* @desc 获取人工盘点数据
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 申请单ID */
applyId: number,
success: (data: Array<material.ManualStockDTO>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/manual/data/${applyId}`,
})
.then((data: AxiosResponse<Array<material.ManualStockDTO>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,21 @@
/**
* @desc 获取报废出库申请单
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 申请单ID */
applyId: number,
success: (data: Array<material.ScrapOutApplyDTO>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/scrap-out/${applyId}`,
})
.then((data: AxiosResponse<Array<material.ScrapOutApplyDTO>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -4,24 +4,38 @@
*/
import searchPage from './searchPage'
import saveApply from './saveApply'
import downloadAuditExcel from './downloadAuditExcel'
import downloadExcel from './downloadExcel'
import detail from './detail'
import searchAuditPage from './searchAuditPage'
import auditApply from './auditApply'
import getComparisonRes from './getComparisonRes'
import updateReviewResult from './updateReviewResult'
import submitManualStock from './submitManualStock'
import getManualCheckData from './getManualCheckData'
import getManualStock from './getManualStock'
import saveScanData from './saveScanData'
import submitScrapOutReview from './submitScrapOutReview'
import getScrapOutById from './getScrapOutById'
import submitReview from './submitReview'
import getWaitScanData from './getWaitScanData'
export default {
searchPage,
saveApply,
downloadAuditExcel,
downloadExcel,
detail,
searchAuditPage,
auditApply,
getComparisonRes,
updateReviewResult,
submitManualStock,
getManualCheckData,
getManualStock,
saveScanData,
submitScrapOutReview,
getScrapOutById,
submitReview,
getWaitScanData,
}

View File

@@ -1,39 +1,37 @@
/**
* @desc 分页查询盘点列表
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
import type { AxiosResponse } from 'axios';
import type { IPage } from '@/api/api';
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
import type { IPage } from '@/api/api'
export interface Params {
/** 页码 */
page?: number;
/** 页面大小 */
size?: number;
/** 盘点类型 */
auditType?: 'ALL' | 'PARTIAL';
/** 盘点人 */
taker?: string;
/** 创建日期 */
createDate?: string;
/** 审核状态 */
reviewResults?: Array<
'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT'
>;
/** 页码 */
page?: number
/** 页面大小 */
size?: number
/** 盘点类型 */
auditType?: 'SCAN' | 'MANUAL'
/** 盘点人 */
taker?: string
/** 创建日期 */
createDate?: string
/** 审核状态 */
reviewResults?: Array<'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT'>
}
export default async function (
params: Params,
success: (data: IPage<material.ApplyForm>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
params: Params,
success: (data: IPage<material.ApplyForm>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/audit-applies`,
return http({
method: 'get',
url: `/audit-applies`,
params,
})
.then((data: AxiosResponse<IPage<material.ApplyForm>, unknown>) => {
success(data.data);
})
.catch((error: { code: string; error?: string }) => fail(error));
params,
})
.then((data: AxiosResponse<IPage<material.ApplyForm>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -1,37 +1,35 @@
/**
* @desc 分页查询申请列表
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
import type { AxiosResponse } from 'axios';
import type { IPage } from '@/api/api';
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
import type { IPage } from '@/api/api'
export interface Params {
/** 申请类型(1入库 3出库 4盘点) */
applyType: number;
/** 页码 */
page?: number;
/** 页面大小 */
size?: number;
/** 物料类型 */
type?: string;
/** 物料编码 */
code?: string;
/** 物料名称 */
name?: string;
/** 申请类型(1入库 3出库 4盘点) */
applyType: number
/** 页码 */
page?: number
/** 页面大小 */
size?: number
/** 物料类型 */
type?: string
/** 编码/名称 */
key?: string
}
export default async function (
params: Params,
success: (data: IPage<material.ApplyDTO>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
params: Params,
success: (data: IPage<material.ApplyDTO>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/applies`,
return http({
method: 'get',
url: `/applies`,
params,
})
.then((data: AxiosResponse<IPage<material.ApplyDTO>, unknown>) => {
success(data.data);
})
.catch((error: { code: string; error?: string }) => fail(error));
params,
})
.then((data: AxiosResponse<IPage<material.ApplyDTO>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,22 @@
/**
* @desc 提交人工盘点数据
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 请求体 */
requestBody: Array<material.ManualStockDetail>,
success: (data: void) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'post',
url: `/manual/audit-apply`,
data: requestBody,
})
.then((data: AxiosResponse<void, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,22 @@
/**
* @desc 提交报废出库审核
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 请求体 */
requestBody: material.ReviewDTO,
success: (data: void) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'post',
url: `/scrap-out-review`,
data: requestBody,
})
.then((data: AxiosResponse<void, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -1,8 +1,11 @@
import apply from './apply';
import apply from './apply'
import material from './material';
import material from './material'
import type from './type'
export default {
apply,
material,
};
apply,
material,
type,
}

View File

@@ -1,18 +1,28 @@
/**
* @desc 查询所有物料列表
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
import type { AxiosResponse } from 'axios';
export default async function (
success: (data: Array<material.Material>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/material/list`,
})
.then((data: AxiosResponse<Array<material.Material>, unknown>) => {
success(data.data);
})
.catch((error: { code: string; error?: string }) => fail(error));
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export interface Params {
/** 类型 */
type?: string
/** 是否赋码 */
assignRule?: boolean
}
export default async function (
params: Params,
success: (data: Array<material.Material>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'post',
url: `/material/list`,
params,
})
.then((data: AxiosResponse<Array<material.Material>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,28 @@
/**
* @desc 导出物料列表
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export interface Params {
/** 类型 */
type?: string
/** 搜索关键词 */
key?: string
}
export default async function (
params: Params,
success: (data: void) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/material/download-excel`,
params,
})
.then((data: AxiosResponse<void, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -3,6 +3,7 @@
*
*/
import saveOrUpdateMaterial from './saveOrUpdateMaterial'
import downloadExcel from './downloadExcel'
import all from './all'
import detail from './detail'
import deleteMaterial from './deleteMaterial'
@@ -11,6 +12,7 @@ import materials from './materials'
export default {
saveOrUpdateMaterial,
downloadExcel,
all,
detail,
deleteMaterial,

View File

@@ -1,33 +1,33 @@
/**
* @desc 分页查询物料列表
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
import type { AxiosResponse } from 'axios';
import type { IPage } from '@/api/api';
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
import type { IPage } from '@/api/api'
export interface Params {
/** 页码 */
page?: number;
/** 页面大小 */
size?: number;
/** 类型 */
type?: number;
/** 搜索关键词 */
key?: string;
/** 页码 */
page?: number
/** 页面大小 */
size?: number
/** 类型 */
type?: string
/** 搜索关键词 */
key?: string
}
export default async function (
params: Params,
success: (data: IPage<material.Material>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
params: Params,
success: (data: IPage<material.Material>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/materials`,
return http({
method: 'get',
url: `/materials`,
params,
})
.then((data: AxiosResponse<IPage<material.Material>, unknown>) => {
success(data.data);
})
.catch((error: { code: string; error?: string }) => fail(error));
params,
})
.then((data: AxiosResponse<IPage<material.Material>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -1,22 +1,22 @@
/**
* @desc 增加/编辑物料
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
import type { AxiosResponse } from 'axios';
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 请求体 */
requestBody: material.Material,
/** 请求体 */
requestBody: material.Material,
success: (data: material.Material) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
success: (data: material.Material) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'put',
url: `/material`,
data: requestBody,
})
.then((data: AxiosResponse<material.Material, unknown>) => {
success(data.data);
})
.catch((error: { code: string; error?: string }) => fail(error));
return http({
method: 'put',
url: `/material`,
data: requestBody,
})
.then((data: AxiosResponse<material.Material, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,22 @@
/**
* @desc 增加/更新地区
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** 请求体 */
requestBody: material.Type,
success: (data: material.Type) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'post',
url: `/area`,
data: requestBody,
})
.then((data: AxiosResponse<material.Type, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,26 @@
/**
* @desc 查询类型
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export interface Params {
/** 搜索关键词 */
key?: string
}
export default async function (
params: Params,
success: (data: Array<material.Type>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/areas`,
params,
})
.then((data: AxiosResponse<Array<material.Type>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,21 @@
/**
* @desc 删除类型
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
/** id */
id: number,
success: (data: void) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'delete',
url: `/area/${id}`,
})
.then((data: AxiosResponse<void, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,17 @@
/**
* @description 物料类型管理
*
*/
import addAndModify from './addAndModify'
import levels from './levels'
import trees from './trees'
import deleteArea from './deleteArea'
import areas from './areas'
export default {
addAndModify,
levels,
trees,
deleteArea,
areas,
}

View File

@@ -0,0 +1,20 @@
/**
* @desc 级别
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
import type { Codebook } from '@/api/api'
export default async function (
success: (data: Array<Codebook>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/area/levels`,
})
.then((data: AxiosResponse<Array<Codebook>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@@ -0,0 +1,18 @@
/**
* @desc 类型树
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export default async function (
success: (data: Array<material.TypeTree>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'get',
url: `/area/trees`,
})
.then((data: AxiosResponse<Array<material.TypeTree>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

1
src/components.d.ts vendored
View File

@@ -56,6 +56,7 @@ declare module 'vue' {
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ATransfer: typeof import('ant-design-vue/es')['Transfer']
ATree: typeof import('ant-design-vue/es')['Tree']
ATreeSelect: typeof import('ant-design-vue/es')['TreeSelect']
ATypographyLink: typeof import('ant-design-vue/es')['TypographyLink']
ATypographyParagraph: typeof import('ant-design-vue/es')['TypographyParagraph']
ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle']

View File

@@ -169,7 +169,7 @@
line-height: 28px;
}
@media (width <= 576px) {
@media (width <=576px) {
.content {
display: block;
}

View File

@@ -2,6 +2,7 @@ import { RouteRecordRaw } from 'vue-router'
import XEUtils from 'xe-utils'
import i18n from '@/locales'
import { routes } from '@/router'
import { useUserStore } from '@/stores/user'
const { t } = i18n.global
export interface MenuNode {
key: string
@@ -73,8 +74,8 @@ export function menuTree() {
export function hasPermission(key?: string) {
console.log(key)
// return useUserStore().hasPermission(key);
return true
return useUserStore().hasPermission(key) // 过滤菜单是否显示
// return true
}
export function permissions() {
@@ -91,6 +92,7 @@ export function fliteredMenus() {
return !item.hidden
})
.filter((item) => {
if (item.key === 'Dashboard') return true // 首页显示
return hasPermission(item.keyPath)
})
}

View File

@@ -1,5 +1,6 @@
{
"index": "首页",
"dashboard": "首页",
"acl": {
"name": "系统管理",
"users": "用户管理",

View File

@@ -2,7 +2,7 @@ import { http } from '@/settings/http'
import { useUserStore } from '@/stores/user'
import axios, { InternalAxiosRequestConfig, type AxiosError, type AxiosResponse } from 'axios'
import type { App } from 'vue'
import router from '@/router'
import { notification } from 'ant-design-vue'
export interface GlobalError {
/** 错误码 */
@@ -24,9 +24,13 @@ export function defaultSuccess(data: unknown): void {
console.log(data)
}
export function defaultError(error: { code: string; error?: string }): void {
if (error) {
router.push({ path: '/message', query: { status: error.code, message: error.error } })
}
window.console.log(error)
notification.error({
message: '操作异常: ',
placement: 'top',
description: error.error + '(' + error.code + ')',
duration: 6,
})
}
const config = {
baseURL: http.prefix,

View File

@@ -2,8 +2,15 @@ import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import UserLayout from '@/layout/user-layout.vue'
import AdminLayout from '@/layout/admin-layout.vue'
import BlankLayout from '@/layout/blank-layout.vue'
import router from '@/router'
export const routes: Array<RouteRecordRaw> = [
{
path: '/admin/dashboard',
meta: { title: 'menus.dashboard', icon: 'icon-dashboard', flat: true },
name: 'Dashboard',
component: () => import('../views/admin/dashboard-page.vue'),
},
{
path: '/admin/acl', // 系统管理
name: 'ACL',
@@ -35,11 +42,17 @@ export const routes: Array<RouteRecordRaw> = [
meta: { title: 'menus.acl.material', icon: 'icon-permission' },
component: () => import('../views/stock/material/material-page.vue'),
},
{
path: '/admin/acl/type', //物料类型
name: 'Type',
meta: { title: '物料类型', icon: 'icon-permission' },
component: () => import('../views/stock/type/type-page.vue'),
},
],
},
{
path: '/stock/name', // 库存管理
name: 'STOCK',
name: 'Stock',
meta: { title: 'menus.stock.name', icon: 'icon-acl', flat: true },
component: BlankLayout,
redirect: () => ({ name: 'Inbound' }),
@@ -66,32 +79,32 @@ export const routes: Array<RouteRecordRaw> = [
},
{
path: '/statistic/name', // 统计报表
name: 'STATISTIC',
name: 'Statistic',
meta: { title: 'menus.statistic.name', icon: 'icon-acl', flat: true },
component: BlankLayout,
redirect: () => ({ name: 'S-Inbound' }),
redirect: () => ({ name: 'Query-Stock' }),
children: [
{
path: '/statistic/stock', //库存
name: 'S-stock',
name: 'Query-Stock',
meta: { title: 'menus.statistic.stock', icon: 'icon-permission' },
component: () => import('../views/stock/report/materialReport-page.vue'),
},
{
path: '/statistic/inbound', //入库
name: 'S-Inbound',
name: 'Query-Inbound',
meta: { title: 'menus.statistic.inbound', icon: 'icon-permission' },
component: () => import('../views/stock/report/inboundReport-page.vue'),
},
{
path: '/statistic/outbound', //出库
name: 'S-Outbound',
name: 'Query-Outbound',
meta: { title: 'menus.statistic.outbound', icon: 'icon-permission' },
component: () => import('../views/stock/report/outboundReport-page.vue'),
},
{
path: '/statistic/stocktaking', //盘点
name: 'S-stocktaking',
name: 'Query-Stocktaking',
meta: { title: 'menus.statistic.stocktaking', icon: 'icon-permission' },
component: () => import('../views/stock/report/stocktakingReport-page.vue'),
},
@@ -99,48 +112,117 @@ export const routes: Array<RouteRecordRaw> = [
},
]
const getNewRouter = () => {
// TODO 用户store判断是否登录,登录就返回过滤的,没有登录就返回全量
return createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'Index',
redirect: '/login',
},
{
path: '/login',
name: 'Login',
redirect: '/login/user',
component: UserLayout,
children: [
{
path: 'user',
name: 'LoginPage',
component: () => import('../views/login/user-login.vue'),
},
],
},
{
path: '/admin',
name: 'Admin',
redirect: '/admin/dashboard',
component: AdminLayout,
meta: { title: 'menus.index', icon: 'icon-home' },
children: routes,
},
{
path: '/:catchAll(.*)',
redirect: '/message',
},
{
path: '/message',
name: 'Message',
component: () => import('@/views/message/message-page.vue'),
},
],
})
export default createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'Index',
redirect: '/login',
},
{
path: '/login',
name: 'Login',
redirect: '/login/user',
component: UserLayout,
children: [
{
path: 'user',
name: 'LoginPage',
component: () => import('../views/login/user-login.vue'),
},
],
},
{
path: '/admin',
name: 'Admin',
redirect: '/admin/dashboard',
component: AdminLayout,
meta: { title: 'menus.index', icon: 'icon-home' },
children: routes,
},
{
path: '/:catchAll(.*)',
redirect: '/message',
},
{
path: '/message',
name: 'Message',
component: () => import('@/views/message/message-page.vue'),
},
],
})
export const getP = (permissions: Array<string>): RouteRecordRaw => {
const newRoutes: RouteRecordRaw[] = filterRoutesByPermission(routes, permissions)
return {
path: '/admin',
name: 'Admin',
redirect: '/admin/dashboard',
component: AdminLayout,
meta: { title: 'menus.index', icon: 'icon-home' },
children: newRoutes,
}
}
export default getNewRouter()
// 过滤路由,最多两层
const filterRoutesByPermission = (routes: RouteRecordRaw[], permissions: string[]): RouteRecordRaw[] => {
return routes
.map((route) => {
// 确保 route.name 是字符串
const parentName = typeof route.name === 'string' ? route.name : undefined
if ('Dashboard' === parentName) return route // 首页不做权限过滤
const hasParentPermission = parentName && permissions.includes(parentName)
// 如果有子路由,过滤子路由
if (Array.isArray(route.children) && route.children.length > 0) {
route.children = route.children.filter((child) => {
// 确保 child.name 是字符串
const childName = typeof child.name === 'string' ? child.name : undefined
// 拼接父级和子级的权限字符串
const fullName = parentName && childName ? `${parentName}.${childName}` : childName
// 确保 fullName 是字符串并且存在于 permissions 中
return typeof fullName === 'string' && permissions.includes(fullName)
})
// 如果子路由中有任何一个有权限,或者父级路由本身有权限,则保留该父级路由
if (route.children.length > 0 || hasParentPermission) {
return route
}
} else if (hasParentPermission) {
// 如果没有子路由,直接检查父级路由的权限
return route
}
// 如果不符合任何条件,返回 undefined表示该路由将被过滤掉
return undefined
})
.filter((route): route is RouteRecordRaw => route !== undefined) // 过滤掉 undefined 的项并确保类型正确
}
// 白名单
const whiteList = ['Index', 'Admin', 'Message', 'LoginPage', 'Login']
// 路由守卫
router.beforeEach(async (to, _, next) => {
// 使用 _ 来忽略 from 参数
if (to.name && whiteList.includes(to.name as string)) {
next()
return
}
// 其他页面需要登录 用pinia会报错
const user = localStorage.getItem('user')
if (user) {
// 将json字符串转换为对象auth.AuthUser
const obj: auth.AuthUser = JSON.parse(user)
if (obj.token && obj.token.length > 0) {
next()
return
}
}
window.console.log('没有登录')
next({ name: 'LoginPage' })
})

View File

@@ -1,9 +1,11 @@
export interface Http {
prefix: string // 前缀
timeout: number
downloadUrl: string
}
export const http: Http = {
prefix: import.meta.env.VITE_HTTP_PREFIX,
timeout: 10 * 1000,
downloadUrl: import.meta.env.VITE_UPLOAD_PREFIX,
}

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
import router from '@/router'
import router, { getP } from '@/router'
export const useUserStore = defineStore('user', {
state: (): auth.AuthUser => ({
@@ -31,10 +31,13 @@ export const useUserStore = defineStore('user', {
},
userLogin(user: auth.AuthUser) {
Object.assign(this, user)
// 过了路由的地址
// router.addRoute
router.push({ path: '/admin/acl/users' }) // 登录后跳转到用户列表页面
// 根据权限动态添加地址
if (!this.isAdmin) {
const path = getP(this.permissions)
router.addRoute(path)
// router.replace(router.currentRoute.value.fullPath)
}
router.push({ path: '/admin/dashboard' })
},
logout() {
this.mobile = ''
@@ -47,7 +50,11 @@ export const useUserStore = defineStore('user', {
this.sex = 'FEMALE'
this.token = ''
this.refreshToken = ''
router.push({ path: '/login/user' }) // 退出登录后跳转到登录页面,未实现,要清理缓存?
router.push({ path: '/login/user' })
},
hasPermission(permission?: string) {
if (this.isAdmin) return true
return permission !== undefined && this.permissions.includes(permission)
},
},
persist: {

View File

@@ -40,7 +40,7 @@
watch(
roles,
(newVal: acl.RoleInfo[]) => {
targetKeys.value = newVal.filter((item) => item.selected).map((item) => item.key)
targetKeys.value = newVal.filter((item) => item.selected).map((item) => item.key as string)
},
{ immediate: true, deep: true },
)

View File

@@ -100,10 +100,6 @@
)
}
const columns = [
{
title: t('pages.acl.role.table.columns.key'),
dataIndex: 'key',
},
{
title: t('pages.acl.role.table.columns.name'),
dataIndex: 'name',
@@ -115,7 +111,6 @@
{
title: t('pages.acl.role.table.columns.operation'),
dataIndex: 'operation',
width: 300,
},
]
const pagination = computed(() => {
@@ -178,7 +173,7 @@
const tryLoadPermissions = (expanded: boolean, record: acl.Role & { permissions?: acl.Permission[] }) => {
if (expanded && !record.permissions) {
api.aclApi.role.permissions(record.key, (data) => {
api.aclApi.role.permissions(record.key as unknown as string, (data) => {
record.permissions = data
})
}

View File

@@ -15,26 +15,6 @@ export const config: FormConfig = {
}
export const formItems: FormItem[] = [
{
group: 'form',
type: 'input',
config: {
autoLink: true,
hasFeedback: false,
label: t('pages.acl.role.form.add.filed.key'),
name: 'key',
required: true,
},
properties: {
size: 'default',
type: 'text',
allowClear: true,
bordered: true,
showCount: true,
placeholder: t('pages.acl.role.form.add.placeholder.key'),
},
rules: [],
},
{
group: 'form',
type: 'input',

View File

@@ -177,12 +177,10 @@
{
title: t('pages.acl.user.table.columns.sex'),
dataIndex: 'sex',
width: 75,
},
{
title: t('pages.acl.user.table.columns.operation'),
dataIndex: 'operation',
width: 400,
},
]

View File

@@ -5,6 +5,8 @@ const { t } = i18n.global
import { useAppStore } from '@/stores/app'
import { FormItem, FormConfig } from '@/components/form-render/form-render-types'
import { Rule } from 'ant-design-vue/es/form'
import api from '@/api'
export const config: FormConfig = {
layout: 'horizontal',
@@ -40,7 +42,12 @@ export const formItems = (sexes: Codebook[]): FormItem[] => {
placeholder: t('pages.acl.user.form.add.placeholder.name'),
maxlength: 20,
},
rules: [],
rules: [
{
validator: validate,
trigger: ['blur', 'change'],
},
],
},
{
type: 'radio',
@@ -80,7 +87,16 @@ export const formItems = (sexes: Codebook[]): FormItem[] => {
showCount: true,
placeholder: t('pages.acl.user.form.add.placeholder.password'),
},
rules: [],
rules: [
{
type: 'string',
required: true,
whitespace: true,
trigger: ['blur', 'change'],
pattern: new RegExp('^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$'),
message: '8位数字字母组合',
},
],
},
{
group: 'form',
@@ -143,3 +159,19 @@ export const formItems = (sexes: Codebook[]): FormItem[] => {
},
]
}
const validate = async (_rule: Rule, value: string): Promise<void> => {
const regex = /^[a-zA-Z0-9]{4,8}$/
if (!regex.test(value)) {
return Promise.reject('用户名只能是4-8位字母或数字')
}
return new Promise<void>((resolve, reject) => {
api.aclApi.user.existName({ name: value }, (data) => {
if (data) {
reject('用户名已经存在')
} else {
resolve()
}
})
})
}

View File

@@ -0,0 +1,4 @@
<template>
<p>这是首页欢迎使用库管系统</p>
</template>
<script setup lang="ts"></script>

View File

@@ -14,13 +14,13 @@
<vxe-column field="name" title="物料名称" />
<vxe-column field="assignRule" title="是否扫码">
<template #default="{ row }">
{{ row.assignRule === 'HIGH_VALUE' ? '是' : '否' }}
{{ row.assignRule ? '是' : '否' }}
</template>
</vxe-column>
<vxe-column field="applyNum" title="申请数量" :edit-render="{ name: 'VxeInput', props: { type: 'integer' } }" />
<vxe-column field="scanNum" title="扫码数量" :edit-render="{ name: 'VxeInput', props: { type: 'integer' } }">
<vxe-column field="scanNum" title="扫码数量">
<template #default="{ row }">
{{ row.assignRule === 'LOW_VALUE' ? '~' : row.scanNum }}
{{ row.assignRule ? row.scanNum : '~' }}
</template>
</vxe-column>
<vxe-column field="remark" title="备注" :edit-render="{ name: 'VxeInput' }" />

View File

@@ -3,7 +3,7 @@
<div>
<a-form :model="formData" name="basic" layout="horizontal" label-wrap>
<a-form-item label="申请人" name="applicant">
<a-input :value="formData.applicant" style="width: 40%" />
<a-input v-model:value="formData.applicant" style="width: 40%" />
</a-form-item>
<a-form-item label="申请类型" name="applyType">
@@ -11,23 +11,40 @@
<a-radio-button v-if="applyType === 'PURCHASE_RECEIPT'" value="PURCHASE_RECEIPT">采购入库</a-radio-button>
<a-radio-button v-if="applyType === 'PURCHASE_RECEIPT'" value="RETURN_RECEIPT">归还入库</a-radio-button>
<a-radio-button v-if="applyType === 'LOAN_OUT'" value="LOAN_OUT">出库外借</a-radio-button>
<a-radio-button v-if="applyType === 'LOAN_OUT'" value="SCRAP_OUT">报废出库</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item v-if="formData.applyType === 'SCRAP_OUT'" label="审核人" name="reviewer">
<a-select v-model:value="formData.reviewer" style="width: 40%" :options="personList"></a-select>
</a-form-item>
<a-form-item label="申请日期" name="applyDate" style="width: 40%">
<a-date-picker v-model:value="formData.applyDate" />
</a-form-item>
<a-form-item label="选择物料" name="selected">
<a-select
v-model:value="formData.slectedList"
mode="multiple"
bordered
placeholder="请选择物料"
style="width: 40%"
show-search
:options="options"
@deselect="removeEvent($event)"
@select="insertEvent($event)"
></a-select>
<div style="display: flex; justify-content: space-between">
<a-tree-select
v-model:value="typeVal"
show-search
style="width: 49%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择物料类型"
allow-clear
:tree-data="types"
tree-node-filter-prop="label"
/>
<a-select
v-model:value="formData.slectedList"
mode="multiple"
bordered
placeholder="请选择物料"
style="width: 49%"
show-search
:options="options"
:filter-option="filterOption"
@deselect="removeEvent($event)"
@select="insertEvent($event)"
></a-select>
</div>
</a-form-item>
</a-form>
</div>
@@ -49,10 +66,11 @@
<vxe-column field="name" title="物料名称" />
<vxe-column field="code" title="编码" />
<vxe-column field="spec" title="规格" />
<vxe-column field="type" title="类型" />
<vxe-column field="price" title="价格" />
<vxe-column field="typeName" title="类型" />
<vxe-column field="assignRule" title="是否赋码">
<template #default="{ row }">
{{ row.assignRule === 'HIGH_VALUE' ? '是' : '否' }}
{{ row.assignRule ? '是' : '否' }}
</template>
</vxe-column>
<vxe-column
@@ -79,10 +97,10 @@
<script setup lang="ts">
import { VxeTableInstance } from 'vxe-table'
import api from '@/api'
import { useUserStore } from '@/stores/user'
import dayjs, { Dayjs } from 'dayjs'
import { LabeledValue, DefaultOptionType } from 'ant-design-vue/es/select'
import { SelectHandler } from 'ant-design-vue/es/vc-select/Select'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
// 和外层界面的数据交互
const props = defineProps(
@@ -104,6 +122,7 @@
applyDate: formData.value.applyDate.format('YYYY-MM-DD HH:mm:ss'),
isConfirm: true,
result: '',
reviewer: formData.value.reviewer,
}
}
defineExpose({ getTableData, getApplyForm })
@@ -113,16 +132,41 @@
applyDate: Dayjs // 申请日期
applyType: string // 申请类型
slectedList: number[] // 入库物料
reviewer: string // 审核人
}
const userStore = useUserStore()
const formData = ref<FormData>({
applicant: userStore.userName,
applicant: '',
applyDate: dayjs(),
applyType: props.applyType,
slectedList: [],
reviewer: '',
})
// 审核人
const personList = ref<Array<{ value: string | undefined; label: string | undefined }>>([])
api.aclApi.user.all((data) => {
personList.value = data.map((item) => {
return {
value: item?.name,
label: item?.fullName,
}
})
})
// 物料类型树
const types = ref<Array<TreeDataNode>>([])
api.materialApi.type.trees((data) => {
types.value = children(data)
})
const children = (res: Array<material.TypeTree>): Array<TreeDataNode> => {
return res.map((areaTree) => ({
label: areaTree.label,
value: areaTree.value,
children: areaTree.children ? children(areaTree.children) : [],
key: areaTree.value,
}))
}
// vxe-table相关的设置
export interface RowVO {
id: number
@@ -130,26 +174,38 @@
code: string
spec: string
type: string
typeName: string
quantity: number
disabled: boolean
checked: boolean
assignRule: string
price: number
assignRule: boolean
}
const tableRef = ref<VxeTableInstance<RowVO>>()
// vxe-table 数据结果
const tableData = ref<Array<RowVO>>([])
// 物料选择器
const typeVal = ref<string>()
const options = ref<{ label: string; value: number }[]>([])
const materialList = ref<material.Material[]>([])
const getMaterialList = async () => {
await api.materialApi.material.all((data) => {
await api.materialApi.material.all({ type: typeVal.value }, (data) => {
materialList.value = data
})
options.value = materialList.value.map((item) => {
return { label: item.name ? item.name : '未知', value: item.id ? item.id : -1 }
return { label: item.name ? item.name + '(' + item.spec + ')' : '未知', value: item.id ? item.id : -1 }
})
}
getMaterialList()
watch(
typeVal,
(newVal) => {
if (newVal !== '') {
getMaterialList()
}
},
{ immediate: true, deep: true },
)
// 选中时在末尾新增一行
// value: number, option: { label: string, value: number }
@@ -160,12 +216,14 @@
if (m) {
const row: RowVO = {
id: m.id ? m.id : -1,
name: m.name ? m.name : '未知',
code: m.code ? m.code : '未知',
spec: m.spec ? m.spec : '未知',
type: m.type ? m.type : '未知',
name: m.name ? m.name : '',
code: m.code ? m.code : '',
spec: m.spec ? m.spec : '',
typeName: m.typeName ? m.typeName : '',
type: m.type ? m.type : '',
price: m.price ? m.price : 0,
quantity: 1,
assignRule: m.assignRule ? m.assignRule : 'HIGH_VALUE',
assignRule: m.assignRule ? m.assignRule : false,
disabled: false,
checked: false,
}
@@ -175,6 +233,10 @@
}
}
}
// 过滤选项
const filterOption = (input: string, option: { label: string; value: number }) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
// 取消选中时删除table中的行
const removeEvent = (value: Insert) => {

View File

@@ -56,7 +56,6 @@
const loading = ref(false) // 提交按钮的loading状态
const items = [{ title: '录入申请' }, { title: '扫码点货' }, { title: '人工确认' }]
/**
* 点击下一步
*/
@@ -67,7 +66,7 @@
// 再step1中点击下一步的时候就去获取step1中的表单数据
selectData.value = applyFormRef.value.getTableData()
selectData.value.forEach((item: RowVO) => {
if (item.assignRule === 'HIGH_VALUE') {
if (item.assignRule) {
totalValue.value += item.quantity
}
})
@@ -91,7 +90,7 @@
applyNum: item.quantity,
assignRule: item.assignRule,
scanNum: groupedSums[item.code] || 0,
remark: item.quantity != groupedSums[item.code] && item.assignRule === 'HIGH_VALUE' ? `数量不一致` : '',
remark: item.quantity != groupedSums[item.code] && item.assignRule ? `数量不一致` : '',
}
})
}

View File

@@ -1,157 +0,0 @@
<template>
<page-container>
<!-- 页面操作栏 -->
<template #ops>
<a-row>
<a-col :span="18">
<a-input-search
v-model:value="searchKey"
:placeholder="`请输入`"
allow-clear
enter-button
@search="loadData()"
></a-input-search>
</a-col>
<a-col :span="6">
<a-button type="primary" style="margin-left: 10px" @click="showModal">
<template #icon>
<icon-font type="icon-plus" />
</template>
{{ pageType === '1' ? '申请入库' : '申请出库' }}
</a-button>
</a-col>
</a-row>
</template>
<!-- 页面表格内容 -->
<div style="min-height: calc(100vh - 305px)">
<!-- 表格行 -->
<a-table
:columns="columns"
:data-source="pagedata?.records"
bordered
:pagination="pagination"
:loading="loading"
row-key="key"
></a-table>
</div>
</page-container>
<!-- 弹窗 -->
<a-modal v-model:open="open" title="Basic Modal" width="100%" wrap-class-name="full-modal" @ok="handleOk">
<apply></apply>
</a-modal>
</template>
<script setup lang="ts">
import api from '@/api'
import { IPage } from '@/api/api'
import apply from './apply-modal.vue'
const props = defineProps({
pageType: {
type: String as PropType<'1' | '2'>, // 1 入库 2 出库
required: false,
default: '1',
},
})
const { pageType } = toRefs(props)
const searchKey = ref('')
const pagedata = ref<IPage<material.ApplyDTO>>()
const loading = ref(false)
// 加载数据的方法
const loadData = async (page = 1, size = 10) => {
loading.value = true
api.materialApi.apply.searchPage(
//1: 采购入库 2: 归还入库 3: 出库外借
{
applyType: 1,
page: page || pagedata.value?.current,
size: size || pagedata.value?.size,
type: searchKey.value,
code: searchKey.value,
name: searchKey.value,
},
(data) => {
loading.value = false
pagedata.value = data
},
)
}
//初始加载
loadData()
//表格列
const columns = [
{
title: '类型',
dataIndex: 'type',
},
{
title: '申请人',
dataIndex: 'applicant',
},
{
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '是否确认',
dataIndex: 'isConfirm',
},
{
title: '创建人',
dataIndex: 'createdBy',
},
{
title: '创建时间',
dataIndex: 'createdTime',
},
]
//分页信息
const pagination = computed(() => {
return {
current: pagedata.value?.current,
pageSize: pagedata.value?.size,
total: pagedata.value?.total,
onChange: (page: number, pageSize: number) => {
loadData(page, pageSize)
},
}
})
const open = ref<boolean>(false)
const showModal = () => {
open.value = true
}
const handleOk = (e: MouseEvent) => {
console.log(e)
open.value = false
}
</script>
<style lang="less">
.full-modal {
.ant-modal {
max-width: 100%;
top: 0;
padding-bottom: 0;
margin: 0;
}
.ant-modal-content {
display: flex;
flex-direction: column;
height: calc(100vh);
}
.ant-modal-body {
flex: 1;
}
}
</style>

View File

@@ -0,0 +1,170 @@
<template>
<!-- vxe-table -->
<div>
<vxe-table
ref="tableRef"
v-model:data="tableData"
border
show-overflow
max-height="500"
size="medium"
empty-text="请先选择物料"
:edit-config="{ trigger: 'click', mode: 'cell', autoFocus: true }"
>
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="id" title="物料id" :visible="false" />
<vxe-column field="name" title="物料名称" />
<vxe-column field="code" title="编码" />
<vxe-column field="spec" title="规格" />
<vxe-column field="price" title="价格" />
<vxe-column field="typeName" title="类型" />
<vxe-column field="stock" title="库存数量" />
<vxe-column
field="manualStock"
title="手动核实数量"
:edit-render="{ name: 'input', autoSelect: true }"
keyboard-config="{edit: true, del: true}"
cell-type="number"
>
<template #edit="{ row }">
<vxe-number-input v-model="row.manualStock" type="integer"></vxe-number-input>
</template>
</vxe-column>
<vxe-column
field="msg"
title="异常原因"
:edit-render="{ name: 'input', autoSelect: true }"
keyboard-config="{edit: true, del: true}"
cell-type="string"
>
<template #edit="{ row }">
<vxe-input v-model="row.msg" placeholder="请输入异常原因" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column field="oprator" title="操作">
<template #default="{ row }">
<vxe-button mode="text" status="error" @click="removeRow(row)">删除</vxe-button>
</template>
</vxe-column>
</vxe-table>
</div>
</template>
<script lang="ts" setup>
import api from '@/api'
import dayjs, { Dayjs } from 'dayjs'
import { VxeTableInstance } from 'vxe-table'
//组件交互
const props = defineProps({
applyId: {
type: Number,
required: false,
default: 0,
},
})
const getTableData = (): Array<material.ManualStockDetail> => {
const $table = tableRef.value
if (!$table) {
return []
}
return $table?.getTableData().fullData.map((item) => {
// 只需要保存哪一个物料id,库存数量,手动核实数量
return {
materialId: item.id,
stock: item.stock,
manualStock: item.manualStock,
msg: item.msg,
applyId: props.applyId,
}
})
}
defineExpose({ getTableData })
// vxe-table相关的设置
export interface RowVO {
id: number
name: string
code: string
spec: string
type: string
typeName: string
stock: number
manualStock: number
disabled: boolean
checked: boolean
price: number
assignRule: boolean
msg: string
}
// vxe引用
const tableRef = ref<VxeTableInstance<RowVO>>()
// vxe-table 数据结果
const tableData = ref<Array<RowVO>>([])
// 获取接口数据
const fetchData = () => {
api.materialApi.apply.getManualCheckData(props.applyId, (data) => {
tableData.value = data.map((item) => {
return {
id: item.id ? item.id : 0,
name: item.name ? item.name : '',
code: item.code ? item.code : '',
spec: item.spec ? item.spec : '',
type: item.type ? item.type : '',
typeName: item.typeName ? item.typeName : '',
stock: item.stock ? item.stock : 0,
manualStock: 0,
disabled: false,
checked: false,
price: item.price ? item.price : 0,
assignRule: false,
msg: '',
}
})
})
}
// 监测applyId变化重新获取数据
fetchData()
watch(
() => props.applyId,
(newVal, oldVal) => {
if (newVal !== oldVal) {
fetchData()
}
},
)
interface FormData {
applicant: string // 申请人
applyDate: Dayjs // 申请日期
slectedList: number[] // 入库物料
reviewer: string // 审核人
}
const formData = ref<FormData>({
applicant: '',
applyDate: dayjs(),
slectedList: [],
reviewer: '',
})
// 手动删除table中的行
const removeRow = async (row: RowVO) => {
//打印tableData数组
const $table = tableRef.value
if ($table) {
// 根据row.id找到对应选中列表中的数据
formData.value.slectedList.forEach((item, index) => {
if (item.toString() === row.id.toString()) {
formData.value.slectedList.splice(index, 1)
}
})
$table.remove(row)
}
}
</script>

View File

@@ -1,6 +1,6 @@
<template>
<a-button type="primary" @click="beiginScan">开始扫码</a-button>
<a-button type="primary" danger style="left: 20px">重新扫码</a-button>
<a-button type="primary" danger style="left: 20px" @click="clearScanData">全部重扫</a-button>
<a-input
ref="snInput"
v-model:value="value"
@@ -38,7 +38,7 @@
<vxe-column field="name" title="物料名称" />
<vxe-column field="code" title="编码" />
<vxe-column field="spec" title="规格" />
<vxe-column field="type" title="类型" />
<vxe-column field="typeName" title="类型" />
<vxe-column field="times" title="扫码时间" />
<vxe-column field="oprator" title="操作">
<template #default="{ row }">
@@ -109,7 +109,7 @@
* 请求接口获取缓存数据
*/
const getCache = () => {
api.materialApi.material.all((data) => {
api.materialApi.material.all({}, (data) => {
data.forEach((item) => {
if (item.code) {
materialMapCache.value.set(item.code, item) // 物料编码定义为6位
@@ -122,11 +122,12 @@
interface TableRowVO {
id: number
barcode: string
assignRule: string
assignRule: boolean
name: string
code: string
spec: string
type: string
typeName: string
times: string
}
@@ -176,7 +177,7 @@
return
}
// 找到条形码对应的物料编码
const code = sn.slice(0, 6)
const code = sn.slice(0, 5)
const materialInfo = materialMapCache.value.get(code)
if (!materialInfo) {
value.value = ''
@@ -188,11 +189,12 @@
const row: TableRowVO = {
id: materialInfo?.id ?? 0,
barcode: sn,
assignRule: materialInfo?.assignRule ?? '',
assignRule: materialInfo?.assignRule ?? false,
name: materialInfo?.name ?? '',
code: materialInfo?.code ?? '',
spec: materialInfo?.spec ?? '',
type: materialInfo?.type ?? '',
typeName: materialInfo?.typeName ?? '',
times: new Date().toLocaleString(),
}
$table.insert(row)
@@ -205,4 +207,14 @@
value.value = ''
}
}
// 清空扫码数据
const clearScanData = () => {
const $table = applyDetailTableRef.value
if ($table) {
$table.remove()
snListCache.value = []
remainderValue.value = 0
}
}
</script>

View File

@@ -1,9 +1,5 @@
<template>
<a-result
status="success"
title="Successfully Purchased Cloud Server ECS!"
sub-title="Order number: 2017182818828182881 Cloud server configuration takes 1-5 minutes, please wait."
>
<a-result status="success" title="操作成功!" sub-title="操作已完成请仔细核对实物与清单确保无误">
<template #extra>
<a-button type="primary" @click="select">查询申请</a-button>
<a-button @click="again">继续提交申请</a-button>
@@ -21,11 +17,11 @@
type: String,
default: '',
},
}, // 申请类型 applyType页面传值的使用1和21: 采购入库 和 归还入库 2: 出库外借
}, // 申请类型 applyType页面传值的使用PURCHASE_RECEIPT和LOAN_OUTPURCHASE_RECEIPT: 采购入库 和 归还入库 LOAN_OUT: 出库外借
)
const select = () => {
if (props.applyType === '1') {
if (props.applyType === 'PURCHASE_RECEIPT') {
router.push({ path: '/statistic/inbound' })
} else {
router.push({ path: '/statistic/outbound' })

View File

@@ -1,4 +1,5 @@
import { FormItem, FormConfig } from '@/components/form-render/form-render-types'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
export const config: FormConfig = {
layout: 'horizontal',
@@ -13,7 +14,7 @@ export const config: FormConfig = {
},
}
export const formItems: FormItem[] = [
export const formItems = (types: TreeDataNode[]): FormItem[] => [
{
group: 'form',
type: 'input',
@@ -34,22 +35,24 @@ export const formItems: FormItem[] = [
},
rules: [],
},
{
type: 'tree-select',
group: 'form',
type: 'input',
config: {
autoLink: true,
hasFeedback: false,
label: '物料类型',
name: 'type',
required: true,
extra: '',
help: '',
htmlFor: 'sgsg',
},
properties: {
size: 'default',
type: 'text',
allowClear: false,
bordered: true,
showCount: false,
placeholder: '请输入物料类型',
treeData: types,
placeholder: '请选择物料类型',
},
rules: [],
},
@@ -71,23 +74,35 @@ export const formItems: FormItem[] = [
rules: [],
},
{
type: 'select',
type: 'input-number',
group: 'form',
config: {
autoLink: true,
hasFeedback: false,
label: '赋码规则',
name: 'assignRule',
label: '价格',
name: 'price',
required: true,
},
properties: {
size: 'default',
controls: true,
placeholder: '请填写赋码规则',
options: [
{ label: '低值易耗品', value: 'LOW_VALUE' },
{ label: '高价值工具类', value: 'HIGH_VALUE' },
],
placeholder: '请填写价格',
},
rules: [],
},
{
type: 'switch',
group: 'form',
config: {
autoLink: true,
hasFeedback: false,
label: '是否赋码',
name: 'assignRule',
required: true,
},
properties: {
size: 'default',
},
rules: [],
},

View File

@@ -2,17 +2,29 @@
<page-container>
<!-- 页面操作栏 -->
<template #ops>
<a-row>
<a-col :span="18">
<a-row style="width: 600px">
<a-col :span="8">
<a-tree-select
v-model:value="typeVal"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择物料类型"
allow-clear
:tree-data="types"
tree-node-filter-prop="label"
/>
</a-col>
<a-col :span="8">
<a-input-search
v-model:value="searchKey"
:placeholder="`请输入物料名称`"
:placeholder="`名称/编码`"
allow-clear
enter-button
@search="loadData()"
></a-input-search>
</a-col>
<a-col :span="6">
<a-col :span="8">
<a-button type="primary" style="margin-left: 10px" @click="addOrEdit()">
<template #icon>
<icon-font type="icon-plus" />
@@ -36,7 +48,7 @@
<!-- 操作按钮列 -->
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'assignRule'">
{{ record.assignRule === 'HIGH_VALUE' ? '高价值工具类' : '低值易耗品' }}
{{ record.assignRule ? '' : '' }}
</template>
<template v-if="column.dataIndex === 'operation'">
<a-button type="link" :style="{ color: token.colorPrimary }" @click="addOrEdit(record)">
@@ -46,7 +58,12 @@
编辑
</a-button>
<a-button type="link" :style="{ color: token.colorPrimary }" @click="showPrintModal(record.id)">
<a-button
v-if="record.assignRule"
type="link"
:style="{ color: token.colorPrimary }"
@click="showPrintModal(record.id)"
>
<template #icon>
<icon-font type="icon-print" />
</template>
@@ -80,10 +97,10 @@
title="条形码打印"
width="100%"
wrap-class-name="full-modal"
@ok="openPrint = false"
@cancel="openPrint = false"
@ok="closePrint"
@cancel="closePrint"
>
<printCode :material-id="materialIdRef"></printCode>
<printCode ref="printcodeRef" :material-id="materialIdRef"></printCode>
</a-modal>
</template>
@@ -95,6 +112,7 @@
import { config, formItems } from './form'
import { FormDataType } from '@/components/form-render/form-render-types'
import printCode from './print-code.vue'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
const { useToken } = theme
const { token } = useToken()
@@ -103,12 +121,34 @@
const materialPage = ref<IPage<material.Material>>()
const loading = ref(false)
const materialIdRef = ref<number>()
const printcodeRef = ref()
const typeVal = ref<string>() // 物料类型选择值
//类型树
const types = ref<Array<TreeDataNode>>([])
api.materialApi.type.trees((data) => {
types.value = children(data)
})
const children = (res: Array<material.TypeTree>): Array<TreeDataNode> => {
return res.map((areaTree) => ({
label: areaTree.label,
value: areaTree.value,
children: areaTree.children ? children(areaTree.children) : [],
key: areaTree.value,
}))
}
// 加载数据的方法
const loadData = async (page = 1, size = 10) => {
loading.value = true
api.materialApi.material.materials(
{ page: page || materialPage.value?.current, size: size || materialPage.value?.size, key: searchKey.value },
{
page: page || materialPage.value?.current,
size: size || materialPage.value?.size,
key: searchKey.value,
type: typeVal.value,
},
(data) => {
loading.value = false
materialPage.value = data
@@ -120,30 +160,33 @@
//表格列
const columns = [
{
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{
title: '物料编码',
dataIndex: 'code',
},
{
title: '物料ID',
dataIndex: 'id',
},
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '价格',
dataIndex: 'price',
},
{
title: '物料类型',
dataIndex: 'type',
dataIndex: 'typeName',
},
{
title: '物料型号',
dataIndex: 'spec',
},
{
title: '赋码规则',
title: '是否赋码',
dataIndex: 'assignRule',
},
{
@@ -153,7 +196,6 @@
{
title: '操作',
dataIndex: 'operation',
width: 400,
},
]
@@ -179,7 +221,7 @@
})
//表单配置
const items = computed(() => {
return formItems
return formItems(types.value)
})
//弹窗标题
@@ -234,8 +276,21 @@
})
}
// 打开打印弹窗
const showPrintModal = (applyId: number) => {
openPrint.value = true
materialIdRef.value = applyId
}
// 关闭打印弹窗
const closePrint = () => {
openPrint.value = false
printcodeRef.value.clearContent()
}
</script>
<style lang="less">
.notshow {
display: none;
}
</style>

View File

@@ -24,11 +24,18 @@
// 接收父组件传递的条形码数量
materialId: {
type: Number,
default: 1,
default: 0,
},
})
const clearContent = () => {
inputRef.value = 0
const container = document.getElementById('barcodes-container')
if (container) container.innerHTML = ''
}
// 向父组件暴露清空条形码容器的函数
defineExpose({ clearContent })
const inputRef = ref(1)
const inputRef = ref(0)
watch(inputRef, (newVal) => {
fetchAndPrintBarcodes(newVal)
@@ -68,7 +75,7 @@
`,
onLoadingStart: () => console.log('开始加载'),
onLoadingEnd: () => console.log('加载完成'),
// onError: (err: Error) => console.error('打印出错:', err),
onError: (err: unknown) => console.error('打印出错:', err),
}
// 显示条形码容器以便打印
@@ -77,7 +84,6 @@
// 调用 printJS 进行打印
printJS(printOptions)
}
// 定义一个函数来从后端获取条形码字符串并打印
const fetchAndPrintBarcodes = (count: number) => {
if (count <= 0) return

View File

@@ -2,16 +2,31 @@
<page-container>
<!-- 页面操作栏 -->
<template #ops>
<a-row>
<a-col :span="18">
<a-row style="width: 600px">
<a-col :span="8">
<a-tree-select
v-model:value="typeVal"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择物料类型"
allow-clear
:tree-data="types"
tree-node-filter-prop="label"
/>
</a-col>
<a-col :span="8">
<a-input-search
v-model:value="searchKey"
:placeholder="`请输入`"
:placeholder="`名称/编码/型号`"
allow-clear
enter-button
@search="loadData()"
></a-input-search>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="downloadData()">导出</a-button>
</a-col>
</a-row>
</template>
<!-- 页面表格内容 -->
@@ -24,7 +39,13 @@
:pagination="pagination"
:loading="loading"
row-key="key"
></a-table>
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'applyTypeInfo'">
{{ record.applyTypeInfo.description }}
</template>
</template>
</a-table>
</div>
</page-container>
</template>
@@ -32,10 +53,13 @@
<script setup lang="ts">
import api from '@/api'
import { IPage } from '@/api/api'
import { http } from '@/settings/http'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
const searchKey = ref('')
const pagedata = ref<IPage<material.ApplyDTO>>()
const loading = ref(false)
const typeVal = ref<string>() // 物料类型选择值
// 加载数据的方法
const loadData = async (page = 1, size = 10) => {
@@ -45,9 +69,8 @@
page: page,
size: size,
applyType: 1,
type: searchKey.value,
code: searchKey.value,
name: searchKey.value,
type: typeVal.value,
key: searchKey.value,
},
(data) => {
pagedata.value = data
@@ -58,16 +81,56 @@
//初始加载
loadData()
//导出数据
const downloadData = () => {
// 创建 URLSearchParams 对象
const params = new URLSearchParams()
if (typeVal.value) {
params.append('type', typeVal.value)
}
if (searchKey.value) {
params.append('key', searchKey.value)
}
params.append('applyType', '1')
const url = `${http.prefix}/apply/download-excel?${params.toString()}`
window.open(url)
}
//表格列
const columns = [
{
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '物料类型',
dataIndex: 'typeName',
},
{
title: '物料编码',
dataIndex: 'code',
},
{
title: '物料型号',
dataIndex: 'spec',
},
{
title: '入库类型',
dataIndex: 'applyTypeInfo',
},
{
title: '申请数量',
dataIndex: 'applyNum',
},
{
title: '确认数量',
dataIndex: 'confirmNum',
},
{
title: '申请人',
dataIndex: 'applicant',
@@ -76,14 +139,6 @@
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '申请数量',
dataIndex: 'applyNum',
},
{
title: '确认数量',
dataIndex: 'confirmNum',
},
{
title: '备注',
dataIndex: 'remark',
@@ -101,4 +156,18 @@
},
}
})
// 物料类型树
const types = ref<Array<TreeDataNode>>([])
api.materialApi.type.trees((data) => {
types.value = children(data)
})
const children = (res: Array<material.TypeTree>): Array<TreeDataNode> => {
return res.map((areaTree) => ({
label: areaTree.label,
value: areaTree.value,
children: areaTree.children ? children(areaTree.children) : [],
key: areaTree.value,
}))
}
</script>

View File

@@ -2,16 +2,32 @@
<page-container>
<!-- 页面操作栏 -->
<template #ops>
<a-row>
<a-col :span="18">
<a-row style="width: 600px">
<a-col :span="8">
<a-tree-select
v-model:value="typeVal"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择物料类型"
allow-clear
:tree-data="types"
tree-node-filter-prop="label"
/>
</a-col>
<a-col :span="8">
<a-input-search
v-model:value="searchKey"
:placeholder="`请输入物料名称`"
placeholder="名称/编码/型号"
allow-clear
enter-button
width="100%"
@search="loadData()"
></a-input-search>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="downloadData()">导出</a-button>
</a-col>
</a-row>
</template>
<!-- 页面表格内容 -->
@@ -28,7 +44,7 @@
<!-- 操作按钮列 -->
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'assignRule'">
{{ record.assignRule === 1 ? '高价值工具类' : '低值易耗品' }}
{{ record.assignRule ? '' : '' }}
<!-- 这里定义为0和10再回显的时候不展示-->
</template>
</template>
@@ -40,16 +56,25 @@
<script setup lang="ts">
import api from '@/api'
import { IPage } from '@/api/api'
import { http } from '@/settings/http'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
const searchKey = ref('')
const materialPage = ref<IPage<material.Material>>()
const loading = ref(false)
const typeVal = ref<string>() // 物料类型选择值
// 加载数据的方法
const loadData = async (page = 1, size = 10) => {
loading.value = true
api.materialApi.material.materials(
{ page: page || materialPage.value?.current, size: size || materialPage.value?.size, key: searchKey.value },
{
page: page || materialPage.value?.current,
size: size || materialPage.value?.size,
key: searchKey.value,
type: typeVal.value,
},
(data) => {
loading.value = false
materialPage.value = data
@@ -59,28 +84,53 @@
//初始加载
loadData()
//导出数据
const downloadData = () => {
// 创建 URLSearchParams 对象
const params = new URLSearchParams()
if (typeVal.value) {
params.append('type', typeVal.value)
}
if (searchKey.value) {
params.append('key', searchKey.value)
}
const url = `${http.prefix}/material/download-excel?${params.toString()}`
window.open(url)
}
//表格列
const columns = [
{
title: '物料编码',
dataIndex: 'code',
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '物料编码',
dataIndex: 'code',
},
{
title: '物料类型',
dataIndex: 'type',
dataIndex: 'typeName',
},
{
title: '物料型号',
dataIndex: 'spec',
},
{
title: '赋码规则',
title: '是否赋码',
dataIndex: 'assignRule',
},
{
title: '价格',
dataIndex: 'price',
},
{
title: '库存数量',
dataIndex: 'stock',
@@ -103,4 +153,18 @@
},
}
})
// 物料类型树
const types = ref<Array<TreeDataNode>>([])
api.materialApi.type.trees((data) => {
types.value = children(data)
})
const children = (res: Array<material.TypeTree>): Array<TreeDataNode> => {
return res.map((areaTree) => ({
label: areaTree.label,
value: areaTree.value,
children: areaTree.children ? children(areaTree.children) : [],
key: areaTree.value,
}))
}
</script>

View File

@@ -2,16 +2,31 @@
<page-container>
<!-- 页面操作栏 -->
<template #ops>
<a-row>
<a-col :span="18">
<a-row style="width: 600px">
<a-col :span="8">
<a-tree-select
v-model:value="typeVal"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择物料类型"
allow-clear
:tree-data="types"
tree-node-filter-prop="label"
/>
</a-col>
<a-col :span="8">
<a-input-search
v-model:value="searchKey"
:placeholder="`请输入`"
:placeholder="`名称/编码/型号`"
allow-clear
enter-button
@search="loadData()"
></a-input-search>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="downloadData()">导出</a-button>
</a-col>
</a-row>
</template>
<!-- 页面表格内容 -->
@@ -24,19 +39,69 @@
:pagination="pagination"
:loading="loading"
row-key="key"
></a-table>
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'applyTypeInfo'">
{{ record.applyTypeInfo.description }}
</template>
<template v-if="column.dataIndex === 'reviewResult'">
<template v-if="record.reviewResult === 'PASS'"><span style="color: #52c41a">审核通过</span></template>
<template v-if="record.reviewResult === 'REJECT'"><span style="color: #f5222d">审核不通过</span></template>
</template>
<template v-if="column.dataIndex === 'operation'">
<a-button
v-if="record.applyType === 'SCRAP_OUT' && !record.confirm && record.reviewer === userStore.userName"
type="link"
style="margin-left: 10px"
@click="showResultModal(record.applyId)"
>
审核
</a-button>
</template>
</template>
</a-table>
</div>
</page-container>
<!-- 审核弹窗 -->
<a-modal v-model:open="openResult" title="报废出库数据:" width="80%" :confirm-loading="confirmLoading" @ok="submit">
<vxe-table border show-overflow max-height="500" size="medium" :data="reviewData">
<vxe-column type="seq" width="70" />
<vxe-column field="name" title="物料名称" />
<vxe-column field="typeName" title="类型" />
<vxe-column field="spec" title="规格" />
<vxe-column field="price" title="价格" />
<vxe-column field="applicant" title="申请人" />
<vxe-column field="confirmQuantity" title="报废数量" />
<vxe-column field="remark" title="报废原因" />
</vxe-table>
<a-form layout="vertical" style="margin-top: 20px">
<a-form-item label="审核意见">
<a-textarea v-model:value="remark" placeholder="请输入审核意见" :row="3" :maxlength="1000" />
</a-form-item>
<a-form-item label="审核结果">
<a-radio-group v-model:value="reviewResult" button-style="solid">
<a-radio-button value="PASS">审核通过</a-radio-button>
<a-radio-button value="REJECT" style="margin-left: 20px">审核不通过</a-radio-button>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import api from '@/api'
import { IPage } from '@/api/api'
import { useUserStore } from '@/stores/user'
import { http } from '@/settings/http'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
const searchKey = ref('')
const searchKey = ref()
const pagedata = ref<IPage<material.ApplyDTO>>()
const loading = ref(false)
const typeVal = ref<string>() // 物料类型选择值
const userStore = useUserStore()
// 加载数据的方法
const loadData = async (page = 1, size = 10) => {
loading.value = true
@@ -45,9 +110,8 @@
page: page,
size: size,
applyType: 3,
type: searchKey.value,
code: searchKey.value,
name: searchKey.value,
type: typeVal.value,
key: searchKey.value,
},
(data) => {
pagedata.value = data
@@ -58,24 +122,49 @@
//初始加载
loadData()
//导出数据
const downloadData = () => {
// 创建 URLSearchParams 对象
const params = new URLSearchParams()
if (typeVal.value) {
params.append('type', typeVal.value)
}
if (searchKey.value) {
params.append('key', searchKey.value)
}
params.append('applyType', '3')
const url = `${http.prefix}/apply/download-excel?${params.toString()}`
window.open(url)
}
//表格列
const columns = [
{
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '物料类型',
dataIndex: 'typeName',
},
{
title: '物料编码',
dataIndex: 'code',
},
{
title: '申请人',
dataIndex: 'applicant',
title: '物料型号',
dataIndex: 'spec',
},
{
title: '申请日期',
dataIndex: 'applyDate',
title: '出库类型',
dataIndex: 'applyTypeInfo',
},
{
title: '申请数量',
dataIndex: 'applyNum',
@@ -84,10 +173,30 @@
title: '确认数量',
dataIndex: 'confirmNum',
},
{
title: '申请人',
dataIndex: 'applicant',
},
{
title: '审核人',
dataIndex: 'reviewer',
},
{
title: '审核状态',
dataIndex: 'reviewResult',
},
{
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '备注',
dataIndex: 'remark',
},
{
title: '操作',
dataIndex: 'operation',
},
]
//分页信息
@@ -101,4 +210,50 @@
},
}
})
// 审核
const openResult = ref<boolean>(false)
const confirmLoading = ref(false)
const reviewResult = ref<'PASS' | 'REJECT'>('PASS')
const remark = ref('')
const reviewData = ref<Array<material.ScrapOutApplyDTO>>([])
const applyIdRef = ref(0)
//点击审核
const showResultModal = (applyId: number) => {
openResult.value = true
applyIdRef.value = applyId
api.materialApi.apply.getScrapOutById(applyId, (data) => {
reviewData.value = data
})
}
// 审核提交
const submit = () => {
confirmLoading.value = true
api.materialApi.apply.submitScrapOutReview(
{
applyId: applyIdRef.value.toString(),
reviewResult: reviewResult.value,
remark: remark.value,
},
() => {
confirmLoading.value = false
openResult.value = false
loadData()
},
)
}
// 物料类型树
const types = ref<Array<TreeDataNode>>([])
api.materialApi.type.trees((data) => {
types.value = children(data)
})
const children = (res: Array<material.TypeTree>): Array<TreeDataNode> => {
return res.map((areaTree) => ({
label: areaTree.label,
value: areaTree.value,
children: areaTree.children ? children(areaTree.children) : [],
key: areaTree.value,
}))
}
</script>

View File

@@ -3,16 +3,41 @@
<!-- 页面操作栏 -->
<template #ops>
<a-row>
<a-col :span="18">
<a-input-search
v-model:value="searchKey"
:placeholder="`请输入`"
allow-clear
enter-button
@search="loadData()"
></a-input-search>
<a-col :span="6" style="width: 280px">
<a-date-picker
v-model:value="startDate"
:format="'YYYY-MM-DD HH:mm:ss'"
placeholder="开始日期大于"
style="width: 100%"
/>
</a-col>
<a-col :span="6" style="width: 180px">
<a-select
v-model:value="status"
placeholder="盘点状态"
:max-tag-count="1"
mode="multiple"
style="width: 100%"
>
<a-select-option value="WAIT_SCAN">待扫码</a-select-option>
<a-select-option value="WAIT_SUBMIT">待提交</a-select-option>
<a-select-option value="WAIT_REVIEW">待审核</a-select-option>
<a-select-option value="PASS">审核通过</a-select-option>
<a-select-option value="REJECT">退回</a-select-option>
</a-select>
</a-col>
<a-col :span="6" style="width: 180px">
<a-select v-model:value="auditType" :allow-clear="true" placeholder="盘点类型" style="width: 100%">
<a-select-option value="SCAN">扫码盘点</a-select-option>
<a-select-option value="MANUAL">人工盘点</a-select-option>
</a-select>
</a-col>
<a-col :span="6" style="width: 180px">
<a-input-search v-model:value="taker" :placeholder="`盘点人`" allow-clear enter-button @search="loadData()" />
</a-col>
</a-row>
<a-button type="primary" @click="downloadData()">导出</a-button>
</template>
<!-- 页面表格内容 -->
<div style="min-height: calc(100vh - 305px)">
@@ -27,27 +52,32 @@
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'auditType'">
{{ record.auditType === 'ALL' ? '全部盘点' : '部分盘点' }}
{{ record.auditType === 'SCAN' ? '扫码盘点' : '人工盘点' }}
</template>
<template v-if="column.dataIndex === 'reviewResult'">
<template v-if="record.reviewResult === 'WAIT_SCAN'">待扫码</template>
<template v-if="record.reviewResult === 'WAIT_SUBMIT'">待提交</template>
<template v-if="record.reviewResult === 'WAIT_REVIEW'">待审核</template>
<template v-if="record.reviewResult === 'PASS'">审核通过</template>
<template v-if="record.reviewResult === 'REJECT'">退回</template>
<!-- 不同状态不同颜色 -->
<template v-if="record.reviewResult === 'WAIT_SCAN'"><span style="color: #faad14">待扫码</span></template>
<template v-if="record.reviewResult === 'WAIT_SUBMIT'"><span style="color: #1890ff">待提交</span></template>
<template v-if="record.reviewResult === 'WAIT_REVIEW'"><span style="color: #722ed1">审核</span></template>
<template v-if="record.reviewResult === 'PASS'"><span style="color: #52c41a">审核通过</span></template>
<template v-if="record.reviewResult === 'REJECT'"><span style="color: #f5222d">退回</span></template>
</template>
<template v-if="column.dataIndex === 'operation'">
<a-button
v-if="record.reviewResult === 'WAIT_REVIEW'"
type="link"
style="margin-left: 10px"
@click="showResultModal(record.id)"
@click="showResultModal(record.id, record.auditType, true)"
>
<template #icon>
<icon-font type="icon-plus" />
</template>
审核
</a-button>
<a-button
type="link"
style="margin-left: 10px"
@click="showResultModal(record.id, record.auditType, false)"
>
查看
</a-button>
</template>
</template>
</a-table>
@@ -56,20 +86,38 @@
<!-- 审核弹窗 -->
<a-modal v-model:open="openResult" title="盘点异常数据:" width="80%" :confirm-loading="confirmLoading" @ok="submit">
<result-form :apply-id="applyIdRef">
<a-form layout="vertical" style="margin-top: 20px">
<a-form-item label="审核意见">
<a-textarea v-model:value="remark" placeholder="请输入审核意见" :row="3" :maxlength="1000" />
</a-form-item>
<result-form v-if="auditTypeRef === 'SCAN'" :apply-id="applyIdRef" />
<vxe-table
v-if="auditTypeRef === 'MANUAL'"
v-model:data="manualData"
border
show-overflow
max-height="500"
size="medium"
>
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="id" title="物料id" :visible="false" />
<vxe-column field="name" title="物料名称" />
<vxe-column field="code" title="编码" />
<vxe-column field="spec" title="规格" />
<vxe-column field="price" title="价格" />
<vxe-column field="typeName" title="类型" />
<vxe-column field="stock" title="库存数量" />
<vxe-column field="manualStock" title="手动核实数量" />
<vxe-column field="msg" title="异常原因" />
</vxe-table>
<a-form v-if="flag" layout="vertical" style="margin-top: 20px">
<a-form-item label="审核意见">
<a-textarea v-model:value="remark" placeholder="请输入审核意见" :row="3" :maxlength="1000" />
</a-form-item>
<a-form-item label="审核结果">
<a-radio-group v-model:value="reviewResult" button-style="solid">
<a-radio-button value="PASS">审核通过</a-radio-button>
<a-radio-button value="REJECT" style="margin-left: 20px">退回重盘</a-radio-button>
</a-radio-group>
</a-form-item>
</a-form>
</result-form>
<a-form-item label="审核结果">
<a-radio-group v-model:value="reviewResult" button-style="solid">
<a-radio-button value="PASS">审核通过</a-radio-button>
<a-radio-button value="REJECT" style="margin-left: 20px">退回重盘</a-radio-button>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
</template>
@@ -77,14 +125,20 @@
import api from '@/api'
import { IPage } from '@/api/api'
import resultForm from '../stocktaking/result-form.vue'
import { Dayjs } from 'dayjs'
import { http } from '@/settings/http'
const applyIdRef = ref()
const searchKey = ref('')
const auditPage = ref<IPage<material.ApplyForm>>()
const loading = ref(false)
const confirmLoading = ref(false)
const reviewResult = ref<'PASS' | 'REJECT'>('PASS')
const remark = ref('')
// 搜索条件
const startDate = ref<Dayjs>()
const taker = ref('')
const status = ref<Array<'PASS' | 'REJECT' | 'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW'>>()
const auditType = ref<'SCAN' | 'MANUAL'>()
// 加载数据的方法
const loadData = async (page = 1, size = 10) => {
@@ -93,7 +147,13 @@
{
page: page,
size: size,
reviewResults: ['WAIT_SUBMIT', 'WAIT_SCAN', 'WAIT_REVIEW', 'PASS', 'REJECT'],
auditType: auditType.value,
//ISO 8601 的完整日期时间格式为YYYY-MM-DDTHH:mm:ss其中 T 是日期和时间之间的分隔符。
// 对于 LocalDateTime不需要时区信息即不包含 Z 或者 +/-HH:mm因为 LocalDateTime 本身没有时区概念。
// 获取 ISO 8601 格式的字符串,但需要去掉 'Z' 和时区部分
createDate: startDate.value?.toISOString().replace('Z', '').slice(0, -1),
taker: taker.value,
reviewResults: status.value,
},
(data) => {
auditPage.value = data
@@ -104,11 +164,42 @@
//初始加载
loadData()
//导出数据
const downloadData = () => {
// 创建 URLSearchParams 对象
const params = new URLSearchParams()
// 动态添加参数
if (auditType.value) {
params.append('auditType', auditType.value)
}
if (startDate.value) {
// 格式化日期并移除 'Z' 和毫秒部分
const formattedDate = startDate.value.toISOString().replace('Z', '').slice(0, -4)
params.append('createDate', formattedDate)
}
if (taker.value) {
params.append('taker', taker.value)
}
if (status.value && status.value.length > 0) {
// 为数组中的每个值单独添加参数
status.value.forEach((value) => {
params.append('reviewResults', value)
})
}
// 构建完整的 URL
const url = `${http.prefix}/apply/audit/download-excel?${params.toString()}`
// 打开下载链接
window.open(url)
}
//表格列
const columns = [
{
title: '申请id',
dataIndex: 'id',
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{
title: '盘点类型',
@@ -156,15 +247,38 @@
}
})
// 定义一个标记,用于控制是查看还是审核 flag=true审核flag=false查看
const flag = ref(false)
const openResult = ref<boolean>(false)
const showResultModal = (applyId: number) => {
openResult.value = true
const auditTypeRef = ref<'SCAN' | 'MANUAL'>()
// 点击 审核或者查看 按钮
const showResultModal = (applyId: number, auditType: 'SCAN' | 'MANUAL', select: boolean) => {
flag.value = select
auditTypeRef.value = auditType
applyIdRef.value = applyId
openResult.value = true
if (auditType === 'MANUAL') {
getManualData(applyId)
}
}
// 查询人工盘点数据
const manualData = ref<Array<material.ManualStockDTO>>()
const getManualData = (applyId: number) => {
api.materialApi.apply.getManualStock(applyId, (data) => {
manualData.value = data
})
}
//提交审核结果
const submit = () => {
confirmLoading.value = true
if (!flag.value) {
confirmLoading.value = false
openResult.value = false
return
}
api.materialApi.apply.submitReview(
{
applyId: applyIdRef.value,

View File

@@ -1,4 +1,5 @@
import { FormItem, FormConfig } from '@/components/form-render/form-render-types'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
export const config: FormConfig = {
layout: 'horizontal',
@@ -15,8 +16,9 @@ export const config: FormConfig = {
export const formItems = (
materialOptions: Array<{ value: string | undefined; label: string | undefined }>,
required: Ref<boolean>,
required: boolean,
persons: Array<{ value: string | undefined; label: string | undefined }>,
types: Array<TreeDataNode>,
): FormItem[] => [
{
group: 'form',
@@ -32,14 +34,30 @@ export const formItems = (
size: 'default',
optionType: 'button',
buttonStyle: 'solid',
defaultValue: 'ALL',
defaultValue: 'SCAN',
options: [
{ value: 'ALL', label: '全部盘点' },
{ value: 'PARTIAL', label: '部分盘点' },
{ value: 'SCAN', label: '扫码盘点' },
{ value: 'MANUAL', label: '人工盘点' },
],
},
rules: [],
},
{
type: 'tree-select',
group: 'form',
config: {
label: '物料类型选择',
name: 'types',
required: true,
},
properties: {
size: 'default',
placeholder: '请选择物料类型',
multiple: true,
treeData: types,
},
rules: [],
},
{
group: 'form',
type: 'select',
@@ -49,11 +67,12 @@ export const formItems = (
hasFeedback: false,
label: '物料选择',
name: 'ids',
required: true,
},
properties: {
size: 'default',
mode: 'multiple',
required: required.value,
required: required,
placeholder: '请选择物料',
options: materialOptions,
},

View File

@@ -17,7 +17,7 @@
<template #icon>
<icon-font type="icon-plus" />
</template>
申请扫码
新增盘点
</a-button>
</a-col>
</a-row>
@@ -35,28 +35,28 @@
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'auditType'">
{{ record.auditType === 'ALL' ? '全部盘点' : '部分盘点' }}
{{ record.auditType === 'SCAN' ? '扫码盘点' : '人工盘点' }}
</template>
<template v-if="column.dataIndex === 'reviewResult'">
<template v-if="record.reviewResult === 'WAIT_SCAN'">待扫码</template>
<template v-if="record.reviewResult === 'WAIT_SUBMIT'">待提交</template>
<template v-if="record.reviewResult === 'WAIT_REVIEW'">待审核</template>
<template v-if="record.reviewResult === 'PASS'">审核通过</template>
<template v-if="record.reviewResult === 'REJECT'">退回</template>
<template v-if="record.reviewResult === 'WAIT_SCAN'"><span style="color: #faad14">待盘点</span></template>
<template v-if="record.reviewResult === 'WAIT_SUBMIT'"><span style="color: #1890ff">待提交</span></template>
<template v-if="record.reviewResult === 'WAIT_REVIEW'"><span style="color: #722ed1">待审核</span></template>
<template v-if="record.reviewResult === 'PASS'"><span style="color: #52c41a">审核通过</span></template>
<template v-if="record.reviewResult === 'REJECT'"><span style="color: #f5222d">退回</span></template>
</template>
<template v-if="column.dataIndex === 'operation'">
<a-button
v-if="record.reviewResult === 'WAIT_SCAN' || record.reviewResult === 'REJECT'"
type="link"
@click="showModal(record.id)"
@click="showModal(record.id, record.auditType)"
>
<template #icon>
<icon-font type="icon-edit" />
</template>
开始扫码
开始盘点
</a-button>
<a-button
v-if="record.reviewResult === 'WAIT_SUBMIT' || record.reviewResult === 'WAIT_SCAN'"
v-if="record.reviewResult === 'WAIT_SUBMIT'"
type="link"
style="margin-left: 10px"
@click="showResultModal(record.id)"
@@ -78,7 +78,6 @@
v-model="applyForm"
:form-items="items"
:config="formConfig"
:disabled-fields="disabledFields"
title="扫码盘点"
@ok="doSave"
/>
@@ -109,6 +108,10 @@
>
<result-form ref="handleResultRef" :apply-id="applyIdRef"></result-form>
</a-modal>
<!-- 手动盘点弹窗 -->
<a-modal v-model:open="openManual" title="手动盘点" width="80%" :confirm-loading="confirmLoading" @ok="manualHandle">
<manual-form ref="manualFormRef" :apply-id="applyIdRef"></manual-form>
</a-modal>
</template>
<script setup lang="ts">
@@ -121,20 +124,24 @@
import { useUserStore } from '@/stores/user'
import scanForm from '../component/scan-form.vue'
import resultForm from './result-form.vue'
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface'
import manualForm from '../component/manual-form.vue'
const searchKey = ref('')
const userStore = useUserStore()
const scanFormRef = ref() // 盘点作业弹窗组件
const handleResultRef = ref() // 盘点结果弹窗组件
const confirmLoading = ref(false) // 盘点作业弹窗提交按钮loading
const manualFormRef = ref() // 手动盘点弹窗组件
// 新增申请表单对象
const applyForm = ref<Partial<material.AuditApplyInfo>>({
auditType: 'ALL',
applicant: userStore.userName,
auditType: 'SCAN',
applicant: userStore.fullName,
type: 'AUDIT',
applyDate: dayjs() + '',
confirm: false,
reviewResult: 'WAIT_SCAN',
types: [],
})
const msg = ref()
const totalValue = ref(0) // 盘点总值
@@ -146,28 +153,55 @@
//新增申请的下拉框选择对象
const materialList = ref<Array<{ value: string | undefined; label: string | undefined }>>([])
const personList = ref<Array<{ value: string | undefined; label: string | undefined }>>([])
const required = ref(false)
api.materialApi.material.all((data) => {
materialList.value = data.map((item) => {
return {
value: item?.id + '',
label: item?.name,
}
})
})
const required = ref(true)
api.aclApi.user.all((data) => {
personList.value = data.map((item) => {
return {
value: item?.name,
value: item?.fullName,
label: item?.fullName,
}
})
})
//是否必填
const disabledFields = computed(() => {
return applyForm.value?.auditType === 'ALL' ? ['materials'] : []
// //是否必填
// const disabledFields = computed(() => {
// return applyForm.value?.auditType === 'ALL' ? ['ids'] : []
// })
// 监听选择类型后查询物料
watch(
applyForm,
() => {
if (applyForm.value && applyForm.value.types && applyForm.value.types.length > 0) {
api.materialApi.material.all({ type: applyForm.value.types.join(',') }, (data) => {
materialList.value = data.map((item) => {
return {
value: item?.id + '',
label: item?.name + '(' + item.spec + ')',
}
})
})
}
},
{
immediate: true,
deep: true,
},
)
// 物料类型树
const types = ref<Array<TreeDataNode>>([])
api.materialApi.type.trees((data) => {
types.value = children(data)
})
const children = (res: Array<material.TypeTree>): Array<TreeDataNode> => {
return res.map((areaTree) => ({
label: areaTree.label,
value: areaTree.value,
children: areaTree.children ? children(areaTree.children) : [],
key: areaTree.value,
}))
}
//抽屉组件
const formDrawer = ref<typeof FormDrawer>()
@@ -176,7 +210,8 @@
})
//表单配置
const items = computed(() => {
return formItems(materialList.value, required, personList.value)
//required 参数未生效
return formItems(materialList.value, required.value, personList.value, types.value)
})
// 加载数据的方法
const loadData = async (page = 1, size = 10) => {
@@ -199,8 +234,9 @@
//表格列
const columns = [
{
title: '申请ID',
dataIndex: 'id',
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{
title: '盘点类型',
@@ -218,6 +254,10 @@
title: '任务状态',
dataIndex: 'reviewResult',
},
{
title: '审核意见',
dataIndex: 'reviewRemark',
},
{
title: '盘点审核人',
dataIndex: 'reviewer',
@@ -230,7 +270,6 @@
{
title: '操作',
dataIndex: 'operation',
width: 400,
},
]
@@ -263,7 +302,33 @@
// 盘点作业弹窗相关
const open = ref<boolean>(false)
const applyIdRef = ref()
const showModal = (applyId: number) => {
const showModal = (applyId: number, auditType: string) => {
if (auditType === 'SCAN') {
showScanModal(applyId)
} else {
showManualModal(applyId)
}
}
// 人工盘点
const openManual = ref<boolean>(false)
const showManualModal = (applyId: number) => {
applyIdRef.value = applyId
openManual.value = true
}
const manualHandle = () => {
//提交数据
if (manualFormRef.value) {
const res: Array<material.ManualStockDetail> = manualFormRef.value.getTableData()
api.materialApi.apply.submitManualStock(res, () => {
openManual.value = false
loadData(1)
})
}
}
// 扫码弹窗
const showScanModal = (applyId: number) => {
open.value = true
applyIdRef.value = applyId
let m = '需要扫码的物料: '
@@ -273,9 +338,9 @@
data.forEach((item) => (totalValue.value += item.count || 0))
msg.value = m
})
window.console.log(m)
msg.value = m
}
// 盘点作业提交数据
//关闭弹窗 提交数据

View File

@@ -0,0 +1,101 @@
import { Codebook } from '@/api/api'
import { FormItem, FormConfig } from '@/components/form-render/form-render-types'
export const config: FormConfig = {
layout: 'horizontal',
colon: true,
hideRequiredMark: false,
labelAlign: 'right',
scrollToFirstError: false,
validateOnRuleChange: true,
labelCol: {
span: 4,
offset: 0,
},
}
export const formItems = (levels: Codebook[]): FormItem[] => [
{
group: 'form',
type: 'input',
config: {
autoLink: true,
hasFeedback: false,
label: '类型编码',
name: 'no',
required: true,
help: '格式一级编码两位组成00-99取值只要保证唯一即可二级编码四位组成,父级的两位+00-99取值只要保证唯一即可',
},
properties: {
size: 'default',
type: 'text',
allowClear: true,
bordered: true,
showCount: true,
},
rules: [],
},
{
group: 'form',
type: 'input',
config: {
autoLink: true,
hasFeedback: false,
label: '上级编码',
name: 'parentNo',
},
properties: {
size: 'default',
type: 'text',
allowClear: true,
bordered: true,
showCount: true,
},
rules: [],
},
{
type: 'select',
group: 'form',
config: {
autoLink: true,
hasFeedback: false,
label: '类型级别',
name: 'level',
required: true,
},
properties: {
size: 'default',
allowClear: false,
autoClearSearchValue: true,
autofocus: false,
bordered: true,
defaultActiveFirstOption: true,
dropdownMatchSelectWidth: true,
labelInValue: false,
showSearch: false,
options: levels.map((item) => {
return { value: item.name, label: item.description }
}),
},
rules: [],
},
{
group: 'form',
type: 'input',
config: {
autoLink: true,
hasFeedback: false,
label: '类型名称',
name: 'name',
required: true,
},
properties: {
size: 'default',
type: 'text',
allowClear: true,
bordered: true,
showCount: true,
},
rules: [],
},
]

View File

@@ -0,0 +1,179 @@
<template>
<page-container sub-title="物料类型管理">
<template #ops>
<a-row>
<a-col :span="16">
<a-input-search
v-model:value="searchKey"
placeholder="搜索物料"
enter-button
@search="loadData()"
></a-input-search>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="addOrEdit()">
<template #icon>
<icon-font type="icon-plus"></icon-font>
</template>
添加
</a-button>
</a-col>
</a-row>
</template>
<div style="min-height: calc(100vh - 218px)">
<vxe-table
ref="xTree"
border
show-overflow
size="medium"
:loading="loading"
height="600"
:column-config="{ resizable: true }"
:scroll-y="{ enabled: true, gt: 0 }"
:expand-config="{
expandAll: true,
}"
:tree-config="{
iconOpen: 'vxe-icon-square-minus',
iconClose: 'vxe-icon-square-plus',
transform: true,
rowField: 'no',
parentField: 'parentNo',
expandAll: true,
line: true,
}"
:data="areas"
>
<vxe-column field="name" title="类型" tree-node></vxe-column>
<vxe-column field="no" title="类型编码"></vxe-column>
<vxe-column field="levelInfo.description" title="类型级别"></vxe-column>
<vxe-column field="parentNo" title="上级类型编码"></vxe-column>
<vxe-column field="ops" title="操作" width="350">
<template #default="{ row }">
<a-button type="link" @click="addOrEdit({ parentNo: row.no })">
<template #icon>
<icon-font type="icon-children" />
</template>
添加子级
</a-button>
<a-button type="link" style="margin-left: 10px" @click="addOrEdit(row)">
<template #icon>
<icon-font type="icon-edit" />
</template>
编辑
</a-button>
<a-popconfirm :title="$t('确定要删除地区', { name: row.name })" @confirm="deleteArea(row.id)">
<a-button type="link" danger style="margin-left: 10px">
<template #icon>
<icon-font type="icon-delete" />
</template>
删除
</a-button>
</a-popconfirm>
</template>
</vxe-column>
</vxe-table>
</div>
</page-container>
<form-drawer
ref="formDrawer"
v-model="area"
:form-items="items"
:config="formConfig"
:title="modalTitle"
:disabled-fields="disabledFields"
:hidden-fields="hiddenFields"
@ok="doSave"
/>
</template>
<script lang="ts" setup>
import api from '@/api'
import { config, formItems } from './form'
import FormDrawer from '@/components/form-render/form-drawer.vue'
import { notification } from 'ant-design-vue'
import { Codebook } from '@/api/api'
import { FormDataType } from '@/components/form-render/form-render-types'
const areas = ref<Array<material.Type>>([])
const loading = ref(false)
const searchKey = ref('')
const area = ref<Partial<material.Type>>({})
const levels = ref<Codebook[]>([])
api.materialApi.type.levels((data) => {
levels.value = data
})
//表单
const formDrawer = ref<typeof FormDrawer>()
const formConfig = computed(() => {
return config
})
//表单配置
const items = computed(() => {
return formItems(levels.value)
})
//表单不可用字段
const disabledFields = computed(() => {
const no = area.value && area.value.id ? 'no' : ''
const parentNo = area.value && area.value.parentNo ? 'parentNo' : ''
return [no, parentNo]
})
//表单隐藏字段
const hiddenFields = computed(() => {
return area.value && area.value.parentNo ? [] : ['parentNo']
})
//弹窗标题
const modalTitle = computed(() => {
return area.value.id ? '编辑物料类型' : '添加物料类型'
})
// 加载数据
const loadData = () => {
loading.value = true
api.materialApi.type.areas(
{
key: searchKey.value,
},
(data) => {
areas.value = data
loading.value = false
},
)
}
loadData()
// 新增或编辑
const addOrEdit = (u?: Record<string, unknown>) => {
area.value = { ...u }
formDrawer.value?.show()
}
// 抽屉保存
const doSave = (_data: FormDataType) => {
api.materialApi.type.addAndModify(_data as unknown as material.Type, () => {
formDrawer.value?.close()
notification.success({
message: '操作成功',
description: '新增保存成功,页面将自动刷新!',
onClose: () => {
loadData()
},
})
})
}
// 删除
const deleteArea = (id: number) => {
api.materialApi.type.deleteArea(id, () => {
notification.success({
message: '操作成功',
description: '删除成功,页面将自动刷新!',
onClose: () => {
loadData()
},
})
})
}
</script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -59,7 +59,7 @@ export default defineConfig(({ mode }) => {
open: true,
proxy: {
'/api': {
// target: 'https://iot.riemann.tech/api/',
// target: 'https://ims.riemann.tech/api/',
target: 'http://127.0.0.1:8888',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),

File diff suppressed because one or more lines are too long