Archived
forked from admin/userbase
推送/操作日志下载改location.href;移动组关闭自动select;所在组改单选
deploy-userbase-front / build-and-deploy (push) Successful in 3m3s
deploy-userbase-front / build-and-deploy (push) Successful in 3m3s
This commit is contained in:
@@ -90,5 +90,6 @@ export default {
|
|||||||
'AppSettings.getPushSetting': ['GET', '/api/app/GetPushSetting/{id}'],
|
'AppSettings.getPushSetting': ['GET', '/api/app/GetPushSetting/{id}'],
|
||||||
'Push.test': ['POST', '/api/push/Test'],
|
'Push.test': ['POST', '/api/push/Test'],
|
||||||
'Push.recordList': ['GET', '/api/push/RecordList'],
|
'Push.recordList': ['GET', '/api/push/RecordList'],
|
||||||
'Push.retry': ['POST', '/api/push/Retry/{id}']
|
'Push.retry': ['POST', '/api/push/Retry/{id}'],
|
||||||
|
'Push.download': ['GET', '/api/push/Download/{id}']
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const DEFAULT_CACHE_KEY = 'bsch.group-tree.selected-node'
|
|||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
treeApi: () => Promise<any[]>;
|
treeApi: () => Promise<any[]>;
|
||||||
autoClick?: boolean;
|
autoClick?: boolean;
|
||||||
|
autoSelect?: boolean;
|
||||||
defaultSearchVisible?: boolean;
|
defaultSearchVisible?: boolean;
|
||||||
showCloseButton?: boolean;
|
showCloseButton?: boolean;
|
||||||
searchPlaceholder?: string;
|
searchPlaceholder?: string;
|
||||||
@@ -43,6 +44,7 @@ const props = withDefaults(defineProps<{
|
|||||||
checkable?: boolean;
|
checkable?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
autoClick: false,
|
autoClick: false,
|
||||||
|
autoSelect: true,
|
||||||
defaultSearchVisible: true,
|
defaultSearchVisible: true,
|
||||||
showCloseButton: false,
|
showCloseButton: false,
|
||||||
searchPlaceholder: '输入组名称搜索',
|
searchPlaceholder: '输入组名称搜索',
|
||||||
@@ -155,9 +157,12 @@ async function loadTree(preferredId?: number | null) {
|
|||||||
if (!treeData.value.length) return;
|
if (!treeData.value.length) return;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const selectId = resolveInitialSelectId(treeData.value, preferredId);
|
// 默认自动选中首个可用节点(触发 on-change);移动组等场景传 autoSelect=false 关闭,避免打开即误触"不能移动到自身"
|
||||||
if (selectId != null) {
|
if (props.autoSelect) {
|
||||||
refMyTree.value?.reloadTree(selectId);
|
const selectId = resolveInitialSelectId(treeData.value, preferredId);
|
||||||
|
if (selectId != null) {
|
||||||
|
refMyTree.value?.reloadTree(selectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="move-row move-label">移动至:</div>
|
<div class="move-row move-label">移动至:</div>
|
||||||
<GroupTreeSearchPanel v-if="visible" v-model:curTreeNode="selectedNode" :treeApi="moveTreeApi"
|
<GroupTreeSearchPanel v-if="visible" v-model:curTreeNode="selectedNode" :treeApi="moveTreeApi"
|
||||||
:defaultSearchVisible="true" :showCloseButton="false" @on-change="onPickTarget" />
|
:autoSelect="false" :defaultSearchVisible="true" :showCloseButton="false" @on-change="onPickTarget" />
|
||||||
<div v-if="targetName" class="move-target">已选择目标:{{ targetName }}</div>
|
<div v-if="targetName" class="move-target">已选择目标:{{ targetName }}</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -104,22 +104,26 @@ async function open(row: any, groups: any[], curGroupId?: number | null, onSucce
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3) 已绑定角色 -> appRoles 回填(相互独立)
|
// 3) 已绑定角色 -> appRoles 回填(相互独立)
|
||||||
|
// 所在组(只读)默认选中「当前组」:优先用进入编辑时的左侧树/列表组(curGroupId),否则取用户所属组的第一个
|
||||||
|
const defaultGroupId = (curGroupId != null && userGroupIdsArr.value.includes(curGroupId))
|
||||||
|
? curGroupId
|
||||||
|
: (userGroupIdsArr.value[0] ?? null);
|
||||||
try {
|
try {
|
||||||
const userRolesResp = await Apis.GroupAuthz.userRoles({ params: { userId: row.id } });
|
const userRolesResp = await Apis.GroupAuthz.userRoles({ params: { userId: row.id } });
|
||||||
const userRoleIds: number[] = (Array.isArray(userRolesResp) ? userRolesResp : [])
|
const userRoleIds: number[] = (Array.isArray(userRolesResp) ? userRolesResp : [])
|
||||||
.map((r: any) => r.id)
|
.map((r: any) => r.id)
|
||||||
.filter((x: any): x is number => x != null);
|
.filter((x: any): x is number => x != null);
|
||||||
userRolesLoaded.value = true;
|
userRolesLoaded.value = true;
|
||||||
detailData.value = buildInitForm(row, userRoleIds, userGroupIdsArr.value);
|
detailData.value = buildInitForm(row, userRoleIds, defaultGroupId);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// 已绑定角色加载失败:仍回填其它字段,但标记未加载成功(提交时不回写,避免误清空)
|
// 已绑定角色加载失败:仍回填其它字段,但标记未加载成功(提交时不回写,避免误清空)
|
||||||
detailData.value = buildInitForm(row, [], userGroupIdsArr.value);
|
detailData.value = buildInitForm(row, [], defaultGroupId);
|
||||||
Message.error('加载用户已绑定角色失败:' + (e?.message || '未知错误'));
|
Message.error('加载用户已绑定角色失败:' + (e?.message || '未知错误'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- 把行数据映射成表单回填数据 ---------- */
|
/* ---------- 把行数据映射成表单回填数据 ---------- */
|
||||||
function buildInitForm(row: any, userRoleIds: number[], userGroupIds: number[] = []) {
|
function buildInitForm(row: any, userRoleIds: number[], defaultGroupId: number | null = null) {
|
||||||
const roles: number[] = Array.isArray(row.roles) ? row.roles : [];
|
const roles: number[] = Array.isArray(row.roles) ? row.roles : [];
|
||||||
// 用户身份(业务身份)= 1..7;角色属性已移除(超管手动入库、管理员走勾选)
|
// 用户身份(业务身份)= 1..7;角色属性已移除(超管手动入库、管理员走勾选)
|
||||||
const userType = roles.filter(r => r >= 1 && r <= 7);
|
const userType = roles.filter(r => r >= 1 && r <= 7);
|
||||||
@@ -135,7 +139,7 @@ function buildInitForm(row: any, userRoleIds: number[], userGroupIds: number[] =
|
|||||||
gender: row.gender ?? 0,
|
gender: row.gender ?? 0,
|
||||||
userType,
|
userType,
|
||||||
appRoles: userRoleIds || [],
|
appRoles: userRoleIds || [],
|
||||||
userGroupIds: userGroupIds || [],
|
userGroupId: defaultGroupId ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,11 +57,11 @@
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<!-- 第 5 行:所在组(只读)(仅编辑展示,默认选中当前组;提交时不带 groupId,不改变所在组) -->
|
<!-- 第 5 行:所在组(只读)(仅编辑展示,普通单选、默认选中当前组;提交时不带 groupId,不改变所在组) -->
|
||||||
<Row v-if="isEdit">
|
<Row v-if="isEdit">
|
||||||
<Col span="24">
|
<Col span="24">
|
||||||
<FormItem label="所在组(只读):">
|
<FormItem label="所在组(只读):">
|
||||||
<Select v-model="form.userGroupIds" multiple transfer
|
<Select v-model="form.userGroupId" transfer clearable
|
||||||
placeholder="默认选中当前组(提交不改变所在组)">
|
placeholder="默认选中当前组(提交不改变所在组)">
|
||||||
<Option v-for="g in userGroups" :key="g.id" :value="g.id" :label="g.name" />
|
<Option v-for="g in userGroups" :key="g.id" :value="g.id" :label="g.name" />
|
||||||
</Select>
|
</Select>
|
||||||
@@ -144,7 +144,7 @@ const defaultForm = (): Record<string, any> => ({
|
|||||||
gender: 0,
|
gender: 0,
|
||||||
userType: [] as number[],
|
userType: [] as number[],
|
||||||
appRoles: [] as number[],
|
appRoles: [] as number[],
|
||||||
userGroupIds: [] as number[],
|
userGroupId: null as number | null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = reactive(defaultForm());
|
const form = reactive(defaultForm());
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ function formatTime(val: any): string {
|
|||||||
|
|
||||||
function handleDownload() {
|
function handleDownload() {
|
||||||
if (!props.row?.id) return;
|
if (!props.row?.id) return;
|
||||||
window.open(`${$conf.serviceHost}/api/oplog/Download/${props.row.id}`);
|
location.href = `${$conf.serviceHost}/api/oplog/Download/${props.row.id}`;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,8 @@
|
|||||||
<span>{{ formatTime(row?.createdAt) }}</span>
|
<span>{{ formatTime(row?.createdAt) }}</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="请求体:">
|
<FormItem label="请求体:">
|
||||||
<pre class="json-pre">{{ formatJson(row?.payload) }}</pre>
|
<a v-if="row?.id" href="javascript:;" class="download-link" @click.prevent="handleDownload">下载请求体</a>
|
||||||
|
<span v-else>-</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -54,6 +55,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
||||||
import Utils from '@/utils';
|
import Utils from '@/utils';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -83,13 +85,11 @@ function onVisibleChange(val: boolean) {
|
|||||||
if (!val) emit('update:modelValue', false);
|
if (!val) emit('update:modelValue', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatJson(val: any): string {
|
const { $conf } = useCurrentInstance();
|
||||||
if (!val) return '-';
|
|
||||||
try {
|
function handleDownload() {
|
||||||
return JSON.stringify(JSON.parse(val), null, 2);
|
if (!props.row?.id) return;
|
||||||
} catch {
|
location.href = `${$conf.serviceHost}/api/push/Download/${props.row.id}`;
|
||||||
return String(val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(val: any): string {
|
function formatTime(val: any): string {
|
||||||
@@ -127,18 +127,12 @@ function formatTime(val: any): string {
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.json-pre {
|
.download-link {
|
||||||
margin: 0;
|
color: #577afb;
|
||||||
padding: 10px 12px;
|
|
||||||
background: #f7f8fa;
|
&:hover {
|
||||||
border: 1px solid #e8eaec;
|
color: #3d5afe;
|
||||||
border-radius: 4px;
|
}
|
||||||
max-height: 240px;
|
|
||||||
overflow: auto;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user