Compare commits

..

No commits in common. "6eab93291823deafea17bb43a328e50dd3164904" and "015a8140bb791e8738626efb20da6f5483ff6676" have entirely different histories.

33 changed files with 551 additions and 1710 deletions

View File

@ -9,7 +9,7 @@ COPY ./ $PROJECT_DIR
# 若网络不通,可以使用淘宝源
# RUN pnpm config set registry https://registry.npmmirror.com
# 若不存在安装pnpm
RUN npm install -g pnpm@8.10.2
RUN npm install -g pnpm
# 构建项目
# 安装生成依赖

View File

@ -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."

View File

@ -13,7 +13,7 @@ events {
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
client_max_body_size 50m;
client_max_body_size 40m;
map $time_iso8601 $logdate {
'~^(?<ymd>\d{4}-\d{2}-\d{2})' $ymd;
default 'date-not-found';

View File

@ -1,26 +0,0 @@
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 || {}),
});
}

View File

@ -32,14 +32,9 @@ 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,
@ -69,8 +64,4 @@ export default {
materialsInOut,
project,
vehicleUsage,
saleQuotationGroup,
saleQuotationComponent,
saleQuotationTemplate,
saleQuotation,
};

View File

@ -1,19 +1,5 @@
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,

View File

@ -1,9 +0,0 @@
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 }),
});
}

View File

@ -1,104 +0,0 @@
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: '删除成功' }),
});
}

View File

@ -1,104 +0,0 @@
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: '删除成功' }),
});
}

View File

@ -1,104 +0,0 @@
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: '删除成功' }),
});
}

View File

