This commit is contained in:
fengchuanhn@gmail.com
2026-05-22 16:25:19 +08:00
parent c8d7d3e8f3
commit a2ff5b065e
4 changed files with 37 additions and 99 deletions

View File

@@ -160,19 +160,17 @@ pub async fn send_heartbeat(token: &str) -> Result<(), String> {
let server_code = response_server_verify_code(&value); let server_code = response_server_verify_code(&value);
if server_code != Some(expected.as_str()) { if server_code != Some(expected.as_str()) {
return Err("会员已过期".into()); return Err("会员已过期".into());
} }
if let Some(ok) = value.get("ok").and_then(|v| v.as_bool()) { if value.get("ok").and_then(|v| v.as_bool()) == Some(false) {
if !ok { let msg = value
let msg = value .get("message")
.get("message") .and_then(|m| m.as_str())
.and_then(|m| m.as_str()) .unwrap_or("心跳校验失败");
.unwrap_or("心跳校验失败"); return Err(msg.to_string());
return Err(msg.to_string());
}
} }
Ok(()) Ok(())
} }

View File

@@ -25,7 +25,7 @@ function oemRequest(token, path, options = {}) {
* roleId?: number | null, * roleId?: number | null,
* }} [params] * }} [params]
*/ */
export function listAgentUsersApi( export function listOemUsersApi(
token, token,
{ page = 1, pageSize = 20, username, oemId, agentId, roleId } = {}, { page = 1, pageSize = 20, username, oemId, agentId, roleId } = {},
) { ) {

View File

@@ -7,7 +7,7 @@ import {
updateAdminCardKeyApi, updateAdminCardKeyApi,
deleteAdminCardKeyApi, deleteAdminCardKeyApi,
} from "../../api/oemCardKeys.js"; } from "../../api/oemCardKeys.js";
import { listAgentUsersApi } from "../../api/oemUsers.js"; import { listOemUsersApi } from "../../api/oemUsers.js";
const auth = useAuthStore(); const auth = useAuthStore();
@@ -93,7 +93,7 @@ function showMessage(severity, message) {
async function loadFilterOptions() { async function loadFilterOptions() {
const [ agentRes] = await Promise.all([ const [ agentRes] = await Promise.all([
listAgentUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }), listOemUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }),
]); ]);
if (agentRes.ok && Array.isArray(agentRes.data?.items)) { if (agentRes.ok && Array.isArray(agentRes.data?.items)) {
@@ -304,16 +304,7 @@ onMounted(async () => {
class="admin-users__search" class="admin-users__search"
@keyup.enter="applyFilters" @keyup.enter="applyFilters"
/> />
<Select
v-model="filters.oem_id"
:options="oemOptions"
option-label="username"
option-value="id"
placeholder="全部 OEM"
show-clear
filter
class="admin-users__filter-select"
/>
<Select <Select
v-model="filters.agent_id" v-model="filters.agent_id"
:options="agentOptions" :options="agentOptions"
@@ -368,11 +359,7 @@ onMounted(async () => {
</template> </template>
</Column> </Column>
<Column field="duration_days" header="时长(天)" style="width: 6rem" /> <Column field="duration_days" header="时长(天)" style="width: 6rem" />
<Column header="OEM" style="width: 7rem">
<template #body="{ data }">
{{ ownerLabel(data.oem_id) }}
</template>
</Column>
<Column header="代理" style="width: 7rem"> <Column header="代理" style="width: 7rem">
<template #body="{ data }"> <template #body="{ data }">
{{ ownerLabel(data.agent_id) }} {{ ownerLabel(data.agent_id) }}
@@ -400,7 +387,7 @@ onMounted(async () => {
</Column> </Column>
<Column field="remark" header="备注"> <Column field="remark" header="备注">
<template #body="{ data }"> <template #body="{ data }">
{{ data.remark || "—" }} {{ data.oem_remark || "—" }}
</template> </template>
</Column> </Column>
<Column header="操作" style="width: 9rem"> <Column header="操作" style="width: 9rem">
@@ -437,10 +424,7 @@ onMounted(async () => {
> >
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<template v-if="dialogMode === 'create'"> <template v-if="dialogMode === 'create'">
<div class="flex flex-col gap-2">
<label class="text-xs text-text-muted uppercase">时长</label>
<InputNumber v-model="form.duration_days" :min="1" :max="3650" class="w-full" />
</div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="text-xs text-text-muted uppercase">生成数量</label> <label class="text-xs text-text-muted uppercase">生成数量</label>
<InputNumber v-model="form.count" :min="1" :max="100" class="w-full" /> <InputNumber v-model="form.count" :min="1" :max="100" class="w-full" />
@@ -488,16 +472,7 @@ onMounted(async () => {
disabled disabled
/> />
</div> </div>
<div class="flex flex-col gap-2">
<label class="text-xs text-text-muted uppercase">时长</label>
<InputNumber
v-model="form.duration_days"
:min="1"
:max="3650"
class="w-full"
:disabled="isActivated"
/>
</div>
</template> </template>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View File

@@ -2,7 +2,7 @@
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
import { useAuthStore, ROLE_ADMIN, ROLE_AGENT,ROLE_OEM } from "../../stores/auth.js"; import { useAuthStore, ROLE_ADMIN, ROLE_AGENT,ROLE_OEM } from "../../stores/auth.js";
import { import {
listAgentUsersApi, listOemUsersApi,
createOemUserApi, createOemUserApi,
updateOemUserApi, updateOemUserApi,
deleteOemUserApi, deleteOemUserApi,
@@ -10,11 +10,10 @@ import {
const auth = useAuthStore(); const auth = useAuthStore();
const ROLE_OPTIONS = [ /** 新建/编辑时可分配的角色(仅普通用户、代理) */
const FORM_ROLE_OPTIONS = [
{ label: "普通用户", value: 0 }, { label: "普通用户", value: 0 },
{ label: "管理员", value: 1 },
{ label: "代理", value: 2 }, { label: "代理", value: 2 },
{ label: "OEM", value: 3 },
]; ];
const ROLE_LABELS = { const ROLE_LABELS = {
@@ -49,8 +48,6 @@ const emptyForm = () => ({
password: "", password: "",
phone: "", phone: "",
role_id: 0, role_id: 0,
vip_end_time: "",
clear_vip_end_time: false,
}); });
const form = ref(emptyForm()); const form = ref(emptyForm());
@@ -66,14 +63,6 @@ function formatDateTime(value) {
return d.toLocaleString("zh-CN", { hour12: false }); return d.toLocaleString("zh-CN", { hour12: false });
} }
function toDatetimeLocalValue(iso) {
if (!iso) return "";
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return "";
const pad = (n) => String(n).padStart(2, "0");
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
function roleLabel(roleId) { function roleLabel(roleId) {
return ROLE_LABELS[roleId] ?? `角色 ${roleId}`; return ROLE_LABELS[roleId] ?? `角色 ${roleId}`;
} }
@@ -104,7 +93,7 @@ function showMessage(severity, message) {
async function loadFilterOptions() { async function loadFilterOptions() {
const [ agentRes] = await Promise.all([ const [ agentRes] = await Promise.all([
listAgentUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }), listOemUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }),
]); ]);
if (agentRes.ok && Array.isArray(agentRes.data?.items)) { if (agentRes.ok && Array.isArray(agentRes.data?.items)) {
@@ -115,27 +104,25 @@ async function loadFilterOptions() {
async function loadUsers() { async function loadUsers() {
loading.value = true; loading.value = true;
feedback.value = { severity: "", message: "" }; feedback.value = { severity: "", message: "" };
// const res = await listOemUsersApi(auth.token, { const res = await listOemUsersApi(auth.token, {
// page: page.value, page: page.value,
// pageSize: pageSize.value, pageSize: pageSize.value,
// username: filters.value.username, username: filters.value.username,
// oemId: filters.value.oem_id, oemId: filters.value.oem_id,
// agentId: filters.value.agent_id, agentId: filters.value.agent_id,
// }); });
// loading.value = false; loading.value = false;
// if (!res.ok) { if (!res.ok) {
// showMessage("error", res.message || "加载用户列表失败"); showMessage("error", res.message || "加载用户列表失败");
// return; return;
// } }
// const data = res.data; const data = res.data;
// users.value = Array.isArray(data?.items) ? data.items : []; users.value = Array.isArray(data?.items) ? data.items : [];
// total.value = typeof data?.total === "number" ? data.total : 0; total.value = typeof data?.total === "number" ? data.total : 0;
users.value=[]; if (typeof data?.page === "number") page.value = data.page;
total.value=0; if (typeof data?.page_size === "number") pageSize.value = data.page_size;
// if (typeof data?.page === "number") page.value = data.page;
// if (typeof data?.page_size === "number") pageSize.value = data.page_size;
} }
async function onPage(event) { async function onPage(event) {
@@ -170,8 +157,6 @@ function openEditDialog(row) {
password: "", password: "",
phone: row.phone || "", phone: row.phone || "",
role_id: row.role_id, role_id: row.role_id,
vip_end_time: toDatetimeLocalValue(row.vip_end_time),
clear_vip_end_time: false,
}; };
dialogVisible.value = true; dialogVisible.value = true;
} }
@@ -187,12 +172,6 @@ function buildPayload() {
payload.password = form.value.password; payload.password = form.value.password;
} }
if (form.value.clear_vip_end_time) {
payload.clear_vip_end_time = true;
} else if (form.value.vip_end_time) {
payload.vip_end_time = new Date(form.value.vip_end_time).toISOString();
}
return payload; return payload;
} }
@@ -415,26 +394,12 @@ onMounted(async () => {
<label class="text-xs text-text-muted uppercase">角色</label> <label class="text-xs text-text-muted uppercase">角色</label>
<Select <Select
v-model="form.role_id" v-model="form.role_id"
:options="ROLE_OPTIONS" :options="FORM_ROLE_OPTIONS"
option-label="label" option-label="label"
option-value="value" option-value="value"
class="w-full" class="w-full"
/> />
</div> </div>
<div class="flex flex-col gap-2">
<label class="text-xs text-text-muted uppercase">VIP 到期时间</label>
<InputText
v-model="form.vip_end_time"
type="datetime-local"
class="w-full"
:disabled="form.clear_vip_end_time"
/>
<label v-if="dialogMode === 'edit'" class="flex items-center gap-2 text-sm text-text-muted">
<Checkbox v-model="form.clear_vip_end_time" :binary="true" input-id="clear-vip" />
<span>清除 VIP 到期时间</span>
</label>
</div>
</div> </div>
<template #footer> <template #footer>