feat: add short IM user numbers
This commit is contained in:
@@ -8,6 +8,53 @@
|
|||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
USE `tinode`;
|
USE `tinode`;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 0. User account number.
|
||||||
|
-- account_no is the short user number shown in Admin. The real primary key
|
||||||
|
-- im_users.id remains the internal Tinode/Snowflake user ID.
|
||||||
|
-- Existing users are backfilled from 1000 by created time; new users are
|
||||||
|
-- assigned by IM Server in the user creation transaction.
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
SET @sql := IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_users' AND COLUMN_NAME = 'account_no') = 0,
|
||||||
|
'ALTER TABLE `im_users` ADD COLUMN `account_no` int unsigned NOT NULL DEFAULT 0 COMMENT ''用户编号'' AFTER `id`',
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
PREPARE stmt FROM @sql;
|
||||||
|
EXECUTE stmt;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
SET @im_user_account_no := (
|
||||||
|
SELECT GREATEST(COALESCE(MAX(`account_no`), 999), 999)
|
||||||
|
FROM `im_users`
|
||||||
|
WHERE `account_no` > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
UPDATE `im_users` AS u
|
||||||
|
JOIN (
|
||||||
|
SELECT ordered.`id`, (@im_user_account_no := @im_user_account_no + 1) AS next_no
|
||||||
|
FROM (
|
||||||
|
SELECT `id`
|
||||||
|
FROM `im_users`
|
||||||
|
WHERE `account_no` = 0
|
||||||
|
ORDER BY `createdat`, `id`
|
||||||
|
) AS ordered
|
||||||
|
) AS seq ON seq.`id` = u.`id`
|
||||||
|
SET u.`account_no` = seq.next_no
|
||||||
|
WHERE u.`account_no` = 0;
|
||||||
|
|
||||||
|
SET @sql := IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_users' AND INDEX_NAME = 'uk_im_users_account_no') = 0,
|
||||||
|
'CREATE UNIQUE INDEX `uk_im_users_account_no` ON `im_users` (`account_no`)',
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
PREPARE stmt FROM @sql;
|
||||||
|
EXECUTE stmt;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 1. Applied runtime policy snapshots.
|
-- 1. Applied runtime policy snapshots.
|
||||||
-- policy_type: security, user_policy_groups, sensitive_words.
|
-- policy_type: security, user_policy_groups, sensitive_words.
|
||||||
|
|||||||
@@ -69,16 +69,17 @@ func (s *sSysImUser) Create(ctx context.Context, in *sysin.ImUserCreateInp) (res
|
|||||||
"password": in.Password,
|
"password": in.Password,
|
||||||
}
|
}
|
||||||
var wire struct {
|
var wire struct {
|
||||||
UserId int64 `json:"user_id"`
|
DisplayNo int `json:"display_no"`
|
||||||
Username string `json:"username"`
|
UserId int64 `json:"user_id"`
|
||||||
Nickname string `json:"nickname"`
|
Username string `json:"username"`
|
||||||
Phone string `json:"phone"`
|
Nickname string `json:"nickname"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
}
|
}
|
||||||
path := "/internal/admin-query/tenants/" + url.PathEscape(tenant.Code) + "/users"
|
path := "/internal/admin-query/tenants/" + url.PathEscape(tenant.Code) + "/users"
|
||||||
if err = imadmin.DefaultClient(ctx).InternalPost(ctx, path, payload, &wire); err != nil {
|
if err = imadmin.DefaultClient(ctx).InternalPost(ctx, path, payload, &wire); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res = &sysin.ImUserCreateModel{UserId: wire.UserId, Username: wire.Username, Nickname: wire.Nickname, Phone: wire.Phone}
|
res = &sysin.ImUserCreateModel{DisplayNo: wire.DisplayNo, UserId: wire.UserId, Username: wire.Username, Nickname: wire.Nickname, Phone: wire.Phone}
|
||||||
if in.GroupId > 0 {
|
if in.GroupId > 0 {
|
||||||
if err = service.SysImUserPolicyGroup().MemberAdd(ctx, &sysin.ImUserPolicyGroupMemberAddInp{GroupId: in.GroupId, UserId: res.UserId}); err != nil {
|
if err = service.SysImUserPolicyGroup().MemberAdd(ctx, &sysin.ImUserPolicyGroupMemberAddInp{GroupId: in.GroupId, UserId: res.UserId}); err != nil {
|
||||||
return nil, gerror.Wrap(err, "用户已创建,但加入用户分组失败")
|
return nil, gerror.Wrap(err, "用户已创建,但加入用户分组失败")
|
||||||
@@ -123,6 +124,7 @@ func (s *sSysImUser) Import(ctx context.Context, in *sysin.ImUserImportInp) (res
|
|||||||
res.FailedCount++
|
res.FailedCount++
|
||||||
} else {
|
} else {
|
||||||
item.Success = true
|
item.Success = true
|
||||||
|
item.DisplayNo = created.DisplayNo
|
||||||
item.UserId = created.UserId
|
item.UserId = created.UserId
|
||||||
item.Username = created.Username
|
item.Username = created.Username
|
||||||
res.SuccessCount++
|
res.SuccessCount++
|
||||||
@@ -205,7 +207,7 @@ func (s *sSysImUser) List(ctx context.Context, in *sysin.ImUserListInp) (list []
|
|||||||
values.Set("user_ids", strings.Join(userIds, ","))
|
values.Set("user_ids", strings.Join(userIds, ","))
|
||||||
}
|
}
|
||||||
if in.UserId != "" {
|
if in.UserId != "" {
|
||||||
values.Set("user_id", in.UserId)
|
values.Set("account_no", in.UserId)
|
||||||
}
|
}
|
||||||
if in.Nickname != "" {
|
if in.Nickname != "" {
|
||||||
values.Set("nickname", in.Nickname)
|
values.Set("nickname", in.Nickname)
|
||||||
@@ -239,17 +241,6 @@ func (s *sSysImUser) List(ctx context.Context, in *sysin.ImUserListInp) (list []
|
|||||||
if err = fillImUserRegisterTypes(ctx, tenant.Id, list); err != nil {
|
if err = fillImUserRegisterTypes(ctx, tenant.Id, list); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
page := in.Page
|
|
||||||
if page < 1 {
|
|
||||||
page = 1
|
|
||||||
}
|
|
||||||
perPage := in.PerPage
|
|
||||||
if perPage < 1 {
|
|
||||||
perPage = 10
|
|
||||||
}
|
|
||||||
for index, item := range list {
|
|
||||||
item.DisplayNo = 1000 + (page-1)*perPage + index
|
|
||||||
}
|
|
||||||
return list, totalCount, nil
|
return list, totalCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import (
|
|||||||
type ImUserListInp struct {
|
type ImUserListInp struct {
|
||||||
form.PageReq
|
form.PageReq
|
||||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||||
UserId string `json:"userId" dc:"用户ID"`
|
UserId string `json:"userId" dc:"后台展示编号"`
|
||||||
GroupId int64 `json:"groupId" dc:"用户策略分组ID"`
|
GroupId int64 `json:"groupId" dc:"用户策略分组ID"`
|
||||||
Keyword string `json:"keyword" dc:"用户名、昵称或手机号"`
|
Keyword string `json:"keyword" dc:"用户名、昵称、手机号或编号"`
|
||||||
Nickname string `json:"nickname" dc:"昵称"`
|
Nickname string `json:"nickname" dc:"昵称"`
|
||||||
AuthName string `json:"authName" dc:"登录标识"`
|
AuthName string `json:"authName" dc:"登录标识"`
|
||||||
State *int `json:"state" dc:"账号状态"`
|
State *int `json:"state" dc:"账号状态"`
|
||||||
@@ -88,10 +88,11 @@ func (in *ImUserCreateInp) Filter(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ImUserCreateModel struct {
|
type ImUserCreateModel struct {
|
||||||
UserId int64 `json:"userId" dc:"用户ID"`
|
DisplayNo int `json:"displayNo" dc:"后台展示编号"`
|
||||||
Username string `json:"username" dc:"用户名"`
|
UserId int64 `json:"userId" dc:"用户ID"`
|
||||||
Nickname string `json:"nickname" dc:"昵称"`
|
Username string `json:"username" dc:"用户名"`
|
||||||
Phone string `json:"phone" dc:"手机号"`
|
Nickname string `json:"nickname" dc:"昵称"`
|
||||||
|
Phone string `json:"phone" dc:"手机号"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImUserBatchCreateInp struct {
|
type ImUserBatchCreateInp struct {
|
||||||
@@ -114,11 +115,12 @@ func (in *ImUserBatchCreateInp) Filter(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ImUserBatchItemModel struct {
|
type ImUserBatchItemModel struct {
|
||||||
Row int `json:"row" dc:"行号"`
|
Row int `json:"row" dc:"行号"`
|
||||||
Username string `json:"username" dc:"用户名"`
|
DisplayNo int `json:"displayNo" dc:"后台展示编号"`
|
||||||
UserId int64 `json:"userId" dc:"用户ID"`
|
Username string `json:"username" dc:"用户名"`
|
||||||
Success bool `json:"success" dc:"是否成功"`
|
UserId int64 `json:"userId" dc:"用户ID"`
|
||||||
Reason string `json:"reason" dc:"失败原因"`
|
Success bool `json:"success" dc:"是否成功"`
|
||||||
|
Reason string `json:"reason" dc:"失败原因"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImUserBatchCreateModel struct {
|
type ImUserBatchCreateModel struct {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
v-model:show="showModal"
|
v-model:show="showModal"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
:title="'登录设备 ' + userId"
|
:title="'登录设备 #' + (displayNo || userId)"
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
:style="{ width: dialogWidth }"
|
:style="{ width: dialogWidth }"
|
||||||
>
|
>
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
const showModal = ref(false);
|
const showModal = ref(false);
|
||||||
const actionRef = ref();
|
const actionRef = ref();
|
||||||
const tenantId = ref(0);
|
const tenantId = ref(0);
|
||||||
|
const displayNo = ref(0);
|
||||||
const userId = ref('');
|
const userId = ref('');
|
||||||
const dialogWidth = computed(() => adaModalWidth(1080));
|
const dialogWidth = computed(() => adaModalWidth(1080));
|
||||||
const scrollX = computed(() => adaTableScrollX(deviceColumns, 0));
|
const scrollX = computed(() => adaTableScrollX(deviceColumns, 0));
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
|
|
||||||
function openModal(record: State) {
|
function openModal(record: State) {
|
||||||
tenantId.value = record.tenantId;
|
tenantId.value = record.tenantId;
|
||||||
|
displayNo.value = record.displayNo;
|
||||||
userId.value = record.userId;
|
userId.value = record.userId;
|
||||||
showModal.value = true;
|
showModal.value = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-modal v-model:show="showModal" preset="dialog" title="重置IM用户密码" :mask-closable="false">
|
<n-modal v-model:show="showModal" preset="dialog" title="重置IM用户密码" :mask-closable="false">
|
||||||
<n-form ref="formRef" :model="formValue" :rules="rules" label-placement="left" :label-width="90">
|
<n-form
|
||||||
<n-form-item label="用户ID">
|
ref="formRef"
|
||||||
<n-text>{{ userId }}</n-text>
|
:model="formValue"
|
||||||
|
:rules="rules"
|
||||||
|
label-placement="left"
|
||||||
|
:label-width="90"
|
||||||
|
>
|
||||||
|
<n-form-item label="编号">
|
||||||
|
<n-text>{{ displayNo || '-' }}</n-text>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="新密码" path="newPassword">
|
<n-form-item label="新密码" path="newPassword">
|
||||||
<n-input
|
<n-input
|
||||||
@@ -44,6 +50,7 @@
|
|||||||
const showModal = ref(false);
|
const showModal = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const tenantId = ref(0);
|
const tenantId = ref(0);
|
||||||
|
const displayNo = ref(0);
|
||||||
const userId = ref('');
|
const userId = ref('');
|
||||||
const formValue = reactive({ newPassword: '', confirmPassword: '' });
|
const formValue = reactive({ newPassword: '', confirmPassword: '' });
|
||||||
const rules = {
|
const rules = {
|
||||||
@@ -66,6 +73,7 @@
|
|||||||
|
|
||||||
function openModal(record: State) {
|
function openModal(record: State) {
|
||||||
tenantId.value = record.tenantId;
|
tenantId.value = record.tenantId;
|
||||||
|
displayNo.value = record.displayNo;
|
||||||
userId.value = record.userId;
|
userId.value = record.userId;
|
||||||
formValue.newPassword = '';
|
formValue.newPassword = '';
|
||||||
formValue.confirmPassword = '';
|
formValue.confirmPassword = '';
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
transform-origin="center"
|
transform-origin="center"
|
||||||
:title="'用户治理 #' + formValue.userId"
|
:title="'用户治理 #' + (formValue.displayNo || formValue.userId)"
|
||||||
:style="{ width: dialogWidth }"
|
:style="{ width: dialogWidth }"
|
||||||
>
|
>
|
||||||
<n-spin :show="loading" description="请稍候...">
|
<n-spin :show="loading" description="请稍候...">
|
||||||
<n-descriptions label-placement="left" bordered :column="settingStore.isMobile ? 1 : 2">
|
<n-descriptions label-placement="left" bordered :column="settingStore.isMobile ? 1 : 2">
|
||||||
<n-descriptions-item label="商户">{{ formValue.tenantName || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="商户">{{ formValue.tenantName || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="用户ID">{{ formValue.userId }}</n-descriptions-item>
|
<n-descriptions-item label="编号">{{ formValue.displayNo || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="昵称">{{ formValue.nickname || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="昵称">{{ formValue.nickname || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="状态">{{ stateLabel }}</n-descriptions-item>
|
<n-descriptions-item label="状态">{{ stateLabel }}</n-descriptions-item>
|
||||||
</n-descriptions>
|
</n-descriptions>
|
||||||
@@ -29,7 +29,12 @@
|
|||||||
<n-switch v-model:value="actionForm.hard" />
|
<n-switch v-model:value="actionForm.hard" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="原因">
|
<n-form-item label="原因">
|
||||||
<n-input v-model:value="actionForm.reason" type="textarea" clearable placeholder="请输入操作原因" />
|
<n-input
|
||||||
|
v-model:value="actionForm.reason"
|
||||||
|
type="textarea"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入操作原因"
|
||||||
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
@@ -68,7 +73,9 @@
|
|||||||
const showModal = ref(false);
|
const showModal = ref(false);
|
||||||
const formValue = ref<State>(newState(null));
|
const formValue = ref<State>(newState(null));
|
||||||
const dialogWidth = computed(() => adaModalWidth(720));
|
const dialogWidth = computed(() => adaModalWidth(720));
|
||||||
const stateLabel = computed(() => dict.getLabel('ImUserStateOptions', formValue.value.state) || '-');
|
const stateLabel = computed(
|
||||||
|
() => dict.getLabel('ImUserStateOptions', formValue.value.state) || '-'
|
||||||
|
);
|
||||||
|
|
||||||
const actionOptions = [
|
const actionOptions = [
|
||||||
{ label: '封禁用户', value: 'ban' },
|
{ label: '封禁用户', value: 'ban' },
|
||||||
|
|||||||
@@ -58,9 +58,9 @@
|
|||||||
class="user-dialog"
|
class="user-dialog"
|
||||||
>
|
>
|
||||||
<n-form label-placement="left" label-width="90">
|
<n-form label-placement="left" label-width="90">
|
||||||
<n-form-item label="用户"
|
<n-form-item label="用户">
|
||||||
>{{ operationUser?.username }}({{ operationUser?.userId }})</n-form-item
|
{{ operationUser?.username }}({{ operationUser?.displayNo || operationUser?.userId }})
|
||||||
>
|
</n-form-item>
|
||||||
<n-form-item label="IP/CIDR">
|
<n-form-item label="IP/CIDR">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="ipWhitelistText"
|
v-model:value="ipWhitelistText"
|
||||||
@@ -90,9 +90,9 @@
|
|||||||
class="user-dialog"
|
class="user-dialog"
|
||||||
>
|
>
|
||||||
<n-form label-placement="left" label-width="90">
|
<n-form label-placement="left" label-width="90">
|
||||||
<n-form-item label="用户"
|
<n-form-item label="用户">
|
||||||
>{{ operationUser?.username }}({{ operationUser?.userId }})</n-form-item
|
{{ operationUser?.username }}({{ operationUser?.displayNo || operationUser?.userId }})
|
||||||
>
|
</n-form-item>
|
||||||
<n-form-item label="用户分组" required>
|
<n-form-item label="用户分组" required>
|
||||||
<n-select
|
<n-select
|
||||||
v-model:value="adjustGroupId"
|
v-model:value="adjustGroupId"
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export const schemas = ref<FormSchema[]>([
|
|||||||
label: '关键词',
|
label: '关键词',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
clearable: true,
|
clearable: true,
|
||||||
placeholder: '用户名/昵称/手机号',
|
placeholder: '用户名/昵称/手机号/编号',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
transform-origin="center"
|
transform-origin="center"
|
||||||
:title="'IM用户详情 #' + formValue.userId"
|
:title="'IM用户详情 #' + (formValue.displayNo || formValue.userId)"
|
||||||
:style="{
|
:style="{
|
||||||
width: dialogWidth,
|
width: dialogWidth,
|
||||||
}"
|
}"
|
||||||
@@ -15,25 +15,41 @@
|
|||||||
<n-spin :show="loading" description="请稍候...">
|
<n-spin :show="loading" description="请稍候...">
|
||||||
<n-descriptions label-placement="left" bordered :column="settingStore.isMobile ? 1 : 2">
|
<n-descriptions label-placement="left" bordered :column="settingStore.isMobile ? 1 : 2">
|
||||||
<n-descriptions-item label="商户">{{ formValue.tenantName }}</n-descriptions-item>
|
<n-descriptions-item label="商户">{{ formValue.tenantName }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="用户ID">{{ formValue.userId }}</n-descriptions-item>
|
<n-descriptions-item label="编号">{{ formValue.displayNo || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="登录账号">{{ formValue.username }}</n-descriptions-item>
|
<n-descriptions-item label="登录账号">{{ formValue.username }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="手机号">{{ formValue.phone || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="手机号">{{ formValue.phone || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="昵称">{{ formValue.nickname || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="昵称">{{ formValue.nickname || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="公开账号">{{ formValue.publicAccount || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="公开账号">{{
|
||||||
|
formValue.publicAccount || '-'
|
||||||
|
}}</n-descriptions-item>
|
||||||
<n-descriptions-item label="状态">{{ stateLabel }}</n-descriptions-item>
|
<n-descriptions-item label="状态">{{ stateLabel }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="认证版本">{{ formValue.authVersion }}</n-descriptions-item>
|
<n-descriptions-item label="认证版本">{{ formValue.authVersion }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="登录标识">{{ formValue.authNames || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="登录标识">{{
|
||||||
<n-descriptions-item label="凭证">{{ formValue.credentials || '-' }}</n-descriptions-item>
|
formValue.authNames || '-'
|
||||||
|
}}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="凭证">{{
|
||||||
|
formValue.credentials || '-'
|
||||||
|
}}</n-descriptions-item>
|
||||||
<n-descriptions-item label="性别">{{ formValue.gender || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="性别">{{ formValue.gender || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="地区">{{ formValue.region || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="地区">{{ formValue.region || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="出生日期">{{ formValue.birthDate || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="出生日期">{{
|
||||||
|
formValue.birthDate || '-'
|
||||||
|
}}</n-descriptions-item>
|
||||||
<n-descriptions-item label="密码更新时间">
|
<n-descriptions-item label="密码更新时间">
|
||||||
{{ formValue.passwordUpdatedAt || '-' }}
|
{{ formValue.passwordUpdatedAt || '-' }}
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="最后在线">{{ formValue.lastSeen || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="最后在线">{{
|
||||||
<n-descriptions-item label="资料版本">{{ formValue.profileVersion }}</n-descriptions-item>
|
formValue.lastSeen || '-'
|
||||||
<n-descriptions-item label="创建时间">{{ formValue.createdAt || '-' }}</n-descriptions-item>
|
}}</n-descriptions-item>
|
||||||
<n-descriptions-item label="修改时间">{{ formValue.updatedAt || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="资料版本">{{
|
||||||
|
formValue.profileVersion
|
||||||
|
}}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="创建时间">{{
|
||||||
|
formValue.createdAt || '-'
|
||||||
|
}}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="修改时间">{{
|
||||||
|
formValue.updatedAt || '-'
|
||||||
|
}}</n-descriptions-item>
|
||||||
<n-descriptions-item label="客户端" :span="2">
|
<n-descriptions-item label="客户端" :span="2">
|
||||||
{{ formValue.userAgent || '-' }}
|
{{ formValue.userAgent || '-' }}
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
|
|||||||
Reference in New Issue
Block a user