Compare commits

..

No commits in common. "develop" and "master" have entirely different histories.

91 changed files with 8354 additions and 11654 deletions

View File

@ -419,12 +419,7 @@ module.exports = {
breaklineNumber: 100, breaklineNumber: 100,
breaklineChar: '|', breaklineChar: '|',
skipQuestions: [], skipQuestions: [],
issuePrefixes: [ issuePrefixes: [{ value: 'closed', name: 'closed: ISSUES has been processed' }],
{
value: 'closed',
name: 'closed: ISSUES has been processed',
},
],
customIssuePrefixAlign: 'top', customIssuePrefixAlign: 'top',
emptyIssuePrefixAlias: 'skip', emptyIssuePrefixAlias: 'skip',
customIssuePrefixAlias: 'custom', customIssuePrefixAlias: 'custom',

1
.env
View File

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

View File

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

View File

@ -34,7 +34,7 @@ jobs:
SOURCE: dist/ SOURCE: dist/
REMOTE_HOST: ${{ vars.NGINX_SERVER }} REMOTE_HOST: ${{ vars.NGINX_SERVER }}
REMOTE_USER: ${{ vars.NGINX_SERVER_USER }} REMOTE_USER: ${{ vars.NGINX_SERVER_USER }}
TARGET: ${{ vars.WWW_ROOT }}${{ vars.IMS_DOMAIN }} TARGET: ${{ vars.WWW_ROOT }}${{ vars.DOMAIN }}
EXCLUDE: "" EXCLUDE: ""
SCRIPT_BEFORE: ${{ vars.BAKUP_SCRIPT }} ${{ vars.IMS_DOMAIN }} SCRIPT_BEFORE: ${{ vars.BAKUP_SCRIPT }} ${{ vars.IMS_DOMAIN }}
SCRIPT_AFTER: echo $RSYNC_STDOUT SCRIPT_AFTER: echo $RSYNC_STDOUT

BIN
dist.zip

Binary file not shown.

View File

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

View File

@ -35,10 +35,8 @@
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"exceljs": "^4.4.0", "exceljs": "^4.4.0",
"jsbarcode": "^3.11.6",
"pinia": "^2.2.4", "pinia": "^2.2.4",
"pinia-plugin-persistedstate": "^4.1.1", "pinia-plugin-persistedstate": "^4.1.1",
"print-js": "^1.6.0",
"sortablejs": "^1.15.3", "sortablejs": "^1.15.3",
"vue": "^3.5.11", "vue": "^3.5.11",
"vue-codemirror": "^6.1.1", "vue-codemirror": "^6.1.1",
@ -54,7 +52,6 @@
"@commitlint/cli": "^19.5.0", "@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0", "@commitlint/config-conventional": "^19.5.0",
"@eslint/js": "^9.12.0", "@eslint/js": "^9.12.0",
"@types/jsbarcode": "^3.11.4",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@vitejs/plugin-vue": "^5.1.4", "@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1", "@vitejs/plugin-vue-jsx": "^4.0.1",

26
pnpm-lock.yaml generated
View File

@ -65,18 +65,12 @@ importers:
exceljs: exceljs:
specifier: ^4.4.0 specifier: ^4.4.0
version: 4.4.0 version: 4.4.0
jsbarcode:
specifier: ^3.11.6
version: 3.11.6
pinia: pinia:
specifier: ^2.2.4 specifier: ^2.2.4
version: 2.2.4(typescript@5.6.3)(vue@3.5.11(typescript@5.6.3)) version: 2.2.4(typescript@5.6.3)(vue@3.5.11(typescript@5.6.3))
pinia-plugin-persistedstate: pinia-plugin-persistedstate:
specifier: ^4.1.1 specifier: ^4.1.1
version: 4.1.1(pinia@2.2.4(typescript@5.6.3)(vue@3.5.11(typescript@5.6.3)))(rollup@4.21.2)(webpack-sources@3.2.3) version: 4.1.1(pinia@2.2.4(typescript@5.6.3)(vue@3.5.11(typescript@5.6.3)))(rollup@4.21.2)(webpack-sources@3.2.3)
print-js:
specifier: ^1.6.0
version: 1.6.0
sortablejs: sortablejs:
specifier: ^1.15.3 specifier: ^1.15.3
version: 1.15.3 version: 1.15.3
@ -117,9 +111,6 @@ importers:
'@eslint/js': '@eslint/js':
specifier: ^9.12.0 specifier: ^9.12.0
version: 9.12.0 version: 9.12.0
'@types/jsbarcode':
specifier: ^3.11.4
version: 3.11.4
'@types/sortablejs': '@types/sortablejs':
specifier: ^1.15.8 specifier: ^1.15.8
version: 1.15.8 version: 1.15.8
@ -976,9 +967,6 @@ packages:
'@types/http-errors@1.8.2': '@types/http-errors@1.8.2':
resolution: {integrity: sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==} resolution: {integrity: sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==}
'@types/jsbarcode@3.11.4':
resolution: {integrity: sha512-VBcpTAnEMH0Gbh8JpV14CgOtJjCYjsvR2FoDRyoYPE0gUxtApf8N4c+HKEOyz/iiIZkMzqrzBA3XX7+KgKxxsA==}
'@types/json-schema@7.0.15': '@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@ -2395,9 +2383,6 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true hasBin: true
jsbarcode@3.11.6:
resolution: {integrity: sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA==}
jsbn@0.1.1: jsbn@0.1.1:
resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
@ -3003,9 +2988,6 @@ packages:
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
print-js@1.6.0:
resolution: {integrity: sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==}
process-nextick-args@2.0.1: process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
@ -4776,10 +4758,6 @@ snapshots:
'@types/http-errors@1.8.2': {} '@types/http-errors@1.8.2': {}
'@types/jsbarcode@3.11.4':
dependencies:
'@types/node': 22.5.3
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
'@types/node@12.20.55': {} '@types/node@12.20.55': {}
@ -6406,8 +6384,6 @@ snapshots:
dependencies: dependencies:
argparse: 2.0.1 argparse: 2.0.1
jsbarcode@3.11.6: {}
jsbn@0.1.1: {} jsbn@0.1.1: {}
jsesc@2.5.2: {} jsesc@2.5.2: {}
@ -6966,8 +6942,6 @@ snapshots:
prettier@3.3.3: {} prettier@3.3.3: {}
print-js@1.6.0: {}
process-nextick-args@2.0.1: {} process-nextick-args@2.0.1: {}
proxy-from-env@1.1.0: {} proxy-from-env@1.1.0: {}

View File

@ -5,27 +5,30 @@
"origins": [ "origins": [
{ {
"name": "auth", "name": "auth",
"originUrl": "http://127.0.0.1:8888/v3/api-docs/auth", "originUrl": "http://127.0.0.1:7777/v3/api-docs/auth",
"originType": "SwaggerV3", "originType": "SwaggerV3",
"usingOperationId": true "usingOperationId": true
}, },
{ {
"name": "acl", "name": "acl",
"originUrl": "http://127.0.0.1:8888/v3/api-docs/acl", "originUrl": "http://127.0.0.1:7777/v3/api-docs/acl",
"originType": "SwaggerV3", "originType": "SwaggerV3",
"usingOperationId": true "usingOperationId": true
}, },
{ {
"name": "dictionary", "name": "dictionary",
"originUrl": "http://127.0.0.1:8888/v3/api-docs/dictionary", "originUrl": "http://127.0.0.1:7777/v3/api-docs/dictionary",
"originType": "SwaggerV3", "originType": "SwaggerV3",
"usingOperationId": true "usingOperationId": true
}, },
{ {
"name": "material", "name": "material",
"originUrl": "http://127.0.0.1:8888/v3/api-docs/material", "originUrl": "http://127.0.0.1:7777/v3/api-docs/material",
"originType": "SwaggerV3", "originType": "SwaggerV3",
"usingOperationId": true "usingOperationId": true
} }
] ]
} }

View File

