chore(types): 同步用户身份字段(删 SysUserVo.roles,identityNames 口径注释订正)
- SysUserVo.roles 已停写(后端本次一并删除该字段),前端类型同步移除 - identityNames 注明:组上下文列表只含当前组范围解析出的学校下的身份(与编辑弹窗「所在组」同源)
This commit is contained in:
Vendored
+4
-2
@@ -311,12 +311,14 @@ export interface SysUserVo {
|
||||
email?: string;
|
||||
gender?: number;
|
||||
profilePicture?: string;
|
||||
roles?: number[];
|
||||
disabled?: number;
|
||||
type?: number;
|
||||
sortNo?: number;
|
||||
groupStatus?: number;
|
||||
/** 主校身份名(如「老师/校领导」):列表「用户身份」列展示字段(roles 列已停写) */
|
||||
/**
|
||||
* 用户身份名(如「老师、校领导」):列表「用户身份」列展示字段。
|
||||
* 组上下文列表只含「当前组范围解析出的学校」下的身份(与编辑弹窗「所在组」同源);未选组才全量。
|
||||
*/
|
||||
identityNames?: string;
|
||||
expired?: boolean;
|
||||
createTime?: string;
|
||||
|
||||
@@ -157,7 +157,8 @@ const columns = [
|
||||
{ title: '手机号', key: 'phone', minWidth: 140 },
|
||||
{
|
||||
title: '用户身份', key: 'identityNames', minWidth: 180,
|
||||
// 数据源:后端按页批量带出的主校身份名(is_main 行,roles 列已停写,不再直读旧列)
|
||||
// 数据源:后端按页批量带出的身份名,且只取「当前所选组范围解析出的学校」下的身份行
|
||||
// (与编辑弹窗「所在组」回显同口径,roles 列已停写不再直读旧列)
|
||||
render: (h: any, { row }: any) => h('span', row.identityNames || '-'),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
<template>
|
||||
<div class="main-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="test-card">
|
||||
<h3 class="card-title">测试推送</h3>
|
||||
<p class="card-desc">
|
||||
手动触发一次推送(投递消息队列,由订阅方异步接收)。下方将展示本次请求预览(URL / 头 / 体),
|
||||
实际响应结果与耗时请在「推送日志」中查看。
|
||||
</p>
|
||||
|
||||
<Form :label-width="100" class="test-form">
|
||||
<FormItem label="订阅方应用" required>
|
||||
<Select v-model="form.appId" filterable placeholder="选择要推送的订阅方应用" style="width: 360px">
|
||||
<Option v-for="opt in appOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="订阅接口" required>
|
||||
<Select v-model="form.interfacePath" filterable placeholder="选择接口" style="width: 360px"
|
||||
@on-change="onInterfaceChange">
|
||||
<Option v-for="opt in interfaceOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="目标ID" :required="!isSync">
|
||||
<Input v-model="targetIdsText" :disabled="isSync" :placeholder="isSync ? '全量同步无需指定' : '可选,逗号分隔,如 10,11,12'"
|
||||
style="width: 360px" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" :loading="sending" @click="handleSend">发送测试推送</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div v-if="preview" class="result-card">
|
||||
<div class="result-head">
|
||||
<h3 class="card-title">请求预览</h3>
|
||||
<Button type="text" @click="goLog">查看推送日志</Button>
|
||||
</div>
|
||||
<Alert v-if="preview.batch && preview.batch > 1" type="info" show-icon>
|
||||
本次将拆分为 {{ preview.batch }} 条消息依次发送
|
||||
</Alert>
|
||||
<Form :label-width="100" class="result-form">
|
||||
<FormItem label="请求URL:">
|
||||
<span class="break-all">{{ preview.url }}</span>
|
||||
</FormItem>
|
||||
<FormItem label="请求方法:">
|
||||
<span>{{ preview.method }}</span>
|
||||
</FormItem>
|
||||
<FormItem label="请求头:">
|
||||
<pre class="kv-pre">{{ formatHeaders(preview.headers) }}</pre>
|
||||
</FormItem>
|
||||
<FormItem label="请求体:">
|
||||
<pre class="json-pre">{{ formatJson(preview.body) }}</pre>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<Alert type="success" show-icon>{{ sendMessage }}</Alert>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { Message } from 'view-ui-plus';
|
||||
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
||||
|
||||
defineOptions({ name: 'PushTest' });
|
||||
|
||||
const router = useRouter();
|
||||
const { $utils } = useCurrentInstance();
|
||||
|
||||
const interfaceOptions = [
|
||||
{ value: 'group/SyncGroup', label: '组全量同步', sync: true },
|
||||
{ value: 'user/SyncUser', label: '用户全量同步', sync: true },
|
||||
{ value: 'group/AddGroup', label: '添加组', sync: false },
|
||||
{ value: 'group/UpdateGroup', label: '更新组', sync: false },
|
||||
{ value: 'group/DeleteGroup', label: '删除组', sync: false },
|
||||
{ value: 'user/AddUser', label: '添加用户', sync: false },
|
||||
{ value: 'user/UpdateUser', label: '更新用户', sync: false },
|
||||
{ value: 'user/DeleteUser', label: '删除用户', sync: false },
|
||||
];
|
||||
|
||||
const appOptions = ref<{ value: number; label: string }[]>([]);
|
||||
const form = ref({ appId: undefined as number | undefined, interfacePath: '' });
|
||||
const targetIdsText = ref('');
|
||||
const sending = ref(false);
|
||||
const preview = ref<Record<string, any> | null>(null);
|
||||
const sendMessage = ref('');
|
||||
|
||||
const isSync = computed(() => {
|
||||
const opt = interfaceOptions.find((o) => o.value === form.value.interfacePath);
|
||||
return !!opt?.sync;
|
||||
});
|
||||
|
||||
function onInterfaceChange() {
|
||||
if (isSync.value) targetIdsText.value = '';
|
||||
}
|
||||
|
||||
function goLog() {
|
||||
router.push('/push-log');
|
||||
}
|
||||
|
||||
function formatHeaders(headers: Record<string, string> | undefined): string {
|
||||
if (!headers) return '-';
|
||||
return Object.entries(headers)
|
||||
.map(([k, v]) => `${k}: ${v}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function formatJson(val: any): string {
|
||||
if (val == null) return '-';
|
||||
if (typeof val === 'string') {
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(val), null, 2);
|
||||
} catch {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(val, null, 2);
|
||||
}
|
||||
|
||||
function parseTargetIds(): number[] | null {
|
||||
if (isSync.value) return null;
|
||||
const txt = targetIdsText.value.trim();
|
||||
if (!txt) return null;
|
||||
const ids = txt
|
||||
.split(/[,,\s]+/)
|
||||
.map((s) => Number(s.trim()))
|
||||
.filter((n) => Number.isFinite(n));
|
||||
return ids.length ? ids : null;
|
||||
}
|
||||
|
||||
function handleSend() {
|
||||
if (form.value.appId == null) {
|
||||
Message.error('请选择订阅方应用');
|
||||
return;
|
||||
}
|
||||
if (!form.value.interfacePath) {
|
||||
Message.error('请选择订阅接口');
|
||||
return;
|
||||
}
|
||||
sending.value = true;
|
||||
preview.value = null;
|
||||
sendMessage.value = '';
|
||||
(Apis as any)
|
||||
.PushSubscription.test({
|
||||
data: {
|
||||
appId: form.value.appId,
|
||||
interfacePath: form.value.interfacePath,
|
||||
targetIds: parseTargetIds(),
|
||||
},
|
||||
cacheFor: null,
|
||||
})
|
||||
.then((res: any) => {
|
||||
preview.value = res?.preview || null;
|
||||
sendMessage.value = res?.message || '已发送';
|
||||
})
|
||||
.catch((e: any) => console.warn(e?.message || '发送失败'))
|
||||
.finally(() => (sending.value = false));
|
||||
}
|
||||
|
||||
async function loadApps() {
|
||||
try {
|
||||
const res = await (Apis as any).AppSettings.appList({
|
||||
params: { pageIndex: 1, pageSize: 9999 },
|
||||
cacheFor: null,
|
||||
});
|
||||
const list = (res && res.content) || [];
|
||||
appOptions.value = list.map((a: any) => ({ value: a.id, label: `${a.name}(${a.appKey})` }));
|
||||
} catch (e: any) {
|
||||
appOptions.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
loadApps();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.test-card,
|
||||
.result-card {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0 0 6px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
margin: 0 0 12px;
|
||||
color: #808695;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.test-form {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.result-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.break-all {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.kv-pre,
|
||||
.json-pre {
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #e8eaec;
|
||||
border-radius: 4px;
|
||||
max-height: 280px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.kv-pre {
|
||||
max-height: 140px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user