feat: license文件导入支持
This commit is contained in:
@@ -27,11 +27,13 @@ export namespace LicenseApi {
|
||||
neCodeList: NeCode[]; // 操作
|
||||
oldLicense: License;
|
||||
hasHistory: boolean;
|
||||
showUpload?: boolean; // 是否显示上传按钮
|
||||
}
|
||||
export interface NeCode {
|
||||
id: number; // 主键
|
||||
neList: number[]; // 网元开关
|
||||
activationCode: string; // 激活码
|
||||
fileUrl: string; // 文件地址
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +62,11 @@ export function createLicense(data: LicenseApi.License) {
|
||||
return requestClient.post('/license/license/create', data);
|
||||
}
|
||||
|
||||
/** 修改License */
|
||||
export function updateLicenseDetail(data: LicenseApi.NeCode) {
|
||||
return requestClient.put('/license/license/update-detail', data);
|
||||
}
|
||||
|
||||
/** 修改License */
|
||||
export function updateLicense(data: LicenseApi.License) {
|
||||
return requestClient.put('/license/license/update', data);
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import type { UploadChangeParam } from 'ant-design-vue';
|
||||
|
||||
import type { LicenseApi } from '#/api/license/license';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
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 { DictTagGroup } from '#/components/dict-tag';
|
||||
import { useUpload } from '#/components/upload/use-upload';
|
||||
import { $t } from '#/locales';
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
|
||||
@@ -18,6 +22,8 @@ const props = defineProps<{
|
||||
formData?: LicenseApi.License;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['getDetailById']);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '网元',
|
||||
@@ -101,136 +107,6 @@ const columns = [
|
||||
title: 'License文件',
|
||||
dataIndex: '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(),
|
||||
});
|
||||
|
||||
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>
|
||||
|
||||
<template>
|
||||
@@ -252,7 +183,73 @@ const [Description] = useDescription({
|
||||
:data-source="props.formData?.neCodeList"
|
||||
:columns="columns"
|
||||
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>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { LicenseApi } from '#/api/license/license';
|
||||
import type { ProjectApi } from '#/api/license/project';
|
||||
@@ -7,7 +7,6 @@ import type { DescriptionItemSchema } from '#/components/description';
|
||||
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { formatDate, formatDateTime } from '@vben/utils';
|
||||
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
@@ -21,7 +20,6 @@ import { DictTag } from '#/components/dict-tag';
|
||||
import { $t } from '#/locales';
|
||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
const customerList = ref<CustomerApi.Customer[]>([]);
|
||||
const projectList = ref<ProjectApi.Project[]>([]);
|
||||
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',
|
||||
label: $t('license.neList'),
|
||||
@@ -181,26 +199,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'Textarea',
|
||||
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(
|
||||
onActionClick?: OnActionClickFn<LicenseApi.License>,
|
||||
): VxeTableGridOptions<LicenseApi.License>['columns'] {
|
||||
export function useGridColumns(): VxeTableGridOptions<LicenseApi.License>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'customerName',
|
||||
|
||||
@@ -5,6 +5,8 @@ import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { getLicense } from '#/api/license/license';
|
||||
|
||||
import Detail from '../components/detail.vue';
|
||||
|
||||
const formData = ref<LicenseApi.License>();
|
||||
@@ -29,9 +31,17 @@ const [Modal, modalApi] = useVbenModal({
|
||||
if (!data || !data.id) {
|
||||
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>
|
||||
|
||||
<template>
|
||||
@@ -41,6 +51,6 @@ const [Modal, modalApi] = useVbenModal({
|
||||
:show-cancel-button="false"
|
||||
:show-confirm-button="false"
|
||||
>
|
||||
<Detail :form-data="formData" />
|
||||
<Detail :form-data="formData" @get-detail-by-id="getDetailById" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user