@ -447,6 +447,7 @@ export default class MyGenerator extends CodeGenerator {
const hasPagination = inter.response.typeName.indexOf('Pagination') >= 0 const hasPagination = inter.response.typeName.indexOf('Pagination') >= 0
const hasCodebook = inter.response.typeArgs[0] && inter.response.typeArgs[0].typeName.indexOf('Codebook') >= 0 const hasCodebook = inter.response.typeArgs[0] && inter.response.typeArgs[0].typeName.indexOf('Codebook') >= 0
const hasVxe = !!inter.parameters.find((p) => p.dataType.typeName.indexOf('VXETableSaveDTO') >= 0) const hasVxe = !!inter.parameters.find((p) => p.dataType.typeName.indexOf('VXETableSaveDTO') >= 0)
// const defTypes = this.getAllRefTypeNames(inter);
const imports = [ const imports = [
hasResult ? 'Result' : null, hasResult ? 'Result' : null,
hasIPage ? 'IPage' : null, hasIPage ? 'IPage' : null,

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

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

View File

@ -1,26 +0,0 @@
/**
* @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,32 +2,30 @@
* @description * @description
* *
*/ */
import saveOrUpdateUser from './saveOrUpdateUser' import saveOrUpdateUser from './saveOrUpdateUser';
import all from './all' import all from './all';
import existName from './existName' import sexes from './sexes';
import sexes from './sexes' import detail from './detail';
import detail from './detail' import deleteUser from './deleteUser';
import deleteUser from './deleteUser' import resetPassword from './resetPassword';
import resetPassword from './resetPassword' import permissionInfos from './permissionInfos';
import permissionInfos from './permissionInfos' import permissions from './permissions';
import permissions from './permissions' import grant from './grant';
import grant from './grant' import roleInfos from './roleInfos';
import roleInfos from './roleInfos' import grantRole from './grantRole';
import grantRole from './grantRole' import users from './users';
import users from './users'
export default { export default {
saveOrUpdateUser, saveOrUpdateUser,
all, all,
existName, sexes,
sexes, detail,
detail, deleteUser,
deleteUser, resetPassword,
resetPassword, permissionInfos,
permissionInfos, permissions,
permissions, grant,
grant, roleInfos,
roleInfos, grantRole,
grantRole, users,
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 { declare namespace auth {
/** /**
* AuthUser * AuthUser
*/ */
export interface AuthUser { export interface AuthUser {
/** anonymous */ /** anonymous */
anonymous?: boolean anonymous?: boolean;
/** 用户头像 */ /** 用户头像 */
avatar?: string avatar?: string;
/** 邮箱 */ /** 邮箱 */
email?: string email?: string;
/** extInfo */ /** extInfo */
extInfo?: auth.NutMap extInfo?: auth.NutMap;
/** 姓名 */ /** 姓名 */
fullName: string fullName: string;
/** 电话 */ /** 电话 */
mobile?: string mobile?: string;
/** 密码 */ /** 密码 */
password: string password: string;
/** 权限列表 */ /** 权限列表 */
permissions: Array<string> permissions: Array<string>;
/** jwt refreshToken */ /** jwt refreshToken */
refreshToken: string refreshToken: string;
/** 角色列表 */ /** 角色列表 */
roles: Array<string> roles: Array<string>;
/** 性别 */ /** 性别 */
sex?: 'MALE' | 'FEMALE' sex?: 'MALE' | 'FEMALE';
/** sexInfo */ /** sexInfo */
sexInfo?: auth.Codebook sexInfo?: auth.Codebook;
/** jwt Token */ /** jwt Token */
token: string token: string;
/** 用户名 */ /** 用户名 */
userName: string userName: string;
} }
/** /**
* *
*/ */
export interface LoginDTO { export interface LoginDTO {
/** 验证码 */ /** 验证码 */
captcha?: string captcha?: string;
/** 手机号 */ /** 手机号 */
mobile?: string mobile?: string;
/** 密码 */ /** 密码 */
password?: string password?: string;
/** 登录类型 */ /** 登录类型 */
type: 'ACCOUNT' | 'WECHAT' type: 'ACCOUNT' | 'WECHAT';
/** 用户名 */ /** 用户名 */
userName?: string userName?: string;
/** uuid */ /** uuid */
uuid?: string uuid?: string;
} }
} }

View File

@ -1,18 +1,18 @@
/** /**
* @desc * @desc
*/ */
import { defaultSuccess, defaultError, http } from '@/plugins/axios' import { defaultSuccess, defaultError, http } from '@/plugins/axios';
import type { AxiosResponse } from 'axios' import type { AxiosResponse } from 'axios';
export default async function ( export default async function (
success: (data: auth.AuthUser) => void = defaultSuccess, success: (data: auth.AuthUser) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError, fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> { ): Promise<void> {
return http({ return http({
method: 'get', method: 'get',
url: `/auth/current-user`, url: `/auth/current-user`,
}) })
.then((data: AxiosResponse<auth.AuthUser, unknown>) => { .then((data: AxiosResponse<auth.AuthUser, unknown>) => {
success(data.data) success(data.data);
}) })
.catch((error: { code: string; error?: string }) => fail(error)) .catch((error: { code: string; error?: string }) => fail(error));
} }

View File

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

View File

@ -1,517 +1,345 @@
declare namespace material { declare namespace material {
/** /**
* ApplyDTO * ApplyDTO
*/ */
export interface ApplyDTO { export interface ApplyDTO {
/** 申请人 */ /** 申请人 */
applicant?: string applicant?: string;
/** 申请日期 */ /** 申请日期 */
applyDate?: string applyDate?: string;
/** 申请id */ /** 申请数量 */
applyId?: number applyNum?: string;
/** 申请数量 */ /** 申请类型 */
applyNum?: string applyType?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT';
/** 申请类型 */ /** 物料编码 */
applyType?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT' | 'SCRAP_OUT' code?: string;
/** applyTypeInfo */ /** 确认数量 */
applyTypeInfo?: material.Codebook confirmNum?: string;
/** 物料编码 */ /** 物料名称 */
code?: string name?: string;
/** 是否确认 */ /** 备注 */
confirm?: boolean remark?: string;
/** 确认数量 */ /** 物料规格 */
confirmNum?: string spec?: string;
/** 物料名称 */ /** 物料类型 */
name?: string type?: string;
}
/** 备注 */ /**
remark?: string *
*/
export interface ApplyDetail {
/** 申请单ID */
applyId?: number;
/** 审核状态 */ /** assignRule */
reviewResult?: 'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT' assignRule?: 'LOW_VALUE' | 'HIGH_VALUE';
/** 审核人 */ /** 确认数量 */
reviewer?: string confirmQuantity?: number;
/** 物料规格 */ /** createdTime */
spec?: string createdTime?: string;
/** 物料类型 */ /** 异常情况说明 */
type?: string exceptionRemark?: string;
/** 类型名称 */ /** id */
typeName?: string id?: number;
}
/** /** 物料Id */
* materialId?: number;
*/
export interface ApplyDetail {
/** 申请单ID */
applyId?: number
/** 是否需要赋码 */ /** 申请数量 */
assignRule?: boolean quantity?: number;
/** 确认数量 */ /** updatedTime */
confirmQuantity?: number updatedTime?: string;
}
/** createdTime */ /**
createdTime?: string * //
*/
export interface ApplyForm {
/** 申请人 */
applicant?: string;
/** 异常情况说明 */ /** 申请日期 */
exceptionRemark?: string applyDate?: string;
/** id */ /** 盘点类型1: 全盘 2: 部分盘点) */
id?: number auditType?: 'ALL' | 'PARTIAL';
/** 物料Id */ /** createdTime */
materialId?: number createdTime?: string;
/** 申请数量 */ /** 异常原因 */
quantity?: number exception?: string;
/** updatedTime */ /** 处理摘要 */
updatedTime?: string handle?: string;
}
/** /** id */
* // id?: number;
*/
export interface ApplyForm {
/** 申请人 */
applicant?: string
/** 申请日期 */ /** 是否确认0: 未确认 1: 已确认) */
applyDate?: string isConfirm?: boolean;
/** 盘点类型1: 扫码 2: 人工) */ /** 结果(系统自动生成) */
auditType?: 'SCAN' | 'MANUAL' result?: string;
/** 是否确认0: 未确认 1: 已确认) */ /** 审核结果(1-待扫码 2-待提交 3-待审核 4-审核通过 5退回) */
confirm?: boolean reviewResult?:
| 'WAIT_SCAN'
| 'WAIT_SUBMIT'
| 'WAIT_REVIEW'
| 'PASS'
| 'REJECT';
/** createdTime */ /** 盘点审核人 */
createdTime?: string reviewer?: string;
/** id */ /** 盘点人 */
id?: number taker?: string;
/** 审核意见 */ /** 类型1: 采购入库申请 2: 归还入库申请 3: 出库外借申请 4: 盘点申请) */
reviewRemark?: string type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT';
/** 审核结果(1-待扫码 2-待提交 3-待审核 4-审核通过 5退回) */ /** updatedTime */
reviewResult?: 'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT' updatedTime?: string;
}
/** 盘点审核人 */ /**
reviewer?: string *
*/
export interface ApplyInfo {
/** 申请单明细 */
applyDetails?: Array<material.ApplyDetail>;
/** 盘点人 */ /** applyForm */
taker?: string applyForm?: material.ApplyForm;
/** 类型1: 采购入库申请 2: 归还入库申请 3: 出库外借申请 4: 盘点申请) */ /** 扫码的条码集合 */
type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT' | 'SCRAP_OUT' detailList?: Array<material.MaterialStockDetail>;
}
/** updatedTime */ /**
updatedTime?: string *
} */
export interface AuditApplyInfo {
/** 申请人 */
applicant?: string;
/** /** 申请日期 */
* applyDate?: string;
*/
export interface ApplyInfo {
/** 申请单明细 */
applyDetails?: Array<material.ApplyDetail>
/** applyForm */ /** 盘点类型1: 全盘 2: 部分盘点) */
applyForm?: material.ApplyForm auditType?: 'ALL' | 'PARTIAL';
/** 扫码的条码集合 */ /** createdTime */
detailList?: Array<material.MaterialStockDetail> createdTime?: string;
}
/** /** 异常原因 */
* exception?: string;
*/
export interface AuditApplyInfo {
/** 申请人 */
applicant?: string
/** 申请日期 */ /** 处理摘要 */
applyDate?: string handle?: string;
/** 盘点类型1: 扫码 2: 人工) */ /** id */
auditType?: 'SCAN' | 'MANUAL' id?: number;
/** 是否确认0: 未确认 1: 已确认) */ /** 物料ids */
confirm?: boolean ids?: Array<string>;
/** createdTime */ /** 是否确认0: 未确认 1: 已确认) */
createdTime?: string isConfirm?: boolean;
/** id */ /** 结果(系统自动生成) */
id?: number result?: string;
/** 物料ids */ /** 审核结果(1-待扫码 2-待提交 3-待审核 4-审核通过 5退回) */
ids?: Array<string> reviewResult?:
| 'WAIT_SCAN'
| 'WAIT_SUBMIT'
| 'WAIT_REVIEW'
| 'PASS'
| 'REJECT';
/** 审核意见 */ /** 盘点审核人 */
reviewRemark?: string reviewer?: string;
/** 审核结果(1-待扫码 2-待提交 3-待审核 4-审核通过 5退回) */ /** 盘点人 */
reviewResult?: 'WAIT_SCAN' | 'WAIT_SUBMIT' | 'WAIT_REVIEW' | 'PASS' | 'REJECT' taker?: string;
/** 盘点审核人 */ /** 类型1: 采购入库申请 2: 归还入库申请 3: 出库外借申请 4: 盘点申请) */
reviewer?: string type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT';
/** 盘点人 */ /** updatedTime */
taker?: string updatedTime?: string;
}
/** 类型1: 采购入库申请 2: 归还入库申请 3: 出库外借申请 4: 盘点申请) */ /**
type?: 'PURCHASE_RECEIPT' | 'RETURN_RECEIPT' | 'LOAN_OUT' | 'AUDIT' | 'SCRAP_OUT' * ComparisonResDTO
*/
export interface ComparisonResDTO {
/** 库存明细 */
details?: Array<material.StockDetailInfo>;
/** 物料类型 */ /** 差异数据 */
types?: Array<string> exceptionalData?: Array<material.StocktakingScanExceptionalData>;
}
/** updatedTime */ /**
updatedTime?: string *
} */
export interface Material {
/** 赋码规则(2-低值易耗品(不参与赋码) 1-高价值工具类(参与唯一赋码) */
assignRule?: 'LOW_VALUE' | 'HIGH_VALUE';
/** /** 编码 */
* ComparisonResDTO code?: string;
*/
export interface ComparisonResDTO {
/** 库存明细 */
details?: Array<material.StockDetailInfo>
/** 差异数据 */ /** createdTime */
exceptionalData?: Array<material.StocktakingScanExceptionalData> createdTime?: string;
}
/** /** 备注 */
* ManualStockDTO description?: string;
*/
export interface ManualStockDTO {
/** 申请id */
applyId?: number
/** 编码 */ /** id */
code?: string id?: number;
/** 手动核实库存数量 */ /** 名称 */
manualStock?: number name?: string;
/** 物料id */ /** 规格 */
materialId?: number spec?: string;
/** 异常原因 */ /** 库存数量 */
msg?: string stock?: number;
/** 名称 */ /** 类型 */
name?: string type?: string;
/** 价格 */ /** updatedTime */
price?: number updatedTime?: string;
}
/** 规格 */ /**
spec?: string *
*/
export interface MaterialStockDetail {
/** 入库申请单 */
applyId?: number;
/** 库存数量 */ /** 条码 */
stock?: number barcode?: string;
/** 类型 */ /** createdTime */
type?: string createdTime?: string;
/** typeName */ /** id */
typeName?: string id?: number;
}
/** /** 物料ID */
* materialId?: number;
*/
export interface ManualStockDetail {
/** 申请id */
applyId?: number
/** createdTime */ /** 状态 */
createdTime?: string status?: 'IN' | 'OUT' | 'OFF' | 'LOST';
/** id */ /** updatedTime */
id?: number updatedTime?: string;
}
/** 手动核实库存数量 */ /**
manualStock?: number *
*/
export interface StockDetailInfo {
/** 物料编码 */
materialCode?: string;
/** 物料id */ /** 物料名称 */
materialId?: number materialId?: number;
/** 异常原因 */ /** 物料名称 */
msg?: string materialName?: string;
/** 库存数量 */ /** 实际库存(明细数据中统计来的) */
stock?: number stockReal?: number;
/** updatedTime */ /** 在存总数(物料表) */
updatedTime?: string stockTotal?: number;
}
/** /** 扫码数量 */
* stockTotalScan?: number;
*/ }
export interface Material {
/** 是否赋码 */
assignRule?: boolean
/** 编码 */ /**
code?: string * ()
*/
export interface StocktakingScanDetail {
/** 申请单id */
applyId?: number;
/** createdTime */ /** 条码 */
createdTime?: string barcode?: string;
/** 备注 */ /** createdTime */
description?: string createdTime?: string;
/** id */ /** id */
id?: number id?: number;
/** 名称 */ /** 物料ID */
name?: string materialId?: number;
/** 价格 */ /** updatedTime */
price?: number updatedTime?: string;
}
/** 规格 */ /**
spec?: string *
*/
export interface StocktakingScanExceptionalData {
/** 申请单id */
applyId?: number;
/** 库存数量 */ /** 条码 */
stock?: number barcode?: string;
/** 类型 */ /** createdTime */
type?: string createdTime?: string;
/** typeName */ /** 异常处理 */
typeName?: string exceptionHandle?: 'INBOUND' | 'OUTBOUND' | 'DISCARD';
/** updatedTime */ /** 异常类型 */
updatedTime?: string exceptionReason?:
} | 'SOCK_IN_BUT_SCAN_NOT_EXIST'
| 'SOCK_OUT_BUT_SCAN_EXIST'
| 'SOCK_NOT_EXIST_BUT_SCAN_EXIST';
/** /** id */
* id?: number;
*/
export interface MaterialStockDetail {
/** 入库申请单 */
applyId?: number
/** 条码 */ /** 物料ID */
barcode?: string materialId?: number;
/** createdTime */ /** materialName */
createdTime?: string materialName?: string;
/** id */ /** 备注 */
id?: number remark?: string;
/** 物料ID */ /** updatedTime */
materialId?: number updatedTime?: string;
}
/** 状态 */
status?: 'IN' | 'OUT' | 'OFF' | 'LOST' | 'SCRAP_OUT'
/** updatedTime */
updatedTime?: string
}
/**
*
*/
export interface ReviewDTO {
/** 申请单 */
applyId?: string
/** 审核意见 */
remark?: string
/** 审核结果 */
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
}
/**
*
*/
export interface StockDetailInfo {
/** 物料编码 */
materialCode?: string
/** 物料名称 */
materialId?: number
/** 物料名称 */
materialName?: string
/** 实际库存(明细数据中统计来的) */
stockReal?: number
/** 在存总数(物料表) */
stockTotal?: number
/** 扫码数量 */
stockTotalScan?: number
}
/**
* ()
*/
export interface StocktakingScanDetail {
/** 申请单id */
applyId?: number
/** 条码 */
barcode?: string
/** createdTime */
createdTime?: string
/** id */
id?: number
/** 物料ID */
materialId?: number
/** updatedTime */
updatedTime?: string
}
/**
*
*/
export interface StocktakingScanExceptionalData {
/** 申请单id */
applyId?: number
/** 条码 */
barcode?: string
/** createdTime */
createdTime?: string
/** 异常处理 */
exceptionHandle?: 'MARK_LOST' | 'MARK_KEEP' | 'MARK_RETURN' | 'MARK_DISCARD' | 'MARK_NEW'
/** 异常类型 */
exceptionReason?: 'SOCK_IN_BUT_SCAN_NOT_EXIST' | 'SOCK_OUT_BUT_SCAN_EXIST' | 'SOCK_NOT_EXIST_BUT_SCAN_EXIST'
/** id */
id?: number
/** 物料ID */
materialId?: number
/** materialName */
materialName?: string
/** 备注 */
remark?: string
/** updatedTime */
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
*/
export interface WaitScanInfo {
/** 物料数量 */
count?: number
/** 物料名称 */
materialName?: string
}
} }

View File

@ -1,32 +0,0 @@
/**
* @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

@ -1,30 +0,0 @@
/**
* @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

@ -1,21 +0,0 @@
/**
* @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

@ -1,21 +0,0 @@
/**
* @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

@ -1,21 +0,0 @@
/**
* @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

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

View File

@ -2,40 +2,22 @@
* @description / * @description /
* *
*/ */
import searchPage from './searchPage' import searchPage from './searchPage';
import saveApply from './saveApply' import saveApply from './saveApply';
import downloadAuditExcel from './downloadAuditExcel' import detail from './detail';
import downloadExcel from './downloadExcel' import searchAuditPage from './searchAuditPage';
import detail from './detail' import auditApply from './auditApply';
import searchAuditPage from './searchAuditPage' import getComparisonRes from './getComparisonRes';
import auditApply from './auditApply' import updateReviewResult from './updateReviewResult';
import getComparisonRes from './getComparisonRes' import saveScanData from './saveScanData';
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 { export default {
searchPage, searchPage,
saveApply, saveApply,
downloadAuditExcel, detail,
downloadExcel, searchAuditPage,
detail, auditApply,
searchAuditPage, getComparisonRes,
auditApply, updateReviewResult,
getComparisonRes, saveScanData,
updateReviewResult, };
submitManualStock,
getManualCheckData,
getManualStock,
saveScanData,
submitScrapOutReview,
getScrapOutById,
submitReview,
getWaitScanData,
}

View File

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

View File

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

View File

@ -1,22 +0,0 @@
/**
* @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

@ -1,22 +0,0 @@
/**
* @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: `/submit-review`,
data: requestBody,
})
.then((data: AxiosResponse<void, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@ -1,22 +0,0 @@
/**
* @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,11 +1,8 @@
import apply from './apply' import apply from './apply';
import material from './material' import material from './material';
import type from './type'
export default { export default {
apply, apply,
material, material,
type, };
}

View File

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

View File

@ -1,28 +0,0 @@
/**
* @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

@ -1,29 +0,0 @@
/**
* @desc
*/
import { defaultSuccess, defaultError, http } from '@/plugins/axios'
import type { AxiosResponse } from 'axios'
export interface Params {
/** 条形码数量 */
count: number
}
export default async function (
/** 物料id */
id: number,
params: Params,
success: (data: Array<string>) => void = defaultSuccess,
fail: (error: { code: string; error?: string }) => void = defaultError,
): Promise<void> {
return http({
method: 'post',
url: `/material/${id}/generate-barcodes`,
params,
})
.then((data: AxiosResponse<Array<string>, unknown>) => {
success(data.data)
})
.catch((error: { code: string; error?: string }) => fail(error))
}

View File

@ -2,20 +2,16 @@
* @description * @description
* *
*/ */
import saveOrUpdateMaterial from './saveOrUpdateMaterial' import saveOrUpdateMaterial from './saveOrUpdateMaterial';
import downloadExcel from './downloadExcel' import all from './all';
import all from './all' import detail from './detail';
import detail from './detail' import deleteMaterial from './deleteMaterial';
import deleteMaterial from './deleteMaterial' import materials from './materials';
import generateBarcodes from './generateBarcodes'
import materials from './materials'
export default { export default {
saveOrUpdateMaterial, saveOrUpdateMaterial,
downloadExcel, all,
all, detail,
detail, deleteMaterial,
deleteMaterial, materials,
generateBarcodes, };
materials,
}

View File

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

View File

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

View File

@ -1,22 +0,0 @@
/**
* @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

@ -1,26 +0,0 @@
/**
* @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

@ -1,21 +0,0 @@
/**
* @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

@ -1,17 +0,0 @@
/**
* @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

@ -1,20 +0,0 @@
/**
* @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

@ -1,18 +0,0 @@
/**
* @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))
}

73
src/api/pontCore.ts Normal file
View File

@ -0,0 +1,73 @@
/**
* @description pont内置请求单例
*/
class PontCoreManager {
// static singleInstance = undefined as PontCoreManager;
private static singleInstance: PontCoreManager | null = null;
static getSignleInstance() {
if (!PontCoreManager.singleInstance) {
PontCoreManager.singleInstance = new PontCoreManager();
return PontCoreManager.singleInstance;
}
return PontCoreManager.singleInstance;
}
/**
* fetch请求
* @param url url
* @param options fetch
*/
fetch(url: string, options = {}) {
return fetch(url, options).then((res) => {
return res.json();
});
}
/**
* 使fetch请求
*/
useFetch(fetch: (url: string, options?: any) => Promise<any>) {
if (typeof fetch !== 'function') {
console.error('fetch should be a function ');
return;
}
this.fetch = fetch;
}
getUrl(path: string, queryParams: any) {
const params = {
...(queryParams || ({} as any)),
};
const url = path.replace(/\{([^\\}]*(?:\\.[^\\}]*)*)\}/gm, ( key) => {
// eslint-disable-next-line no-param-reassign
key = key.trim();
if (params[key] !== undefined) {
const value = params[key];
delete params[key];
return value;
}
console.warn('Please set value for template key: ', key);
return '';
});
const paramStr = Object.keys(params)
.map((key) => {
return params[key] === undefined ? '' : `${key}=${params[key]}`;
})
.filter((id) => id)
.join('&');
if (paramStr) {
return `${url}?${paramStr}`;
}
return url;
}
}
export const PontCore = PontCoreManager.getSignleInstance();

582
src/auto-imports.d.ts vendored
View File

@ -6,304 +6,292 @@
// biome-ignore lint: disable // biome-ignore lint: disable
export {} export {}
declare global { declare global {
const EffectScope: (typeof import('vue'))['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
const asyncComputed: (typeof import('@vueuse/core'))['asyncComputed'] const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: (typeof import('@vueuse/core'))['autoResetRef'] const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: (typeof import('vue'))['computed'] const computed: typeof import('vue')['computed']
const computedAsync: (typeof import('@vueuse/core'))['computedAsync'] const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: (typeof import('@vueuse/core'))['computedEager'] const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: (typeof import('@vueuse/core'))['computedInject'] const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: (typeof import('@vueuse/core'))['computedWithControl'] const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const controlledComputed: (typeof import('@vueuse/core'))['controlledComputed'] const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: (typeof import('@vueuse/core'))['controlledRef'] const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: (typeof import('vue'))['createApp'] const createApp: typeof import('vue')['createApp']
const createEventHook: (typeof import('@vueuse/core'))['createEventHook'] const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: (typeof import('@vueuse/core'))['createGlobalState'] const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: (typeof import('@vueuse/core'))['createInjectionState'] const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createReactiveFn: (typeof import('@vueuse/core'))['createReactiveFn'] const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createReusableTemplate: (typeof import('@vueuse/core'))['createReusableTemplate'] const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
const createSharedComposable: (typeof import('@vueuse/core'))['createSharedComposable'] const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createTemplatePromise: (typeof import('@vueuse/core'))['createTemplatePromise'] const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
const createUnrefFn: (typeof import('@vueuse/core'))['createUnrefFn'] const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
const customRef: (typeof import('vue'))['customRef'] const customRef: typeof import('vue')['customRef']
const debouncedRef: (typeof import('@vueuse/core'))['debouncedRef'] const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
const debouncedWatch: (typeof import('@vueuse/core'))['debouncedWatch'] const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: (typeof import('vue'))['defineAsyncComponent'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: (typeof import('vue'))['defineComponent'] const defineComponent: typeof import('vue')['defineComponent']
const eagerComputed: (typeof import('@vueuse/core'))['eagerComputed'] const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: (typeof import('vue'))['effectScope'] const effectScope: typeof import('vue')['effectScope']
const extendRef: (typeof import('@vueuse/core'))['extendRef'] const extendRef: typeof import('@vueuse/core')['extendRef']
const getCurrentInstance: (typeof import('vue'))['getCurrentInstance'] const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: (typeof import('vue'))['getCurrentScope'] const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: (typeof import('vue'))['h'] const h: typeof import('vue')['h']
const ignorableWatch: (typeof import('@vueuse/core'))['ignorableWatch'] const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: (typeof import('vue'))['inject'] const inject: typeof import('vue')['inject']
const injectLocal: (typeof import('@vueuse/core'))['injectLocal'] const injectLocal: typeof import('@vueuse/core')['injectLocal']
const isDefined: (typeof import('@vueuse/core'))['isDefined'] const isDefined: typeof import('@vueuse/core')['isDefined']
const isProxy: (typeof import('vue'))['isProxy'] const isProxy: typeof import('vue')['isProxy']
const isReactive: (typeof import('vue'))['isReactive'] const isReactive: typeof import('vue')['isReactive']
const isReadonly: (typeof import('vue'))['isReadonly'] const isReadonly: typeof import('vue')['isReadonly']
const isRef: (typeof import('vue'))['isRef'] const isRef: typeof import('vue')['isRef']
const makeDestructurable: (typeof import('@vueuse/core'))['makeDestructurable'] const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const markRaw: (typeof import('vue'))['markRaw'] const markRaw: typeof import('vue')['markRaw']
const nextTick: (typeof import('vue'))['nextTick'] const nextTick: typeof import('vue')['nextTick']
const onActivated: (typeof import('vue'))['onActivated'] const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: (typeof import('vue'))['onBeforeMount'] const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: (typeof import('vue-router'))['onBeforeRouteLeave'] const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: (typeof import('vue-router'))['onBeforeRouteUpdate'] const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: (typeof import('vue'))['onBeforeUnmount'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: (typeof import('vue'))['onBeforeUpdate'] const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onClickOutside: (typeof import('@vueuse/core'))['onClickOutside'] const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
const onDeactivated: (typeof import('vue'))['onDeactivated'] const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: (typeof import('vue'))['onErrorCaptured'] const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onKeyStroke: (typeof import('@vueuse/core'))['onKeyStroke'] const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
const onLongPress: (typeof import('@vueuse/core'))['onLongPress'] const onLongPress: typeof import('@vueuse/core')['onLongPress']
const onMounted: (typeof import('vue'))['onMounted'] const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: (typeof import('vue'))['onRenderTracked'] const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: (typeof import('vue'))['onRenderTriggered'] const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: (typeof import('vue'))['onScopeDispose'] const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: (typeof import('vue'))['onServerPrefetch'] const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onStartTyping: (typeof import('@vueuse/core'))['onStartTyping'] const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
const onUnmounted: (typeof import('vue'))['onUnmounted'] const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: (typeof import('vue'))['onUpdated'] const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: (typeof import('vue'))['onWatcherCleanup'] const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const pausableWatch: (typeof import('@vueuse/core'))['pausableWatch'] const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const provide: (typeof import('vue'))['provide'] const provide: typeof import('vue')['provide']
const provideLocal: (typeof import('@vueuse/core'))['provideLocal'] const provideLocal: typeof import('@vueuse/core')['provideLocal']
const reactify: (typeof import('@vueuse/core'))['reactify'] const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: (typeof import('@vueuse/core'))['reactifyObject'] const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
const reactive: (typeof import('vue'))['reactive'] const reactive: typeof import('vue')['reactive']
const reactiveComputed: (typeof import('@vueuse/core'))['reactiveComputed'] const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
const reactiveOmit: (typeof import('@vueuse/core'))['reactiveOmit'] const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
const reactivePick: (typeof import('@vueuse/core'))['reactivePick'] const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: (typeof import('vue'))['readonly'] const readonly: typeof import('vue')['readonly']
const ref: (typeof import('vue'))['ref'] const ref: typeof import('vue')['ref']
const refAutoReset: (typeof import('@vueuse/core'))['refAutoReset'] const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
const refDebounced: (typeof import('@vueuse/core'))['refDebounced'] const refDebounced: typeof import('@vueuse/core')['refDebounced']
const refDefault: (typeof import('@vueuse/core'))['refDefault'] const refDefault: typeof import('@vueuse/core')['refDefault']
const refThrottled: (typeof import('@vueuse/core'))['refThrottled'] const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: (typeof import('@vueuse/core'))['refWithControl'] const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: (typeof import('vue'))['resolveComponent'] const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: (typeof import('@vueuse/core'))['resolveRef'] const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: (typeof import('@vueuse/core'))['resolveUnref'] const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const shallowReactive: (typeof import('vue'))['shallowReactive'] const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: (typeof import('vue'))['shallowReadonly'] const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: (typeof import('vue'))['shallowRef'] const shallowRef: typeof import('vue')['shallowRef']
const syncRef: (typeof import('@vueuse/core'))['syncRef'] const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: (typeof import('@vueuse/core'))['syncRefs'] const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: (typeof import('@vueuse/core'))['templateRef'] const templateRef: typeof import('@vueuse/core')['templateRef']
const throttledRef: (typeof import('@vueuse/core'))['throttledRef'] const throttledRef: typeof import('@vueuse/core')['throttledRef']
const throttledWatch: (typeof import('@vueuse/core'))['throttledWatch'] const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
const toRaw: (typeof import('vue'))['toRaw'] const toRaw: typeof import('vue')['toRaw']
const toReactive: (typeof import('@vueuse/core'))['toReactive'] const toReactive: typeof import('@vueuse/core')['toReactive']
const toRef: (typeof import('vue'))['toRef'] const toRef: typeof import('vue')['toRef']
const toRefs: (typeof import('vue'))['toRefs'] const toRefs: typeof import('vue')['toRefs']
const toValue: (typeof import('vue'))['toValue'] const toValue: typeof import('vue')['toValue']
const triggerRef: (typeof import('vue'))['triggerRef'] const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: (typeof import('@vueuse/core'))['tryOnBeforeMount'] const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
const tryOnBeforeUnmount: (typeof import('@vueuse/core'))['tryOnBeforeUnmount'] const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
const tryOnMounted: (typeof import('@vueuse/core'))['tryOnMounted'] const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: (typeof import('@vueuse/core'))['tryOnScopeDispose'] const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
const tryOnUnmounted: (typeof import('@vueuse/core'))['tryOnUnmounted'] const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
const unref: (typeof import('vue'))['unref'] const unref: typeof import('vue')['unref']
const unrefElement: (typeof import('@vueuse/core'))['unrefElement'] const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: (typeof import('@vueuse/core'))['until'] const until: typeof import('@vueuse/core')['until']
const useActiveElement: (typeof import('@vueuse/core'))['useActiveElement'] const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useAnimate: (typeof import('@vueuse/core'))['useAnimate'] const useAnimate: typeof import('@vueuse/core')['useAnimate']
const useArrayDifference: (typeof import('@vueuse/core'))['useArrayDifference'] const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
const useArrayEvery: (typeof import('@vueuse/core'))['useArrayEvery'] const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
const useArrayFilter: (typeof import('@vueuse/core'))['useArrayFilter'] const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
const useArrayFind: (typeof import('@vueuse/core'))['useArrayFind'] const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
const useArrayFindIndex: (typeof import('@vueuse/core'))['useArrayFindIndex'] const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
const useArrayFindLast: (typeof import('@vueuse/core'))['useArrayFindLast'] const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
const useArrayIncludes: (typeof import('@vueuse/core'))['useArrayIncludes'] const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes']
const useArrayJoin: (typeof import('@vueuse/core'))['useArrayJoin'] const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
const useArrayMap: (typeof import('@vueuse/core'))['useArrayMap'] const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: (typeof import('@vueuse/core'))['useArrayReduce'] const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
const useArraySome: (typeof import('@vueuse/core'))['useArraySome'] const useArraySome: typeof import('@vueuse/core')['useArraySome']
const useArrayUnique: (typeof import('@vueuse/core'))['useArrayUnique'] const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
const useAsyncQueue: (typeof import('@vueuse/core'))['useAsyncQueue'] const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: (typeof import('@vueuse/core'))['useAsyncState'] const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: (typeof import('vue'))['useAttrs'] const useAttrs: typeof import('vue')['useAttrs']
const useBase64: (typeof import('@vueuse/core'))['useBase64'] const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: (typeof import('@vueuse/core'))['useBattery'] const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: (typeof import('@vueuse/core'))['useBluetooth'] const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
const useBreakpoints: (typeof import('@vueuse/core'))['useBreakpoints'] const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBroadcastChannel: (typeof import('@vueuse/core'))['useBroadcastChannel'] const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: (typeof import('@vueuse/core'))['useBrowserLocation'] const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useCached: (typeof import('@vueuse/core'))['useCached'] const useCached: typeof import('@vueuse/core')['useCached']
const useClipboard: (typeof import('@vueuse/core'))['useClipboard'] const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useClipboardItems: (typeof import('@vueuse/core'))['useClipboardItems'] const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
const useCloned: (typeof import('@vueuse/core'))['useCloned'] const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: (typeof import('@vueuse/core'))['useColorMode'] const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useConfirmDialog: (typeof import('@vueuse/core'))['useConfirmDialog'] const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useCounter: (typeof import('@vueuse/core'))['useCounter'] const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: (typeof import('vue'))['useCssModule'] const useCssModule: typeof import('vue')['useCssModule']
const useCssVar: (typeof import('@vueuse/core'))['useCssVar'] const useCssVar: typeof import('@vueuse/core')['useCssVar']
const useCssVars: (typeof import('vue'))['useCssVars'] const useCssVars: typeof import('vue')['useCssVars']
const useCurrentElement: (typeof import('@vueuse/core'))['useCurrentElement'] const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
const useCycleList: (typeof import('@vueuse/core'))['useCycleList'] const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: (typeof import('@vueuse/core'))['useDark'] const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: (typeof import('@vueuse/core'))['useDateFormat'] const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: (typeof import('@vueuse/core'))['useDebounce'] const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebounceFn: (typeof import('@vueuse/core'))['useDebounceFn'] const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
const useDebouncedRefHistory: (typeof import('@vueuse/core'))['useDebouncedRefHistory'] const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDeviceMotion: (typeof import('@vueuse/core'))['useDeviceMotion'] const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
const useDeviceOrientation: (typeof import('@vueuse/core'))['useDeviceOrientation'] const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
const useDevicePixelRatio: (typeof import('@vueuse/core'))['useDevicePixelRatio'] const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
const useDevicesList: (typeof import('@vueuse/core'))['useDevicesList'] const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
const useDisplayMedia: (typeof import('@vueuse/core'))['useDisplayMedia'] const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
const useDocumentVisibility: (typeof import('@vueuse/core'))['useDocumentVisibility'] const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
const useDraggable: (typeof import('@vueuse/core'))['useDraggable'] const useDraggable: typeof import('@vueuse/core')['useDraggable']
const useDropZone: (typeof import('@vueuse/core'))['useDropZone'] const useDropZone: typeof import('@vueuse/core')['useDropZone']
const useElementBounding: (typeof import('@vueuse/core'))['useElementBounding'] const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
const useElementByPoint: (typeof import('@vueuse/core'))['useElementByPoint'] const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
const useElementHover: (typeof import('@vueuse/core'))['useElementHover'] const useElementHover: typeof import('@vueuse/core')['useElementHover']
const useElementSize: (typeof import('@vueuse/core'))['useElementSize'] const useElementSize: typeof import('@vueuse/core')['useElementSize']
const useElementVisibility: (typeof import('@vueuse/core'))['useElementVisibility'] const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
const useEventBus: (typeof import('@vueuse/core'))['useEventBus'] const useEventBus: typeof import('@vueuse/core')['useEventBus']
const useEventListener: (typeof import('@vueuse/core'))['useEventListener'] const useEventListener: typeof import('@vueuse/core')['useEventListener']
const useEventSource: (typeof import('@vueuse/core'))['useEventSource'] const useEventSource: typeof import('@vueuse/core')['useEventSource']
const useEyeDropper: (typeof import('@vueuse/core'))['useEyeDropper'] const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: (typeof import('@vueuse/core'))['useFavicon'] const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: (typeof import('@vueuse/core'))['useFetch'] const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: (typeof import('@vueuse/core'))['useFileDialog'] const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: (typeof import('@vueuse/core'))['useFileSystemAccess'] const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: (typeof import('@vueuse/core'))['useFocus'] const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: (typeof import('@vueuse/core'))['useFocusWithin'] const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: (typeof import('@vueuse/core'))['useFps'] const useFps: typeof import('@vueuse/core')['useFps']
const useFullscreen: (typeof import('@vueuse/core'))['useFullscreen'] const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: (typeof import('@vueuse/core'))['useGamepad'] const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: (typeof import('@vueuse/core'))['useGeolocation'] const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useId: (typeof import('vue'))['useId'] const useId: typeof import('vue')['useId']
const useIdle: (typeof import('@vueuse/core'))['useIdle'] const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: (typeof import('@vueuse/core'))['useImage'] const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: (typeof import('@vueuse/core'))['useInfiniteScroll'] const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
const useIntersectionObserver: (typeof import('@vueuse/core'))['useIntersectionObserver'] const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: (typeof import('@vueuse/core'))['useInterval'] const useInterval: typeof import('@vueuse/core')['useInterval']
const useIntervalFn: (typeof import('@vueuse/core'))['useIntervalFn'] const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useKeyModifier: (typeof import('@vueuse/core'))['useKeyModifier'] const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
const useLastChanged: (typeof import('@vueuse/core'))['useLastChanged'] const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLink: (typeof import('vue-router'))['useLink'] const useLink: typeof import('vue-router')['useLink']
const useLocalStorage: (typeof import('@vueuse/core'))['useLocalStorage'] const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useMagicKeys: (typeof import('@vueuse/core'))['useMagicKeys'] const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: (typeof import('@vueuse/core'))['useManualRefHistory'] const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useMediaControls: (typeof import('@vueuse/core'))['useMediaControls'] const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: (typeof import('@vueuse/core'))['useMediaQuery'] const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMemoize: (typeof import('@vueuse/core'))['useMemoize'] const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: (typeof import('@vueuse/core'))['useMemory'] const useMemory: typeof import('@vueuse/core')['useMemory']
const useModel: (typeof import('vue'))['useModel'] const useModel: typeof import('vue')['useModel']
const useMounted: (typeof import('@vueuse/core'))['useMounted'] const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: (typeof import('@vueuse/core'))['useMouse'] const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: (typeof import('@vueuse/core'))['useMouseInElement'] const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
const useMousePressed: (typeof import('@vueuse/core'))['useMousePressed'] const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
const useMutationObserver: (typeof import('@vueuse/core'))['useMutationObserver'] const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
const useNavigatorLanguage: (typeof import('@vueuse/core'))['useNavigatorLanguage'] const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: (typeof import('@vueuse/core'))['useNetwork'] const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: (typeof import('@vueuse/core'))['useNow'] const useNow: typeof import('@vueuse/core')['useNow']
const useObjectUrl: (typeof import('@vueuse/core'))['useObjectUrl'] const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
const useOffsetPagination: (typeof import('@vueuse/core'))['useOffsetPagination'] const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
const useOnline: (typeof import('@vueuse/core'))['useOnline'] const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: (typeof import('@vueuse/core'))['usePageLeave'] const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
const useParallax: (typeof import('@vueuse/core'))['useParallax'] const useParallax: typeof import('@vueuse/core')['useParallax']
const useParentElement: (typeof import('@vueuse/core'))['useParentElement'] const useParentElement: typeof import('@vueuse/core')['useParentElement']
const usePerformanceObserver: (typeof import('@vueuse/core'))['usePerformanceObserver'] const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver']
const usePermission: (typeof import('@vueuse/core'))['usePermission'] const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: (typeof import('@vueuse/core'))['usePointer'] const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: (typeof import('@vueuse/core'))['usePointerLock'] const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: (typeof import('@vueuse/core'))['usePointerSwipe'] const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: (typeof import('@vueuse/core'))['usePreferredColorScheme'] const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: (typeof import('@vueuse/core'))['usePreferredContrast'] const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
const usePreferredDark: (typeof import('@vueuse/core'))['usePreferredDark'] const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: (typeof import('@vueuse/core'))['usePreferredLanguages'] const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const usePreferredReducedMotion: (typeof import('@vueuse/core'))['usePreferredReducedMotion'] const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
const usePrevious: (typeof import('@vueuse/core'))['usePrevious'] const usePrevious: typeof import('@vueuse/core')['usePrevious']
const useRafFn: (typeof import('@vueuse/core'))['useRafFn'] const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: (typeof import('@vueuse/core'))['useRefHistory'] const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useResizeObserver: (typeof import('@vueuse/core'))['useResizeObserver'] const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRoute: (typeof import('vue-router'))['useRoute'] const useRoute: typeof import('vue-router')['useRoute']
const useRouter: (typeof import('vue-router'))['useRouter'] const useRouter: typeof import('vue-router')['useRouter']
const useScreenOrientation: (typeof import('@vueuse/core'))['useScreenOrientation'] const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
const useScreenSafeArea: (typeof import('@vueuse/core'))['useScreenSafeArea'] const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
const useScriptTag: (typeof import('@vueuse/core'))['useScriptTag'] const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useScroll: (typeof import('@vueuse/core'))['useScroll'] const useScroll: typeof import('@vueuse/core')['useScroll']
const useScrollLock: (typeof import('@vueuse/core'))['useScrollLock'] const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
const useSessionStorage: (typeof import('@vueuse/core'))['useSessionStorage'] const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: (typeof import('@vueuse/core'))['useShare'] const useShare: typeof import('@vueuse/core')['useShare']
const useSlots: (typeof import('vue'))['useSlots'] const useSlots: typeof import('vue')['useSlots']
const useSorted: (typeof import('@vueuse/core'))['useSorted'] const useSorted: typeof import('@vueuse/core')['useSorted']
const useSpeechRecognition: (typeof import('@vueuse/core'))['useSpeechRecognition'] const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: (typeof import('@vueuse/core'))['useSpeechSynthesis'] const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: (typeof import('@vueuse/core'))['useStepper'] const useStepper: typeof import('@vueuse/core')['useStepper']
const useStorage: (typeof import('@vueuse/core'))['useStorage'] const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: (typeof import('@vueuse/core'))['useStorageAsync'] const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: (typeof import('@vueuse/core'))['useStyleTag'] const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSupported: (typeof import('@vueuse/core'))['useSupported'] const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: (typeof import('@vueuse/core'))['useSwipe'] const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRef: (typeof import('vue'))['useTemplateRef'] const useTemplateRef: typeof import('vue')['useTemplateRef']
const useTemplateRefsList: (typeof import('@vueuse/core'))['useTemplateRefsList'] const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextDirection: (typeof import('@vueuse/core'))['useTextDirection'] const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
const useTextSelection: (typeof import('@vueuse/core'))['useTextSelection'] const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useTextareaAutosize: (typeof import('@vueuse/core'))['useTextareaAutosize'] const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
const useThrottle: (typeof import('@vueuse/core'))['useThrottle'] const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: (typeof import('@vueuse/core'))['useThrottleFn'] const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useThrottledRefHistory: (typeof import('@vueuse/core'))['useThrottledRefHistory'] const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
const useTimeAgo: (typeof import('@vueuse/core'))['useTimeAgo'] const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: (typeof import('@vueuse/core'))['useTimeout'] const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: (typeof import('@vueuse/core'))['useTimeoutFn'] const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimeoutPoll: (typeof import('@vueuse/core'))['useTimeoutPoll'] const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
const useTimestamp: (typeof import('@vueuse/core'))['useTimestamp'] const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: (typeof import('@vueuse/core'))['useTitle'] const useTitle: typeof import('@vueuse/core')['useTitle']
const useToNumber: (typeof import('@vueuse/core'))['useToNumber'] const useToNumber: typeof import('@vueuse/core')['useToNumber']
const useToString: (typeof import('@vueuse/core'))['useToString'] const useToString: typeof import('@vueuse/core')['useToString']
const useToggle: (typeof import('@vueuse/core'))['useToggle'] const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: (typeof import('@vueuse/core'))['useTransition'] const useTransition: typeof import('@vueuse/core')['useTransition']
const useUrlSearchParams: (typeof import('@vueuse/core'))['useUrlSearchParams'] const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: (typeof import('@vueuse/core'))['useUserMedia'] const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useVModel: (typeof import('@vueuse/core'))['useVModel'] const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: (typeof import('@vueuse/core'))['useVModels'] const useVModels: typeof import('@vueuse/core')['useVModels']
const useVibrate: (typeof import('@vueuse/core'))['useVibrate'] const useVibrate: typeof import('@vueuse/core')['useVibrate']
const useVirtualList: (typeof import('@vueuse/core'))['useVirtualList'] const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useWakeLock: (typeof import('@vueuse/core'))['useWakeLock'] const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
const useWebNotification: (typeof import('@vueuse/core'))['useWebNotification'] const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: (typeof import('@vueuse/core'))['useWebSocket'] const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
const useWebWorker: (typeof import('@vueuse/core'))['useWebWorker'] const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
const useWebWorkerFn: (typeof import('@vueuse/core'))['useWebWorkerFn'] const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
const useWindowFocus: (typeof import('@vueuse/core'))['useWindowFocus'] const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
const useWindowScroll: (typeof import('@vueuse/core'))['useWindowScroll'] const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: (typeof import('@vueuse/core'))['useWindowSize'] const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: (typeof import('vue'))['watch'] const watch: typeof import('vue')['watch']
const watchArray: (typeof import('@vueuse/core'))['watchArray'] const watchArray: typeof import('@vueuse/core')['watchArray']
const watchAtMost: (typeof import('@vueuse/core'))['watchAtMost'] const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: (typeof import('@vueuse/core'))['watchDebounced'] const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchDeep: (typeof import('@vueuse/core'))['watchDeep'] const watchDeep: typeof import('@vueuse/core')['watchDeep']
const watchEffect: (typeof import('vue'))['watchEffect'] const watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: (typeof import('@vueuse/core'))['watchIgnorable'] const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchImmediate: (typeof import('@vueuse/core'))['watchImmediate'] const watchImmediate: typeof import('@vueuse/core')['watchImmediate']
const watchOnce: (typeof import('@vueuse/core'))['watchOnce'] const watchOnce: typeof import('@vueuse/core')['watchOnce']
const watchPausable: (typeof import('@vueuse/core'))['watchPausable'] const watchPausable: typeof import('@vueuse/core')['watchPausable']
const watchPostEffect: (typeof import('vue'))['watchPostEffect'] const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: (typeof import('vue'))['watchSyncEffect'] const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: (typeof import('@vueuse/core'))['watchThrottled'] const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchTriggerable: (typeof import('@vueuse/core'))['watchTriggerable'] const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: (typeof import('@vueuse/core'))['watchWithFilter'] const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: (typeof import('@vueuse/core'))['whenever'] const whenever: typeof import('@vueuse/core')['whenever']
} }
// for type re-export // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
Component, import('vue')
ComponentPublicInstance,
ComputedRef,
ExtractDefaultPropTypes,
ExtractPropTypes,
ExtractPublicPropTypes,
InjectionKey,
PropType,
Ref,
VNode,
WritableComputedRef,
} from 'vue'
import('vue')
} }

4
src/components.d.ts vendored
View File

@ -17,13 +17,11 @@ declare module 'vue' {
ADivider: typeof import('ant-design-vue/es')['Divider'] ADivider: typeof import('ant-design-vue/es')['Divider']
ADrawer: typeof import('ant-design-vue/es')['Drawer'] ADrawer: typeof import('ant-design-vue/es')['Drawer']
ADropdown: typeof import('ant-design-vue/es')['Dropdown'] ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AFlex: typeof import('ant-design-vue/es')['Flex']
AFloatButton: typeof import('ant-design-vue/es')['FloatButton'] AFloatButton: typeof import('ant-design-vue/es')['FloatButton']
AForm: typeof import('ant-design-vue/es')['Form'] AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem'] AFormItem: typeof import('ant-design-vue/es')['FormItem']
AImage: typeof import('ant-design-vue/es')['Image'] AImage: typeof import('ant-design-vue/es')['Image']
AInput: typeof import('ant-design-vue/es')['Input'] AInput: typeof import('ant-design-vue/es')['Input']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
AInputSearch: typeof import('ant-design-vue/es')['InputSearch'] AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
ALayout: typeof import('ant-design-vue/es')['Layout'] ALayout: typeof import('ant-design-vue/es')['Layout']
@ -52,11 +50,9 @@ declare module 'vue' {
ATabPane: typeof import('ant-design-vue/es')['TabPane'] ATabPane: typeof import('ant-design-vue/es')['TabPane']
ATabs: typeof import('ant-design-vue/es')['Tabs'] ATabs: typeof import('ant-design-vue/es')['Tabs']
ATag: typeof import('ant-design-vue/es')['Tag'] ATag: typeof import('ant-design-vue/es')['Tag']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip'] ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ATransfer: typeof import('ant-design-vue/es')['Transfer'] ATransfer: typeof import('ant-design-vue/es')['Transfer']
ATree: typeof import('ant-design-vue/es')['Tree'] ATree: typeof import('ant-design-vue/es')['Tree']
ATreeSelect: typeof import('ant-design-vue/es')['TreeSelect']
ATypographyLink: typeof import('ant-design-vue/es')['TypographyLink'] ATypographyLink: typeof import('ant-design-vue/es')['TypographyLink']
ATypographyParagraph: typeof import('ant-design-vue/es')['TypographyParagraph'] ATypographyParagraph: typeof import('ant-design-vue/es')['TypographyParagraph']
ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle'] ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle']

View File

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

View File

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

View File

@ -67,7 +67,6 @@
import { config } from '@/settings/application' import { config } from '@/settings/application'
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface' import { MenuInfo } from 'ant-design-vue/es/menu/src/interface'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import api from '@/api'
const t = useI18n() const t = useI18n()
const user = useUserStore() const user = useUserStore()
@ -82,13 +81,11 @@
} }
const logout = () => { const logout = () => {
Modal.confirm({ Modal.confirm({
title: '退出登录', title: t.t('layout.user.modal.logout.title'),
icon: createVNode(ExclamationCircleOutlined), icon: createVNode(ExclamationCircleOutlined),
content: createVNode('div', { style: 'color:red;' }, '确定退出系统吗?'), content: createVNode('div', { style: 'color:red;' }, t.t('layout.user.modal.logout.content')),
onOk() { onOk() {
api.authApi.auth.logout(() => { console.log(1)
user.logout()
})
}, },
}) })
} }

View File

@ -29,18 +29,13 @@
import SubMenu from './sub-menu.vue' import SubMenu from './sub-menu.vue'
import XEUtils from 'xe-utils' import XEUtils from 'xe-utils'
import { fliteredMenuTree } from './menu' import { fliteredMenuTree } from './menu'
import { useRoute } from 'vue-router'
const app = useAppStore() const app = useAppStore()
// const router = useRouter()
const theme = computed(() => { const theme = computed(() => {
return app.layout.navTheme === 'light' ? 'light' : 'dark' return app.layout.navTheme === 'light' ? 'light' : 'dark'
}) })
const menuTree = computed(() => { const menuTree = computed(() => {
//
// router.currentRoute();
return fliteredMenuTree() return fliteredMenuTree()
}) })
const route = useRoute() const route = useRoute()

View File

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

View File

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

View File

@ -1,18 +1,11 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import UserLayout from '@/layout/user-layout.vue' import UserLayout from '@/layout/user-layout.vue'
import AdminLayout from '@/layout/admin-layout.vue' import AdminLayout from '@/layout/admin-layout.vue'
import BlankLayout from '@/layout/blank-layout.vue' import BlankLayout from '@/layout/blank-layout.vue'
import router from '@/router'
export const routes: Array<RouteRecordRaw> = [ export const routes = [
{ {
path: '/admin/dashboard', path: '/admin/acl', // 系统管理
meta: { title: 'menus.dashboard', icon: 'icon-dashboard', flat: true },
name: 'Dashboard',
component: () => import('../views/admin/dashboard-page.vue'),
},
{
path: '/admin/acl', // 系统管理
name: 'ACL', name: 'ACL',
meta: { title: 'menus.acl.name', icon: 'icon-acl', flat: true }, meta: { title: 'menus.acl.name', icon: 'icon-acl', flat: true },
component: BlankLayout, component: BlankLayout,
@ -39,20 +32,14 @@ export const routes: Array<RouteRecordRaw> = [
{ {
path: '/admin/acl/material', //物料 path: '/admin/acl/material', //物料
name: 'Material', name: 'Material',
meta: { title: 'menus.acl.material', icon: 'icon-permission' }, meta: {title: 'menus.acl.material',icon: 'icon-permission'},
component: () => import('../views/stock/material/material-page.vue'), 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', // 库存管理 path: '/stock/name', // 库存管理
name: 'Stock', name: 'STOCK',
meta: { title: 'menus.stock.name', icon: 'icon-acl', flat: true }, meta: { title: 'menus.stock.name', icon: 'icon-acl', flat: true },
component: BlankLayout, component: BlankLayout,
redirect: () => ({ name: 'Inbound' }), redirect: () => ({ name: 'Inbound' }),
@ -60,56 +47,56 @@ export const routes: Array<RouteRecordRaw> = [
{ {
path: '/stock/inbound', //入库 path: '/stock/inbound', //入库
name: 'Inbound', name: 'Inbound',
meta: { title: 'menus.stock.inbound', icon: 'icon-permission' }, meta: {title: 'menus.stock.inbound',icon: 'icon-permission'},
component: () => import('../views/stock/inboud/inboud-page.vue'), component: ()=> import('../views/stock/inboud/inboud-page.vue'),
}, },
{ {
path: '/stock/outbound', //出库 path: '/stock/outbound', //出库
name: 'Outbound', name: 'Outbound',
meta: { title: 'menus.stock.outbound', icon: 'icon-permission' }, meta: {title: 'menus.stock.outbound',icon: 'icon-permission'},
component: () => import('../views/stock/outboud/outbound-page.vue'), component: ()=> import('../views/stock/outboud/outbound-page.vue'),
}, },
{ {
path: '/stock/stocktaking', //盘点 path: '/stock/stocktaking', //盘点
name: 'Stocktaking', name: 'Stocktaking',
meta: { title: 'menus.stock.stocktaking', icon: 'icon-permission' }, meta: {title: 'menus.stock.stocktaking',icon: 'icon-permission'},
component: () => import('../views/stock/stocktaking/stocktaking-page.vue'), component: ()=> import('../views/stock/stocktaking/stocktaking-page.vue')
}, },
], ]
}, },
{ {
path: '/statistic/name', // 统计报表 path: '/statistic/name', // 统计报表
name: 'Statistic', name: 'STATISTIC',
meta: { title: 'menus.statistic.name', icon: 'icon-acl', flat: true }, meta: { title: 'menus.statistic.name', icon: 'icon-acl', flat: true },
component: BlankLayout, component: BlankLayout,
redirect: () => ({ name: 'Query-Stock' }), redirect: () => ({ name: 'S-Inbound' }),
children: [ children: [
{ {
path: '/statistic/stock', //库存 path: '/statistic/stock', //库存
name: 'Query-Stock', name: 'S-stock',
meta: { title: 'menus.statistic.stock', icon: 'icon-permission' }, meta: {title: 'menus.statistic.stock',icon: 'icon-permission'},
component: () => import('../views/stock/report/materialReport-page.vue'), component: ()=> import('../views/stock/report/materialReport-page.vue'),
}, },
{ {
path: '/statistic/inbound', //入库 path: '/statistic/inbound', //入库
name: 'Query-Inbound', name: 'S-Inbound',
meta: { title: 'menus.statistic.inbound', icon: 'icon-permission' }, meta: {title: 'menus.statistic.inbound',icon: 'icon-permission'},
component: () => import('../views/stock/report/inboundReport-page.vue'), component: ()=> import('../views/stock/report/inboundReport-page.vue'),
}, },
{ {
path: '/statistic/outbound', //出库 path: '/statistic/outbound', //出库
name: 'Query-Outbound', name: 'S-Outbound',
meta: { title: 'menus.statistic.outbound', icon: 'icon-permission' }, meta: {title: 'menus.statistic.outbound',icon: 'icon-permission'},
component: () => import('../views/stock/report/outboundReport-page.vue'), component: ()=> import('../views/stock/report/outboundReport-page.vue'),
}, },
{ {
path: '/statistic/stocktaking', //盘点 path: '/statistic/stocktaking', //盘点
name: 'Query-Stocktaking', name: 'S-stocktaking',
meta: { title: 'menus.statistic.stocktaking', icon: 'icon-permission' }, meta: {title: 'menus.statistic.stocktaking',icon: 'icon-permission'},
component: () => import('../views/stock/report/stocktakingReport-page.vue'), component: ()=> import('../views/stock/report/stocktakingReport-page.vue'),
}, },
], ]
}, }
] ]
export default createRouter({ export default createRouter({
@ -152,77 +139,3 @@ export default createRouter({
}, },
], ],
}) })
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,
}
}
// 过滤路由,最多两层
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,11 +1,9 @@
export interface Http { export interface Http {
prefix: string // 前缀 prefix: string // 前缀
timeout: number timeout: number
downloadUrl: string
} }
export const http: Http = { export const http: Http = {
prefix: import.meta.env.VITE_HTTP_PREFIX, prefix: import.meta.env.VITE_HTTP_PREFIX,
timeout: 10 * 1000, timeout: 10 * 1000,
downloadUrl: import.meta.env.VITE_UPLOAD_PREFIX,
} }

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import router, { getP } from '@/router' import router from '@/router'
export const useUserStore = defineStore('user', { export const useUserStore = defineStore('user', {
state: (): auth.AuthUser => ({ state: (): auth.AuthUser => ({
@ -18,12 +18,6 @@ export const useUserStore = defineStore('user', {
isAdmin(state) { isAdmin(state) {
return state.roles.includes('admin') return state.roles.includes('admin')
}, },
isLogin(state) {
return state.token && state.refreshToken
},
getPermission(state) {
return state.permissions
},
}, },
actions: { actions: {
refreshUserInfo(user: auth.AuthUser) { refreshUserInfo(user: auth.AuthUser) {
@ -31,13 +25,7 @@ export const useUserStore = defineStore('user', {
}, },
userLogin(user: auth.AuthUser) { userLogin(user: auth.AuthUser) {
Object.assign(this, user) Object.assign(this, user)
// 根据权限动态添加地址 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() { logout() {
this.mobile = '' this.mobile = ''
@ -50,11 +38,6 @@ export const useUserStore = defineStore('user', {
this.sex = 'FEMALE' this.sex = 'FEMALE'
this.token = '' this.token = ''
this.refreshToken = '' this.refreshToken = ''
router.push({ path: '/login/user' })
},
hasPermission(permission?: string) {
if (this.isAdmin) return true
return permission !== undefined && this.permissions.includes(permission)
}, },
}, },
persist: { persist: {

View File

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

View File

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

View File

@ -15,6 +15,26 @@ export const config: FormConfig = {
} }
export const formItems: FormItem[] = [ 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', group: 'form',
type: 'input', type: 'input',

View File

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

View File

@ -5,8 +5,6 @@ const { t } = i18n.global
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { FormItem, FormConfig } from '@/components/form-render/form-render-types' 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 = { export const config: FormConfig = {
layout: 'horizontal', layout: 'horizontal',
@ -42,12 +40,7 @@ export const formItems = (sexes: Codebook[]): FormItem[] => {
placeholder: t('pages.acl.user.form.add.placeholder.name'), placeholder: t('pages.acl.user.form.add.placeholder.name'),
maxlength: 20, maxlength: 20,
}, },
rules: [ rules: [],
{
validator: validate,
trigger: ['blur', 'change'],
},
],
}, },
{ {
type: 'radio', type: 'radio',
@ -87,16 +80,7 @@ export const formItems = (sexes: Codebook[]): FormItem[] => {
showCount: true, showCount: true,
placeholder: t('pages.acl.user.form.add.placeholder.password'), 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', group: 'form',
@ -159,19 +143,3 @@ 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

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

View File

@ -0,0 +1,27 @@
<template>
<a-row bordered>
<a-col :span="8">
<a-button type="primary" width="90%">开始扫码</a-button>
</a-col>
<a-col :span="8">
</a-col>
<a-col :span="8">
<a-input bordered size="large" ref="snInput" placeholder="输入条形码" style="width: 90%" />
</a-col>
</a-row>
<vxe-table border stripe show-overflow ref="applyDetailTableRef" max-height="500" :column-config="{ resizable: true }"
:keyboard-config="{ isEsc: true }" size="medium">
<vxe-column type="seq" title="序号" width="60"></vxe-column>
<vxe-column field="id" title="物料id" :visible="false"></vxe-column>
<vxe-column field="name" title="物料名称" />
<vxe-column field="code" title="编码" />
<vxe-column field="spec" title="规格" />
<vxe-column field="type" title="类型" />
<vxe-column field="times" title="库存数量" />
<vxe-column field="oprator" title="盘点数量" />
</vxe-table>
</template>
<script setup lang="ts">
</script>

View File

@ -1,269 +0,0 @@
<template>
<!-- 基本信息的表单 -->
<div>
<a-form :model="formData" name="basic" layout="horizontal" label-wrap>
<a-form-item label="申请人" name="applicant">
<a-input v-model:value="formData.applicant" style="width: 40%" />
</a-form-item>
<a-form-item label="申请类型" name="applyType">
<a-radio-group v-model:value="formData.applyType" button-style="solid" style="width: 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">
<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>
<!-- 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="assignRule" title="是否赋码">
<template #default="{ row }">
{{ row.assignRule ? '是' : '否' }}
</template>
</vxe-column>
<vxe-column
field="quantity"
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.quantity" type="integer"></vxe-number-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 setup lang="ts">
import { VxeTableInstance } from 'vxe-table'
import api from '@/api'
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(
{
applyType: {
type: String,
default: '',
},
}, // applyType使121: 2:
)
const getTableData = () => {
const $table = tableRef.value
return $table?.getTableData().fullData
}
const getApplyForm = () => {
return {
type: formData.value.applyType,
applicant: formData.value.applicant,
applyDate: formData.value.applyDate.format('YYYY-MM-DD HH:mm:ss'),
isConfirm: true,
result: '',
reviewer: formData.value.reviewer,
}
}
defineExpose({ getTableData, getApplyForm })
interface FormData {
applicant: string //
applyDate: Dayjs //
applyType: string //
slectedList: number[] //
reviewer: string //
}
const formData = ref<FormData>({
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
name: string
code: string
spec: string
type: string
typeName: string
quantity: number
disabled: boolean
checked: boolean
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({ type: typeVal.value }, (data) => {
materialList.value = data
})
options.value = materialList.value.map((item) => {
return { label: item.name ? item.name + '(' + item.spec + ')' : '未知', value: item.id ? item.id : -1 }
})
}
watch(
typeVal,
(newVal) => {
if (newVal !== '') {
getMaterialList()
}
},
{ immediate: true, deep: true },
)
//
// value: number, option: { label: string, value: number }
type Insert = SelectHandler<LabeledValue, DefaultOptionType> | undefined
const insertEvent = (value: Insert) => {
// value
const m = materialList.value.find((item) => item.id === value)
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 : '',
typeName: m.typeName ? m.typeName : '',
type: m.type ? m.type : '',
price: m.price ? m.price : 0,
quantity: 1,
assignRule: m.assignRule ? m.assignRule : false,
disabled: false,
checked: false,
}
const $table = tableRef.value
if ($table) {
$table.insertAt(row, -1)
}
}
}
//
const filterOption = (input: string, option: { label: string; value: number }) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
// table
const removeEvent = (value: Insert) => {
const $table = tableRef.value
// value
if ($table) {
$table.getTableData().fullData.forEach((item) => {
if (item.id === value) {
$table.remove(item)
}
})
}
}
// 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

@ -0,0 +1,153 @@
<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 './applyModal.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

@ -13,16 +13,16 @@
<vxe-column field="id" title="物料id" :visible="false" /> <vxe-column field="id" title="物料id" :visible="false" />
<vxe-column field="name" title="物料名称" /> <vxe-column field="name" title="物料名称" />
<vxe-column field="assignRule" title="是否扫码"> <vxe-column field="assignRule" title="是否扫码">
<template #default="{ row }"> <template #default="{ row }">
{{ row.assignRule ? '是' : '否' }} {{ row.assignRule === 'HIGH_VALUE' ? '是' : '否' }}
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="applyNum" title="申请数量" :edit-render="{ name: 'VxeInput', props: { type: 'integer' } }" /> <vxe-column field="applyNum" title="申请数量" :edit-render="{ name: 'VxeInput', props: { type: 'integer' } }" />
<vxe-column field="scanNum" title="扫码数量"> <vxe-column field="scanNum" title="扫码数量" :edit-render="{ name: 'VxeInput', props: { type: 'integer' } }" >
<template #default="{ row }"> <template #default="{ row }">
{{ row.assignRule ? row.scanNum : '~' }} {{ row.assignRule === 'LOW_VALUE' ? '~' : row.scanNum }}
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="remark" title="备注" :edit-render="{ name: 'VxeInput' }" /> <vxe-column field="remark" title="备注" :edit-render="{ name: 'VxeInput' }" />
<vxe-column title="操作"> <vxe-column title="操作">
@ -57,7 +57,7 @@
return $table.getTableData().fullData.map((row) => { return $table.getTableData().fullData.map((row) => {
return { return {
materialId: row.id, materialId: row.id,
assignRule: row.assignRule, assignRule: row.assignRule,
quantity: row.applyNum, quantity: row.applyNum,
confirmQuantity: row.scanNum, confirmQuantity: row.scanNum,
exceptionRemark: row.remark, exceptionRemark: row.remark,

View File

@ -0,0 +1,189 @@
<template>
<!-- 基本信息的表单 -->
<div>
<a-form :model="formData" name="basic" layout="horizontal" labelWrap>
<a-form-item label="申请人" name="applicant">
<a-input :value="formData.applicant" style="width: 40%;" />
</a-form-item>
<a-form-item label="申请类型" name="applyType">
<a-radio-group v-model:value="formData.applyType" button-style="solid" style="width: 40%;">
<a-radio-button value='PURCHASE_RECEIPT' v-if="applyType === 'PURCHASE_RECEIPT'">采购入库</a-radio-button>
<a-radio-button value='RETURN_RECEIPT' v-if="applyType === 'PURCHASE_RECEIPT'">归还入库</a-radio-button>
<a-radio-button value='LOAN_OUT' v-if="applyType === 'LOAN_OUT'">出库外借</a-radio-button>
</a-radio-group>
</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" @select="insertEvent"></a-select>
</a-form-item>
</a-form>
</div>
<!-- vxe-table -->
<div>
<vxe-table border show-overflow ref="tableRef" max-height="500" size="medium" v-model:data="tableData"
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="type" title="类型" />
<vxe-column field="assignRule" title="是否赋码">
<template #default="{ row }">
{{ row.assignRule === 'HIGH_VALUE' ? '是' : '否' }}
</template>
</vxe-column>
<vxe-column field="quantity" 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.quantity" type="integer"></vxe-number-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 setup lang="ts">
import { VxeTableInstance } from 'vxe-table'
import api from '@/api'
import { useUserStore } from '@/stores/user'
import dayjs, { Dayjs } from 'dayjs';
//
const props = defineProps(
{
applyType: {
type: String,
default: ''
}
} // applyType使121: 2:
)
const getTableData = ()=> {
const $table = tableRef.value
return $table?.getTableData().fullData;
}
const getApplyForm = () => {
return {
type: formData.value.applyType,
applicant: formData.value.applicant,
applyDate: formData.value.applyDate.format('YYYY-MM-DD HH:mm:ss'),
isConfirm: true,
result: ''
}
}
defineExpose({ getTableData, getApplyForm })
interface FormData {
applicant: string; //
applyDate: Dayjs; //
applyType: string; //
slectedList: number[]; //
}
const userStore = useUserStore()
const formData = ref<FormData>({
applicant: userStore.userName,
applyDate: dayjs(),
applyType: props.applyType,
slectedList: []
});
// vxe-table
export interface RowVO {
id: number
name: string
code: string
spec: string
type: string
quantity: number
disabled: boolean
checked: boolean
assignRule: string
}
const tableRef = ref<VxeTableInstance<RowVO>>()
// vxe-table
const tableData = ref<Array<RowVO>>([])
//
const options = ref<{ label: string, value: number }[]>([])
const materialList = ref<material.Material[]>([])
const getMaterialList = async () => {
await api.materialApi.material.all((data) => {
materialList.value = data
})
options.value = materialList.value.map((item) => {
return { label: item.name ? item.name : '未知', value: item.id ? item.id : -1 }
})
}
getMaterialList()
//
// value: number, option: { label: string, value: number }
const insertEvent = (value: any) => {
// value
const m = materialList.value.find((item) => item.id === (value ? parseInt(value) : -1))
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 : '未知',
quantity: 1,
assignRule: m.assignRule ? m.assignRule : 'HIGH_VALUE',
disabled: false,
checked: false
}
const $table = tableRef.value
if ($table) {
$table.insertAt(row, -1)
}
}
}
// table
const removeEvent = (value: any) => {
const $table = tableRef.value
// value
if ($table) {
$table.getTableData().fullData.forEach((item) => {
if (item.id === (value ? parseInt(value) : -1)) {
$table.remove(item)
}
})
}
}
// 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

@ -18,7 +18,7 @@
<apply-confirm ref="applyConfirmRef" :table-data="conformData"></apply-confirm> <apply-confirm ref="applyConfirmRef" :table-data="conformData"></apply-confirm>
</div> </div>
<div v-show="current === 3" class="step-4"> <div v-show="current === 3" class="step-4">
<successResult :apply-type="applyType"></successResult> <successResul></successResul>
</div> </div>
<!-- 按钮 --> <!-- 按钮 -->
@ -30,10 +30,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import applyForm, { RowVO } from './apply-form.vue' import applyForm,{RowVO} from '../component/applyForm.vue'
import scanForm from './scan-form.vue' import scanForm from '../component/scanForm.vue'
import applyConfirm from './apply-confirm.vue' import applyConfirm from '../component/applyConfirm.vue'
import successResult from './success-result.vue' import successResul from '../component/successResul.vue'
import api from '@/api' import api from '@/api'
// //
defineProps( defineProps(
@ -56,17 +56,17 @@
const loading = ref(false) // loading const loading = ref(false) // loading
const items = [{ title: '录入申请' }, { title: '扫码点货' }, { title: '人工确认' }] const items = [{ title: '录入申请' }, { title: '扫码点货' }, { title: '人工确认' }]
/** /**
* 点击下一步 * 点击下一步
*/ */
type GroupedSums = Record<string, number>
const nextStep = () => { const nextStep = () => {
if (applyFormRef.value && current.value === 0) { if (applyFormRef.value && current.value === 0) {
totalValue.value = 0 totalValue.value = 0
// step1step1 // step1step1
selectData.value = applyFormRef.value.getTableData() selectData.value = applyFormRef.value.getTableData()
selectData.value.forEach((item: RowVO) => { selectData.value.forEach((item:RowVO) => {
if (item.assignRule) { if (item.assignRule === 'HIGH_VALUE') {
totalValue.value += item.quantity totalValue.value += item.quantity
} }
}) })
@ -74,7 +74,7 @@
if (scanFormRef.value && current.value === 1) { if (scanFormRef.value && current.value === 1) {
// step2step2 // step2step2
const items = scanFormRef.value.getTableData() const items = scanFormRef.value.getTableData()
const groupedSums = items.reduce((acc: GroupedSums, item: RowVO) => { const groupedSums = items.reduce((acc:any, item:RowVO) => {
if (!acc[item.code]) { if (!acc[item.code]) {
acc[item.code] = 0 acc[item.code] = 0
} }
@ -82,7 +82,7 @@
return acc return acc
}, {}) }, {})
// //
conformData.value = selectData.value.map((item: RowVO) => { conformData.value = selectData.value.map((item :RowVO) => {
return { return {
id: item.id, id: item.id,
name: item.name, name: item.name,
@ -90,7 +90,7 @@
applyNum: item.quantity, applyNum: item.quantity,
assignRule: item.assignRule, assignRule: item.assignRule,
scanNum: groupedSums[item.code] || 0, scanNum: groupedSums[item.code] || 0,
remark: item.quantity != groupedSums[item.code] && item.assignRule ? `数量不一致` : '', remark: (item.quantity != groupedSums[item.code] && item.assignRule === 'HIGH_VALUE') ? `数量不一致` : '',
} }
}) })
} }
@ -109,17 +109,14 @@
// //
const applyDetails = applyConfirmRef.value.getTableData() // const applyDetails = applyConfirmRef.value.getTableData() //
const applyForm = applyFormRef.value.getApplyForm() // const applyForm = applyFormRef.value.getApplyForm() //
const detailList = scanFormRef.value.getTableData() // const detailList = scanFormRef.value.getTableData() //
api.materialApi.apply.saveApply( api.materialApi.apply.saveApply({ applyForm: applyForm, applyDetails: applyDetails, detailList: detailList }, () => {
{ applyForm: applyForm, applyDetails: applyDetails, detailList: detailList }, console.log('申请成功')
() => { //
console.log('申请成功') loading.value = false
// current.value = 3
loading.value = false })
current.value = 3
},
)
} }
</script> </script>

View File

@ -1,170 +0,0 @@
<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,220 +0,0 @@
<template>
<a-button type="primary" @click="beiginScan">开始扫码</a-button>
<a-button type="primary" danger style="left: 20px" @click="clearScanData">全部重扫</a-button>
<a-input
ref="snInput"
v-model:value="value"
bordered
size="large"
placeholder="输入条形码"
style="margin-top: 20px; margin-bottom: 20px"
@press-enter="autoInsertOneRow"
/>
<slot></slot>
<a-row style="margin-bottom: 20px">
<a-col :span="12">
<a-statistic title="扫码合计" :value="totalValue" style="margin-right: 50px" />
</a-col>
<a-col :span="12">
<a-statistic title="已扫码" :value="remainderValue" />
</a-col>
</a-row>
<!-- Esc键退出编辑功能 -->
<vxe-table
ref="applyDetailTableRef"
border
stripe
show-overflow
max-height="500"
:column-config="{ resizable: true }"
:keyboard-config="{ isEsc: true }"
size="medium"
empty-text="请先扫码物料体条码"
:edit-config="{ trigger: 'click', mode: 'cell' }"
>
<vxe-column type="seq" title="序号" width="60"></vxe-column>
<vxe-column field="id" title="物料id" :visible="false"></vxe-column>
<vxe-column field="barcode" title="物料条码" />
<vxe-column field="name" title="物料名称" />
<vxe-column field="code" title="编码" />
<vxe-column field="spec" title="规格" />
<vxe-column field="typeName" title="类型" />
<vxe-column field="times" title="扫码时间" />
<vxe-column field="oprator" title="操作">
<template #default="{ row }">
<vxe-button mode="text" status="error" @click="removeStep2Row(row)">删除</vxe-button>
</template>
</vxe-column>
</vxe-table>
</template>
<script lang="ts" setup>
import { VxeTableInstance } from 'vxe-table'
import { useTemplateRef } from 'vue'
import { api } from '@/api'
import { message } from 'ant-design-vue'
//
const props = defineProps({
totalValue: {
type: Number,
default: 0,
},
applyId: {
type: Number,
required: false,
default: 0,
},
})
//
const getTableData = () => {
const $table = applyDetailTableRef.value
if ($table) {
return $table.getTableData().fullData.map((item) => ({
assignRule: item.assignRule,
materialId: item.id,
barcode: item.barcode,
code: item.code,
date: item.times,
}))
}
}
//
const getApplyData = () => {
const $table = applyDetailTableRef.value
if ($table) {
return $table.getTableData().fullData.map((item) => ({
applyId: props.applyId,
materialId: item.id,
barcode: item.barcode,
}))
}
}
defineExpose({ getTableData, getApplyData })
const remainderValue = ref(0)
//
const value = ref('')
const input = useTemplateRef('snInput')
const snListCache = ref<string[]>([]) //
// map
const materialMapCache = ref<Map<string, material.Material>>(new Map<string, material.Material>()) // ,keycode
/**
* 请求接口获取缓存数据
*/
const getCache = () => {
api.materialApi.material.all({}, (data) => {
data.forEach((item) => {
if (item.code) {
materialMapCache.value.set(item.code, item) // 6
}
})
})
}
getCache()
interface TableRowVO {
id: number
barcode: string
assignRule: boolean
name: string
code: string
spec: string
type: string
typeName: string
times: string
}
// table
const applyDetailTableRef = ref<VxeTableInstance<TableRowVO>>()
// table
const removeStep2Row = async (row: TableRowVO) => {
const $table = applyDetailTableRef.value
if ($table) {
$table.remove(row)
//sn
snListCache.value = snListCache.value.filter((item) => item !== row.barcode)
// -1
remainderValue.value = remainderValue.value - 1
}
}
function beiginScan() {
// input.value?.focus()
const el = input.value as HTMLInputElement | null
if (el) {
el.focus()
}
}
//
const autoInsertOneRow = async () => {
const $table = applyDetailTableRef.value
const sn = value.value.trim()
/**
* 1. 判断sn是否合法
* 2. 判断sn是否已经存在于表格中需要缓存sn列表
* 3. 根据sn获取物料信息并新增一行到表格中,需要缓存物料信息
* 4. 插入成功后缓存sn
*/
if (sn.length === 0) {
value.value = ''
message.warning('【' + sn + '】条形码为空,请重新扫码', 5)
return
}
if (snListCache.value.includes(sn)) {
value.value = ''
message.warning('【' + sn + '】条形码重复,请重新扫码', 5)
return
}
//
const code = sn.slice(0, 5)
const materialInfo = materialMapCache.value.get(code)
if (!materialInfo) {
value.value = ''
message.warning('【' + sn + '】条形码不合法,请重新扫码', 5)
return
}
if ($table) {
const row: TableRowVO = {
id: materialInfo?.id ?? 0,
barcode: sn,
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)
// sn
snListCache.value.push(sn)
//+1
remainderValue.value = remainderValue.value + 1
//
value.value = ''
}
}
//
const clearScanData = () => {
const $table = applyDetailTableRef.value
if ($table) {
$table.remove()
snListCache.value = []
remainderValue.value = 0
}
}
</script>

View File

@ -0,0 +1,202 @@
<template>
<a-button type="primary" @click="beiginScan">开始扫码</a-button>
<a-button type="primary" danger style="left: 20px;">重新扫码</a-button>
<a-input bordered size="large" ref="snInput" v-model:value="value" placeholder="输入条形码" @pressEnter="autoInsertOneRow"
style="margin-top: 20px; margin-bottom: 20px;" />
<a-row style="margin-bottom: 20px;">
<a-col :span="12">
<a-statistic title="扫码合计" :value="totalValue" style="margin-right: 50px" />
</a-col>
<a-col :span="12">
<a-statistic title="已扫码" :value="remainderValue" />
</a-col>
</a-row>
<!-- Esc键退出编辑功能 -->
<vxe-table border stripe show-overflow ref="applyDetailTableRef" max-height="500" :column-config="{ resizable: true }"
:keyboard-config="{ isEsc: true }" size="medium" empty-text="请先扫码物料体条码"
:edit-config="{ trigger: 'click', mode: 'cell' }">
<vxe-column type="seq" title="序号" width="60"></vxe-column>
<vxe-column field="id" title="物料id" :visible="false"></vxe-column>
<vxe-column field="barcode" title="物料条码" />
<vxe-column field="name" title="物料名称" />
<vxe-column field="code" title="编码" />
<vxe-column field="spec" title="规格" />
<vxe-column field="type" title="类型" />
<vxe-column field="times" title="扫码时间" />
<vxe-column field="oprator" title="操作">
<template #default="{ row }">
<vxe-button mode="text" status="error" @click="removeStep2Row(row)">删除</vxe-button>
</template>
</vxe-column>
</vxe-table>
</template>
<script lang="ts" setup>
import { VxeTableInstance } from 'vxe-table'
import { useTemplateRef } from 'vue'
import { api } from '@/api'
import { message } from 'ant-design-vue';
//
const props = defineProps(
{
totalValue: {
type: Number,
default: 0
},
applyId: {
type: Number,
required: false
}
}
)
//
const getTableData = () => {
const $table = applyDetailTableRef.value
if ($table) {
return $table.getTableData().fullData.map(item => ({
assignRule: item.assignRule,
materialId: item.id,
barcode: item.barcode,
code: item.code,
date: item.times
}))
}
}
//
const getApplyData = () => {
const $table = applyDetailTableRef.value
if ($table) {
return $table.getTableData().fullData.map(item => ({
applyId: props.applyId,
materialId: item.id,
barcode: item.barcode
}))
}}
defineExpose({ getTableData,getApplyData })
const remainderValue = ref(0)
//
const value = ref('')
const input = useTemplateRef('snInput');
const snListCache = ref<string[]>([]) //
// map
const materialMapCache = ref<Map<string, material.Material>>(new Map<string, material.Material>()) // ,keycode
/**
* 请求接口获取缓存数据
*/
const getCache = () => {
api.materialApi.material.all((data) => {
data.forEach(item => {
if (item.code) {
materialMapCache.value.set(item.code, item) // 6
}
})
})
}
getCache();
interface TableRowVO {
id: number
barcode: string
assignRule: string
name: string
code: string
spec: string
type: string
times: string
}
// table
const applyDetailTableRef = ref<VxeTableInstance<TableRowVO>>()
// table
const removeStep2Row = async (row: TableRowVO) => {
const $table = applyDetailTableRef.value
if ($table) {
$table.remove(row)
//sn
snListCache.value = snListCache.value.filter(item => item!== row.barcode)
// -1
remainderValue.value = remainderValue.value - 1
}
}
function beiginScan() {
// input.value?.focus()
const el = input.value as HTMLInputElement | null;
if (el) {
el.focus();
}
}
//
const autoInsertOneRow = async () => {
const $table = applyDetailTableRef.value
const sn = value.value.trim();
/**
* 1. 判断sn是否合法
* 2. 判断sn是否已经存在于表格中需要缓存sn列表
* 3. 根据sn获取物料信息并新增一行到表格中,需要缓存物料信息
* 4. 插入成功后缓存sn
*/
if (sn.length === 0) {
value.value = ''
message.warning('【' + sn + '】条形码为空,请重新扫码', 5)
return;
}
if (snListCache.value.includes(sn)) {
value.value = ''
message.warning('【' + sn + '】条形码重复,请重新扫码', 5)
return;
}
//
const code = sn.slice(0, 6)
const materialInfo = materialMapCache.value.get(code)
if (!materialInfo) {
value.value = ''
message.warning('【' + sn + '】条形码不合法,请重新扫码', 5)
return;
}
if ($table) {
const row: TableRowVO = {
id: materialInfo?.id ?? 0,
barcode: sn,
assignRule: materialInfo?.assignRule ?? '',
name: materialInfo?.name ?? '',
code: materialInfo?.code ?? '',
spec: materialInfo?.spec ?? '',
type: materialInfo?.type ?? '',
times: new Date().toLocaleString()
}
$table.insert(row)
// sn
snListCache.value.push(sn)
//+1
remainderValue.value = remainderValue.value + 1
//
value.value = ''
}
}
</script>

View File

@ -1,34 +0,0 @@
<template>
<a-result status="success" title="操作成功!" sub-title="操作已完成请仔细核对实物与清单确保无误">
<template #extra>
<a-button type="primary" @click="select">查询申请</a-button>
<a-button @click="again">继续提交申请</a-button>
</template>
</a-result>
</template>
<script lang="ts" setup>
import router from '@/router'
//
const props = defineProps(
{
applyType: {
type: String,
default: '',
},
}, // applyType使PURCHASE_RECEIPTLOAN_OUTPURCHASE_RECEIPT: LOAN_OUT:
)
const select = () => {
if (props.applyType === 'PURCHASE_RECEIPT') {
router.push({ path: '/statistic/inbound' })
} else {
router.push({ path: '/statistic/outbound' })
}
}
const again = () => {
router.go(0)
}
</script>

View File

@ -0,0 +1,24 @@
<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."
>
<template #extra>
<a-button type="primary" @click="select">查询申请</a-button>
<a-button @click="again">继续提交申请</a-button>
</template>
</a-result>
</template>
<script lang="ts" setup>
import router from '@/router'
const select = () => {
router.push({ path: '/statistic/inbound' }) //
}
const again = () => {
router.push({ path: '/stock/inbound' })
}
</script>

View File

@ -1,7 +1,8 @@
<template> <template>
<apply-modal apply-type="PURCHASE_RECEIPT"></apply-modal> <apply-modal apply-type='PURCHASE_RECEIPT'></apply-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import applyModal from '@/views/stock/component/apply-modal.vue' import applyModal from "@/views/stock/component/applyModal.vue";
</script> </script>

View File

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

View File

@ -2,29 +2,17 @@
<page-container> <page-container>
<!-- 页面操作栏 --> <!-- 页面操作栏 -->
<template #ops> <template #ops>
<a-row style="width: 600px"> <a-row>
<a-col :span="8"> <a-col :span="18">
<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 <a-input-search
v-model:value="searchKey" v-model:value="searchKey"
:placeholder="`名称/编码`" :placeholder="`请输入物料名称`"
allow-clear allow-clear
enter-button enter-button
@search="loadData()" @search="loadData()"
></a-input-search> ></a-input-search>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="6">
<a-button type="primary" style="margin-left: 10px" @click="addOrEdit()"> <a-button type="primary" style="margin-left: 10px" @click="addOrEdit()">
<template #icon> <template #icon>
<icon-font type="icon-plus" /> <icon-font type="icon-plus" />
@ -45,10 +33,10 @@
:loading="loading" :loading="loading"
row-key="key" row-key="key"
> >
<!-- 操作按钮列 --> <!-- 操作按钮列 -->
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'assignRule'"> <template v-if="column.dataIndex === 'assignRule'">
{{ record.assignRule ? '是' : '否' }} {{ record.assignRule === 'HIGH_VALUE' ? '高价值工具类' : '低值易耗品' }}
</template> </template>
<template v-if="column.dataIndex === 'operation'"> <template v-if="column.dataIndex === 'operation'">
<a-button type="link" :style="{ color: token.colorPrimary }" @click="addOrEdit(record)"> <a-button type="link" :style="{ color: token.colorPrimary }" @click="addOrEdit(record)">
@ -58,12 +46,7 @@
编辑 编辑
</a-button> </a-button>
<a-button <a-button type="link" :style="{ color: token.colorPrimary }">
v-if="record.assignRule"
type="link"
:style="{ color: token.colorPrimary }"
@click="showPrintModal(record.id)"
>
<template #icon> <template #icon>
<icon-font type="icon-print" /> <icon-font type="icon-print" />
</template> </template>
@ -91,17 +74,6 @@
:title="modalTitle" :title="modalTitle"
@ok="doSave" @ok="doSave"
/> />
<!-- 打印条码弹窗 -->
<a-modal
v-model:open="openPrint"
title="条形码打印"
width="100%"
wrap-class-name="full-modal"
@ok="closePrint"
@cancel="closePrint"
>
<printCode ref="printcodeRef" :material-id="materialIdRef"></printCode>
</a-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -109,46 +81,20 @@
import api from '@/api' import api from '@/api'
import { IPage } from '@/api/api' import { IPage } from '@/api/api'
import { notification, theme } from 'ant-design-vue' import { notification, theme } from 'ant-design-vue'
import { config, formItems } from './form' import {config, formItems} from './form'
import { FormDataType } from '@/components/form-render/form-render-types' 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 { useToken } = theme
const { token } = useToken() const { token } = useToken()
const searchKey = ref('') const searchKey = ref('')
const openPrint = ref(false)
const materialPage = ref<IPage<material.Material>>() const materialPage = ref<IPage<material.Material>>()
const loading = ref(false) 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) => { const loadData = async (page = 1, size = 10) => {
loading.value = true loading.value = true
api.materialApi.material.materials( 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) => { (data) => {
loading.value = false loading.value = false
materialPage.value = data materialPage.value = data
@ -160,11 +106,6 @@
// //
const columns = [ const columns = [
{
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{ {
title: '物料编码', title: '物料编码',
dataIndex: 'code', dataIndex: 'code',
@ -173,29 +114,26 @@
title: '物料名称', title: '物料名称',
dataIndex: 'name', dataIndex: 'name',
}, },
{
title: '价格',
dataIndex: 'price',
},
{ {
title: '物料类型', title: '物料类型',
dataIndex: 'typeName', dataIndex: 'type',
}, },
{ {
title: '物料型号', title: '物料型号',
dataIndex: 'spec', dataIndex: 'spec',
}, },
{ {
title: '是否赋码', title: '赋码规则',
dataIndex: 'assignRule', dataIndex: 'assignRule',
}, },
{ {
title: '备注', title: '备注',
dataIndex: 'description', dataIndex: 'description',
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'operation', dataIndex: 'operation',
width: 400,
}, },
] ]
@ -221,7 +159,7 @@
}) })
// //
const items = computed(() => { const items = computed(() => {
return formItems(types.value) return formItems
}) })
// //
@ -229,23 +167,24 @@
return material.value.id ? '编辑物料' : '添加物料' return material.value.id ? '编辑物料' : '添加物料'
}) })
//, //,
const addOrEdit = (u?: Record<string, unknown>) => { const addOrEdit = (u?: Record<string, unknown>) => {
/* /*
1. const addOrEdit = (u?: Record<string, unknown>) => { - 这里定义了一个名为 addOrEdit 的箭头函数 1. const addOrEdit = (u?: Record<string, unknown>) => { - 这里定义了一个名为 addOrEdit 的箭头函数
它接受一个可选参数 u这个参数被定义为 Record<string, unknown> 类型 它接受一个可选参数 u这个参数被定义为 Record<string, unknown> 类型
这意味着它是一个对象其键是字符串值可以是任何类型的数据? 表示这个参数是可选的 这意味着它是一个对象其键是字符串值可以是任何类型的数据? 表示这个参数是可选的
2. material.value = { ...(u ?? { channels: 3 }) } - 这一行的作用是更新 material 变量的值 2. material.value = { ...(u ?? { channels: 3 }) } - 这一行的作用是更新 material 变量的值
这里使用了 ES6 的解构赋值和空值合并运算符??如果传入的参数 u 存在且不是 null undefined 这里使用了 ES6 的解构赋值和空值合并运算符??如果传入的参数 u 存在且不是 null undefined
则将 u 的所有属性复制到一个新的对象中如果 u null undefined则使用 { channels: 3 } 作为默认值 则将 u 的所有属性复制到一个新的对象中如果 u null undefined则使用 { channels: 3 } 作为默认值
然后这个新对象被赋值给 material.value这里的 material 很可能是一个响应式变量通常在 Vue.js 框架中使用用来表示数据模型的一部分 然后这个新对象被赋值给 material.value这里的 material 很可能是一个响应式变量通常在 Vue.js 框架中使用用来表示数据模型的一部分
value 属性则用来访问或修改这个响应式变量的实际值 value 属性则用来访问或修改这个响应式变量的实际值
3. formDrawer.value?.show() - 这一行代码尝试调用 formDrawer 对象的 show 方法来显示表单抽屉 3. formDrawer.value?.show() - 这一行代码尝试调用 formDrawer 对象的 show 方法来显示表单抽屉
这里使用了可选链操作符?.这表示只有当 formDrawer formDrawer.value 都存在即不是 null undefined才会调用 show 方法 这里使用了可选链操作符?.这表示只有当 formDrawer formDrawer.value 都存在即不是 null undefined才会调用 show 方法
这是为了避免因访问不存在的对象属性而引发错误 这是为了避免因访问不存在的对象属性而引发错误
*/ */
// material.value = { ...(u ?? { channels: 3 }) } // material.value = { ...(u ?? { channels: 3 }) }
material.value = { ...u } // u 使 u 使 {} material.value = { ...u}; // u 使 u 使 {}
formDrawer.value?.show() formDrawer.value?.show()
} }
@ -260,7 +199,8 @@
loadData(1) loadData(1)
}, },
}) })
}) });
} }
// //
@ -275,22 +215,4 @@
}) })
}) })
} }
//
const showPrintModal = (applyId: number) => {
openPrint.value = true
materialIdRef.value = applyId
}
//
const closePrint = () => {
openPrint.value = false
printcodeRef.value.clearContent()
}
</script> </script>
<style lang="less">
.notshow {
display: none;
}
</style>

View File

@ -1,132 +0,0 @@
<template>
<div>
<a-flex gap="middle" horizontal>
<span class="ant-form-text">请输入打印的条码数量:</span>
<a-form-item name="input-number" no-style>
<a-input-number v-model:value="inputRef" :min="1" :max="30" />
</a-form-item>
<button @click="printCode">打印条形码</button>
</a-flex>
<div id="barcodes-container">
<!-- 条形码将被动态插入到这里 -->
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import JsBarcode from 'jsbarcode'
import printJS from 'print-js'
import api from '@/api'
const props = defineProps({
//
materialId: {
type: Number,
default: 0,
},
})
const clearContent = () => {
inputRef.value = 0
const container = document.getElementById('barcodes-container')
if (container) container.innerHTML = ''
}
//
defineExpose({ clearContent })
const inputRef = ref(0)
watch(inputRef, (newVal) => {
fetchAndPrintBarcodes(newVal)
})
const printCode = () => {
const container = document.getElementById('barcodes-container')
if (!container) {
window.console.log('条形码容器未找到')
return
}
// 40mm x 30mm
const widthInches = (40 * 0.0393701).toFixed(3) // 1.575
const heightInches = (30 * 0.0393701).toFixed(3) // 1.181
//
const printOptions: printJS.Configuration = {
printable: 'barcodes-container',
type: 'html',
scanStyles: false,
honorMarginPadding: false,
targetStyles: ['*'],
style: `
@page {
size: ${widthInches}in ${heightInches}in;
margin: 0;
}
body {
margin: 0;
padding: 0;
font-size: 6px;
}
#barcodes-container canvas {
display: block;
margin-bottom: 2mm; /* 调整条形码之间的间距 */
}
`,
onLoadingStart: () => console.log('开始加载'),
onLoadingEnd: () => console.log('加载完成'),
onError: (err: unknown) => console.error('打印出错:', err),
}
// 便
container.style.display = 'block'
// printJS
printJS(printOptions)
}
//
const fetchAndPrintBarcodes = (count: number) => {
if (count <= 0) return
//
const container = document.getElementById('barcodes-container')
if (!container) throw new Error('条形码容器未找到')
container.innerHTML = ''
//
api.materialApi.material.generateBarcodes(props.materialId, { count: count }, (res: Array<string>) => {
res.forEach((barcodeString: string, index: number) => {
const canvasId = `barcode-${index}`
const canvasElement = document.createElement('canvas')
//
canvasElement.width = 472 // 40mm
canvasElement.height = 354 // 30mm
canvasElement.id = canvasId
container.appendChild(canvasElement)
// 使JsBarcode
JsBarcode(`#${canvasId}`, barcodeString, {
// format: "EAN13", //
width: 1, // 线
height: 70, //
displayValue: true, //
fontOptions: 'bold', //
fontSize: 12, //
textMargin: 5, //
margin: 10, //
})
})
})
}
//
// onMounted(fetchAndPrintBarcodes);
</script>
<style scoped>
/* 针对打印样式的样式 */
@media print {
#barcodes-container {
display: block;
}
}
</style>

