推送功能添加
deploy-userbase-front / build-and-deploy (push) Successful in 2m50s

This commit is contained in:
chenxing
2026-08-14 09:08:01 +08:00
parent 4077ef64d8
commit 85619785f6
92 changed files with 862 additions and 15442 deletions
+215
View File
@@ -0,0 +1,215 @@
<template>
<Modal v-model="show" :mask-closable="false" width="680" title="推送设置" @on-visible-change="onVisibleChange">
<div v-if="loading" class="loading-tip">加载中...</div>
<Form v-else :label-width="110" class="push-setting-form">
<FormItem label="应用">
<span>{{ appName || '-' }}</span>
</FormItem>
<FormItem label="推送总开关">
<i-switch v-model="form.pushEnabled" />
<span class="switch-tip">{{ form.pushEnabled ? '开启' : '关闭' }}</span>
</FormItem>
<FormItem label="推送地址" required>
<Input v-model="form.pushBaseUrl" placeholder="如 http://host/notice/api/base/(不含接口后缀)" />
</FormItem>
<FormItem label="订阅接口" required>
<Select v-model="form.pushEventTypes" multiple filterable placeholder="请选择要订阅的接口(必选)">
<Option v-for="opt in interfaceOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</Option>
</Select>
</FormItem>
<FormItem label="关联学校">
<Select v-model="form.pushSchoolIds" multiple filterable placeholder="留空=全局(监听所有学校变化)">
<Option v-for="opt in schoolOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</Option>
</Select>
</FormItem>
<FormItem label="推送角色">
<Select v-model="form.pushRoleIds" multiple filterable placeholder="留空=全部角色">
<Option v-for="opt in roleOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</Option>
</Select>
</FormItem>
<FormItem label="签名密钥">
<Input v-model="form.pushSecret" type="password" password placeholder="留空=复用应用密钥 appSecret;清空则改为复用" />
<p class="field-tip">不修改请保持原值;清空输入框表示改用应用密钥。</p>
</FormItem>
</Form>
<template #footer>
<Button @click="show = false">取消</Button>
<Button type="primary" :loading="saving" @click="handleSave">保存</Button>
</template>
</Modal>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { Message } from 'view-ui-plus';
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
const props = defineProps<{
modelValue: boolean;
appId: number;
appName?: string;
}>();
const emit = defineEmits(['update:modelValue', 'saved']);
const { $utils } = useCurrentInstance();
const show = computed({
get: () => props.modelValue,
set: val => emit('update:modelValue', val),
});
const interfaceOptions = [
{ value: 'group/SyncGroup', label: '组全量同步' },
{ value: 'user/SyncUser', label: '用户全量同步' },
{ value: 'group/AddGroup', label: '添加组' },
{ value: 'group/UpdateGroup', label: '更新组' },
{ value: 'group/DeleteGroup', label: '删除组' },
{ value: 'user/AddUser', label: '添加用户' },
{ value: 'user/UpdateUser', label: '更新用户' },
{ value: 'user/DeleteUser', label: '删除用户' },
];
const roleOptions = [
{ value: 1, label: '老师' },
{ value: 2, label: '校领导' },
{ value: 3, label: '学生' },
{ value: 4, label: '家长' },
{ value: 5, label: '局领导' },
{ value: 6, label: '工作号' },
{ value: 7, label: '管理员' },
];
const schoolOptions = ref<{ value: number; label: string }[]>([]);
const loading = ref(false);
const saving = ref(false);
const form = ref({
pushEnabled: false,
pushBaseUrl: '',
pushEventTypes: [] as string[],
pushSchoolIds: [] as number[],
pushRoleIds: [] as number[],
pushSecret: '',
});
const originalSecret = ref<string>('');
function flattenSchools(nodes: any[], prefix = ''): { value: number; label: string }[] {
const out: { value: number; label: string }[] = [];
(nodes || []).forEach((n) => {
const label = prefix ? `${prefix} / ${n.name}` : n.name;
out.push({ value: Number(n.id), label });
if (Array.isArray(n.children) && n.children.length) {
out.push(...flattenSchools(n.children, label));
}
});
return out;
}
async function loadSchools() {
try {
const data = await (Apis as any).SysGroup.getSchoolTree({ cacheFor: null });
schoolOptions.value = flattenSchools(Array.isArray(data) ? data : []);
} catch (e: any) {
schoolOptions.value = [];
}
}
async function loadSettings() {
loading.value = true;
try {
const data = await (Apis as any).AppSettings.getPushSetting({
pathParams: { id: props.appId },
cacheFor: null,
});
const d = data || {};
form.value.pushEnabled = d.pushEnabled === 1;
form.value.pushBaseUrl = d.pushBaseUrl || '';
form.value.pushEventTypes = d.pushEventTypes ? String(d.pushEventTypes).split(',').filter(Boolean) : [];
form.value.pushSchoolIds = d.pushSchoolIds
? String(d.pushSchoolIds).split(',').filter(Boolean).map(Number)
: [];
form.value.pushRoleIds = d.pushRoleIds
? String(d.pushRoleIds).split(',').filter(Boolean).map(Number)
: [];
form.value.pushSecret = d.pushSecret || '';
originalSecret.value = d.pushSecret || '';
} catch (e: any) {
Message.error(e?.message || '加载推送设置失败');
} finally {
loading.value = false;
}
}
watch(
() => props.modelValue,
(val) => {
if (val && props.appId) {
loadSchools();
loadSettings();
}
}
);
function onVisibleChange(val: boolean) {
if (!val) emit('update:modelValue', false);
}
function handleSave() {
if (!form.value.pushBaseUrl.trim()) {
Message.error('请填写推送地址');
return;
}
if (!form.value.pushEventTypes.length) {
Message.error('请至少选择一个订阅接口');
return;
}
saving.value = true;
const data: any = {
pushBaseUrl: form.value.pushBaseUrl.trim(),
pushEnabled: form.value.pushEnabled ? 1 : 0,
pushEventTypes: form.value.pushEventTypes.join(','),
pushSchoolIds: form.value.pushSchoolIds.length ? form.value.pushSchoolIds.join(',') : null,
pushRoleIds: form.value.pushRoleIds.length ? form.value.pushRoleIds.join(',') : null,
// 与原值相同=不修改(传 null 保留);清空=复用应用密钥(传 "")
pushSecret: form.value.pushSecret === originalSecret.value ? null : form.value.pushSecret,
};
(Apis as any)
.AppSettings.savePushSetting({
pathParams: { id: props.appId },
data,
cacheFor: null,
})
.then(() => {
Message.success('保存成功');
show.value = false;
emit('saved');
})
.catch((e: any) => Message.error(e?.message || '保存失败'))
.finally(() => (saving.value = false));
}
</script>
<style lang="scss" scoped>
.push-setting-form {
:deep(.ivu-form-item) {
margin-bottom: 18px;
}
.switch-tip {
margin-left: 8px;
color: #808695;
}
.field-tip {
margin: 4px 0 0;
color: #999;
font-size: 12px;
}
}
.loading-tip {
padding: 24px 0;
text-align: center;
color: #999;
}
</style>