feat: License生成和下载
This commit is contained in:
@@ -16,6 +16,7 @@ export namespace LicenseApi {
|
|||||||
userNumber: number; // 用户数
|
userNumber: number; // 用户数
|
||||||
ranNumber: number; // 基站数
|
ranNumber: number; // 基站数
|
||||||
activationCode: string; // 激活码
|
activationCode: string; // 激活码
|
||||||
|
fileUrl: string; // 激活码
|
||||||
licenseContent: string; // License内容
|
licenseContent: string; // License内容
|
||||||
applicant: number; // 申请人
|
applicant: number; // 申请人
|
||||||
applicationTime: Dayjs | string; // 申请时间
|
applicationTime: Dayjs | string; // 申请时间
|
||||||
@@ -54,6 +55,11 @@ export function applyLicense(data: LicenseApi.License) {
|
|||||||
return requestClient.put('/license/license/apply', data);
|
return requestClient.put('/license/license/apply', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 生成License */
|
||||||
|
export function generateLicense(id: number) {
|
||||||
|
return requestClient.get(`/license/license/generate?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** 删除License */
|
/** 删除License */
|
||||||
export function deleteLicense(id: number) {
|
export function deleteLicense(id: number) {
|
||||||
return requestClient.delete(`/license/license/delete?id=${id}`);
|
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",
|
"licenseAdminHelp": "Assigned to persons who have permission to generate licenses, and send email reminders",
|
||||||
"apply": "Apply",
|
"apply": "Apply",
|
||||||
"applyAction": "Apply For {0}",
|
"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的人员,并且发送邮件提醒",
|
"licenseAdminHelp": "指派给有权限生成License的人员,并且发送邮件提醒",
|
||||||
"apply": "申请",
|
"apply": "申请",
|
||||||
"applyAction": "申请{0}",
|
"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 { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { LicenseApi } from '#/api/license/license';
|
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 { useAccess } from '@vben/access';
|
||||||
|
import { downloadFileFromBlobPart, formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { z } from '#/adapter/form';
|
import { z } from '#/adapter/form';
|
||||||
import { getCustomerList } from '#/api/license/customer';
|
import { getCustomerList } from '#/api/license/customer';
|
||||||
import { isLicenseSnUnique } from '#/api/license/license';
|
import { isLicenseSnUnique } from '#/api/license/license';
|
||||||
import { getProjectList } from '#/api/license/project';
|
import { getProjectList } from '#/api/license/project';
|
||||||
import { getLicenseAdminList, getSimpleUserList } from '#/api/system/user';
|
import { getLicenseAdminList, getSimpleUserList } from '#/api/system/user';
|
||||||
|
import { DictTag, DictTagGroup } 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';
|
||||||
|
|
||||||
|
// import Button from '../../../../../../packages/@core/ui-kit/shadcn-ui/src/components/button/button.vue';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
const { hasAccessByCodes } = useAccess();
|
||||||
let projectList = await getProjectList({});
|
let projectList = await getProjectList({});
|
||||||
const customerList = await getCustomerList();
|
const customerList = await getCustomerList();
|
||||||
@@ -474,7 +481,29 @@ export function useGridColumns(
|
|||||||
{
|
{
|
||||||
code: 'apply',
|
code: 'apply',
|
||||||
text: $t('license.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',
|
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>
|
<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>
|
</template>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import type {
|
|||||||
} from '#/adapter/vxe-table';
|
} from '#/adapter/vxe-table';
|
||||||
import type { LicenseApi } from '#/api/license/license';
|
import type { LicenseApi } from '#/api/license/license';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||||
|
|
||||||
@@ -21,6 +23,8 @@ import { $t } from '#/locales';
|
|||||||
import { useGridColumns, useGridFormSchema } from './data';
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
import Form from './modules/form.vue';
|
import Form from './modules/form.vue';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const [FormModal, formModalApi] = useVbenModal({
|
const [FormModal, formModalApi] = useVbenModal({
|
||||||
connectedComponent: Form,
|
connectedComponent: Form,
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
@@ -41,6 +45,27 @@ function onApply(row: LicenseApi.License) {
|
|||||||
formModalApi.setData({ ...row, action: 1 }).open();
|
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 */
|
/** 编辑License */
|
||||||
function onEdit(row: LicenseApi.License) {
|
function onEdit(row: LicenseApi.License) {
|
||||||
formModalApi.setData(row).open();
|
formModalApi.setData(row).open();
|
||||||
@@ -49,14 +74,14 @@ function onEdit(row: LicenseApi.License) {
|
|||||||
/** 删除License */
|
/** 删除License */
|
||||||
async function onDelete(row: LicenseApi.License) {
|
async function onDelete(row: LicenseApi.License) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.deleting', [row.sn]),
|
content: $t('ui.actionMessage.deleting', [row.serialNo]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteLicense(row.id as number);
|
await deleteLicense(row.id as number);
|
||||||
hideLoading();
|
hideLoading();
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', [row.sn]));
|
message.success($t('ui.actionMessage.deleteSuccess', [row.serialNo]));
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch {
|
} catch {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
@@ -80,10 +105,18 @@ function onActionClick({ code, row }: OnActionClickParams<LicenseApi.License>) {
|
|||||||
onDelete(row);
|
onDelete(row);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'download': {
|
||||||
|
onDownload(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'edit': {
|
case 'edit': {
|
||||||
onEdit(row);
|
onEdit(row);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'generate': {
|
||||||
|
onGenerate(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,18 +87,21 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
// 提交表单
|
// 提交表单
|
||||||
const data = (await formApi.getValues()) as LicenseApi.License;
|
const data = (await formApi.getValues()) as LicenseApi.License;
|
||||||
data.neList = state.checkedList;
|
data.neList = state.checkedList;
|
||||||
|
const action = formData.value?.action || 0;
|
||||||
try {
|
try {
|
||||||
if (formData.value?.id) {
|
if (formData.value?.id) {
|
||||||
await (formData.value?.action === 1
|
await (action === 1 ? applyLicense(data) : updateLicense(data));
|
||||||
? applyLicense(data)
|
|
||||||
: updateLicense(data));
|
|
||||||
} else {
|
} else {
|
||||||
await createLicense(data);
|
await createLicense(data);
|
||||||
}
|
}
|
||||||
// 关闭并提示
|
// 关闭并提示
|
||||||
await modalApi.close();
|
await modalApi.close();
|
||||||
emit('success');
|
emit('success');
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
if (action === 1) {
|
||||||
|
message.success($t('license.applySuccess'), 3);
|
||||||
|
} else {
|
||||||
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user