View File

@ -1,7 +1,8 @@
<template> <template>
<apply-modal apply-type="LOAN_OUT"></apply-modal> <apply-modal apply-type='LOAN_OUT'></apply-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import applyModal from '@/views/stock/component/apply-modal.vue' import applyModal from "@/views/stock/component/applyModal.vue";
</script> </script>

View File

@ -2,172 +2,96 @@
<page-container> <page-container>
<!-- 页面操作栏 --> <!-- 页面操作栏 -->
<template #ops> <template #ops>
<a-row style="width: 600px"> <a-row>
<a-col :span="8"> <a-col :span="18">
<a-tree-select <a-input-search v-model:value="searchKey" :placeholder="`请输入`" allow-clear enter-button
v-model:value="typeVal" @search="loadData()"></a-input-search>
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="`名称/编码/型号`"
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-col>
</a-row> </a-row>
</template> </template>
<!-- 页面表格内容 --> <!-- 页面表格内容 -->
<div style="min-height: calc(100vh - 305px)"> <div style="min-height: calc(100vh - 305px)">
<!-- 表格行 --> <!-- 表格行 -->
<a-table <a-table :columns="columns" :data-source="pagedata?.records" bordered :pagination="pagination"
:columns="columns" :loading="loading" row-key="key">
:data-source="pagedata?.records"
bordered
:pagination="pagination"
:loading="loading"
row-key="key"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'applyTypeInfo'">
{{ record.applyTypeInfo.description }}
</template>
</template>
</a-table> </a-table>
</div> </div>
</page-container> </page-container>
</template> </template>
<script setup lang="ts"> <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('') import api from '@/api'
const pagedata = ref<IPage<material.ApplyDTO>>() import { IPage } from '@/api/api'
const loading = ref(false)
const typeVal = ref<string>() //
// const searchKey = ref('')
const loadData = async (page = 1, size = 10) => { const pagedata = ref<IPage<material.ApplyDTO>>()
loading.value = true const loading = ref(false)
api.materialApi.apply.searchPage(
{ //
page: page, const loadData = async (page = 1, size = 10) => {
size: size, loading.value = true
applyType: 1, api.materialApi.apply.searchPage(
type: typeVal.value, {
key: searchKey.value, page: page,
}, size: size,
(data) => { applyType: 1,
pagedata.value = data type:searchKey.value,
loading.value = false code: searchKey.value,
}, name: searchKey.value
) },(data)=>{
pagedata.value = data
loading.value = false
})
}
//
loadData()
//
const columns = [
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '物料编码',
dataIndex: 'code',
},
{
title: '申请人',
dataIndex: 'applicant',
},
{
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '申请数量',
dataIndex: 'applyNum',
},
{
title: '确认数量',
dataIndex: 'confirmNum',
},
{
title: '备注',
dataIndex: 'remark',
} }
// ]
loadData()
// //
const downloadData = () => { const pagination = computed(() => {
// URLSearchParams return {
const params = new URLSearchParams() current: pagedata.value?.current,
if (typeVal.value) { pageSize: pagedata.value?.size,
params.append('type', typeVal.value) total: pagedata.value?.total,
} onChange: (page: number, pageSize: number) => {
if (searchKey.value) { loadData(page, pageSize)
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',
},
{
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '备注',
dataIndex: 'remark',
},
]
//
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 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> </script>

View File

@ -2,32 +2,17 @@
<page-container> <page-container>
<!-- 页面操作栏 --> <!-- 页面操作栏 -->
<template #ops> <template #ops>
<a-row style="width: 600px"> <a-row>
<a-col :span="8"> <a-col :span="18">
<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 <a-input-search
v-model:value="searchKey" v-model:value="searchKey"
placeholder="名称/编码/型号" :placeholder="`请输入物料名称`"
allow-clear allow-clear
enter-button enter-button
width="100%"
@search="loadData()" @search="loadData()"
></a-input-search> ></a-input-search>
</a-col> </a-col>
<a-col :span="8">
<a-button type="primary" @click="downloadData()">导出</a-button>
</a-col>
</a-row> </a-row>
</template> </template>
<!-- 页面表格内容 --> <!-- 页面表格内容 -->
@ -41,40 +26,32 @@
:loading="loading" :loading="loading"
row-key="key" row-key="key"
> >
<!-- 操作按钮列 --> <!-- 操作按钮列 -->
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'assignRule'"> <template v-if="column.dataIndex === 'assignRule'">
{{ record.assignRule ? '是' : '否' }} {{ record.assignRule === 1 ? '高价值工具类' : '低值易耗品' }} <!-- 这里定义为0和10再回显的时候不展示-->
<!-- 这里定义为0和10再回显的时候不展示-->
</template> </template>
</template> </template>
</a-table> </a-table>
</div> </div>
</page-container> </page-container>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '@/api' import api from '@/api'
import { IPage } from '@/api/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 searchKey = ref('')
const materialPage = ref<IPage<material.Material>>() const materialPage = ref<IPage<material.Material>>()
const loading = ref(false) const loading = ref(false)
const typeVal = ref<string>() //
// //
const loadData = async (page = 1, size = 10) => { const loadData = async (page = 1, size = 10) => {
loading.value = true loading.value = true
api.materialApi.material.materials( 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) => { (data) => {
loading.value = false loading.value = false
materialPage.value = data materialPage.value = data
@ -84,62 +61,37 @@
// //
loadData() 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 = [ const columns = [
{ {
title: '序号', title: '物料编码',
dataIndex: 'key', dataIndex: 'code',
customRender: ({ index }: { index: number }) => `${index + 1}`,
}, },
{ {
title: '物料名称', title: '物料名称',
dataIndex: 'name', dataIndex: 'name',
}, },
{
title: '物料编码',
dataIndex: 'code',
},
{ {
title: '物料类型', title: '物料类型',
dataIndex: 'typeName', dataIndex: 'type',
}, },
{ {
title: '物料型号', title: '物料型号',
dataIndex: 'spec', dataIndex: 'spec',
}, },
{ {
title: '是否赋码', title: '赋码规则',
dataIndex: 'assignRule', dataIndex: 'assignRule',
}, },
{ {
title: '价格',
dataIndex: 'price',
},
{
title: '库存数量', title: '库存数量',
dataIndex: 'stock', dataIndex: 'stock',
}, },
{ {
title: '备注', title: '备注',
dataIndex: 'description', dataIndex: 'description',
}, }
] ]
// //
@ -154,17 +106,5 @@
} }
}) })
//
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> </script>

View File

@ -2,258 +2,96 @@
<page-container> <page-container>
<!-- 页面操作栏 --> <!-- 页面操作栏 -->
<template #ops> <template #ops>
<a-row style="width: 600px"> <a-row>
<a-col :span="8"> <a-col :span="18">
<a-tree-select <a-input-search v-model:value="searchKey" :placeholder="`请输入`" allow-clear enter-button
v-model:value="typeVal" @search="loadData()"></a-input-search>
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="`名称/编码/型号`"
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-col>
</a-row> </a-row>
</template> </template>
<!-- 页面表格内容 --> <!-- 页面表格内容 -->
<div style="min-height: calc(100vh - 305px)"> <div style="min-height: calc(100vh - 305px)">
<!-- 表格行 --> <!-- 表格行 -->
<a-table <a-table :columns="columns" :data-source="pagedata?.records" bordered :pagination="pagination"
:columns="columns" :loading="loading" row-key="key">
:data-source="pagedata?.records"
bordered
:pagination="pagination"
:loading="loading"
row-key="key"
>
<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> </a-table>
</div> </div>
</page-container> </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> </template>
<script setup lang="ts"> <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() import api from '@/api'
const pagedata = ref<IPage<material.ApplyDTO>>() import { IPage } from '@/api/api'
const loading = ref(false)
const typeVal = ref<string>() //
const userStore = useUserStore() const searchKey = ref('')
// const pagedata = ref<IPage<material.ApplyDTO>>()
const loadData = async (page = 1, size = 10) => { const loading = ref(false)
loading.value = true
api.materialApi.apply.searchPage( //
{ const loadData = async (page = 1, size = 10) => {
page: page, loading.value = true
size: size, api.materialApi.apply.searchPage(
applyType: 3, {
type: typeVal.value, page: page,
key: searchKey.value, size: size,
}, applyType: 3,
(data) => { type:searchKey.value,
pagedata.value = data code: searchKey.value,
loading.value = false name: searchKey.value
}, },(data)=>{
) pagedata.value = data
loading.value = false
})
}
//
loadData()
//
const columns = [
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '物料编码',
dataIndex: 'code',
},
{
title: '申请人',
dataIndex: 'applicant',
},
{
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '申请数量',
dataIndex: 'applyNum',
},
{
title: '确认数量',
dataIndex: 'confirmNum',
},
{
title: '备注',
dataIndex: 'remark',
} }
// ]
loadData()
// //
const downloadData = () => { const pagination = computed(() => {
// URLSearchParams return {
const params = new URLSearchParams() current: pagedata.value?.current,
if (typeVal.value) { pageSize: pagedata.value?.size,
params.append('type', typeVal.value) total: pagedata.value?.total,
} onChange: (page: number, pageSize: number) => {
if (searchKey.value) { loadData(page, pageSize)
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: 'spec',
},
{
title: '出库类型',
dataIndex: 'applyTypeInfo',
},
{
title: '申请数量',
dataIndex: 'applyNum',
},
{
title: '确认数量',
dataIndex: 'confirmNum',
},
{
title: '申请人',
dataIndex: 'applicant',
},
{
title: '审核人',
dataIndex: 'reviewer',
},
{
title: '审核状态',
dataIndex: 'reviewResult',
},
{
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '备注',
dataIndex: 'remark',
},
{
title: '操作',
dataIndex: 'operation',
},
]
//
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 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> </script>

View File

@ -1,295 +1,120 @@
<template> <template>
<page-container> <page-container>
<!-- 页面操作栏 --> <!-- 页面操作栏 -->
<template #ops> <template #ops>
<a-row> <a-row>
<a-col :span="6" style="width: 280px"> <a-col :span="18">
<a-date-picker <a-input-search v-model:value="searchKey" :placeholder="`请输入`" allow-clear enter-button
v-model:value="startDate" @search="loadData()"></a-input-search>
:format="'YYYY-MM-DD HH:mm:ss'" </a-col>
placeholder="开始日期大于" </a-row>
style="width: 100%" </template>
/> <!-- 页面表格内容 -->
</a-col> <div style="min-height: calc(100vh - 305px)">
<a-col :span="6" style="width: 180px"> <!-- 表格行 -->
<a-select <a-table :columns="columns" :data-source="auditPage?.records" bordered :pagination="pagination" :loading="loading"
v-model:value="status" row-key="key">
placeholder="盘点状态" <template #bodyCell="{ column, record }">
:max-tag-count="1" <template v-if="column.dataIndex === 'auditType'">
mode="multiple" {{ record.auditType === 'ALL' ? '全部盘点' : '部分盘点' }}
style="width: 100%" </template>
> <template v-if="column.dataIndex === 'reviewResult'">
<a-select-option value="WAIT_SCAN">待扫码</a-select-option> <template v-if="record.reviewResult === 'WAIT_CHECK'"> 待盘点</template>
<a-select-option value="WAIT_SUBMIT">待提交</a-select-option> <template v-if="record.reviewResult === 'PASS'"> 通过</template>
<a-select-option value="WAIT_REVIEW">待审核</a-select-option> <template v-if="record.reviewResult === 'REJECT'"> 拒绝 </template>
<a-select-option value="PASS">审核通过</a-select-option> <template v-if="record.reviewResult === 'UN_PASS'"> 未通过</template>
<a-select-option value="REJECT">退回</a-select-option> <template v-if="record.reviewResult === 'WAIT_AUDIT'"> 待审核</template>
</a-select> </template>
</a-col> </template>
<a-col :span="6" style="width: 180px"> </a-table>
<a-select v-model:value="auditType" :allow-clear="true" placeholder="盘点类型" style="width: 100%"> </div>
<a-select-option value="SCAN">扫码盘点</a-select-option> </page-container>
<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)">
<!-- 表格行 -->
<a-table
:columns="columns"
:data-source="auditPage?.records"
bordered
:pagination="pagination"
:loading="loading"
row-key="key"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'auditType'">
{{ record.auditType === 'SCAN' ? '扫码盘点' : '人工盘点' }}
</template>
<template v-if="column.dataIndex === 'reviewResult'">
<!-- 不同状态不同颜色 -->
<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, record.auditType, true)"
>
审核
</a-button>
<a-button
type="link"
style="margin-left: 10px"
@click="showResultModal(record.id, record.auditType, false)"
>
查看
</a-button>
</template>
</template>
</a-table>
</div>
</page-container>
<!-- 审核弹窗 -->
<a-modal v-model:open="openResult" title="盘点异常数据:" width="80%" :confirm-loading="confirmLoading" @ok="submit">
<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>
</a-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '@/api' import api from '@/api';
import { IPage } from '@/api/api' import { IPage } from '@/api/api'
import resultForm from '../stocktaking/result-form.vue' import { useUserStore } from '@/stores/user'
import { Dayjs } from 'dayjs'
import { http } from '@/settings/http'
const applyIdRef = 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 searchKey = ref('')
const loadData = async (page = 1, size = 10) => { const auditPage = ref<IPage<material.ApplyForm>>()
loading.value = true const loading = ref(false)
api.materialApi.apply.searchAuditPage(
{
page: page,
size: size,
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
loading.value = false
},
)
}
//
loadData()
//
const downloadData = () => {
// URLSearchParams
const params = new URLSearchParams()
// const userStore = useUserStore()
if (auditType.value) { //
params.append('auditType', auditType.value) const loadData = async (page = 1, size = 10) => {
} loading.value = true
if (startDate.value) { api.materialApi.apply.searchAuditPage(
// 'Z' {
const formattedDate = startDate.value.toISOString().replace('Z', '').slice(0, -4) page: page,
params.append('createDate', formattedDate) size: size,
} taker: userStore.userName
if (taker.value) { }, (data) => {
params.append('taker', taker.value) auditPage.value = data
} loading.value = false
if (status.value && status.value.length > 0) { });
// }
status.value.forEach((value) => { //
params.append('reviewResults', value) loadData()
})
}
// URL //
const url = `${http.prefix}/apply/audit/download-excel?${params.toString()}` const columns = [
{
title: '盘点类型',
dataIndex: 'auditType',
},
{
title: '申请人',
dataIndex: 'applicant',
},
{
title: '盘点人',
dataIndex: 'taker',
},
{
title: '任务状态',
dataIndex: 'reviewResult',
},
{
title: '盘点审核人',
dataIndex: 'reviewer',
},
{
title: '创建时间',
dataIndex: 'createdTime',
},
{
title: '盘点结果(系统生成)',
dataIndex: 'result',
},
{
title: '异常原因',
dataIndex: 'exception',
},
{
title: '处理方式',
dataIndex: 'handle',
},
//
window.open(url)
}
//
const columns = [
{
title: '序号',
dataIndex: 'key',
customRender: ({ index }: { index: number }) => `${index + 1}`,
},
{
title: '盘点类型',
dataIndex: 'auditType',
},
{
title: '申请人',
dataIndex: 'applicant',
},
{
title: '盘点人',
dataIndex: 'taker',
},
{
title: '盘点审核人',
dataIndex: 'reviewer',
},
{
title: '申请日期',
dataIndex: 'applyDate',
},
{
title: '审核意见',
dataIndex: 'reviewRemark',
},
{
title: '审核结果',
dataIndex: 'reviewResult',
},
{
title: '操作',
dataIndex: 'operation',
},
]
// ]
const pagination = computed(() => {
return {
current: auditPage.value?.current,
pageSize: auditPage.value?.size,
total: auditPage.value?.total,
onChange: (page: number, pageSize: number) => {
loadData(page, pageSize)
},
}
})
// flag=trueflag=false //
const flag = ref(false) const pagination = computed(() => {
const openResult = ref<boolean>(false) return {
const auditTypeRef = ref<'SCAN' | 'MANUAL'>() current: auditPage.value?.current,
pageSize: auditPage.value?.size,
total: auditPage.value?.total,
onChange: (page: number, pageSize: number) => {
loadData(page, pageSize)
},
}
})
//
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,
reviewResult: reviewResult.value,
remark: remark.value,
},
() => {
confirmLoading.value = false
openResult.value = false
loadData()
},
)
}
</script> </script>