@ -21,8 +21,6 @@ declare namespace API {
remark: string;
/** 头像 */
avatar: string;
/** 所属域 */
domain: DomainType;
};
type AccountMenus = {
@ -1215,13 +1213,12 @@ 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;
@ -1382,9 +1379,8 @@ declare namespace API {
};
type MaterialsInventoryExportParams = {
time: string[];
filename:string;
projectId?: number;
time: string;
projectId: number;
};
type MaterialsInventoryInfoParams = {
@ -1511,42 +1507,7 @@ 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 = {
/** 项目名称 */
@ -1812,11 +1773,6 @@ declare namespace API {
};
// Materials In out history
type MaterialsInoutExportParams = {
time: string[];
filename:string;
};
type MaterialsInOutUpdateParams = {
id: number;
fileIds?: number[];
@ -1934,108 +1890,4 @@ 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;
};
}

View File

@ -58,11 +58,11 @@ export const useExportExcelModal = () => {
modalProps: {
title: t('component.excel.exportModalTitle'),
onFinish: async (values) => {
const { filename, bookType, time } = values;
const { filename, bookType } = values;
onOk({
filename: bookType ? `${filename.split('.').shift()}.${bookType}` : filename,
filename: `${filename.split('.').shift()}.${bookType}`,
bookType,
time,
});
},
},

View File

@ -12,12 +12,7 @@ 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__';

View File

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

View File

@ -1,4 +1,3 @@
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';

View File

@ -1,26 +1,23 @@
<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>
@ -33,17 +30,14 @@
</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' })">
@ -67,16 +61,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,
@ -86,40 +80,40 @@ import {
Avatar,
Tooltip,
type MenuTheme,
} 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';
} 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';
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) => {
@ -139,13 +133,13 @@ const menus = computed(() => {
];
}
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) {
@ -165,11 +159,11 @@ const findLastChild = (route?: RouteRecordRaw) => {
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);
@ -180,10 +174,10 @@ const clickMenuItem = (menuItem: RouteRecordRaw) => {
} else {
router.push({ name: lastChild?.name });
}
};
};
// 退
const doLogout = () => {
// 退
const doLogout = () => {
Modal.confirm({
title: '您确定要退出登录吗?',
icon: <QuestionCircleOutlined />,
@ -196,11 +190,11 @@ const doLogout = () => {
}
},
});
};
};
</script>
<style lang="less" scoped>
.layout-header {
.layout-header {
display: flex;
position: sticky;
z-index: 10;
@ -213,5 +207,5 @@ const doLogout = () => {
* {
cursor: pointer;
}
}
}
</style>

View File

@ -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, DOMAIN_KEY } from '@/enums/cacheEnum';
import { ACCESS_TOKEN_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('admin');
const name = ref('amdin');
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,7 +87,6 @@ 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 {
@ -98,7 +97,6 @@ export const useUserStore = defineStore('user', () => {
return Promise.reject(error);
}
};
/** 解锁屏幕 */
const unlock = async (params: API.LoginDto) => {
try {
@ -108,17 +106,6 @@ 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 {
@ -126,12 +113,9 @@ 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) {
@ -174,8 +158,6 @@ export const useUserStore = defineStore('user', () => {
perms,
menus,
userInfo,
domain,
changeDomain,
login,
unlock,
afterLogin,

View File

@ -123,8 +123,6 @@ 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;
@ -139,7 +137,6 @@ 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>;

View File

@ -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="用戶名">
<a-input v-model:value="state.formInline.username" size="large" placeholder="admin">
<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="密碼"
placeholder="huaxin123"
autocomplete="new-password"
>
<template #prefix><lock-outlined type="user" /></template>
@ -61,8 +61,8 @@
loading: false,
captcha: '',
formInline: {
username: '',
password: '',
username: 'admin',
password: 'huaxin123',
verifyCode: '',
captchaId: '',
},

View File

@ -21,37 +21,36 @@
</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, 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({
} 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({
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,
{
@ -99,85 +98,72 @@ onMounted(() => {
],
},
];
});
const openExportModal = async () => {
const queryModel = dynamicTableInstance.queryFormRef?.formModel;
if(!queryModel?.time){
message.warning('请选择时间范围')
return;
}
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]}`
});
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) => {
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
})
}
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 openAttachmentUploadModal = async (record: TableListItem) => {
isUploadPopupVisiable.value = true;
fnModal.show({
width: 800,
@ -198,26 +184,26 @@ const openAttachmentUploadModal = async (record: TableListItem) => {
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 ? '编辑' : '新增'}出入库记录`,
@ -251,13 +237,13 @@ const openEditModal = async (record: Partial<TableListItem>) => {
...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
@ -269,9 +255,9 @@ const FilesRender: FunctionalComponent<TableListItem> = (materialsInOut: TableLi
{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,
@ -287,11 +273,12 @@ const openFilesManageModal = (fnModal, tableData: TableListItem) => {
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>

View File

@ -18,26 +18,25 @@
</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';
import { useExportExcelModal } from '@/components/basic/excel';
<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';
defineOptions({
defineOptions({
name: 'MaterialsInventory',
});
const [DynamicTable, dynamicTableInstance] = useTable();
const [showExportModal] = useFormModal();
const exportExcelModal = useExportExcelModal();
let columns = ref<TableColumnItem[]>();
onMounted(() => {
});
const [DynamicTable, dynamicTableInstance] = useTable();
const [showExportModal] = useFormModal();
let columns = ref<TableColumnItem[]>();
onMounted(() => {
columns.value = [
...baseColumns,
@ -72,56 +71,41 @@ onMounted(() => {
// ],
// },
];
});
const exportMI = async () => {
});
const exportMI = async () => {
const { time } = unref<{ time: string; projectId: number }>(
dynamicTableInstance?.queryFormRef?.formModel as { time: string; projectId: number },
);
exportExcelModal.openModal({
formSchemas: [
{
field: 'time',
component: 'RangePicker',
label: '时间范围',
rules: [{ required: true }],
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,
},
],
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>
暂不支持编辑功能,等待后期需求确认
@ -158,14 +142,15 @@ const openEditModal = async (record: Partial<TableListItem>) => {
// ...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

View File

@ -1,35 +0,0 @@
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',
},
];

View File

@ -1,67 +0,0 @@
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,
},
},
];

View File

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

View File

@ -1,10 +0,0 @@
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',
},
];

View File

@ -1,12 +0,0 @@
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,
},
},
];

View File

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

View File

@ -1,13 +0,0 @@
<template>
<div>test</div>
</template>
<script setup lang='ts'>
defineOptions({
name: 'SaleQuotation'
})
</script>
<style lang='less' scoped>
</style>

View File

@ -1,10 +0,0 @@
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',
},
];

View File

@ -1,12 +0,0 @@
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,
},
},
];

View File

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

View File

@ -43,12 +43,12 @@
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt = file.size / 1024 / 1024 < 50;
if (!isLt) {
message.error('Image must smaller than 50MB!');
const isLt2M = file.size / 1024 / 1024 < 30;
if (!isLt2M) {
message.error('Image must smaller than 30MB!');
}
return isJpgOrPng && isLt;
return isJpgOrPng && isLt2M;
};
const uploadAvatar = async (file: FileType) => {

View File

@ -13,7 +13,7 @@
@cancel="onCancel"
>
<a-flex justify="space-between" align="center">
<a-alert message="单个文件不超过50MB最多只能上传10个文件" type="info" show-icon />
<a-alert message="单个文件不超过30MB最多只能上传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 > 50) {
message.error('单个文件不超过50MB');
if (file.size / 1024 / 1024 > 30) {
message.error('单个文件不超过30MB');
} else {
const item: FileItem = {
file,