feat: license文件导入支持
This commit is contained in:
@@ -27,11 +27,13 @@ export namespace LicenseApi {
|
|||||||
neCodeList: NeCode[]; // 操作
|
neCodeList: NeCode[]; // 操作
|
||||||
oldLicense: License;
|
oldLicense: License;
|
||||||
hasHistory: boolean;
|
hasHistory: boolean;
|
||||||
|
showUpload?: boolean; // 是否显示上传按钮
|
||||||
}
|
}
|
||||||
export interface NeCode {
|
export interface NeCode {
|
||||||
id: number; // 主键
|
id: number; // 主键
|
||||||
neList: number[]; // 网元开关
|
neList: number[]; // 网元开关
|
||||||
activationCode: string; // 激活码
|
activationCode: string; // 激活码
|
||||||
|
fileUrl: string; // 文件地址
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +62,11 @@ export function createLicense(data: LicenseApi.License) {
|
|||||||
return requestClient.post('/license/license/create', data);
|
return requestClient.post('/license/license/create', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 修改License */
|
||||||
|
export function updateLicenseDetail(data: LicenseApi.NeCode) {
|
||||||
|
return requestClient.put('/license/license/update-detail', data);
|
||||||
|
}
|
||||||
|
|
||||||
/** 修改License */
|
/** 修改License */
|
||||||
export function updateLicense(data: LicenseApi.License) {
|
export function updateLicense(data: LicenseApi.License) {
|
||||||
return requestClient.put('/license/license/update', data);
|
return requestClient.put('/license/license/update', data);
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { UploadChangeParam } from 'ant-design-vue';
|
||||||
|
|
||||||
import type { LicenseApi } from '#/api/license/license';
|
import type { LicenseApi } from '#/api/license/license';
|
||||||
|
|
||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
|
|
||||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||||
|
|
||||||
import { Button, message } from 'ant-design-vue';
|
import { Button, message, UploadDragger } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { updateLicenseDetail } from '#/api/license/license';
|
||||||
import { useDescription } from '#/components/description';
|
import { useDescription } from '#/components/description';
|
||||||
import { DictTagGroup } from '#/components/dict-tag';
|
import { DictTagGroup } from '#/components/dict-tag';
|
||||||
|
import { useUpload } from '#/components/upload/use-upload';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { DICT_TYPE } from '#/utils';
|
import { DICT_TYPE } from '#/utils';
|
||||||
|
|
||||||
@@ -18,6 +22,8 @@ const props = defineProps<{
|
|||||||
formData?: LicenseApi.License;
|
formData?: LicenseApi.License;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['getDetailById']);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '网元',
|
title: '网元',
|
||||||
@@ -101,136 +107,6 @@ const columns = [
|
|||||||
title: 'License文件',
|
title: 'License文件',
|
||||||
dataIndex: 'fileUrlListMap',
|
dataIndex: 'fileUrlListMap',
|
||||||
key: 'fileUrlListMap',
|
key: 'fileUrlListMap',
|
||||||
customRender: (data: any) => {
|
|
||||||
const getFile = (dataValue: any) => {
|
|
||||||
if (!dataValue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!dataValue[0]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const fileName = `${dataValue[0]?.slice(
|
|
||||||
Math.max(0, dataValue[0].lastIndexOf('/') + 1),
|
|
||||||
dataValue[0].lastIndexOf('_'),
|
|
||||||
)}.ini`;
|
|
||||||
// 创建下载链接
|
|
||||||
const link = h(
|
|
||||||
'span',
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
marginRight: '15px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fileName,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 创建下载按钮
|
|
||||||
const button = h(
|
|
||||||
Button,
|
|
||||||
{
|
|
||||||
onClick: async () => {
|
|
||||||
const res = await fetch(dataValue[0]);
|
|
||||||
if (!res.ok) {
|
|
||||||
message.error($t('license.downloadFailed'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const blob = await res.blob();
|
|
||||||
|
|
||||||
downloadFileFromBlobPart({ fileName, source: blob });
|
|
||||||
},
|
|
||||||
type: 'primary',
|
|
||||||
},
|
|
||||||
() => $t('license.download'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const file = h(
|
|
||||||
'div',
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[link, button],
|
|
||||||
);
|
|
||||||
|
|
||||||
let file1;
|
|
||||||
if (dataValue[1]) {
|
|
||||||
const fileName1 = `${dataValue[1]?.slice(
|
|
||||||
Math.max(0, dataValue[1].lastIndexOf('/') + 1),
|
|
||||||
dataValue[1].lastIndexOf('_'),
|
|
||||||
)}.ini`;
|
|
||||||
// 创建下载链接
|
|
||||||
const link1 = h(
|
|
||||||
'span',
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
marginRight: '15px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fileName1,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 创建下载按钮
|
|
||||||
const button1 = h(
|
|
||||||
Button,
|
|
||||||
{
|
|
||||||
onClick: async () => {
|
|
||||||
const res = await fetch(dataValue[1]);
|
|
||||||
if (!res.ok) {
|
|
||||||
message.error($t('license.downloadFailed'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const blob1 = await res.blob();
|
|
||||||
|
|
||||||
downloadFileFromBlobPart({
|
|
||||||
fileName: fileName1,
|
|
||||||
source: blob1,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
type: 'primary',
|
|
||||||
},
|
|
||||||
() => $t('license.download'),
|
|
||||||
);
|
|
||||||
|
|
||||||
file1 = h(
|
|
||||||
'div',
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
margin: '8px 0 0 0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[link1, button1],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return h(
|
|
||||||
'div',
|
|
||||||
{
|
|
||||||
style: {},
|
|
||||||
},
|
|
||||||
[file, file1],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 包裹容器
|
|
||||||
// return getFile(data.value);
|
|
||||||
|
|
||||||
if (data.value.new) {
|
|
||||||
const after = h(
|
|
||||||
'span',
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
color: 'red',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[` → `, getFile(data.value.new)],
|
|
||||||
);
|
|
||||||
return h('div', {}, [getFile(data.value.old), after]);
|
|
||||||
}
|
|
||||||
return getFile(data.value.old);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -242,6 +118,61 @@ const [Description] = useDescription({
|
|||||||
},
|
},
|
||||||
schema: useDetailSchema(),
|
schema: useDetailSchema(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleDownload = async (item: string) => {
|
||||||
|
const res = await fetch(item);
|
||||||
|
if (!res.ok) {
|
||||||
|
message.error($t('license.downloadFailed'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blob1 = await res.blob();
|
||||||
|
|
||||||
|
downloadFileFromBlobPart({
|
||||||
|
fileName: `${item?.slice(
|
||||||
|
Math.max(0, item.lastIndexOf('/') + 1),
|
||||||
|
item.lastIndexOf('_'),
|
||||||
|
)}.ini`,
|
||||||
|
source: blob1,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 上传前 */
|
||||||
|
function beforeUpload() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (info: UploadChangeParam, record: any) => {
|
||||||
|
record.fileList = info.fileList;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpload = async (record: any) => {
|
||||||
|
try {
|
||||||
|
// 1. 上传,获取 URL
|
||||||
|
record.uploading = true;
|
||||||
|
for (const item of record.fileList) {
|
||||||
|
const { httpRequest } = useUpload();
|
||||||
|
// 将 Blob 转换为 File
|
||||||
|
const fileObj = new File([item.originFileObj], item.name, {
|
||||||
|
type: item.type,
|
||||||
|
});
|
||||||
|
const fileUrl = await httpRequest(fileObj);
|
||||||
|
|
||||||
|
await updateLicenseDetail({
|
||||||
|
id: record.id,
|
||||||
|
neList: [],
|
||||||
|
activationCode: '',
|
||||||
|
fileUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('getDetailById', props.formData?.id || 0);
|
||||||
|
message.success('上传成功');
|
||||||
|
} catch {
|
||||||
|
message.error($t('上传失败'));
|
||||||
|
}
|
||||||
|
record.fileList = [];
|
||||||
|
record.uploading = false;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -252,7 +183,73 @@ const [Description] = useDescription({
|
|||||||
:data-source="props.formData?.neCodeList"
|
:data-source="props.formData?.neCodeList"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
bordered
|
bordered
|
||||||
/>
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'fileUrlListMap'">
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
record.fileUrlListMap.old &&
|
||||||
|
record.fileUrlListMap.old.length > 0
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in record.fileUrlListMap.old"
|
||||||
|
:key="index"
|
||||||
|
style="display: flex; align-items: center; margin-top: 8px"
|
||||||
|
>
|
||||||
|
<span style="margin-right: 15px">{{
|
||||||
|
`${item?.slice(
|
||||||
|
Math.max(0, item.lastIndexOf('/') + 1),
|
||||||
|
item.lastIndexOf('_'),
|
||||||
|
)}.ini`
|
||||||
|
}}</span>
|
||||||
|
<Button @click="handleDownload(item)" type="primary">
|
||||||
|
{{ $t('license.download') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
props.formData?.status === 3 && props.formData?.showUpload
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="w-full">
|
||||||
|
<UploadDragger
|
||||||
|
accept=".ini"
|
||||||
|
:max-count="2"
|
||||||
|
:file-list="record.fileList"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
@change="handleChange($event, record)"
|
||||||
|
>
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<span class="icon-[ep--upload-filled] size-12"></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="ant-upload-text">选择要导入的License文件</p>
|
||||||
|
</UploadDragger>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
style="margin-top: 5px"
|
||||||
|
:disabled="!record.fileList || record.fileList.length === 0"
|
||||||
|
:loading="record.uploading"
|
||||||
|
@click="handleUpload(record)"
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
确定导入
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { CustomerApi } from '#/api/license/customer';
|
import type { CustomerApi } from '#/api/license/customer';
|
||||||
import type { LicenseApi } from '#/api/license/license';
|
import type { LicenseApi } from '#/api/license/license';
|
||||||
import type { ProjectApi } from '#/api/license/project';
|
import type { ProjectApi } from '#/api/license/project';
|
||||||
@@ -7,7 +7,6 @@ import type { DescriptionItemSchema } from '#/components/description';
|
|||||||
|
|
||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
|
||||||
import { formatDate, formatDateTime } from '@vben/utils';
|
import { formatDate, formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
@@ -21,7 +20,6 @@ import { DictTag } from '#/components/dict-tag';
|
|||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
const customerList = ref<CustomerApi.Customer[]>([]);
|
const customerList = ref<CustomerApi.Customer[]>([]);
|
||||||
const projectList = ref<ProjectApi.Project[]>([]);
|
const projectList = ref<ProjectApi.Project[]>([]);
|
||||||
export const formData = ref<LicenseApi.License>();
|
export const formData = ref<LicenseApi.License>();
|
||||||
@@ -146,6 +144,26 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'approver',
|
||||||
|
label: $t('license.approver'),
|
||||||
|
component: 'ApiSelect',
|
||||||
|
rules: 'required',
|
||||||
|
help: $t('license.licenseAdminHelp'),
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
api: async () => {
|
||||||
|
const data = await getLicenseAdminList();
|
||||||
|
return data.map((item) => ({
|
||||||
|
label: item.nickname,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
showSearch: true,
|
||||||
|
filterOption: (input: string, option: any) =>
|
||||||
|
option.label.toLowerCase().includes(input.toLowerCase()),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'neCodeList',
|
fieldName: 'neCodeList',
|
||||||
label: $t('license.neList'),
|
label: $t('license.neList'),
|
||||||
@@ -181,26 +199,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'Textarea',
|
component: 'Textarea',
|
||||||
formItemClass: 'col-span-2',
|
formItemClass: 'col-span-2',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
fieldName: 'approver',
|
|
||||||
label: $t('license.approver'),
|
|
||||||
component: 'ApiSelect',
|
|
||||||
rules: 'required',
|
|
||||||
help: $t('license.licenseAdminHelp'),
|
|
||||||
componentProps: {
|
|
||||||
allowClear: true,
|
|
||||||
api: async () => {
|
|
||||||
const data = await getLicenseAdminList();
|
|
||||||
return data.map((item) => ({
|
|
||||||
label: item.nickname,
|
|
||||||
value: item.id,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
showSearch: true,
|
|
||||||
filterOption: (input: string, option: any) =>
|
|
||||||
option.label.toLowerCase().includes(input.toLowerCase()),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,9 +297,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns(
|
export function useGridColumns(): VxeTableGridOptions<LicenseApi.License>['columns'] {
|
||||||
onActionClick?: OnActionClickFn<LicenseApi.License>,
|
|
||||||
): VxeTableGridOptions<LicenseApi.License>['columns'] {
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
field: 'customerName',
|
field: 'customerName',
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { ref } from 'vue';
|
|||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { getLicense } from '#/api/license/license';
|
||||||
|
|
||||||
import Detail from '../components/detail.vue';
|
import Detail from '../components/detail.vue';
|
||||||
|
|
||||||
const formData = ref<LicenseApi.License>();
|
const formData = ref<LicenseApi.License>();
|
||||||
@@ -29,9 +31,17 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
if (!data || !data.id) {
|
if (!data || !data.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
formData.value = data;
|
// formData.value = data;
|
||||||
|
await getDetailById(data.id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getDetailById = async (id: number) => {
|
||||||
|
// 获取详情
|
||||||
|
const data = await getLicense(id);
|
||||||
|
formData.value = data;
|
||||||
|
formData.value.showUpload = true; // 显示上传按钮
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -41,6 +51,6 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
:show-cancel-button="false"
|
:show-cancel-button="false"
|
||||||
:show-confirm-button="false"
|
:show-confirm-button="false"
|
||||||
>
|
>
|
||||||
<Detail :form-data="formData" />
|
<Detail :form-data="formData" @get-detail-by-id="getDetailById" />
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user