View File

@ -1,120 +1,97 @@
import { FormItem, FormConfig } from '@/components/form-render/form-render-types' 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 = { export const config: FormConfig = {
layout: 'horizontal', layout: 'horizontal',
colon: true, colon: true,
hideRequiredMark: false, hideRequiredMark: false,
labelAlign: 'right', labelAlign: 'right',
scrollToFirstError: false, scrollToFirstError: false,
validateOnRuleChange: true, validateOnRuleChange: true,
labelCol: { labelCol: {
span: 4, span: 4,
offset: 0, offset: 0,
}, },
} }
export const formItems = ( export const formItems = (materialOptions: Array<{ value: string | undefined, label: string | undefined }>,
materialOptions: Array<{ value: string | undefined; label: string | undefined }>, required : Ref<boolean>,
required: boolean, persons: Array<{ value: string | undefined, label: string | undefined }>): FormItem[] => [
persons: Array<{ value: string | undefined; label: string | undefined }>, {
types: Array<TreeDataNode>, group: 'form',
): FormItem[] => [ type: 'radio',
{ config: {
group: 'form', autoLink: true,
type: 'radio', hasFeedback: false,
config: { label: '盘点类型',
autoLink: true, name: 'auditType',
hasFeedback: false, required: true,
label: '盘点类型', },
name: 'auditType', properties: {
required: true, size: 'default',
}, optionType: 'button',
properties: { buttonStyle: 'solid',
size: 'default', defaultValue: "ALL",
optionType: 'button', options: [{ value: "ALL", label: "全部盘点" }, { value: "PARTIAL", label: "部分盘点" }]
buttonStyle: 'solid', },
defaultValue: 'SCAN', rules: [],
options: [ },
{ value: 'SCAN', label: '扫码盘点' }, {
{ value: 'MANUAL', label: '人工盘点' }, group: 'form',
], type: 'select',
}, hidden: true,
rules: [], config: {
}, autoLink: true,
{ hasFeedback: false,
type: 'tree-select', label: '物料选择',
group: 'form', name: 'ids',
config: { },
label: '物料类型选择', properties: {
name: 'types', size: 'default',
required: true, mode: 'multiple',
}, required: required.value,
properties: { placeholder: '请选择物料',
size: 'default', options: materialOptions,
placeholder: '请选择物料类型', },
multiple: true, rules: [],
treeData: types, },
}, {
rules: [], group: 'form',
}, type: 'select',
{ config: {
group: 'form', autoLink: true,
type: 'select', hasFeedback: false,
hidden: true, label: '盘点人员',
config: { name: 'taker',
autoLink: true, required: true,
hasFeedback: false, },
label: '物料选择', properties: {
name: 'ids', size: 'default',
required: true, type: 'text',
}, allowClear: false,
properties: { bordered: true,
size: 'default', showCount: false,
mode: 'multiple', options: persons,
required: required, placeholder: '请输入盘点人员',
placeholder: '请选择物料', },
options: materialOptions, rules: [],
}, },
rules: [], {
}, type: 'select',
{ group: 'form',
group: 'form', config: {
type: 'select', autoLink: true,
config: { hasFeedback: false,
autoLink: true, label: '审核人员',
hasFeedback: false, name: 'reviewer',
label: '盘点人员', required: true,
name: 'taker', },
required: true, properties: {
}, size: 'default',
properties: { controls: true,
size: 'default', placeholder: '请填写审核人员',
type: 'text', options: persons,
allowClear: false, },
bordered: true, rules: [],
showCount: false, }
options: persons, ]
placeholder: '请输入盘点人员',
},
rules: [],
},
{
type: 'select',
group: 'form',
config: {
autoLink: true,
hasFeedback: false,
label: '审核人员',
name: 'reviewer',
required: true,
},
properties: {
size: 'default',
controls: true,
placeholder: '请填写审核人员',
options: persons,
},
rules: [],
},
]

