feat: License生成和下载
This commit is contained in:
@@ -16,6 +16,7 @@ export namespace LicenseApi {
|
||||
userNumber: number; // 用户数
|
||||
ranNumber: number; // 基站数
|
||||
activationCode: string; // 激活码
|
||||
fileUrl: string; // 激活码
|
||||
licenseContent: string; // License内容
|
||||
applicant: number; // 申请人
|
||||
applicationTime: Dayjs | string; // 申请时间
|
||||
@@ -54,6 +55,11 @@ export function applyLicense(data: LicenseApi.License) {
|
||||
return requestClient.put('/license/license/apply', data);
|
||||
}
|
||||
|
||||
/** 生成License */
|
||||
export function generateLicense(id: number) {
|
||||
return requestClient.get(`/license/license/generate?id=${id}`);
|
||||
}
|
||||
|
||||
/** 删除License */
|
||||
export function deleteLicense(id: number) {
|
||||
return requestClient.delete(`/license/license/delete?id=${id}`);
|
||||
|
||||
@@ -18,5 +18,13 @@
|
||||
"licenseAdminHelp": "Assigned to persons who have permission to generate licenses, and send email reminders",
|
||||
"apply": "Apply",
|
||||
"applyAction": "Apply For {0}",
|
||||
"applicationTime": "Application Time"
|
||||
"applicationTime": "Application Time",
|
||||
"generate": "Generate",
|
||||
"generating": "Generating...",
|
||||
"generateSuccess": "Generation Successful",
|
||||
"isDownload": "Do you want to download this file [{0}]?",
|
||||
"download": "Download",
|
||||
"downloadFailed": "Download failed, please try again later",
|
||||
"licenseFile": "License File",
|
||||
"applySuccess": "Application successful, email reminder sent, please wait for approval"
|
||||
}
|
||||
|
||||
@@ -18,5 +18,13 @@
|
||||
"licenseAdminHelp": "指派给有权限生成License的人员,并且发送邮件提醒",
|
||||
"apply": "申请",
|
||||
"applyAction": "申请{0}",
|
||||
"applicationTime": "申请时间"
|
||||
"applicationTime": "申请时间",
|
||||
"generate": "生成",
|
||||
"generating": "正在生成中...",
|
||||
"generateSuccess": "生成成功",
|
||||
"isDownload": "是否下载该文件【{0}】?",
|
||||
"download": "下载",
|
||||
"downloadFailed": "下载失败,请稍后重试",
|
||||
"licenseFile": "License文件",
|
||||
"applySuccess": "申请成功,已发送邮件提醒,请等待审核"
|
||||
}
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { LicenseApi } from '#/api/license/license';
|
||||
import type { DescriptionItemSchema } from '#/components/description';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { downloadFileFromBlobPart, formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { getCustomerList } from '#/api/license/customer';
|
||||
import { isLicenseSnUnique } from '#/api/license/license';
|
||||
import { getProjectList } from '#/api/license/project';
|
||||
import { getLicenseAdminList, getSimpleUserList } from '#/api/system/user';
|
||||
import { DictTag, DictTagGroup } from '#/components/dict-tag';
|
||||
import { $t } from '#/locales';
|
||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
// import Button from '../../../../../../packages/@core/ui-kit/shadcn-ui/src/components/button/button.vue';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
let projectList = await getProjectList({});
|
||||
const customerList = await getCustomerList();
|
||||
@@ -474,7 +481,29 @@ export function useGridColumns(
|
||||
{
|
||||
code: 'apply',
|
||||
text: $t('license.apply'),
|
||||
show: hasAccessByCodes(['license:license:apply']),
|
||||
show: (values: LicenseApi.License) => {
|
||||
return (
|
||||
hasAccessByCodes(['license:license:apply']) &&
|
||||
values.status === 0
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
code: 'generate',
|
||||
text: $t('license.generate'),
|
||||
show: (values: LicenseApi.License) => {
|
||||
return (
|
||||
hasAccessByCodes(['license:license:generate']) &&
|
||||
values.status === 1
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
code: 'download',
|
||||
text: $t('license.download'),
|
||||
show: (values: LicenseApi.License) => {
|
||||
return values.status === 2;
|
||||
},
|
||||
},
|
||||
{
|
||||
code: 'delete',
|
||||
@@ -485,3 +514,123 @@ export function useGridColumns(
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 详情页的字段 */
|
||||
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
return [
|
||||
{
|
||||
field: 'customerName',
|
||||
label: $t('license.customer'),
|
||||
},
|
||||
{
|
||||
field: 'projectName',
|
||||
label: $t('license.project'),
|
||||
},
|
||||
{
|
||||
field: 'serialNo',
|
||||
label: 'SN',
|
||||
},
|
||||
{
|
||||
field: 'expiryDate',
|
||||
label: $t('license.expiryDate'),
|
||||
content: (data) => {
|
||||
return formatDateTime(data?.expiryDate) as string;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'neList',
|
||||
label: $t('license.neList'),
|
||||
content: (data) => {
|
||||
return h(DictTagGroup, {
|
||||
type: DICT_TYPE.LIC_NE_LIST,
|
||||
value: data.neList,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'userNumber',
|
||||
label: $t('license.userNumber'),
|
||||
},
|
||||
{
|
||||
field: 'ranNumber',
|
||||
label: $t('license.ranNumber'),
|
||||
},
|
||||
{
|
||||
field: 'applicantName',
|
||||
label: $t('license.applicant'),
|
||||
},
|
||||
{
|
||||
field: 'applicationTime',
|
||||
label: $t('license.applicationTime'),
|
||||
content: (data) => {
|
||||
return formatDateTime(data?.applicationTime) as string;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: $t('license.status'),
|
||||
content: (data) => {
|
||||
return h(DictTag, {
|
||||
type: DICT_TYPE.LIC_LICENSE_STATUS,
|
||||
value: data.status,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: $t('license.remark'),
|
||||
},
|
||||
{
|
||||
field: 'fileUrl',
|
||||
label: $t('license.licenseFile'),
|
||||
hidden: (data) => data.status !== 2,
|
||||
content: (data) => {
|
||||
const fileName = `${data.fileUrl?.slice(
|
||||
Math.max(0, data.fileUrl.lastIndexOf('/') + 1),
|
||||
data.fileUrl.lastIndexOf('_'),
|
||||
)}.ini`;
|
||||
// 创建下载链接
|
||||
const link = h(
|
||||
'span',
|
||||
{
|
||||
style: {
|
||||
marginRight: '15px',
|
||||
},
|
||||
},
|
||||
fileName,
|
||||
);
|
||||
|
||||
// 创建下载按钮
|
||||
const button = h(
|
||||
Button,
|
||||
{
|
||||
onClick: async () => {
|
||||
const res = await fetch(data.fileUrl);
|
||||
if (!res.ok) {
|
||||
message.error($t('license.downloadFailed'));
|
||||
return;
|
||||
}
|
||||
const blob = await res.blob();
|
||||
|
||||
downloadFileFromBlobPart({ fileName, source: blob });
|
||||
},
|
||||
type: 'primary',
|
||||
},
|
||||
$t('license.download'),
|
||||
);
|
||||
|
||||
// 包裹容器
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
},
|
||||
[link, button],
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,118 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LicenseApi } from '#/api/license/license';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useTabs } from '@vben/hooks';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { generateLicense, getLicense } from '#/api/license/license';
|
||||
import { useDescription } from '#/components/description';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useDetailSchema } from '../data';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const loading = ref(false);
|
||||
const formData = ref<LicenseApi.License>();
|
||||
|
||||
const [Description] = useDescription({
|
||||
componentProps: {
|
||||
bordered: true,
|
||||
column: 1,
|
||||
class: 'mx-4',
|
||||
},
|
||||
schema: useDetailSchema(),
|
||||
});
|
||||
|
||||
/** 获取详情数据 */
|
||||
async function getDetail(id: any) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
formData.value = await getLicense(id);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const tabs = useTabs();
|
||||
/** 返回列表 */
|
||||
function close() {
|
||||
tabs.closeCurrentTab();
|
||||
router.push('/license');
|
||||
}
|
||||
|
||||
/** 生成文件 */
|
||||
async function onGenerate() {
|
||||
if (!formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
const hideLoading = message.loading({
|
||||
content: $t('license.generating'),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await generateLicense(formData.value.id);
|
||||
hideLoading();
|
||||
await getDetail(formData.value.id);
|
||||
|
||||
// 确认是否下载该文件
|
||||
// confirm({
|
||||
// title: $t('license.generateSuccess'),
|
||||
// content: $t('license.isDownload', [fileName]),
|
||||
// confirmText: $t('license.download'),
|
||||
// cancelText: $t('common.cancel'),
|
||||
// }).then(async () => {
|
||||
// const res = await fetch(response);
|
||||
// if (!res.ok) {
|
||||
// message.error($t('license.downloadFailed'));
|
||||
// return;
|
||||
// }
|
||||
// const blob = await res.blob();
|
||||
|
||||
// downloadFileFromBlobPart({ fileName, source: blob });
|
||||
// });
|
||||
message.success($t('license.generateSuccess'));
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
getDetail(route.query.id);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>测试</div>
|
||||
<Page auto-content-height v-loading="loading">
|
||||
<div class="bg-card flex h-[100%] flex-col rounded-md p-4">
|
||||
<div class="flex-1 overflow-auto py-4">
|
||||
<Description :data="formData" :label-style="{ width: '250px' }" />
|
||||
</div>
|
||||
<div class="mt-4 flex justify-center space-x-2">
|
||||
<Button @click="close"> {{ $t('common.back') }}</Button>
|
||||
<Button
|
||||
v-if="
|
||||
hasAccessByCodes(['license:license:generate']) &&
|
||||
formData?.status === 1
|
||||
"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="onGenerate"
|
||||
>
|
||||
{{ $t('license.generate') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,8 @@ import type {
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { LicenseApi } from '#/api/license/license';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||
|
||||
@@ -21,6 +23,8 @@ import { $t } from '#/locales';
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
@@ -41,6 +45,27 @@ function onApply(row: LicenseApi.License) {
|
||||
formModalApi.setData({ ...row, action: 1 }).open();
|
||||
}
|
||||
|
||||
/** 生成License */
|
||||
function onGenerate(row: LicenseApi.License) {
|
||||
router.push({ name: 'LicenseGenerate', query: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 下载License */
|
||||
async function onDownload(row: LicenseApi.License) {
|
||||
const fileName = `${row.fileUrl?.slice(
|
||||
Math.max(0, row.fileUrl.lastIndexOf('/') + 1),
|
||||
row.fileUrl.lastIndexOf('_'),
|
||||
)}.ini`;
|
||||
const res = await fetch(row.fileUrl);
|
||||
if (!res.ok) {
|
||||
message.error($t('license.downloadFailed'));
|
||||
return;
|
||||
}
|
||||
const blob = await res.blob();
|
||||
|
||||
downloadFileFromBlobPart({ fileName, source: blob });
|
||||
}
|
||||
|
||||
/** 编辑License */
|
||||
function onEdit(row: LicenseApi.License) {
|
||||
formModalApi.setData(row).open();
|
||||
@@ -49,14 +74,14 @@ function onEdit(row: LicenseApi.License) {
|
||||
/** 删除License */
|
||||
async function onDelete(row: LicenseApi.License) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.sn]),
|
||||
content: $t('ui.actionMessage.deleting', [row.serialNo]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteLicense(row.id as number);
|
||||
hideLoading();
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.sn]));
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.serialNo]));
|
||||
onRefresh();
|
||||
} catch {
|
||||
hideLoading();
|
||||
@@ -80,10 +105,18 @@ function onActionClick({ code, row }: OnActionClickParams<LicenseApi.License>) {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'download': {
|
||||
onDownload(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
case 'generate': {
|
||||
onGenerate(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,18 +87,21 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as LicenseApi.License;
|
||||
data.neList = state.checkedList;
|
||||
const action = formData.value?.action || 0;
|
||||
try {
|
||||
if (formData.value?.id) {
|
||||
await (formData.value?.action === 1
|
||||
? applyLicense(data)
|
||||
: updateLicense(data));
|
||||
await (action === 1 ? applyLicense(data) : updateLicense(data));
|
||||
} else {
|
||||
await createLicense(data);
|
||||
}
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
if (action === 1) {
|
||||
message.success($t('license.applySuccess'), 3);
|
||||
} else {
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user