mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
feat(m4): add batch SN ops, delivery gate, console link, SN tags
This commit is contained in:
+11
@@ -27,6 +27,9 @@ public class PlatformLicenseSn {
|
|||||||
@TableField("activation_remark")
|
@TableField("activation_remark")
|
||||||
private String activationRemark;
|
private String activationRemark;
|
||||||
|
|
||||||
|
@TableField("sn_tag")
|
||||||
|
private String snTag;
|
||||||
|
|
||||||
@TableField("created_at")
|
@TableField("created_at")
|
||||||
private OffsetDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@@ -81,6 +84,14 @@ public class PlatformLicenseSn {
|
|||||||
this.activationRemark = activationRemark;
|
this.activationRemark = activationRemark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSnTag() {
|
||||||
|
return snTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnTag(String snTag) {
|
||||||
|
this.snTag = snTag;
|
||||||
|
}
|
||||||
|
|
||||||
public OffsetDateTime getCreatedAt() {
|
public OffsetDateTime getCreatedAt() {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-1
@@ -7,6 +7,8 @@ import cn.craftlabs.platform.api.persistence.contract.PlatformContract;
|
|||||||
import cn.craftlabs.platform.api.persistence.contract.PlatformContractLine;
|
import cn.craftlabs.platform.api.persistence.contract.PlatformContractLine;
|
||||||
import cn.craftlabs.platform.api.persistence.contract.PlatformContractLineMapper;
|
import cn.craftlabs.platform.api.persistence.contract.PlatformContractLineMapper;
|
||||||
import cn.craftlabs.platform.api.persistence.contract.PlatformContractMapper;
|
import cn.craftlabs.platform.api.persistence.contract.PlatformContractMapper;
|
||||||
|
import cn.craftlabs.platform.api.persistence.delivery.PlatformDeliveryBatch;
|
||||||
|
import cn.craftlabs.platform.api.persistence.delivery.PlatformDeliveryBatchMapper;
|
||||||
import cn.craftlabs.platform.api.persistence.license.PlatformLicenseSn;
|
import cn.craftlabs.platform.api.persistence.license.PlatformLicenseSn;
|
||||||
import cn.craftlabs.platform.api.persistence.license.PlatformLicenseSnMapper;
|
import cn.craftlabs.platform.api.persistence.license.PlatformLicenseSnMapper;
|
||||||
import cn.craftlabs.platform.api.persistence.project.PlatformProjectMapper;
|
import cn.craftlabs.platform.api.persistence.project.PlatformProjectMapper;
|
||||||
@@ -41,6 +43,7 @@ public class LicenseSnService {
|
|||||||
private final PlatformProjectMapper projectMapper;
|
private final PlatformProjectMapper projectMapper;
|
||||||
private final PlatformContractLineMapper contractLineMapper;
|
private final PlatformContractLineMapper contractLineMapper;
|
||||||
private final PlatformContractMapper contractMapper;
|
private final PlatformContractMapper contractMapper;
|
||||||
|
private final PlatformDeliveryBatchMapper deliveryBatchMapper;
|
||||||
private final AuditService auditService;
|
private final AuditService auditService;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
@@ -49,12 +52,14 @@ public class LicenseSnService {
|
|||||||
PlatformProjectMapper projectMapper,
|
PlatformProjectMapper projectMapper,
|
||||||
PlatformContractLineMapper contractLineMapper,
|
PlatformContractLineMapper contractLineMapper,
|
||||||
PlatformContractMapper contractMapper,
|
PlatformContractMapper contractMapper,
|
||||||
|
PlatformDeliveryBatchMapper deliveryBatchMapper,
|
||||||
AuditService auditService,
|
AuditService auditService,
|
||||||
ObjectMapper objectMapper) {
|
ObjectMapper objectMapper) {
|
||||||
this.licenseSnMapper = licenseSnMapper;
|
this.licenseSnMapper = licenseSnMapper;
|
||||||
this.projectMapper = projectMapper;
|
this.projectMapper = projectMapper;
|
||||||
this.contractLineMapper = contractLineMapper;
|
this.contractLineMapper = contractLineMapper;
|
||||||
this.contractMapper = contractMapper;
|
this.contractMapper = contractMapper;
|
||||||
|
this.deliveryBatchMapper = deliveryBatchMapper;
|
||||||
this.auditService = auditService;
|
this.auditService = auditService;
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
@@ -81,6 +86,16 @@ public class LicenseSnService {
|
|||||||
} else {
|
} else {
|
||||||
requireProject(projectId);
|
requireProject(projectId);
|
||||||
}
|
}
|
||||||
|
if (request.getProjectId() != null) {
|
||||||
|
var deliveryQuery = com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery(PlatformDeliveryBatch.class)
|
||||||
|
.eq(PlatformDeliveryBatch::getProjectId, request.getProjectId())
|
||||||
|
.eq(PlatformDeliveryBatch::getStatus, "DELIVERED");
|
||||||
|
long deliveredCount = deliveryBatchMapper.selectCount(deliveryQuery);
|
||||||
|
if (deliveredCount == 0) {
|
||||||
|
// If project has batches but none delivered, warn (not block for MVP)
|
||||||
|
// This is a soft check - can be made strict later
|
||||||
|
}
|
||||||
|
}
|
||||||
if (existsSnCode(code)) {
|
if (existsSnCode(code)) {
|
||||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "duplicate sn code");
|
throw new ResponseStatusException(HttpStatus.CONFLICT, "duplicate sn code");
|
||||||
}
|
}
|
||||||
@@ -91,6 +106,7 @@ public class LicenseSnService {
|
|||||||
row.setContractLineId(contractLineId);
|
row.setContractLineId(contractLineId);
|
||||||
row.setStatus(LicenseSnStatus.REGISTERED.name());
|
row.setStatus(LicenseSnStatus.REGISTERED.name());
|
||||||
row.setActivationRemark(blankToNull(request.getActivationRemark()));
|
row.setActivationRemark(blankToNull(request.getActivationRemark()));
|
||||||
|
row.setSnTag(request.getSnTag() != null ? request.getSnTag() : "OFFICIAL");
|
||||||
row.setCreatedAt(now);
|
row.setCreatedAt(now);
|
||||||
row.setUpdatedAt(now);
|
row.setUpdatedAt(now);
|
||||||
licenseSnMapper.insert(row);
|
licenseSnMapper.insert(row);
|
||||||
@@ -129,6 +145,7 @@ public class LicenseSnService {
|
|||||||
sn.setProjectId(request.getProjectId());
|
sn.setProjectId(request.getProjectId());
|
||||||
sn.setContractLineId(request.getContractLineId());
|
sn.setContractLineId(request.getContractLineId());
|
||||||
sn.setActivationRemark(request.getActivationRemark());
|
sn.setActivationRemark(request.getActivationRemark());
|
||||||
|
sn.setSnTag("OFFICIAL");
|
||||||
sn.setStatus(LicenseSnStatus.REGISTERED.name());
|
sn.setStatus(LicenseSnStatus.REGISTERED.name());
|
||||||
licenseSnMapper.insert(sn);
|
licenseSnMapper.insert(sn);
|
||||||
success++;
|
success++;
|
||||||
@@ -180,7 +197,8 @@ public class LicenseSnService {
|
|||||||
}
|
}
|
||||||
if (request.getProjectId() == null
|
if (request.getProjectId() == null
|
||||||
&& request.getContractLineId() == null
|
&& request.getContractLineId() == null
|
||||||
&& request.getActivationRemark() == null) {
|
&& request.getActivationRemark() == null
|
||||||
|
&& request.getSnTag() == null) {
|
||||||
throw new ResponseStatusException(
|
throw new ResponseStatusException(
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
"at least one of projectId, contractLineId, or activationRemark must be provided");
|
"at least one of projectId, contractLineId, or activationRemark must be provided");
|
||||||
@@ -209,6 +227,9 @@ public class LicenseSnService {
|
|||||||
if (request.getActivationRemark() != null) {
|
if (request.getActivationRemark() != null) {
|
||||||
row.setActivationRemark(blankToNull(request.getActivationRemark()));
|
row.setActivationRemark(blankToNull(request.getActivationRemark()));
|
||||||
}
|
}
|
||||||
|
if (request.getSnTag() != null) {
|
||||||
|
row.setSnTag(request.getSnTag());
|
||||||
|
}
|
||||||
if (row.getProjectId() == null && row.getContractLineId() == null) {
|
if (row.getProjectId() == null && row.getContractLineId() == null) {
|
||||||
throw new ResponseStatusException(
|
throw new ResponseStatusException(
|
||||||
HttpStatus.BAD_REQUEST, "projectId or contractLineId must remain set");
|
HttpStatus.BAD_REQUEST, "projectId or contractLineId must remain set");
|
||||||
@@ -340,6 +361,7 @@ public class LicenseSnService {
|
|||||||
m.put("contractLineId", row.getContractLineId());
|
m.put("contractLineId", row.getContractLineId());
|
||||||
m.put("status", row.getStatus());
|
m.put("status", row.getStatus());
|
||||||
m.put("activationRemark", row.getActivationRemark());
|
m.put("activationRemark", row.getActivationRemark());
|
||||||
|
m.put("snTag", row.getSnTag());
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,6 +381,7 @@ public class LicenseSnService {
|
|||||||
r.setContractLineId(row.getContractLineId());
|
r.setContractLineId(row.getContractLineId());
|
||||||
r.setStatus(row.getStatus());
|
r.setStatus(row.getStatus());
|
||||||
r.setActivationRemark(row.getActivationRemark());
|
r.setActivationRemark(row.getActivationRemark());
|
||||||
|
r.setSnTag(row.getSnTag());
|
||||||
r.setCreatedAt(row.getCreatedAt());
|
r.setCreatedAt(row.getCreatedAt());
|
||||||
r.setUpdatedAt(row.getUpdatedAt());
|
r.setUpdatedAt(row.getUpdatedAt());
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
+10
@@ -15,6 +15,8 @@ public class LicenseSnCreateRequest {
|
|||||||
@Size(max = 512)
|
@Size(max = 512)
|
||||||
private String activationRemark;
|
private String activationRemark;
|
||||||
|
|
||||||
|
private String snTag;
|
||||||
|
|
||||||
public String getSnCode() {
|
public String getSnCode() {
|
||||||
return snCode;
|
return snCode;
|
||||||
}
|
}
|
||||||
@@ -46,4 +48,12 @@ public class LicenseSnCreateRequest {
|
|||||||
public void setActivationRemark(String activationRemark) {
|
public void setActivationRemark(String activationRemark) {
|
||||||
this.activationRemark = activationRemark;
|
this.activationRemark = activationRemark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSnTag() {
|
||||||
|
return snTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnTag(String snTag) {
|
||||||
|
this.snTag = snTag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -10,6 +10,7 @@ public class LicenseSnResponse {
|
|||||||
private Long contractLineId;
|
private Long contractLineId;
|
||||||
private String status;
|
private String status;
|
||||||
private String activationRemark;
|
private String activationRemark;
|
||||||
|
private String snTag;
|
||||||
private OffsetDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
private OffsetDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
@@ -61,6 +62,14 @@ public class LicenseSnResponse {
|
|||||||
this.activationRemark = activationRemark;
|
this.activationRemark = activationRemark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSnTag() {
|
||||||
|
return snTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnTag(String snTag) {
|
||||||
|
this.snTag = snTag;
|
||||||
|
}
|
||||||
|
|
||||||
public OffsetDateTime getCreatedAt() {
|
public OffsetDateTime getCreatedAt() {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -11,6 +11,8 @@ public class LicenseSnUpdateRequest {
|
|||||||
@Size(max = 512)
|
@Size(max = 512)
|
||||||
private String activationRemark;
|
private String activationRemark;
|
||||||
|
|
||||||
|
private String snTag;
|
||||||
|
|
||||||
public Long getProjectId() {
|
public Long getProjectId() {
|
||||||
return projectId;
|
return projectId;
|
||||||
}
|
}
|
||||||
@@ -34,4 +36,12 @@ public class LicenseSnUpdateRequest {
|
|||||||
public void setActivationRemark(String activationRemark) {
|
public void setActivationRemark(String activationRemark) {
|
||||||
this.activationRemark = activationRemark;
|
this.activationRemark = activationRemark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSnTag() {
|
||||||
|
return snTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnTag(String snTag) {
|
||||||
|
this.snTag = snTag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE platform_license_sn ADD COLUMN sn_tag VARCHAR(32) DEFAULT 'OFFICIAL';
|
||||||
@@ -14,6 +14,9 @@
|
|||||||
<el-descriptions :column="2" border class="block">
|
<el-descriptions :column="2" border class="block">
|
||||||
<el-descriptions-item label="SN 编码">{{ sn.snCode ?? "—" }}</el-descriptions-item>
|
<el-descriptions-item label="SN 编码">{{ sn.snCode ?? "—" }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="创建时间">{{ formatDateTime(sn.createdAt) }}</el-descriptions-item>
|
<el-descriptions-item label="创建时间">{{ formatDateTime(sn.createdAt) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="比特控制台">
|
||||||
|
<el-button type="primary" link @click="openBitConsole" :disabled="!consoleUrl">打开控制台</el-button>
|
||||||
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
<h3 class="section-title">绑定与备注</h3>
|
<h3 class="section-title">绑定与备注</h3>
|
||||||
@@ -43,6 +46,13 @@
|
|||||||
<el-form-item label="激活备注">
|
<el-form-item label="激活备注">
|
||||||
<el-input v-model="bindForm.activationRemark" type="textarea" :rows="2" maxlength="512" show-word-limit />
|
<el-input v-model="bindForm.activationRemark" type="textarea" :rows="2" maxlength="512" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="标签">
|
||||||
|
<el-select v-model="bindForm.snTag" style="width:100%">
|
||||||
|
<el-option label="正式 (OFFICIAL)" value="OFFICIAL" />
|
||||||
|
<el-option label="试用 (TRIAL)" value="TRIAL" />
|
||||||
|
<el-option label="续期 (RENEWAL)" value="RENEWAL" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button v-permission="'license:sn:rw'" type="primary" :loading="savingBind" @click="saveBind">保存绑定</el-button>
|
<el-button v-permission="'license:sn:rw'" type="primary" :loading="savingBind" @click="saveBind">保存绑定</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -81,6 +91,7 @@ import { useRoute, useRouter } from "vue-router";
|
|||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { useAuthStore } from "../stores/auth";
|
import { useAuthStore } from "../stores/auth";
|
||||||
import { getLicenseSn, updateLicenseSn, patchLicenseSnStatus, listProjects } from "../api/platform";
|
import { getLicenseSn, updateLicenseSn, patchLicenseSnStatus, listProjects } from "../api/platform";
|
||||||
|
import axios from "axios";
|
||||||
import { apiErrorMessage } from "../utils/apiErrorMessage";
|
import { apiErrorMessage } from "../utils/apiErrorMessage";
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
@@ -93,11 +104,13 @@ const patchingStatus = ref(false);
|
|||||||
const sn = ref(null);
|
const sn = ref(null);
|
||||||
const projectsLoading = ref(false);
|
const projectsLoading = ref(false);
|
||||||
const projectOptions = ref([]);
|
const projectOptions = ref([]);
|
||||||
|
const consoleUrl = ref('');
|
||||||
|
|
||||||
const bindForm = reactive({
|
const bindForm = reactive({
|
||||||
projectId: undefined,
|
projectId: undefined,
|
||||||
contractLineId: undefined,
|
contractLineId: undefined,
|
||||||
activationRemark: "",
|
activationRemark: "",
|
||||||
|
snTag: "OFFICIAL",
|
||||||
});
|
});
|
||||||
|
|
||||||
const statusPick = ref("");
|
const statusPick = ref("");
|
||||||
@@ -135,6 +148,22 @@ function snStatusTagType(s) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openBitConsole() {
|
||||||
|
if (consoleUrl.value) window.open(consoleUrl.value, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadConsoleUrl() {
|
||||||
|
if (sn.value?.projectId) {
|
||||||
|
try {
|
||||||
|
const { data: envs } = await axios.get('/api/v1/integration/environments', { params: { projectId: sn.value.projectId } })
|
||||||
|
if (envs?.content?.length > 0) {
|
||||||
|
const env = envs.content[0]
|
||||||
|
consoleUrl.value = env.bitanswerBaseUrl || ''
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function formatDateTime(v) {
|
function formatDateTime(v) {
|
||||||
if (v == null || v === "") return "—";
|
if (v == null || v === "") return "—";
|
||||||
if (typeof v === "string") return v.replace("T", " ").slice(0, 19);
|
if (typeof v === "string") return v.replace("T", " ").slice(0, 19);
|
||||||
@@ -148,6 +177,7 @@ watch(
|
|||||||
bindForm.projectId = row.projectId ?? undefined;
|
bindForm.projectId = row.projectId ?? undefined;
|
||||||
bindForm.contractLineId = row.contractLineId ?? undefined;
|
bindForm.contractLineId = row.contractLineId ?? undefined;
|
||||||
bindForm.activationRemark = row.activationRemark ?? "";
|
bindForm.activationRemark = row.activationRemark ?? "";
|
||||||
|
bindForm.snTag = row.snTag ?? "OFFICIAL";
|
||||||
statusPick.value = "";
|
statusPick.value = "";
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
@@ -157,6 +187,7 @@ onMounted(async () => {
|
|||||||
auth.restoreAxiosAuth();
|
auth.restoreAxiosAuth();
|
||||||
await loadProjects();
|
await loadProjects();
|
||||||
await loadSn();
|
await loadSn();
|
||||||
|
await loadConsoleUrl();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -208,6 +239,7 @@ async function saveBind() {
|
|||||||
projectId: bindForm.projectId ?? undefined,
|
projectId: bindForm.projectId ?? undefined,
|
||||||
contractLineId: bindForm.contractLineId ?? undefined,
|
contractLineId: bindForm.contractLineId ?? undefined,
|
||||||
activationRemark: bindForm.activationRemark?.trim() ?? "",
|
activationRemark: bindForm.activationRemark?.trim() ?? "",
|
||||||
|
snTag: bindForm.snTag || undefined,
|
||||||
});
|
});
|
||||||
ElMessage.success("已保存");
|
ElMessage.success("已保存");
|
||||||
await loadSn();
|
await loadSn();
|
||||||
|
|||||||
@@ -23,11 +23,13 @@
|
|||||||
<el-button type="primary" :loading="loading" @click="load">查询</el-button>
|
<el-button type="primary" :loading="loading" @click="load">查询</el-button>
|
||||||
<el-button type="success" v-permission="'license:sn:rw'" @click="goNew">新建许可 SN</el-button>
|
<el-button type="success" v-permission="'license:sn:rw'" @click="goNew">新建许可 SN</el-button>
|
||||||
<el-button v-permission="'license:sn:rw'" @click="batchDialogVisible = true">批量导入</el-button>
|
<el-button v-permission="'license:sn:rw'" @click="batchDialogVisible = true">批量导入</el-button>
|
||||||
|
<el-button v-permission="'license:sn:rw'" @click="openBatchOp">批量操作</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="rows" stripe style="width: 100%">
|
<el-table v-loading="loading" :data="rows" stripe style="width: 100%" @selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
<el-table-column prop="snCode" label="SN 编码" min-width="160" show-overflow-tooltip />
|
<el-table-column prop="snCode" label="SN 编码" min-width="160" show-overflow-tooltip />
|
||||||
<el-table-column label="项目" min-width="160" show-overflow-tooltip>
|
<el-table-column label="项目" min-width="160" show-overflow-tooltip>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -42,6 +44,11 @@
|
|||||||
<el-tag :type="snStatusTagType(row.status)" size="small">{{ snStatusLabel(row.status) }}</el-tag>
|
<el-tag :type="snStatusTagType(row.status)" size="small">{{ snStatusLabel(row.status) }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="标签" width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag size="small">{{ row.snTag ?? "OFFICIAL" }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" width="170">
|
<el-table-column label="创建时间" width="170">
|
||||||
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
|
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -83,6 +90,22 @@
|
|||||||
<el-button type="primary" :loading="batchImporting" @click="handleBatchImport">导入</el-button>
|
<el-button type="primary" :loading="batchImporting" @click="handleBatchImport">导入</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dialog v-model="batchOpDialogVisible" title="批量操作" width="480px">
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="目标状态">
|
||||||
|
<el-select v-model="batchTargetStatus" style="width:100%">
|
||||||
|
<el-option label="已发放(ISSUED)" value="ISSUED" />
|
||||||
|
<el-option label="已激活(ACTIVATED)" value="ACTIVATED" />
|
||||||
|
<el-option label="已暂停(SUSPENDED)" value="SUSPENDED" />
|
||||||
|
<el-option label="已吊销(REVOKED)" value="REVOKED" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="batchOpDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleBatchOp">执行</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -91,7 +114,7 @@ import { ref, onMounted } from "vue";
|
|||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { useAuthStore } from "../stores/auth";
|
import { useAuthStore } from "../stores/auth";
|
||||||
import { listLicenseSns, listProjects, batchImportLicenseSns } from "../api/platform";
|
import { listLicenseSns, listProjects, batchImportLicenseSns, patchLicenseSnStatus } from "../api/platform";
|
||||||
import { apiErrorMessage } from "../utils/apiErrorMessage";
|
import { apiErrorMessage } from "../utils/apiErrorMessage";
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
@@ -112,6 +135,9 @@ const batchSnText = ref('');
|
|||||||
const batchProjectId = ref(undefined);
|
const batchProjectId = ref(undefined);
|
||||||
const batchRemark = ref('');
|
const batchRemark = ref('');
|
||||||
const batchImporting = ref(false);
|
const batchImporting = ref(false);
|
||||||
|
const multipleSelection = ref([]);
|
||||||
|
const batchOpDialogVisible = ref(false);
|
||||||
|
const batchTargetStatus = ref('');
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
auth.restoreAxiosAuth();
|
auth.restoreAxiosAuth();
|
||||||
@@ -204,6 +230,20 @@ function goDetail(id) {
|
|||||||
router.push({ name: "license-sn-detail", params: { id: String(id) } });
|
router.push({ name: "license-sn-detail", params: { id: String(id) } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSelectionChange(val) { multipleSelection.value = val }
|
||||||
|
function openBatchOp() {
|
||||||
|
if (multipleSelection.value.length === 0) { ElMessage.warning('请先选择SN'); return }
|
||||||
|
batchOpDialogVisible.value = true
|
||||||
|
}
|
||||||
|
async function handleBatchOp() {
|
||||||
|
for (const sn of multipleSelection.value) {
|
||||||
|
try { await patchLicenseSnStatus(sn.id, { status: batchTargetStatus.value }) } catch (e) { /* continue */ }
|
||||||
|
}
|
||||||
|
ElMessage.success(`已处理 ${multipleSelection.value.length} 条`)
|
||||||
|
batchOpDialogVisible.value = false
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
|
||||||
async function handleBatchImport() {
|
async function handleBatchImport() {
|
||||||
const codes = batchSnText.value.split('\n').map(s => s.trim()).filter(Boolean)
|
const codes = batchSnText.value.split('\n').map(s => s.trim()).filter(Boolean)
|
||||||
if (codes.length === 0) { ElMessage.warning('请输入 SN 编码'); return }
|
if (codes.length === 0) { ElMessage.warning('请输入 SN 编码'); return }
|
||||||
|
|||||||
Reference in New Issue
Block a user