View File

@ -1,84 +0,0 @@
<template>
<vxe-table
ref="resulRef"
border
stripe
show-overflow
max-height="500"
:column-config="{ resizable: true }"
:data="tableData"
:keyboard-config="{ isEsc: true }"
size="medium"
empty-text="盘点数据和库存数据一致,可以直接提交审核"
:edit-config="{ trigger: 'click', mode: 'cell' }"
>
<vxe-column type="seq" title="序号" width="60"></vxe-column>
<vxe-column field="id" title="ID" :visible="false" />
<vxe-column field="materialName" title="物料名称" />
<vxe-column field="barcode" title="物料条码" />
<vxe-column field="exceptionReason" title="异常原因">
<template #default="{ row }">
<template v-if="row.exceptionReason === 'SOCK_IN_BUT_SCAN_NOT_EXIST'">库房在库状态但是扫码不存在</template>
<template v-if="row.exceptionReason === 'SOCK_OUT_BUT_SCAN_EXIST'">库房不是在库状态但是扫码存在</template>
<template v-if="row.exceptionReason === 'SOCK_NOT_EXIST_BUT_SCAN_EXIST'">库房不存在该条码但是扫码存在</template>
</template>
</vxe-column>
<vxe-column field="exceptionHandle" title="异常处理(手动选择)" :edit-render="handelEditRender"></vxe-column>
<vxe-column field="remark" title="备注说明" width="25%" :edit-render="{ name: 'textarea' }"></vxe-column>
</vxe-table>
<slot></slot>
</template>
<script setup lang="ts">
import api from '@/api'
import { toRefs } from 'vue'
import type { VxeColumnPropTypes, VxeTableInstance } from 'vxe-table'
import type { VxeSelectProps } from 'vxe-pc-ui'
//
const props = defineProps({
applyId: {
type: Number,
required: false,
default: 0,
},
})
const resulRef = ref<VxeTableInstance<material.StocktakingScanExceptionalData>>()
const getTableData = () => {
const $table = resulRef.value
if ($table) {
return $table.getTableData().fullData.map((item) => ({
id: item.id,
exceptionHandle: item.exceptionHandle,
remark: item.remark,
}))
}
}
defineExpose({ getTableData })
const { applyId } = toRefs(props)
const tableData = ref<Array<material.StocktakingScanExceptionalData>>()
const result = ref()
api.materialApi.apply.getComparisonRes(props.applyId + '', (data) => {
result.value = data
tableData.value = data.exceptionalData
})
//id
watch(applyId, () => {
api.materialApi.apply.getComparisonRes(props.applyId + '', (data) => {
result.value = data
tableData.value = data.exceptionalData
})
})
//
const handelEditRender = ref<VxeColumnPropTypes.EditRender<undefined, VxeSelectProps>>({
name: 'VxeSelect',
options: [
{ label: '标记为丢失', value: 'MARK_LOST' },
{ label: '保持不变', value: 'MARK_KEEP' },
{ label: '标记为归还', value: 'MARK_RETURN' },
{ label: '标记为舍弃', value: 'MARK_DISCARD' },
{ label: '标记为新入库', value: 'MARK_NEW' },
],
})
</script>

