Compare commits
10 Commits
015a8140bb
...
6eab932918
Author | SHA1 | Date |
---|---|---|
yixr | 6eab932918 | |
yixr | 6b4becfbd2 | |
yixr | 5fc23fa31f | |
louis | d20d0314b4 | |
louis | 89d1aeedf0 | |
louis | 783f92bf18 | |
louis | 5b38710ddf | |
louis | 780b6658c4 | |
louis | 0383ec977e | |
louis | c3fe96eb86 |
|
@ -9,7 +9,7 @@ COPY ./ $PROJECT_DIR
|
|||
# 若网络不通,可以使用淘宝源
|
||||
# RUN pnpm config set registry https://registry.npmmirror.com
|
||||
# 若不存在安装pnpm
|
||||
RUN npm install -g pnpm
|
||||
RUN npm install -g pnpm@8.10.2
|
||||
|
||||
# 构建项目
|
||||
# 安装生成依赖
|
||||
|
|
|
@ -7,6 +7,6 @@ echo "Pulling latest code..."
|
|||
git pull
|
||||
|
||||
echo "Building and starting docker services..."
|
||||
docker compose --env-file .env --env-file .env.production up -d --build
|
||||
docker-compose --env-file .env --env-file .env.production up -d --build
|
||||
|
||||
echo "build succeeds."
|
|
@ -13,7 +13,7 @@ events {
|
|||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
client_max_body_size 40m;
|
||||
client_max_body_size 50m;
|
||||
map $time_iso8601 $logdate {
|
||||
'~^(?<ymd>\d{4}-\d{2}-\d{2})' $ymd;
|
||||
default 'date-not-found';
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { request, type RequestOptions } from '@/utils/request';
|
||||
|
||||
/** 获取公司列表 GET /api/domain */
|
||||
export async function domainList(params: API.DomainParams, options?: RequestOptions) {
|
||||
return request<{
|
||||
items?: API.DomainEntity[];
|
||||
meta?: {
|
||||
itemCount?: number;
|
||||
totalItems?: number;
|
||||
itemsPerPage?: number;
|
||||
totalPages?: number;
|
||||
currentPage?: number;
|
||||
};
|
||||
}>('/api/domain', {
|
||||
method: 'GET',
|
||||
params: {
|
||||
// page has a default value: 1
|
||||
page: '1',
|
||||
// pageSize has a default value: 10
|
||||
pageSize: '10',
|
||||
|
||||
...params,
|
||||
},
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
|
@ -32,9 +32,14 @@ import * as company from './company';
|
|||
import * as product from './product';
|
||||
import * as materialsInOut from './materialsInOut';
|
||||
import * as vehicleUsage from './vehicleUsage';
|
||||
|
||||
import * as saleQuotationGroup from './saleQuotationGroup';
|
||||
import * as saleQuotationComponent from './saleQuotationComponent';
|
||||
import * as saleQuotationTemplate from './saleQuotationTemplate';
|
||||
import * as saleQuotation from './saleQuotation';
|
||||
import * as domain from './domain';
|
||||
export default {
|
||||
auth,
|
||||
domain,
|
||||
account,
|
||||
captcha,
|
||||
authEmail,
|
||||
|
@ -64,4 +69,8 @@ export default {
|
|||
materialsInOut,
|
||||
project,
|
||||
vehicleUsage,
|
||||
saleQuotationGroup,
|
||||
saleQuotationComponent,
|
||||
saleQuotationTemplate,
|
||||
saleQuotation,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
import { request, type RequestOptions } from '@/utils/request';
|
||||
|
||||
/** 导出出入库记录 GET /api/materials-inventory/export*/
|
||||
export async function materialsInoutExport(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.MaterialsInoutExportParams,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { ...queryParams } = params;
|
||||
return request(`/api/materials-in-out/export`, {
|
||||
method: 'GET',
|
||||
params: { ...queryParams },
|
||||
...(options || { responseType: 'blob', isReturnResult: false }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取原材料出入库记录列表 GET /api/materials-in-out */
|
||||
export async function materialsInOutList(
|
||||
params: API.MaterialsInOutListParams,
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { request, type RequestOptions } from '@/utils/request';
|
||||
const baseApi = '/api/sale_quotation/sale_quotation';
|
||||
/** 导出原材料盘点表 GET /api/sale_quotation/export*/
|
||||
export async function exportSaleQuotation(id: number, options?: RequestOptions) {
|
||||
return request(`${baseApi}/export/${id}`, {
|
||||
method: 'GET',
|
||||
...(options || { responseType: 'blob', isReturnResult: false }),
|
||||
});
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
import { request, type RequestOptions } from '@/utils/request';
|
||||
const baseApi = '/api/sale_quotation/sale_quotation_component';
|
||||
/** 获取报价配件列表 GET /api/sale_quotation_component */
|
||||
export async function saleQuotationComponentList(params: API.SaleQuotationComponentParams, options?: RequestOptions) {
|
||||
return request<{
|
||||
items?: API.SaleQuotationComponentEntity[];
|
||||
meta?: {
|
||||
itemCount?: number;
|
||||
totalItems?: number;
|
||||
itemsPerPage?: number;
|
||||
totalPages?: number;
|
||||
currentPage?: number;
|
||||
};
|
||||
}>(baseApi, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
// page has a default value: 1
|
||||
page: '1',
|
||||
// pageSize has a default value: 10
|
||||
pageSize: '10',
|
||||
|
||||
...params,
|
||||
},
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 新增报价配件 POST /api/sale_quotation_component */
|
||||
export async function saleQuotationComponentCreate(body: API.SaleQuotationComponentDto, options?: RequestOptions) {
|
||||
return request<any>(baseApi, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: body,
|
||||
...(options || { successMsg: '创建成功' }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取报价配件信息 GET /api/sale_quotation_component/${param0} */
|
||||
export async function saleQuotationComponentInfo(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationComponentInfoParams,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<API.SaleQuotationComponentEntity>(`${baseApi}/${param0}`, {
|
||||
method: 'GET',
|
||||
params: { ...queryParams },
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 解除报价配件和附件关联 PUT /api/sale_quotation_component/unlink-attachments/${param0} */
|
||||
export async function unlinkAttachments(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationComponentUpdateParams,
|
||||
body: API.SaleQuotationComponentUpdateDto,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/unlink-attachments/${param0}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/** 更新报价配件 PUT /api/sale_quotation_component/${param0} */
|
||||
export async function saleQuotationComponentUpdate(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationComponentUpdateParams,
|
||||
body: API.SaleQuotationComponentUpdateDto,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/${param0}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || { successMsg: '更新成功' }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除报价配件 DELETE /api/sale_quotation_component/${param0} */
|
||||
export async function saleQuotationComponentDelete(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationComponentDeleteParams,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/${param0}`, {
|
||||
method: 'DELETE',
|
||||
params: { ...queryParams },
|
||||
...(options || { successMsg: '删除成功' }),
|
||||
});
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
import { request, type RequestOptions } from '@/utils/request';
|
||||
const baseApi = '/api/sale_quotation/sale_quotation_group';
|
||||
/** 获取报价分组列表 GET /api/sale_quotation_group */
|
||||
export async function saleQuotationGroupList(params: API.SaleQuotationGroupParams, options?: RequestOptions) {
|
||||
return request<{
|
||||
items?: API.SaleQuotationGroupEntity[];
|
||||
meta?: {
|
||||
itemCount?: number;
|
||||
totalItems?: number;
|
||||
itemsPerPage?: number;
|
||||
totalPages?: number;
|
||||
currentPage?: number;
|
||||
};
|
||||
}>(baseApi, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
// page has a default value: 1
|
||||
page: '1',
|
||||
// pageSize has a default value: 10
|
||||
pageSize: '10',
|
||||
|
||||
...params,
|
||||
},
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 新增报价分组 POST /api/sale_quotation_group */
|
||||
export async function saleQuotationGroupCreate(body: API.SaleQuotationGroupDto, options?: RequestOptions) {
|
||||
return request<any>(baseApi, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: body,
|
||||
...(options || { successMsg: '创建成功' }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取报价分组信息 GET /api/sale_quotation_group/${param0} */
|
||||
export async function saleQuotationGroupInfo(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationGroupInfoParams,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<API.SaleQuotationGroupEntity>(`${baseApi}/${param0}`, {
|
||||
method: 'GET',
|
||||
params: { ...queryParams },
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 解除报价分组和附件关联 PUT /api/sale_quotation_group/unlink-attachments/${param0} */
|
||||
export async function unlinkAttachments(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationGroupUpdateParams,
|
||||
body: API.SaleQuotationGroupUpdateDto,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/unlink-attachments/${param0}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/** 更新报价分组 PUT /api/sale_quotation_group/${param0} */
|
||||
export async function saleQuotationGroupUpdate(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationGroupUpdateParams,
|
||||
body: API.SaleQuotationGroupUpdateDto,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/${param0}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || { successMsg: '更新成功' }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除报价分组 DELETE /api/sale_quotation_group/${param0} */
|
||||
export async function saleQuotationGroupDelete(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationGroupDeleteParams,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/${param0}`, {
|
||||
method: 'DELETE',
|
||||
params: { ...queryParams },
|
||||
...(options || { successMsg: '删除成功' }),
|
||||
});
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
import { request, type RequestOptions } from '@/utils/request';
|
||||
const baseApi = '/api/sale_quotation/sale_quotation_template';
|
||||
/** 获取报价模板列表 GET /api/sale_quotation_template */
|
||||
export async function saleQuotationTemplateList(params: API.SaleQuotationTemplateParams, options?: RequestOptions) {
|
||||
return request<{
|
||||
items?: API.SaleQuotationTemplateEntity[];
|
||||
meta?: {
|
||||
itemCount?: number;
|
||||
totalItems?: number;
|
||||
itemsPerPage?: number;
|
||||
totalPages?: number;
|
||||
currentPage?: number;
|
||||
};
|
||||
}>(baseApi, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
// page has a default value: 1
|
||||
page: '1',
|
||||
// pageSize has a default value: 10
|
||||
pageSize: '10',
|
||||
|
||||
...params,
|
||||
},
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 新增报价模板 POST /api/sale_quotation_template */
|
||||
export async function saleQuotationTemplateCreate(body: API.SaleQuotationTemplateDto, options?: RequestOptions) {
|
||||
return request<any>(baseApi, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: body,
|
||||
...(options || { successMsg: '创建成功' }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取报价模板信息 GET /api/sale_quotation_template/${param0} */
|
||||
export async function saleQuotationTemplateInfo(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationTemplateInfoParams,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<API.SaleQuotationTemplateEntity>(`${baseApi}/${param0}`, {
|
||||
method: 'GET',
|
||||
params: { ...queryParams },
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 解除报价模板和附件关联 PUT /api/sale_quotation_template/unlink-attachments/${param0} */
|
||||
export async function unlinkAttachments(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationTemplateUpdateParams,
|
||||
body: API.SaleQuotationTemplateUpdateDto,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/unlink-attachments/${param0}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/** 更新报价模板 PUT /api/sale_quotation_template/${param0} */
|
||||
export async function saleQuotationTemplateUpdate(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationTemplateUpdateParams,
|
||||
body: API.SaleQuotationTemplateUpdateDto,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/${param0}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || { successMsg: '更新成功' }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除报价模板 DELETE /api/sale_quotation_template/${param0} */
|
||||
export async function saleQuotationTemplateDelete(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.SaleQuotationTemplateDeleteParams,
|
||||
options?: RequestOptions,
|
||||
) {
|
||||
const { id: param0, ...queryParams } = params;
|
||||
return request<any>(`${baseApi}/${param0}`, {
|
||||
method: 'DELETE',
|
||||
params: { ...queryParams },
|
||||
...(options || { successMsg: '删除成功' }),
|
||||
});
|
||||
}
|
|
@ -21,6 +21,8 @@ declare namespace API {
|
|||
remark: string;
|
||||
/** 头像 */
|
||||
avatar: string;
|
||||
/** 所属域 */
|
||||
domain: DomainType;
|
||||
};
|
||||
|
||||
type AccountMenus = {
|
||||
|
@ -1213,12 +1215,13 @@ declare namespace API {
|
|||
status: number;
|
||||
roles: RoleEntity[];
|
||||
dept: DeptEntity;
|
||||
domain: DomainType;
|
||||
accessTokens: AccessTokenEntity[];
|
||||
id: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
type DomainType = number;
|
||||
type UserListParams = {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
|
@ -1379,8 +1382,9 @@ declare namespace API {
|
|||
};
|
||||
|
||||
type MaterialsInventoryExportParams = {
|
||||
time: string;
|
||||
projectId: number;
|
||||
time: string[];
|
||||
filename:string;
|
||||
projectId?: number;
|
||||
};
|
||||
|
||||
type MaterialsInventoryInfoParams = {
|
||||
|
@ -1507,7 +1511,42 @@ declare namespace API {
|
|||
type MaterialsInventoryDeleteParams = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
// SaleQuotationGroup
|
||||
type SaleQuotationGroupEntity = {
|
||||
/** 分组名称 */
|
||||
name: string;
|
||||
/** 是否删除 */
|
||||
isDelete: string;
|
||||
id: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
type SaleQuotationGroupDto = {
|
||||
/** 分组名称 */
|
||||
name: string;
|
||||
};
|
||||
type SaleQuotationGroupUpdateParams = {
|
||||
id: number;
|
||||
};
|
||||
type SaleQuotationGroupParams = {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
name?: string;
|
||||
productId?: number;
|
||||
field?: string;
|
||||
order?: 'ASC' | 'DESC';
|
||||
_t?: number;
|
||||
};
|
||||
type SaleQuotationGroupInfoParams = {
|
||||
id: number;
|
||||
};
|
||||
type SaleQuotationGroupUpdateDto = {
|
||||
/** 分组名称 */
|
||||
name?: string;
|
||||
};
|
||||
type SaleQuotationGroupDeleteParams = {
|
||||
id: number;
|
||||
};
|
||||
// Project
|
||||
type ProjectEntity = {
|
||||
/** 项目名称 */
|
||||
|
@ -1773,6 +1812,11 @@ declare namespace API {
|
|||
};
|
||||
|
||||
// Materials In out history
|
||||
type MaterialsInoutExportParams = {
|
||||
time: string[];
|
||||
filename:string;
|
||||
};
|
||||
|
||||
type MaterialsInOutUpdateParams = {
|
||||
id: number;
|
||||
fileIds?: number[];
|
||||
|
@ -1890,4 +1934,108 @@ declare namespace API {
|
|||
type MaterialsInOutDeleteParams = {
|
||||
id: number;
|
||||
};
|
||||
// SaleQuotationComponent
|
||||
type SaleQuotationComponentEntity = {
|
||||
/** 报价配件名称 */
|
||||
name: string;
|
||||
/** 产品规格 */
|
||||
componentSpecification: string;
|
||||
/** 产品备注 */
|
||||
remark: string;
|
||||
/** 单位 */
|
||||
unit: DictItemEntity;
|
||||
/** 单位 */
|
||||
unitId?: number;
|
||||
/** 单价 */
|
||||
unitPrice: number;
|
||||
/** 是否删除 */
|
||||
isDelete: string;
|
||||
id: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
type SaleQuotationComponentDto = {
|
||||
/** 报价配件名称 */
|
||||
name: string;
|
||||
/** 产品规格 */
|
||||
componentSpecification: string;
|
||||
/** 单价 */
|
||||
unitPrice: number;
|
||||
/** 产品备注 */
|
||||
remark: string;
|
||||
/** 单位 */
|
||||
unit: DictItemEntity;
|
||||
};
|
||||
type SaleQuotationComponentUpdateParams = {
|
||||
id: number;
|
||||
};
|
||||
type SaleQuotationComponentParams = {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
name?: string;
|
||||
productId?: number;
|
||||
field?: string;
|
||||
order?: 'ASC' | 'DESC';
|
||||
_t?: number;
|
||||
};
|
||||
type SaleQuotationComponentInfoParams = {
|
||||
id: number;
|
||||
};
|
||||
type SaleQuotationComponentUpdateDto = {
|
||||
/** 报价配件名称 */
|
||||
name?: string;
|
||||
};
|
||||
type SaleQuotationComponentDeleteParams = {
|
||||
id: number;
|
||||
};
|
||||
// SaleQuotationTemplate
|
||||
type SaleQuotationTemplateEntity = {
|
||||
/** 报价模板名称 */
|
||||
name: string;
|
||||
/** 是否删除 */
|
||||
isDelete: string;
|
||||
id: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
type SaleQuotationTemplateDto = {
|
||||
/** 报价模板名称 */
|
||||
name: string;
|
||||
};
|
||||
type SaleQuotationTemplateUpdateParams = {
|
||||
id: number;
|
||||
};
|
||||
type SaleQuotationTemplateParams = {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
name?: string;
|
||||
productId?: number;
|
||||
field?: string;
|
||||
order?: 'ASC' | 'DESC';
|
||||
_t?: number;
|
||||
};
|
||||
type SaleQuotationTemplateInfoParams = {
|
||||
id: number;
|
||||
};
|
||||
type SaleQuotationTemplateUpdateDto = {
|
||||
/** 报价模板名称 */
|
||||
name?: string;
|
||||
};
|
||||
type SaleQuotationTemplateDeleteParams = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
type DomainEntity = {
|
||||
title: string;
|
||||
id: number;
|
||||
};
|
||||
|
||||
type DomainParams = {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
title?: string;
|
||||
field?: string;
|
||||
order?: 'ASC' | 'DESC';
|
||||
_t?: number;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -58,11 +58,11 @@ export const useExportExcelModal = () => {
|
|||
modalProps: {
|
||||
title: t('component.excel.exportModalTitle'),
|
||||
onFinish: async (values) => {
|
||||
const { filename, bookType } = values;
|
||||
|
||||
const { filename, bookType, time } = values;
|
||||
onOk({
|
||||
filename: `${filename.split('.').shift()}.${bookType}`,
|
||||
filename: bookType ? `${filename.split('.').shift()}.${bookType}` : filename,
|
||||
bookType,
|
||||
time,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
@ -12,7 +12,12 @@ export const USER_INFO_KEY = 'USER__INFO__';
|
|||
|
||||
// role info key
|
||||
export const ROLES_KEY = 'ROLES__KEY__';
|
||||
|
||||
/** 是否锁屏 */
|
||||
export const IS_LOCKSCREEN = 'IS_LOCKSCREEN';
|
||||
|
||||
/** 标签页 */
|
||||
export const TABS_ROUTES = 'TABS_ROUTES';
|
||||
|
||||
/** 域 */
|
||||
export const DOMAIN_KEY = 'DOMAIN__';
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div>
|
||||
公司: <a-select ref="select"
|
||||
v-if="options.length"
|
||||
:disabled="!$auth('system:domain:change')"
|
||||
v-model:value="userStore.domain"
|
||||
style="min-width: 100px;"
|
||||
placeholder="请选择"
|
||||
@change="onChange"
|
||||
:dropdownStyle="{ 'textOverflow': 'ellipsis', 'min-width': 'fit-content' }">
|
||||
<a-select-option :value="item.value"
|
||||
v-for="(item, index) in options"
|
||||
:key="index">{{ item.label }}</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import Api from '@/api';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'DomianPicker'
|
||||
})
|
||||
|
||||
const userStore = useUserStore();
|
||||
const options = ref<{ label: string, value: number }[]>([]);
|
||||
let domains = ref<API.DomainEntity[]>([]);
|
||||
const onChange = (value: SelectValue) => {
|
||||
userStore.changeDomain(value)
|
||||
};
|
||||
onMounted(() => {
|
||||
Api.domain.domainList({}).then((res) => {
|
||||
if (res) {
|
||||
domains.value = res.items ?? [];
|
||||
options.value = domains.value.map((item) => {
|
||||
return {
|
||||
label: item.title,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped></style>
|
|
@ -1,3 +1,4 @@
|
|||
export { default as Search } from './search/index.vue';
|
||||
export { default as FullScreen } from './fullscreen/index.vue';
|
||||
export { default as ProjectSetting } from './setting/index.vue';
|
||||
export { default as DomianPicker } from './domianPicker/index.vue';
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
<template>
|
||||
<Layout.Header :style="headerStyle" class="layout-header">
|
||||
<Layout.Header :style="headerStyle"
|
||||
class="layout-header">
|
||||
<Space :size="20">
|
||||
<slot>
|
||||
<Space :size="20">
|
||||
<span class="menu-fold" @click="() => emit('update:collapsed', !collapsed)">
|
||||
<span class="menu-fold"
|
||||
@click="() => emit('update:collapsed', !collapsed)">
|
||||
<component :is="collapsed ? MenuUnfoldOutlined : MenuFoldOutlined" />
|
||||
</span>
|
||||
<Breadcrumb>
|
||||
<template v-for="(routeItem, rotueIndex) in menus" :key="routeItem?.name">
|
||||
<template v-for="(routeItem, rotueIndex) in menus"
|
||||
:key="routeItem?.name">
|
||||
<Breadcrumb.Item>
|
||||
<TitleI18n :title="routeItem?.meta?.title" />
|
||||
<template v-if="routeItem?.children?.length" #overlay>
|
||||
<template v-if="routeItem?.children?.length"
|
||||
#overlay>
|
||||
<Menu :selected-keys="getSelectKeys(rotueIndex)">
|
||||
<template v-for="childItem in routeItem?.children" :key="childItem.name">
|
||||
<Menu.Item
|
||||
v-if="!childItem.meta?.hideInMenu && !childItem.meta?.hideInBreadcrumb"
|
||||
<template v-for="childItem in routeItem?.children"
|
||||
:key="childItem.name">
|
||||
<Menu.Item v-if="!childItem.meta?.hideInMenu && !childItem.meta?.hideInBreadcrumb"
|
||||
:key="childItem.name"
|
||||
@click="clickMenuItem(childItem)"
|
||||
>
|
||||
@click="clickMenuItem(childItem)">
|
||||
<TitleI18n :title="childItem.meta?.title" />
|
||||
</Menu.Item>
|
||||
</template>
|
||||
|
@ -30,14 +33,17 @@
|
|||
</slot>
|
||||
</Space>
|
||||
<Space :size="20">
|
||||
<DomianPicker />
|
||||
<Search />
|
||||
<Tooltip :title="$t('layout.header.tooltipLock')" placement="bottom">
|
||||
<Tooltip :title="$t('layout.header.tooltipLock')"
|
||||
placement="bottom">
|
||||
<LockOutlined @click="lockscreenStore.setLock(true)" />
|
||||
</Tooltip>
|
||||
<FullScreen />
|
||||
<!-- <LocalePicker /> -->
|
||||
<Dropdown placement="bottomRight">
|
||||
<Avatar :src="userInfo.avatar" :alt="userInfo.username">{{ userInfo.username }}</Avatar>
|
||||
<Avatar :src="userInfo.avatar"
|
||||
:alt="userInfo.username">{{ userInfo.username }}</Avatar>
|
||||
<template #overlay>
|
||||
<Menu>
|
||||
<Menu.Item @click="$router.push({ name: 'account-about' })">
|
||||
|
@ -61,16 +67,16 @@
|
|||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { computed, type CSSProperties } from 'vue';
|
||||
import { useRouter, useRoute, type RouteRecordRaw } from 'vue-router';
|
||||
import {
|
||||
import { computed, type CSSProperties } from 'vue';
|
||||
import { useRouter, useRoute, type RouteRecordRaw } from 'vue-router';
|
||||
import {
|
||||
QuestionCircleOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
PoweroffOutlined,
|
||||
LockOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import {
|
||||
} from '@ant-design/icons-vue';
|
||||
import {
|
||||
Layout,
|
||||
Modal,
|
||||
Dropdown,
|
||||
|
@ -80,40 +86,40 @@
|
|||
Avatar,
|
||||
Tooltip,
|
||||
type MenuTheme,
|
||||
} from 'ant-design-vue';
|
||||
import { Search, FullScreen, ProjectSetting } from './components/';
|
||||
import { LocalePicker } from '@/components/basic/locale-picker';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||
import { TitleI18n } from '@/components/basic/title-i18n';
|
||||
import { useLayoutSettingStore } from '@/store/modules/layoutSetting';
|
||||
} from 'ant-design-vue';
|
||||
import { Search, FullScreen, ProjectSetting, DomianPicker } from './components/';
|
||||
import { LocalePicker } from '@/components/basic/locale-picker';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||
import { TitleI18n } from '@/components/basic/title-i18n';
|
||||
import { useLayoutSettingStore } from '@/store/modules/layoutSetting';
|
||||
|
||||
defineProps({
|
||||
defineProps({
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
},
|
||||
theme: {
|
||||
type: String as PropType<MenuTheme>,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['update:collapsed']);
|
||||
const userStore = useUserStore();
|
||||
const layoutSettingStore = useLayoutSettingStore();
|
||||
const lockscreenStore = useLockscreenStore();
|
||||
});
|
||||
const emit = defineEmits(['update:collapsed']);
|
||||
const userStore = useUserStore();
|
||||
const layoutSettingStore = useLayoutSettingStore();
|
||||
const lockscreenStore = useLockscreenStore();
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const userInfo = computed(() => userStore.userInfo);
|
||||
const headerStyle = computed<CSSProperties>(() => {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const userInfo = computed(() => userStore.userInfo);
|
||||
const headerStyle = computed<CSSProperties>(() => {
|
||||
const { navTheme, layout } = layoutSettingStore.layoutSetting;
|
||||
const isDark = navTheme === 'dark' && layout === 'topmenu';
|
||||
return {
|
||||
backgroundColor: navTheme === 'realDark' || isDark ? '' : 'rgba(255, 255, 255, 0.85)',
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.85)' : '',
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const menus = computed(() => {
|
||||
const menus = computed(() => {
|
||||
if (route.meta?.namePath) {
|
||||
let children = userStore.menus;
|
||||
const paths = route.meta?.namePath?.map((item) => {
|
||||
|
@ -133,13 +139,13 @@
|
|||
];
|
||||
}
|
||||
return route.matched;
|
||||
});
|
||||
});
|
||||
|
||||
const getSelectKeys = (rotueIndex: number) => {
|
||||
const getSelectKeys = (rotueIndex: number) => {
|
||||
return [menus.value[rotueIndex + 1]?.name] as string[];
|
||||
};
|
||||
};
|
||||
|
||||
const findLastChild = (route?: RouteRecordRaw) => {
|
||||
const findLastChild = (route?: RouteRecordRaw) => {
|
||||
if (typeof route?.redirect === 'object') {
|
||||
const redirectValues = Object.values(route.redirect);
|
||||
if (route?.children?.length) {
|
||||
|
@ -159,11 +165,11 @@
|
|||
return route?.redirect;
|
||||
}
|
||||
return route;
|
||||
};
|
||||
const getRouteByName = (name: string) => router.getRoutes().find((n) => n.name === name);
|
||||
};
|
||||
const getRouteByName = (name: string) => router.getRoutes().find((n) => n.name === name);
|
||||
|
||||
// 点击菜单
|
||||
const clickMenuItem = (menuItem: RouteRecordRaw) => {
|
||||
// 点击菜单
|
||||
const clickMenuItem = (menuItem: RouteRecordRaw) => {
|
||||
const lastChild = findLastChild(menuItem);
|
||||
console.log('lastChild', menuItem, lastChild);
|
||||
|
||||
|
@ -174,10 +180,10 @@
|
|||
} else {
|
||||
router.push({ name: lastChild?.name });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const doLogout = () => {
|
||||
// 退出登录
|
||||
const doLogout = () => {
|
||||
Modal.confirm({
|
||||
title: '您确定要退出登录吗?',
|
||||
icon: <QuestionCircleOutlined />,
|
||||
|
@ -190,11 +196,11 @@
|
|||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layout-header {
|
||||
.layout-header {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
|
@ -207,5 +213,5 @@
|
|||
* {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useDictStore } from './dict';
|
|||
import { useRouter, type RouteRecordRaw, useRoute } from 'vue-router';
|
||||
import { store } from '@/store';
|
||||
import Api from '@/api/';
|
||||
import { ACCESS_TOKEN_KEY } from '@/enums/cacheEnum';
|
||||
import { ACCESS_TOKEN_KEY, DOMAIN_KEY } from '@/enums/cacheEnum';
|
||||
import { Storage } from '@/utils/Storage';
|
||||
import { resetRouter } from '@/router';
|
||||
import { generateDynamicRoutes } from '@/router/helper/routeHelper';
|
||||
|
@ -21,12 +21,12 @@ export type MessageEvent = {
|
|||
export const useUserStore = defineStore('user', () => {
|
||||
let eventSource: EventSource | null = null;
|
||||
const token = ref(Storage.get(ACCESS_TOKEN_KEY, null));
|
||||
const name = ref('amdin');
|
||||
const name = ref('admin');
|
||||
const perms = ref<string[]>([]);
|
||||
const menus = ref<RouteRecordRaw[]>([]);
|
||||
const userInfo = ref<Partial<API.UserEntity>>({});
|
||||
const serverConnected = ref(true);
|
||||
|
||||
const domain = ref<API.DomainType>(Storage.get(DOMAIN_KEY, null));
|
||||
watch(serverConnected, (val) => {
|
||||
if (val) {
|
||||
initServerMsgListener();
|
||||
|
@ -87,6 +87,7 @@ export const useUserStore = defineStore('user', () => {
|
|||
// const ex = 7 * 24 * 60 * 60 * 1000;
|
||||
Storage.set(ACCESS_TOKEN_KEY, token.value);
|
||||
};
|
||||
|
||||
/** 登录 */
|
||||
const login = async (params: API.LoginDto) => {
|
||||
try {
|
||||
|
@ -97,6 +98,7 @@ export const useUserStore = defineStore('user', () => {
|
|||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 解锁屏幕 */
|
||||
const unlock = async (params: API.LoginDto) => {
|
||||
try {
|
||||
|
@ -106,6 +108,17 @@ export const useUserStore = defineStore('user', () => {
|
|||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 切换domain */
|
||||
const changeDomain = (value) => {
|
||||
domain.value = value;
|
||||
Storage.set(DOMAIN_KEY, value);
|
||||
setTimeout(() => {
|
||||
// 刷新页面
|
||||
window.location.reload();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
/** 登录成功之后, 获取用户信息以及生成权限路由 */
|
||||
const afterLogin = async () => {
|
||||
try {
|
||||
|
@ -113,9 +126,12 @@ export const useUserStore = defineStore('user', () => {
|
|||
useDictStore();
|
||||
// const wsStore = useWsStore();
|
||||
const userInfoData = await accountProfile();
|
||||
|
||||
userInfo.value = userInfoData;
|
||||
|
||||
if (!Storage.get(DOMAIN_KEY)) {
|
||||
domain.value = userInfoData.domain;
|
||||
Storage.set(DOMAIN_KEY, domain.value);
|
||||
}
|
||||
await fetchPermsAndMenus();
|
||||
initServerMsgListener();
|
||||
} catch (error) {
|
||||
|
@ -158,6 +174,8 @@ export const useUserStore = defineStore('user', () => {
|
|||
perms,
|
||||
menus,
|
||||
userInfo,
|
||||
domain,
|
||||
changeDomain,
|
||||
login,
|
||||
unlock,
|
||||
afterLogin,
|
||||
|
|
|
@ -123,6 +123,8 @@ export function request<T = any>(config: RequestOptions): Promise<BaseResponse<T
|
|||
export async function request(_url: string | RequestOptions, _config: RequestOptions = {}) {
|
||||
const url = isString(_url) ? _url : _url.url;
|
||||
const config = isString(_url) ? _config : _url;
|
||||
const userStore = useUserStore();
|
||||
|
||||
try {
|
||||
// 兼容 from data 文件上传的情况
|
||||
const { requestType, isReturnResult = true, ...rest } = config;
|
||||
|
@ -137,6 +139,7 @@ export async function request(_url: string | RequestOptions, _config: RequestOpt
|
|||
headers: {
|
||||
...rest.headers,
|
||||
...(requestType === 'form' ? { 'Content-Type': 'multipart/form-data' } : {}),
|
||||
'sk-domain':userStore.domain
|
||||
},
|
||||
})) as AxiosResponse<BaseResponse>;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</div>
|
||||
<a-form layout="horizontal" :model="state.formInline" @submit.prevent="handleSubmit">
|
||||
<a-form-item>
|
||||
<a-input v-model:value="state.formInline.username" size="large" placeholder="admin">
|
||||
<a-input v-model:value="state.formInline.username" size="large" placeholder="用戶名">
|
||||
<template #prefix><user-outlined type="user" /></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
|
@ -16,7 +16,7 @@
|
|||
v-model:value="state.formInline.password"
|
||||
size="large"
|
||||
type="password"
|
||||
placeholder="huaxin123"
|
||||
placeholder="密碼"
|
||||
autocomplete="new-password"
|
||||
>
|
||||
<template #prefix><lock-outlined type="user" /></template>
|
||||
|
@ -61,8 +61,8 @@
|
|||
loading: false,
|
||||
captcha: '',
|
||||
formInline: {
|
||||
username: 'admin',
|
||||
password: 'huaxin123',
|
||||
username: '',
|
||||
password: '',
|
||||
verifyCode: '',
|
||||
captchaId: '',
|
||||
},
|
||||
|
|
|
@ -21,36 +21,37 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup
|
||||
lang="tsx">
|
||||
import { useTable } from '@/components/core/dynamic-table';
|
||||
import {
|
||||
<script setup lang="tsx">
|
||||
import { useTable } from '@/components/core/dynamic-table';
|
||||
import {
|
||||
baseColumns,
|
||||
type TableColumnItem,
|
||||
type TableListItem,
|
||||
} from './columns';
|
||||
import Api from '@/api/';
|
||||
import { onMounted, ref, type FunctionalComponent } from 'vue';
|
||||
import { useFormModal, useModal } from '@/hooks/useModal';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { formSchemas } from './formSchemas';
|
||||
import AttachmentManage from '@/components/business/attachment-manage/index.vue';
|
||||
import AttachmentUpload from '@/components/business/attachment-upload/index.vue';
|
||||
import { useExportExcelModal, jsonToSheetXlsx } from '@/components/basic/excel';
|
||||
import { MaterialsInOutEnum } from '@/enums/materialsInventoryEnum';
|
||||
import { formatToDate } from '@/utils/dateUtil';
|
||||
defineOptions({
|
||||
} from './columns';
|
||||
import Api from '@/api/';
|
||||
import { onMounted, ref, type FunctionalComponent } from 'vue';
|
||||
import { useFormModal, useModal } from '@/hooks/useModal';
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { formSchemas } from './formSchemas';
|
||||
import AttachmentManage from '@/components/business/attachment-manage/index.vue';
|
||||
import AttachmentUpload from '@/components/business/attachment-upload/index.vue';
|
||||
import { useExportExcelModal, jsonToSheetXlsx } from '@/components/basic/excel';
|
||||
import { MaterialsInOutEnum } from '@/enums/materialsInventoryEnum';
|
||||
import { formatToDate } from '@/utils/dateUtil';
|
||||
import dayjs from 'dayjs';
|
||||
import fileDownload from 'js-file-download';
|
||||
defineOptions({
|
||||
name: 'MaterialsInOut',
|
||||
});
|
||||
});
|
||||
|
||||
const [DynamicTable, dynamicTableInstance] = useTable({ formProps: { autoSubmitOnEnter: true } });
|
||||
const [showModal] = useFormModal();
|
||||
const exportExcelModal = useExportExcelModal();
|
||||
const [fnModal] = useModal();
|
||||
const isUploadPopupVisiable = ref(false);
|
||||
const [DynamicTable, dynamicTableInstance] = useTable({ formProps: { autoSubmitOnEnter: true } });
|
||||
const [showModal] = useFormModal();
|
||||
const exportExcelModal = useExportExcelModal();
|
||||
const [fnModal] = useModal();
|
||||
const isUploadPopupVisiable = ref(false);
|
||||
|
||||
let columns = ref<TableColumnItem[]>();
|
||||
onMounted(() => {
|
||||
let columns = ref<TableColumnItem[]>();
|
||||
onMounted(() => {
|
||||
columns.value = [
|
||||
...baseColumns,
|
||||
{
|
||||
|
@ -98,72 +99,85 @@
|
|||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
const openExportModal = () => {
|
||||
exportExcelModal.openModal({
|
||||
formSchemas: [
|
||||
{
|
||||
field: 'time',
|
||||
component: 'RangePicker',
|
||||
label:'时间范围',
|
||||
rules: [{ required: true }],
|
||||
},
|
||||
],
|
||||
onOk: ({ filename, bookType }) => {
|
||||
const tableData: TableListItem[] = dynamicTableInstance.tableData;
|
||||
let exportData: any[] = []
|
||||
for (let item of tableData) {
|
||||
exportData.push({
|
||||
projectName: item.project?.name,
|
||||
inOrOut: item.inOrOut === MaterialsInOutEnum.In ? '入库' : '出库',
|
||||
inventoryInOutNumber: item.inventoryInOutNumber,
|
||||
time: formatToDate(item.time),
|
||||
company: item.product?.company?.name,
|
||||
productName: item.product?.name,
|
||||
productSpecification: item.product?.productSpecification,
|
||||
unit: item.product?.unit?.label,
|
||||
quantity: item.quantity,
|
||||
unitPrice: parseFloat(item.unitPrice),
|
||||
amount: parseFloat(item.amount),
|
||||
agent: item.agent,
|
||||
issuanceNumber: item.issuanceNumber,
|
||||
remark: item.remark
|
||||
})
|
||||
const openExportModal = async () => {
|
||||
const queryModel = dynamicTableInstance.queryFormRef?.formModel;
|
||||
if(!queryModel?.time){
|
||||
message.warning('请选择时间范围')
|
||||
return;
|
||||
}
|
||||
jsonToSheetXlsx({
|
||||
data: exportData,
|
||||
header: {
|
||||
inOrOut: '出/入库',
|
||||
inventoryInOutNumber: '出入库单号',
|
||||
time: '时间',
|
||||
projectName: '项目',
|
||||
company: '公司',
|
||||
productName: '产品名',
|
||||
productSpecification: "产品规格",
|
||||
unit: '单位',
|
||||
quantity: '数量',
|
||||
unitPrice: '单价',
|
||||
amount: '金额',
|
||||
agent: '经办人',
|
||||
issuanceNumber: '领料单号',
|
||||
remark: '备注'
|
||||
},
|
||||
filename,
|
||||
write2excelOpts: {
|
||||
bookType,
|
||||
},
|
||||
json2sheetOpts: {
|
||||
// 指定顺序
|
||||
header: [
|
||||
'inOrOut', 'inventoryInOutNumber', 'time', 'projectName', 'company', 'productName', 'productSpecification', 'unit', 'quantity',
|
||||
'unitPrice', 'amount', 'agent', 'issuanceNumber', 'remark'],
|
||||
},
|
||||
const timeRange = (queryModel?.time ?? []).map(item => dayjs(item).format('YYYY-MM-DD'));
|
||||
const response = await Api.materialsInOut.materialsInoutExport({
|
||||
time: timeRange ?? [], filename: `${timeRange[0]}-${timeRange[1]}`
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
const openAttachmentUploadModal = async (record: TableListItem) => {
|
||||
fileDownload(response, `${timeRange[0]}-${timeRange[1]}.xls`);
|
||||
// exportExcelModal.openModal({
|
||||
// formSchemas: [
|
||||
// {
|
||||
// field: 'time',
|
||||
// component: 'RangePicker',
|
||||
// label: '时间范围',
|
||||
// rules: [{ required: true }],
|
||||
// },
|
||||
// ],
|
||||
// onOk: async ({ filename, bookType, time }) => {
|
||||
// const response = await Api.materialsInOut.materialsInoutExport({
|
||||
// time: (time ?? []).map(item => dayjs(item).format('YYYY-MM-DD')), filename
|
||||
// });
|
||||
// fileDownload(response, `${filename}.xls`);
|
||||
// const tableData: TableListItem[] = dynamicTableInstance.tableData;
|
||||
// let exportData: any[] = []
|
||||
// for (let item of tableData) {
|
||||
// exportData.push({
|
||||
// projectName: item.project?.name,
|
||||
// inOrOut: item.inOrOut === MaterialsInOutEnum.In ? '入库' : '出库',
|
||||
// inventoryInOutNumber: item.inventoryInOutNumber,
|
||||
// time: formatToDate(item.time),
|
||||
// company: item.product?.company?.name,
|
||||
// productName: item.product?.name,
|
||||
// productSpecification: item.product?.productSpecification,
|
||||
// unit: item.product?.unit?.label,
|
||||
// quantity: item.quantity,
|
||||
// unitPrice: parseFloat(item.unitPrice),
|
||||
// amount: parseFloat(item.amount),
|
||||
// agent: item.agent,
|
||||
// issuanceNumber: item.issuanceNumber,
|
||||
// remark: item.remark
|
||||
// })
|
||||
// }
|
||||
// jsonToSheetXlsx({
|
||||
// data: exportData,
|
||||
// header: {
|
||||
// inOrOut: '出/入库',
|
||||
// inventoryInOutNumber: '出入库单号',
|
||||
// time: '时间',
|
||||
// projectName: '项目',
|
||||
// company: '公司',
|
||||
// productName: '产品名',
|
||||
// productSpecification: "产品规格",
|
||||
// unit: '单位',
|
||||
// quantity: '数量',
|
||||
// unitPrice: '单价',
|
||||
// amount: '金额',
|
||||
// agent: '经办人',
|
||||
// issuanceNumber: '领料单号',
|
||||
// remark: '备注'
|
||||
// },
|
||||
// filename,
|
||||
// write2excelOpts:
|
||||
// { bookType: 'xls' },
|
||||
// json2sheetOpts: {
|
||||
// // 指定顺序
|
||||
// header: [
|
||||
// 'inOrOut', 'inventoryInOutNumber', 'time', 'projectName', 'company', 'productName', 'productSpecification', 'unit', 'quantity',
|
||||
// 'unitPrice', 'amount', 'agent', 'issuanceNumber', 'remark'],
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
};
|
||||
const openAttachmentUploadModal = async (record: TableListItem) => {
|
||||
isUploadPopupVisiable.value = true;
|
||||
fnModal.show({
|
||||
width: 800,
|
||||
|
@ -184,26 +198,26 @@
|
|||
open: isUploadPopupVisiable.value,
|
||||
footer: null,
|
||||
});
|
||||
};
|
||||
const handleUploadClose = (hasSuccess: boolean) => {
|
||||
};
|
||||
const handleUploadClose = (hasSuccess: boolean) => {
|
||||
fnModal.hide();
|
||||
isUploadPopupVisiable.value = false;
|
||||
};
|
||||
const afterUploadCallback = async (
|
||||
};
|
||||
const afterUploadCallback = async (
|
||||
files: { filename: { path: string; id: number } }[],
|
||||
id: number,
|
||||
) => {
|
||||
) => {
|
||||
await Api.materialsInOut.materialsInOutUpdate(
|
||||
{ id },
|
||||
{ fileIds: files.map((item) => item.filename.id) },
|
||||
);
|
||||
dynamicTableInstance?.reload();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @description 打开新增/编辑弹窗
|
||||
*/
|
||||
const openEditModal = async (record: Partial<TableListItem>) => {
|
||||
const openEditModal = async (record: Partial<TableListItem>) => {
|
||||
const [formRef] = await showModal({
|
||||
modalProps: {
|
||||
title: `${record.id ? '编辑' : '新增'}出入库记录`,
|
||||
|
@ -237,13 +251,13 @@
|
|||
...info,
|
||||
});
|
||||
}
|
||||
};
|
||||
const delRowConfirm = async (record) => {
|
||||
};
|
||||
const delRowConfirm = async (record) => {
|
||||
await Api.materialsInOut.materialsInOutDelete({ id: record });
|
||||
dynamicTableInstance?.reload();
|
||||
};
|
||||
};
|
||||
|
||||
const FilesRender: FunctionalComponent<TableListItem> = (materialsInOut: TableListItem) => {
|
||||
const FilesRender: FunctionalComponent<TableListItem> = (materialsInOut: TableListItem) => {
|
||||
const [fnModal] = useModal();
|
||||
return (
|
||||
<Button
|
||||
|
@ -255,9 +269,9 @@
|
|||
{materialsInOut.files?.length || 0}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const openFilesManageModal = (fnModal, tableData: TableListItem) => {
|
||||
const openFilesManageModal = (fnModal, tableData: TableListItem) => {
|
||||
const fileIds = tableData.files?.map((item) => item.id) || [];
|
||||
fnModal.show({
|
||||
width: 1200,
|
||||
|
@ -273,12 +287,11 @@
|
|||
destroyOnClose: true,
|
||||
footer: null,
|
||||
});
|
||||
};
|
||||
const unlinkAttachments = async (id: number, unlinkIds: number[]) => {
|
||||
};
|
||||
const unlinkAttachments = async (id: number, unlinkIds: number[]) => {
|
||||
await Api.materialsInOut.unlinkAttachments({ id }, { fileIds: unlinkIds });
|
||||
dynamicTableInstance?.reload();
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less"
|
||||
scoped></style>
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -18,25 +18,26 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup
|
||||
lang="tsx">
|
||||
import { useTable } from '@/components/core/dynamic-table';
|
||||
import { baseColumns, type TableColumnItem, type TableListItem } from './columns';
|
||||
import Api from '@/api/';
|
||||
import { onMounted, ref, unref } from 'vue';
|
||||
import { useFormModal } from '@/hooks/useModal';
|
||||
import { exportSchemas } from './exportSchema';
|
||||
import dayjs from 'dayjs';
|
||||
import fileDownload from 'js-file-download';
|
||||
import { message } from 'ant-design-vue';
|
||||
<script setup lang="tsx">
|
||||
import { useTable } from '@/components/core/dynamic-table';
|
||||
import { baseColumns, type TableColumnItem, type TableListItem } from './columns';
|
||||
import Api from '@/api/';
|
||||
import { onMounted, ref, unref } from 'vue';
|
||||
import { useFormModal } from '@/hooks/useModal';
|
||||
import { exportSchemas } from './exportSchema';
|
||||
import dayjs from 'dayjs';
|
||||
import fileDownload from 'js-file-download';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useExportExcelModal } from '@/components/basic/excel';
|
||||
|
||||
defineOptions({
|
||||
defineOptions({
|
||||
name: 'MaterialsInventory',
|
||||
});
|
||||
const [DynamicTable, dynamicTableInstance] = useTable();
|
||||
const [showExportModal] = useFormModal();
|
||||
let columns = ref<TableColumnItem[]>();
|
||||
onMounted(() => {
|
||||
});
|
||||
const [DynamicTable, dynamicTableInstance] = useTable();
|
||||
const [showExportModal] = useFormModal();
|
||||
const exportExcelModal = useExportExcelModal();
|
||||
let columns = ref<TableColumnItem[]>();
|
||||
onMounted(() => {
|
||||
columns.value = [
|
||||
...baseColumns,
|
||||
|
||||
|
@ -71,41 +72,56 @@
|
|||
// ],
|
||||
// },
|
||||
];
|
||||
});
|
||||
const exportMI = async () => {
|
||||
});
|
||||
const exportMI = async () => {
|
||||
const { time } = unref<{ time: string; projectId: number }>(
|
||||
dynamicTableInstance?.queryFormRef?.formModel as { time: string; projectId: number },
|
||||
);
|
||||
|
||||
const [formRef] = await showExportModal({
|
||||
modalProps: {
|
||||
title: `导出条件选择`,
|
||||
width: '50%',
|
||||
okText: '导出',
|
||||
onFinish: async (values) => {
|
||||
const response = await Api.materialsInventory.materialsInventoryExport(values);
|
||||
const { time } = values;
|
||||
fileDownload(response, `${dayjs(time).format('YYYY.MM.盘点表')}.xls`);
|
||||
},
|
||||
},
|
||||
formProps: {
|
||||
labelWidth: 100,
|
||||
schemas: exportSchemas,
|
||||
exportExcelModal.openModal({
|
||||
formSchemas: [
|
||||
{
|
||||
field: 'time',
|
||||
component: 'RangePicker',
|
||||
label: '时间范围',
|
||||
rules: [{ required: true }],
|
||||
},
|
||||
],
|
||||
onOk: async ({ filename, bookType, time }) => {
|
||||
const response = await Api.materialsInventory.materialsInventoryExport({
|
||||
time: (time ?? []).map(item => dayjs(item).format('YYYY-MM-DD')), filename
|
||||
});
|
||||
fileDownload(response, `${filename}.xls`);
|
||||
}
|
||||
});
|
||||
// const [formRef] = await showExportModal({
|
||||
// modalProps: {
|
||||
// title: `导出条件选择`,
|
||||
// width: '50%',
|
||||
// okText: '导出',
|
||||
// onFinish: async (values) => {
|
||||
// const response = await Api.materialsInventory.materialsInventoryExport(values);
|
||||
// const { time } = values;
|
||||
// fileDownload(response, `${dayjs(time).format('YYYY.MM.盘点表')}.xls`);
|
||||
// },
|
||||
// },
|
||||
// formProps: {
|
||||
// labelWidth: 100,
|
||||
// schemas: exportSchemas,
|
||||
// },
|
||||
// });
|
||||
|
||||
// auto fill export time fields
|
||||
if (time) {
|
||||
formRef?.setFieldsValue({
|
||||
time,
|
||||
});
|
||||
}
|
||||
};
|
||||
// if (time) {
|
||||
// formRef?.setFieldsValue({
|
||||
// time,
|
||||
// });
|
||||
// }
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @description 打开新增/编辑弹窗
|
||||
*/
|
||||
const openEditModal = async (record: Partial<TableListItem>) => {
|
||||
const openEditModal = async (record: Partial<TableListItem>) => {
|
||||
message.warning(
|
||||
<div>
|
||||
暂不支持编辑功能,等待后期需求确认
|
||||
|
@ -142,15 +158,14 @@
|
|||
// ...info,
|
||||
// });
|
||||
// }
|
||||
};
|
||||
const delRowConfirm = async (record) => {
|
||||
};
|
||||
const delRowConfirm = async (record) => {
|
||||
await Api.contract.contractDelete({ id: record });
|
||||
dynamicTableInstance?.reload();
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less"
|
||||
scoped></style>
|
||||
<style lang="less" scoped></style>
|
||||
import dayjs from 'dayjs'; import fileDownload from 'js-file-download'; import type { TableQueryItem
|
||||
} from '../in-out/columns'; import { exportSchemas } from '../in-out/exportSchema'; import dayjs
|
||||
from 'dayjs'; import fileDownload from 'js-file-download'; import type { TableQueryItem } from
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import type { TableColumn } from '@/components/core/dynamic-table';
|
||||
|
||||
export type TableListItem = API.SaleQuotationComponentEntity;
|
||||
export type TableColumnItem = TableColumn<TableListItem>;
|
||||
export const baseColumns: TableColumnItem[] = [
|
||||
{
|
||||
title: '配件名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '配件规格',
|
||||
dataIndex: 'componentSpecification',
|
||||
},
|
||||
{
|
||||
title: '单位',
|
||||
dataIndex: 'unit',
|
||||
width: 80,
|
||||
customRender: ({ record }) => {
|
||||
return record?.unit?.label || '';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '单价',
|
||||
hideInSearch: true,
|
||||
width: 80,
|
||||
dataIndex: 'unitPrice',
|
||||
customRender: ({ record }) => {
|
||||
return parseFloat(record.unitPrice) || 0;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,67 @@
|
|||
import type { FormSchema } from '@/components/core/schema-form/';
|
||||
import { DictEnum } from '@/enums/dictEnum';
|
||||
import { useDictStore } from '@/store/modules/dict';
|
||||
const { getDictItemsByCode } = useDictStore();
|
||||
export const formSchemas: FormSchema<API.SaleQuotationComponentEntity>[] = [
|
||||
{
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
label: '配件名称',
|
||||
rules: [{ required: true, type: 'string' }],
|
||||
colProps: {
|
||||
span: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'componentSpecification',
|
||||
component: 'Input',
|
||||
label: '配件规格',
|
||||
colProps: {
|
||||
span: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '单位',
|
||||
component: 'Select',
|
||||
field: 'unitId',
|
||||
colProps: {
|
||||
span: 12,
|
||||
},
|
||||
componentProps: ({ formInstance, schema, formModel }) => ({
|
||||
showSearch: true,
|
||||
filterOption: (input: string, option: any) => {
|
||||
return option.label.indexOf(input) >= 0;
|
||||
},
|
||||
fieldNames: {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
},
|
||||
options: getDictItemsByCode(DictEnum.Unit).map((item) => ({
|
||||
value: item.id,
|
||||
label: item.label,
|
||||
})),
|
||||
|
||||
getPopupContainer: () => document.body,
|
||||
defaultActiveFirstOption: true,
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: '单价',
|
||||
field: 'unitPrice',
|
||||
component: 'InputNumber',
|
||||
colProps: {
|
||||
span: 12,
|
||||
},
|
||||
helpMessage: ({ formModel }) => {
|
||||
return '成本价';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
component: 'InputTextArea',
|
||||
label: '备注',
|
||||
colProps: {
|
||||
span: 24,
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,118 @@
|
|||
<template>
|
||||
<div v-if="columns?.length">
|
||||
<DynamicTable row-key="id"
|
||||
header-title="配件管理"
|
||||
title-tooltip=""
|
||||
:data-request="Api.saleQuotationComponent.saleQuotationComponentList"
|
||||
:columns="columns"
|
||||
bordered
|
||||
size="small">
|
||||
<template #toolbar>
|
||||
<a-button type="primary"
|
||||
:disabled="!$auth('sale_quotation:sale_quotation_component:create')"
|
||||
@click="openEditModal({})">
|
||||
新增
|
||||
</a-button>
|
||||
</template>
|
||||
</DynamicTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { useTable } from '@/components/core/dynamic-table';
|
||||
import { baseColumns, type TableColumnItem, type TableListItem } from './columns';
|
||||
import Api from '@/api/';
|
||||
import { useFormModal, useModal } from '@/hooks/useModal';
|
||||
import { formSchemas } from './formSchemas';
|
||||
|
||||
import { ref, onMounted } from 'vue';
|
||||
defineOptions({
|
||||
name: 'SaleQuotationComponent',
|
||||
});
|
||||
const [DynamicTable, dynamicTableInstance] = useTable({ formProps: { autoSubmitOnEnter: true } });
|
||||
const [showModal] = useFormModal();
|
||||
|
||||
// saleQuotationComponentList;
|
||||
let columns = ref<TableColumnItem[]>();
|
||||
onMounted(() => {
|
||||
columns.value = [
|
||||
...baseColumns,
|
||||
{
|
||||
title: '操作',
|
||||
maxWidth: 150,
|
||||
width: 150,
|
||||
minWidth: 150,
|
||||
fixed: 'right',
|
||||
dataIndex: 'ACTION',
|
||||
hideInSearch: true,
|
||||
actions: ({ record }) => [
|
||||
|
||||
{
|
||||
icon: 'ant-design:edit-outlined',
|
||||
tooltip: '编辑',
|
||||
auth: {
|
||||
perm: 'sale_quotation:sale_quotation_component:update',
|
||||
effect: 'disable',
|
||||
},
|
||||
onClick: () => openEditModal(record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'red',
|
||||
tooltip: '删除此配件',
|
||||
auth: 'sale_quotation:sale_quotation_component:delete',
|
||||
popConfirm: {
|
||||
title: '你确定要删除吗?',
|
||||
placement: 'left',
|
||||
onConfirm: () => delRowConfirm(record.id),
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description 打开新增/编辑弹窗
|
||||
*/
|
||||
const openEditModal = async (record: Partial<TableListItem>) => {
|
||||
const [formRef] = await showModal({
|
||||
modalProps: {
|
||||
title: `${record.id ? '编辑' : '新增'}配件`,
|
||||
width: '50%',
|
||||
onFinish: async (values) => {
|
||||
if (record.id) {
|
||||
await Api.saleQuotationComponent.saleQuotationComponentUpdate({ id: record.id }, values);
|
||||
} else {
|
||||
await Api.saleQuotationComponent.saleQuotationComponentCreate(values);
|
||||
}
|
||||
dynamicTableInstance?.reload();
|
||||
},
|
||||
},
|
||||
formProps: {
|
||||
labelWidth: 100,
|
||||
schemas: formSchemas,
|
||||
},
|
||||
});
|
||||
|
||||
// 如果是编辑的话,需要获取角色详情
|
||||
if (record.id) {
|
||||
const info = await Api.saleQuotationComponent.saleQuotationComponentInfo({ id: record.id });
|
||||
formRef?.setFieldsValue({
|
||||
...info,
|
||||
});
|
||||
}
|
||||
};
|
||||
const delRowConfirm = async (record) => {
|
||||
await Api.saleQuotationComponent.saleQuotationComponentDelete({ id: record });
|
||||
dynamicTableInstance?.reload();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -0,0 +1,10 @@
|
|||
import type { TableColumn } from '@/components/core/dynamic-table';
|
||||
|
||||
export type TableListItem = API.SaleQuotationGroupEntity;
|
||||
export type TableColumnItem = TableColumn<TableListItem>;
|
||||
export const baseColumns: TableColumnItem[] = [
|
||||
{
|
||||
title: '分组名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,12 @@
|
|||
import type { FormSchema } from '@/components/core/schema-form/';
|
||||
export const formSchemas: FormSchema<API.SaleQuotationGroupEntity>[] = [
|
||||
{
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
label: '分组名称',
|
||||
rules: [{ required: true, type: 'string' }],
|
||||
colProps: {
|
||||
span: 12,
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,122 @@
|
|||
<template>
|
||||
<div v-if="columns?.length">
|
||||
<DynamicTable row-key="id"
|
||||
header-title="分组管理"
|
||||
title-tooltip=""
|
||||
:data-request="Api.saleQuotationGroup.saleQuotationGroupList"
|
||||
:columns="columns"
|
||||
bordered
|
||||
size="small">
|
||||
<template #toolbar>
|
||||
<a-button type="primary"
|
||||
:disabled="!$auth('sale_quotation:sale_quotation_group:create')"
|
||||
@click="openEditModal({})">
|
||||
新增
|
||||
</a-button>
|
||||
</template>
|
||||
</DynamicTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { useTable } from '@/components/core/dynamic-table';
|
||||
import { baseColumns, type TableColumnItem, type TableListItem } from './columns';
|
||||
import Api from '@/api/';
|
||||
import { useFormModal, useModal } from '@/hooks/useModal';
|
||||
import { formSchemas } from './formSchemas';
|
||||
|
||||
import { ref, onMounted } from 'vue';
|
||||
defineOptions({
|
||||
name: 'SaleQuotationGroup',
|
||||
});
|
||||
const [DynamicTable, dynamicTableInstance] = useTable({ formProps: { autoSubmitOnEnter: true } });
|
||||
const [showModal] = useFormModal();
|
||||
|
||||
// saleQuotationGroupList;
|
||||
let columns = ref<TableColumnItem[]>();
|
||||
onMounted(() => {
|
||||
columns.value = [
|
||||
...baseColumns,
|
||||
{
|
||||
title: '操作',
|
||||
maxWidth: 150,
|
||||
width: 150,
|
||||
minWidth: 150,
|
||||
fixed: 'right',
|
||||
dataIndex: 'ACTION',
|
||||
hideInSearch: true,
|
||||
actions: ({ record }) => [
|
||||
// {
|
||||
// icon: 'ant-design:plus-outlined',
|
||||
// tooltip: '添加配件',
|
||||
// onClick: () => openEditModal(record),
|
||||
// },
|
||||
{
|
||||
icon: 'ant-design:edit-outlined',
|
||||
tooltip: '编辑',
|
||||
auth: {
|
||||
perm: 'sale_quotation:sale_quotation_group:update',
|
||||
effect: 'disable',
|
||||
},
|
||||
onClick: () => openEditModal(record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'red',
|
||||
tooltip: '删除此分组',
|
||||
auth: 'sale_quotation:sale_quotation_group:delete',
|
||||
popConfirm: {
|
||||
title: '你确定要删除吗?',
|
||||
placement: 'left',
|
||||
onConfirm: () => delRowConfirm(record.id),
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description 打开新增/编辑弹窗
|
||||
*/
|
||||
const openEditModal = async (record: Partial<TableListItem>) => {
|
||||
const [formRef] = await showModal({
|
||||
modalProps: {
|
||||
title: `${record.id ? '编辑' : '新增'}分组`,
|
||||
width: '50%',
|
||||
onFinish: async (values) => {
|
||||
if (record.id) {
|
||||
await Api.saleQuotationGroup.saleQuotationGroupUpdate({ id: record.id }, values);
|
||||
} else {
|
||||
await Api.saleQuotationGroup.saleQuotationGroupCreate(values);
|
||||
}
|
||||
dynamicTableInstance?.reload();
|
||||
},
|
||||
},
|
||||
formProps: {
|
||||
labelWidth: 100,
|
||||
schemas: formSchemas,
|
||||
},
|
||||
});
|
||||
|
||||
// 如果是编辑的话,需要获取角色详情
|
||||
if (record.id) {
|
||||
const info = await Api.saleQuotationGroup.saleQuotationGroupInfo({ id: record.id });
|
||||
formRef?.setFieldsValue({
|
||||
...info,
|
||||
});
|
||||
}
|
||||
};
|
||||
const delRowConfirm = async (record) => {
|
||||
await Api.saleQuotationGroup.saleQuotationGroupDelete({ id: record });
|
||||
dynamicTableInstance?.reload();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<div>test</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
defineOptions({
|
||||
name: 'SaleQuotation'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,10 @@
|
|||
import type { TableColumn } from '@/components/core/dynamic-table';
|
||||
|
||||
export type TableListItem = API.SaleQuotationTemplateEntity;
|
||||
export type TableColumnItem = TableColumn<TableListItem>;
|
||||
export const baseColumns: TableColumnItem[] = [
|
||||
{
|
||||
title: '模板名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,12 @@
|
|||
import type { FormSchema } from '@/components/core/schema-form/';
|
||||
export const formSchemas: FormSchema<API.SaleQuotationTemplateEntity>[] = [
|
||||
{
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
label: '模板名称',
|
||||
rules: [{ required: true, type: 'string' }],
|
||||
colProps: {
|
||||
span: 12,
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,130 @@
|
|||
<template>
|
||||
<div v-if="columns?.length">
|
||||
<DynamicTable row-key="id"
|
||||
header-title="模板管理"
|
||||
title-tooltip=""
|
||||
:data-request="Api.saleQuotationTemplate.saleQuotationTemplateList"
|
||||
:columns="columns"
|
||||
bordered
|
||||
size="small">
|
||||
<template #toolbar>
|
||||
<a-button type="primary"
|
||||
:disabled="!$auth('sale_quotation:sale_quotation_group:create')"
|
||||
@click="openEditModal({})">
|
||||
新增
|
||||
</a-button>
|
||||
</template>
|
||||
</DynamicTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { useTable } from '@/components/core/dynamic-table';
|
||||
import { baseColumns, type TableColumnItem, type TableListItem } from './columns';
|
||||
import Api from '@/api/';
|
||||
import { useFormModal, useModal } from '@/hooks/useModal';
|
||||
import { formSchemas } from './formSchemas';
|
||||
|
||||
import { ref, onMounted } from 'vue';
|
||||
import fileDownload from 'js-file-download';
|
||||
defineOptions({
|
||||
name: 'SaleQuotationTemplate',
|
||||
});
|
||||
const [DynamicTable, dynamicTableInstance] = useTable({ formProps: { autoSubmitOnEnter: true } });
|
||||
const [showModal] = useFormModal();
|
||||
|
||||
// saleQuotationTemplateList;
|
||||
let columns = ref<TableColumnItem[]>();
|
||||
onMounted(() => {
|
||||
columns.value = [
|
||||
...baseColumns,
|
||||
{
|
||||
title: '操作',
|
||||
maxWidth: 150,
|
||||
width: 150,
|
||||
minWidth: 150,
|
||||
fixed: 'right',
|
||||
dataIndex: 'ACTION',
|
||||
hideInSearch: true,
|
||||
actions: ({ record }) => [
|
||||
|
||||
{
|
||||
icon: 'ant-design:edit-outlined',
|
||||
tooltip: '编辑',
|
||||
auth: {
|
||||
perm: 'sale_quotation:sale_quotation_group:update',
|
||||
effect: 'disable',
|
||||
},
|
||||
onClick: () => openEditModal(record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'red',
|
||||
tooltip: '删除此模板',
|
||||
auth: 'sale_quotation:sale_quotation_group:delete',
|
||||
popConfirm: {
|
||||
title: '你确定要删除吗?',
|
||||
placement: 'left',
|
||||
onConfirm: () => delRowConfirm(record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:file-excel-outlined',
|
||||
tooltip: '导出',
|
||||
auth: {
|
||||
perm: 'sale_quotation:sale_quotation:export',
|
||||
effect: 'disable',
|
||||
},
|
||||
onClick: () => onExport(record),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
const onExport = async (record:TableListItem) => {
|
||||
const response = await Api.saleQuotation.exportSaleQuotation(record.id);
|
||||
fileDownload(response, `${record.name}.xls`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 打开新增/编辑弹窗
|
||||
*/
|
||||
const openEditModal = async (record: Partial<TableListItem>) => {
|
||||
const [formRef] = await showModal({
|
||||
modalProps: {
|
||||
title: `${record.id ? '编辑' : '新增'}模板`,
|
||||
width: '50%',
|
||||
onFinish: async (values) => {
|
||||
if (record.id) {
|
||||
await Api.saleQuotationTemplate.saleQuotationTemplateUpdate({ id: record.id }, values);
|
||||
} else {
|
||||
await Api.saleQuotationTemplate.saleQuotationTemplateCreate(values);
|
||||
}
|
||||
dynamicTableInstance?.reload();
|
||||
},
|
||||
},
|
||||
formProps: {
|
||||
labelWidth: 100,
|
||||
schemas: formSchemas,
|
||||
},
|
||||
});
|
||||
|
||||
// 如果是编辑的话,需要获取角色详情
|
||||
if (record.id) {
|
||||
const info = await Api.saleQuotationTemplate.saleQuotationTemplateInfo({ id: record.id });
|
||||
formRef?.setFieldsValue({
|
||||
...info,
|
||||
});
|
||||
}
|
||||
};
|
||||
const delRowConfirm = async (record) => {
|
||||
await Api.saleQuotationTemplate.saleQuotationTemplateDelete({ id: record });
|
||||
dynamicTableInstance?.reload();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -43,12 +43,12 @@
|
|||
if (!isJpgOrPng) {
|
||||
message.error('You can only upload JPG/PNG file!');
|
||||
}
|
||||
const isLt2M = file.size / 1024 / 1024 < 30;
|
||||
if (!isLt2M) {
|
||||
message.error('Image must smaller than 30MB!');
|
||||
const isLt = file.size / 1024 / 1024 < 50;
|
||||
if (!isLt) {
|
||||
message.error('Image must smaller than 50MB!');
|
||||
}
|
||||
|
||||
return isJpgOrPng && isLt2M;
|
||||
return isJpgOrPng && isLt;
|
||||
};
|
||||
|
||||
const uploadAvatar = async (file: FileType) => {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
@cancel="onCancel"
|
||||
>
|
||||
<a-flex justify="space-between" align="center">
|
||||
<a-alert message="单个文件不超过30MB,最多只能上传10个文件" type="info" show-icon />
|
||||
<a-alert message="单个文件不超过50MB,最多只能上传10个文件" type="info" show-icon />
|
||||
<a-upload :multiple="true" :before-upload="beforeUpload" :show-upload-list="false">
|
||||
<a-button type="primary"> 选择文件 </a-button>
|
||||
</a-upload>
|
||||
|
@ -88,8 +88,8 @@
|
|||
};
|
||||
|
||||
const beforeUpload: UploadProps['beforeUpload'] = async (file) => {
|
||||
if (file.size / 1024 / 1024 > 30) {
|
||||
message.error('单个文件不超过30MB');
|
||||
if (file.size / 1024 / 1024 > 50) {
|
||||
message.error('单个文件不超过50MB');
|
||||
} else {
|
||||
const item: FileItem = {
|
||||
file,
|
||||
|
|
Loading…
Reference in New Issue