feat: 支持 Linux 客户端配置与发布校验

This commit is contained in:
2026-07-14 08:43:58 +00:00
parent 6df8e3ad31
commit 1b4293e57c
10 changed files with 173 additions and 36 deletions
+34 -6
View File
@@ -28,6 +28,7 @@ const currentAppId = ref("");
const createdLicenseKey = ref("");
const generatedClientConfig = ref("");
const generatedCrashTestInfo = ref("");
const licenseConfigWarning = ref("");
const runtime = reactive<any>({});
const appForm = reactive({ app_id: "", app_name: "" });
@@ -193,6 +194,14 @@ function handleError(prefix: string, error: unknown) {
ElMessage.error(`${prefix}${friendlyError(error)}`);
}
function isLicenseFull(row: any) {
return Number(row?.used_devices || 0) >= Number(row?.max_devices || 0);
}
function isLicenseUsable(row: any) {
return row?.status === "active" && row?.license_key && !isLicenseFull(row);
}
async function loadRuntime() {
const data = await adminRequest<any>("/admin/runtime-config");
Object.assign(runtime, data || {});
@@ -434,10 +443,9 @@ async function loadLicenses() {
const usable =
licenses.value.find(
item =>
item.status === "active" &&
item.license_key &&
isLicenseUsable(item) &&
item.channel_code === publishForm.channel
) || licenses.value.find(item => item.status === "active" && item.license_key);
) || licenses.value.find(item => isLicenseUsable(item));
if (usable && !configForm.license_key) configForm.license_key = usable.license_key;
}
@@ -457,8 +465,17 @@ async function createLicense() {
}
function fillLicense(row: any) {
if (isLicenseFull(row)) {
ElMessage.warning("这张 License 的设备数已达到上限,请创建或申请新的 License。");
return;
}
if (row.status !== "active") {
ElMessage.warning("这张 License 当前不可用,请启用后再填入配置。");
return;
}
configForm.license_key = row.license_key;
createdLicenseKey.value = row.license_key;
licenseConfigWarning.value = "";
ElMessage.success("License 已填入客户端配置");
}
@@ -530,7 +547,13 @@ async function generateClientConfig() {
generatedCrashTestInfo.value =
result.crash_test_text ||
JSON.stringify(result.crash_test_config || {}, null, 2);
ElMessage.success("客户端配置已生成");
licenseConfigWarning.value = result.license_warning || "";
if (licenseConfigWarning.value) {
configForm.license_key = result.config?.license_key || "";
ElMessage.warning(licenseConfigWarning.value);
} else {
ElMessage.success("客户端配置已生成");
}
} catch (error) {
handleError("生成客户端配置失败", error);
}
@@ -910,11 +933,11 @@ onMounted(loadAll);
<el-table-column prop="license_id" label="License ID" min-width="190" />
<el-table-column prop="customer_name" label="客户" min-width="140" />
<el-table-column prop="channel_code" label="渠道" width="100" />
<el-table-column label="设备" width="110"><template #default="{ row }">{{ row.used_devices }} / {{ row.max_devices }}</template></el-table-column>
<el-table-column label="设备" width="130"><template #default="{ row }"><span>{{ row.used_devices }} / {{ row.max_devices }}</span><el-tag v-if="isLicenseFull(row)" class="ml-xs" type="warning" size="small">已满</el-tag></template></el-table-column>
<el-table-column prop="valid_until" label="有效期" min-width="170" />
<el-table-column label="License" min-width="260"><template #default="{ row }"><span v-if="row.license_key" class="mono">{{ row.license_key }}</span><el-tag v-else type="info">旧授权不可查看</el-tag></template></el-table-column>
<el-table-column label="状态" width="90"><template #default="{ row }"><el-tag :type="row.status === 'active' ? 'success' : 'danger'">{{ row.status === "active" ? "有效" : "禁用" }}</el-tag></template></el-table-column>
<el-table-column label="操作" width="300"><template #default="{ row }"><el-button v-if="row.license_key" link type="primary" @click="fillLicense(row)">填入配置</el-button><el-button v-if="row.license_key" link type="primary" @click="copyLicense(row)">复制</el-button><el-button link :type="row.status === 'active' ? 'danger' : 'success'" @click="toggleLicense(row)">{{ row.status === "active" ? "禁用" : "启用" }}</el-button><el-button link type="danger" @click="deleteLicense(row)">删除</el-button></template></el-table-column>
<el-table-column label="操作" width="320"><template #default="{ row }"><el-button v-if="row.license_key" link type="primary" :disabled="isLicenseFull(row) || row.status !== 'active'" @click="fillLicense(row)">填入配置</el-button><el-button v-if="row.license_key" link type="primary" @click="copyLicense(row)">复制</el-button><el-button link :type="row.status === 'active' ? 'danger' : 'success'" @click="toggleLicense(row)">{{ row.status === "active" ? "禁用" : "启用" }}</el-button><el-button link type="danger" @click="deleteLicense(row)">删除</el-button></template></el-table-column>
</el-table>
</el-card>
@@ -925,6 +948,7 @@ onMounted(loadAll);
<el-form-item label="主程序文件名"><el-input v-model="configForm.main_executable" placeholder="例如 SimCAE.exe" /></el-form-item>
<el-form-item label="License"><el-input v-model="configForm.license_key" placeholder="可从上方 License 列表填入;也可留空让客户端首次启动时输入" /></el-form-item>
</el-form>
<el-alert v-if="licenseConfigWarning" class="mb" type="warning" :closable="false" show-icon :title="licenseConfigWarning" />
<div class="action-row"><el-button type="primary" @click="generateClientConfig">生成配套配置</el-button><el-button :disabled="!generatedClientConfig" @click="copyGeneratedConfig">复制配置</el-button></div>
<div class="config-section-title">客户端 app_config.json</div>
<pre class="code-block">{{ generatedClientConfig || "尚未生成客户端配置" }}</pre>
@@ -1149,6 +1173,10 @@ onMounted(loadAll);
margin-top: 12px;
}
.ml-xs {
margin-left: 6px;
}
.mono {
display: inline-block;
max-width: 360px;