View File

@ -0,0 +1,79 @@
<template>
<vxe-table border stripe show-overflow ref="resulRef" max-height="500" :column-config="{ resizable: true }"
:data="tableData" :keyboard-config="{ isEsc: true }" size="medium" empty-text="盘点数据和库存数据一致,可以直接提交审核"
:edit-config="{ trigger: 'click', mode: 'cell' }">
<vxe-column type="seq" title="序号" width="60"></vxe-column>
<vxe-column field="id" title="ID" :visible="false" />
<vxe-column field="materialName" title="物料名称" />
<vxe-column field="barcode" title="物料条码" />
<vxe-column field="exceptionReason" title="异常原因">
<template #default="{ row }">
<template v-if="row.exceptionReason === 'SOCK_IN_BUT_SCAN_NOT_EXIST'"> 库房在库状态但是扫码不存在</template>
<template v-if="row.exceptionReason === 'SOCK_OUT_BUT_SCAN_EXIST'"> 库房不是在库状态但是扫码存在</template>
<template v-if="row.exceptionReason === 'SOCK_NOT_EXIST_BUT_SCAN_EXIST'"> 库房不存在该条码但是扫码存在</template>
</template>
</vxe-column>
<vxe-column field="exceptionHandle" title="异常处理(手动选择)" :edit-render="handelEditRender"></vxe-column>
<vxe-column field="remark" title="备注说明" width="25%" :edit-render="{ name: 'textarea' }"></vxe-column>
</vxe-table>
</template>
<script setup lang="ts">
import api from '@/api'
import { toRefs } from 'vue';
import type { VxeColumnPropTypes,VxeTableInstance } from 'vxe-table'
import type { VxeSelectProps } from 'vxe-pc-ui'
//
const props = defineProps(
{
applyId: {
type: Number,
required: false,
default: 0
}
}
)
const resulRef = ref<VxeTableInstance<material.StocktakingScanExceptionalData>>()
const getTableData = () => {
const $table = resulRef.value
if ($table) {
return $table.getTableData().fullData.map(item => ({
id: item.id,
exceptionHandle: item.exceptionHandle,
remark: item.remark
}))
}}
defineExpose({getTableData});
const { applyId } = toRefs(props)
const tableData = ref<Array<material.StocktakingScanExceptionalData>>()
const result = ref()
api.materialApi.apply.getComparisonRes(props.applyId + '', (data) => {
result.value = data
tableData.value = data.exceptionalData
})
//id
watch(applyId, () => {
api.materialApi.apply.getComparisonRes(props.applyId + '', (data) => {
result.value = data
tableData.value = data.exceptionalData
})
})
//
const handelEditRender = ref<VxeColumnPropTypes.EditRender<undefined, VxeSelectProps>>({
name: 'VxeSelect',
options: [
{ label: '入库+1', value: 'INBOUND' },
{ label: '不入库-1', value: 'OUTBOUND' },
{ label: '丢弃-1', value: 'DISCARD' },
]
})
</script>

View File

@ -1,403 +1,303 @@
<template> <template>
<page-container> <page-container>
<!-- 页面操作栏 --> <!-- 页面操作栏 -->
<template #ops> <template #ops>
<a-row> <a-row>
<a-col :span="18"> <a-col :span="18">
<a-input-search <a-input-search v-model:value="searchKey" :placeholder="`请输入`" allow-clear enter-button
v-model:value="searchKey" @search="loadData()"></a-input-search>
:placeholder="`请输入`" </a-col>
allow-clear <a-col :span="6">
enter-button <a-button type="primary" style="margin-left: 10px" @click="formDrawer?.show()">
@search="loadData()" <template #icon>
></a-input-search> <icon-font type="icon-plus" />
</a-col> </template>
<a-col :span="6"> 申请扫码
<a-button type="primary" style="margin-left: 10px" @click="formDrawer?.show()"> </a-button>
<template #icon>
<icon-font type="icon-plus" /> </a-col>
</template> </a-row>
新增盘点 </template>
</a-button> <!-- 页面表格内容 -->
</a-col> <div style="min-height: calc(100vh - 305px)">
</a-row> <!-- 表格行 -->
</template> <a-table :columns="columns" :data-source="auditPage?.records" bordered :pagination="pagination" :loading="loading"
<!-- 页面表格内容 --> row-key="key">
<div style="min-height: calc(100vh - 305px)"> <template #bodyCell="{ column, record }">
<!-- 表格行 --> <template v-if="column.dataIndex === 'auditType'">
<a-table {{ record.auditType === 'ALL' ? '全部盘点' : '部分盘点' }}
:columns="columns" </template>
:data-source="auditPage?.records" <template v-if="column.dataIndex === 'reviewResult'">
bordered <template v-if="record.reviewResult === 'WAIT_SCAN'"> 待扫码</template>
:pagination="pagination" <template v-if="record.reviewResult === 'WAIT_SUBMIT'"> 待提交</template>
:loading="loading" <template v-if="record.reviewResult === 'WAIT_REVIEW'"> 待审核 </template>
row-key="key" <template v-if="record.reviewResult === 'PASS'"> 审核通过</template>
> <template v-if="record.reviewResult === 'REJECT'"> 退回</template>
<template #bodyCell="{ column, record }"> </template>
<template v-if="column.dataIndex === 'auditType'"> <template v-if="column.dataIndex === 'operation'">
{{ record.auditType === 'SCAN' ? '扫码盘点' : '人工盘点' }} <a-button type="link" @click="showModal(record.id)" v-if="record.reviewResult === 'WAIT_SCAN'
</template> || record.reviewResult === 'REJECT'">
<template v-if="column.dataIndex === 'reviewResult'"> <template #icon>
<template v-if="record.reviewResult === 'WAIT_SCAN'"><span style="color: #faad14">待盘点</span></template> <icon-font type="icon-edit" />
<template v-if="record.reviewResult === 'WAIT_SUBMIT'"><span style="color: #1890ff">待提交</span></template> </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> </a-button>
<template v-if="record.reviewResult === 'REJECT'"><span style="color: #f5222d">退回</span></template> <a-button type="link" style="margin-left: 10px" @click="showResultModal(record.id)"
</template> v-if="record.reviewResult === 'WAIT_SUBMIT' || record.reviewResult === 'WAIT_SCAN'">
<template v-if="column.dataIndex === 'operation'"> <template #icon>
<a-button <icon-font type="icon-plus" />
v-if="record.reviewResult === 'WAIT_SCAN' || record.reviewResult === 'REJECT'" </template>
type="link" 生成结果
@click="showModal(record.id, record.auditType)" </a-button>
> <a-button type="link" style="margin-left: 10px" v-if="record.reviewResult === 'WAIT_REVIEW'">
<template #icon> <template #icon>
<icon-font type="icon-edit" /> <icon-font type="icon-plus" />
</template> </template>
开始盘点 审核
</a-button> </a-button>
<a-button </template>
v-if="record.reviewResult === 'WAIT_SUBMIT'" </template>
type="link"
style="margin-left: 10px" </a-table>
@click="showResultModal(record.id)" </div>
> </page-container>
<template #icon>
<icon-font type="icon-plus" /> <!-- 新增申请抽屉 -->
</template> <form-drawer ref="formDrawer" v-model="applyForm" :form-items="items" :config="formConfig"
生成结果 :disabled-fields=disabledFields @ok="doSave" title="扫码盘点" />
</a-button> <!-- 盘点作业弹窗 -->
</template> <a-modal v-model:open="open" title="盘点作业" width="100%" wrap-class-name="full-modal" @ok="handleOk"
</template> :confirm-loading="confirmLoading">
</a-table> <scan-form ref="scanFormRef" :apply-id="applyIdRef" :total-value="88"></scan-form>
</div> </a-modal>
</page-container> <!-- 盘点结果弹窗 -->
<a-modal v-model:open="openResult" title="盘点异常数据:" okText= "提交审核" @ok="submitHandle" width="80%" :confirm-loading="confirmLoading">
<result-form ref="handleResultRef" :apply-id="applyIdRef"></result-form>
</a-modal>
<!-- 新增申请抽屉 -->
<form-drawer
ref="formDrawer"
v-model="applyForm"
:form-items="items"
:config="formConfig"
title="扫码盘点"
@ok="doSave"
/>
<!-- 盘点作业弹窗 -->
<a-modal
v-model:open="open"
title="盘点作业"
width="100%"
wrap-class-name="full-modal"
:confirm-loading="confirmLoading"
@ok="handleOk"
@cancel="cancel"
>
<scan-form ref="scanFormRef" :apply-id="applyIdRef" :total-value="totalValue">
<p>
{{ msg }}
</p>
</scan-form>
</a-modal>
<!-- 盘点结果弹窗 -->
<a-modal
v-model:open="openResult"
title="盘点异常数据:"
ok-text="提交审核"
width="80%"
:confirm-loading="confirmLoading"
@ok="submitHandle"
>
<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> </template>
<script setup lang="ts"> <script setup lang="ts">
import api from '@/api' import api from '@/api';
import { IPage } from '@/api/api' import { IPage } from '@/api/api'
import FormDrawer from '@/components/form-render/form-drawer.vue' import FormDrawer from '@/components/form-render/form-drawer.vue'
import { config, formItems } from './form' import { config, formItems } from './form'
import { notification } from 'ant-design-vue' import { notification } from 'ant-design-vue'
import dayjs from 'dayjs' import dayjs from 'dayjs';
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import scanForm from '../component/scan-form.vue' import scanForm from '../component/scanForm.vue'
import resultForm from './result-form.vue' import resultForm from '../stocktaking/resultForm.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: 'SCAN',
applicant: userStore.fullName,
type: 'AUDIT',
applyDate: dayjs() + '',
confirm: false,
reviewResult: 'WAIT_SCAN',
types: [],
})
const msg = ref()
const totalValue = ref(0) //
// const searchKey = ref('')
const auditPage = ref<IPage<material.ApplyForm>>() const userStore = useUserStore()
const loading = ref(false) const scanFormRef = ref(); //
const handleResultRef = ref(); //
const confirmLoading = ref(false) // loading
//
const applyForm = ref<Partial<material.AuditApplyInfo>>({
auditType: 'ALL',
applicant: userStore.userName,
type: 'AUDIT',
applyDate: dayjs() + '',
isConfirm: false,
reviewResult: 'WAIT_SCAN'
// })
const materialList = ref<Array<{ value: string | undefined; label: string | undefined }>>([])
const personList = ref<Array<{ value: string | undefined; label: string | undefined }>>([])
const required = ref(true)
api.aclApi.user.all((data) => {
personList.value = data.map((item) => {
return {
value: item?.fullName,
label: item?.fullName,
}
})
})
// // //
// const disabledFields = computed(() => { const auditPage = ref<IPage<material.ApplyForm>>()
// return applyForm.value?.auditType === 'ALL' ? ['ids'] : [] const loading = ref(false)
// })
// //
watch( const materialList = ref<Array<{ value: string | undefined, label: string | undefined }>>([])
applyForm, const personList = ref<Array<{ value: string | undefined, label: string | undefined }>>([])
() => { const required = ref(false)
if (applyForm.value && applyForm.value.types && applyForm.value.types.length > 0) { api.materialApi.material.all((data) => {
api.materialApi.material.all({ type: applyForm.value.types.join(',') }, (data) => { materialList.value = data.map(item => {
materialList.value = data.map((item) => { return {
return { value: item?.id + '',
value: item?.id + '', label: item?.name
label: item?.name + '(' + item.spec + ')', }
} })
}) })
}) api.aclApi.user.all((data) => {
} personList.value = data.map(item => {
}, return {
{ value: item?.name,
immediate: true, label: item?.fullName
deep: true, }
}, })
) })
// //
const types = ref<Array<TreeDataNode>>([]) const disabledFields = computed(() => {
api.materialApi.type.trees((data) => { if (applyForm.value?.auditType !== 'ALL') {
types.value = children(data) required.value = true
}) }
const children = (res: Array<material.TypeTree>): Array<TreeDataNode> => { return applyForm.value?.auditType === 'ALL' ? ['materials'] : []
return res.map((areaTree) => ({ })
label: areaTree.label,
value: areaTree.value,
children: areaTree.children ? children(areaTree.children) : [],
key: areaTree.value,
}))
}
// //
const formDrawer = ref<typeof FormDrawer>() const formDrawer = ref<typeof FormDrawer>()
const formConfig = computed(() => { const formConfig = computed(() => {
return config return config
}) })
// //
const items = computed(() => { const items = computed(() => {
//required return formItems(materialList.value, required, personList.value)
return formItems(materialList.value, required.value, personList.value, types.value) })
}) //
// const loadData = async (page = 1, size = 10) => {
const loadData = async (page = 1, size = 10) => { loading.value = true
loading.value = true api.materialApi.apply.searchAuditPage(
api.materialApi.apply.searchAuditPage( {
{ page: page,
page: page, size: size,
size: size, reviewResults: ['WAIT_SUBMIT', 'WAIT_SCAN', 'WAIT_REVIEW', 'PASS'],
reviewResults: ['WAIT_SUBMIT', 'WAIT_SCAN', 'WAIT_REVIEW', 'REJECT'], }, (data) => {
}, auditPage.value = data
(data) => { loading.value = false
auditPage.value = data });
loading.value = false }
}, //
) loadData()
}
//
loadData()
// //
const columns = [ const columns = [
{ {
title: '序号', title: '申请ID',
dataIndex: 'key', dataIndex: 'id',
customRender: ({ index }: { index: number }) => `${index + 1}`, },
}, {
{ title: '盘点类型',
title: '盘点类型', dataIndex: 'auditType',
dataIndex: 'auditType', },
}, {
{ title: '申请人',
title: '申请人', dataIndex: 'applicant',
dataIndex: 'applicant', },
}, {
{ title: '盘点人',
title: '盘点人', dataIndex: 'taker',
dataIndex: 'taker', },
}, {
{ title: '任务状态',
title: '任务状态', dataIndex: 'reviewResult',
dataIndex: 'reviewResult', },
}, {
{ title: '盘点审核人',
title: '审核意见', dataIndex: 'reviewer',
dataIndex: 'reviewRemark', },
},
{
title: '盘点审核人',
dataIndex: 'reviewer',
},
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createdTime', dataIndex: 'createdTime',
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'operation', dataIndex: 'operation',
}, width: 400,
] }
]
// //
const pagination = computed(() => { const pagination = computed(() => {
return { return {
current: auditPage.value?.current, current: auditPage.value?.current,
pageSize: auditPage.value?.size, pageSize: auditPage.value?.size,
total: auditPage.value?.total, total: auditPage.value?.total,
onChange: (page: number, pageSize: number) => { onChange: (page: number, pageSize: number) => {
loadData(page, pageSize) loadData(page, pageSize)
}, },
} }
}) })
// //
const doSave = (_data: material.AuditApplyInfo) => { const doSave = (_data: material.AuditApplyInfo) => {
api.materialApi.apply.auditApply(_data, () => { api.materialApi.apply.auditApply(_data, () => {
formDrawer.value?.close() formDrawer.value?.close()
notification.success({ notification.success({
message: '操作成功', message: '操作成功',
description: '盘点申请保存成功,页面将自动刷新!', description: '盘点申请保存成功,页面将自动刷新!',
onClose: () => { onClose: () => {
loadData(1) loadData(1)
}, },
}) })
}) })
}
}
//
const open = ref<boolean>(false);
const applyIdRef = ref();
const showModal = (applyId: number) => {
open.value = true;
applyIdRef.value = applyId;
};
//
//
const handleOk = () => {
confirmLoading.value = true;
if (scanFormRef.value) {
const data = scanFormRef.value.getApplyData();
api.materialApi.apply.saveScanData(applyIdRef.value, data, () => {
open.value = false;
confirmLoading.value = false;
loadData(1)
})
}
};
//
const openResult = ref<boolean>(false);
const showResultModal = (applyId: number) => {
window.console.log("点击传参" + applyId);
openResult.value = true;
applyIdRef.value = applyId;
};
//
const submitHandle = () => {
confirmLoading.value = true;
const handleData =handleResultRef.value.getTableData();
api.materialApi.apply.updateReviewResult(applyIdRef.value, handleData, () => {
openResult.value = false;
loadData(1)
});
confirmLoading.value = false;
}
//
const open = ref<boolean>(false)
const applyIdRef = ref()
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 = '需要扫码的物料: '
//
api.materialApi.apply.getWaitScanData(applyId, (data) => {
const m = `需要扫码的物料: ${data.map((item) => item.materialName).join(', ') || '无'}`
data.forEach((item) => (totalValue.value += item.count || 0))
msg.value = m
})
msg.value = m
}
//
//
const handleOk = () => {
confirmLoading.value = true
if (scanFormRef.value) {
const data = scanFormRef.value.getApplyData()
api.materialApi.apply.saveScanData(applyIdRef.value, data, () => {
open.value = false
confirmLoading.value = false
loadData(1)
})
}
}
//
const cancel = () => {
totalValue.value = 0
msg.value = ''
}
//
const openResult = ref<boolean>(false)
const showResultModal = (applyId: number) => {
window.console.log('点击传参' + applyId)
openResult.value = true
applyIdRef.value = applyId
}
//
const submitHandle = () => {
confirmLoading.value = true
const handleData = handleResultRef.value.getTableData()
api.materialApi.apply.updateReviewResult(applyIdRef.value, handleData, () => {
openResult.value = false
loadData(1)
})
confirmLoading.value = false
}
</script> </script>
<style lang="less"> <style lang="less">
.full-modal { .full-modal {
.ant-modal { .ant-modal {
max-width: 100%; max-width: 100%;
top: 0; top: 0;
padding-bottom: 0; padding-bottom: 0;
margin: 0; margin: 0;
} }
.ant-modal-content { .ant-modal-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: calc(100vh); height: calc(100vh);
} }
.ant-modal-body { .ant-modal-body {
flex: 1; flex: 1;
} }
} }
</style> </style>

View File

@ -1,101 +0,0 @@
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

@ -1,179 +0,0 @@
<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,8 +59,8 @@ export default defineConfig(({ mode }) => {
open: true, open: true,
proxy: { proxy: {
'/api': { '/api': {
// target: 'https://ims.riemann.tech/api/', // target: 'https://iot.riemann.tech/api/',
target: 'http://127.0.0.1:8888', target: 'http://127.0.0.1:7777',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''), rewrite: (path) => path.replace(/^\/api/, ''),
}, },

File diff suppressed because one or more lines are too long