- src/assets/appicon-icons.json:资产库图标清单 - src/components/AppIconPicker.vue:矢量图标+配色选择器(YschAppIcons mask 资产) - AppForm.vue:平台模式 Pc 应用图标换 AppIconPicker(学校模式保留位图上传,学校覆盖线只收 pcIcon);App 位图图标移入高级折叠区(遗留通道,存量 pc_icon/app_icon 链路保留,新应用优先矢量图标键) - AddAppContainer/EditAppContainer:配套接线 依据:根目录《AppIcon资产库落地-执行报告-20261004.md》P2 管理端图标选择器
130 lines
5.2 KiB
Vue
130 lines
5.2 KiB
Vue
<template>
|
|
<AppForm v-model="visible" :initForm="detailData" :categoryList="categoryList" :isEdit="true" :loading="submitting"
|
|
:schoolMode="isSchoolMode" @submit="handleSubmit" @cancel="visible = false" @reset-secret="handleResetSecret" />
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref } from "vue";
|
|
import AppForm from "./AppForm.vue";
|
|
import MyConfirm from '@/components/MyConfirm'
|
|
import { UserRoleEnum } from "@/types";
|
|
import { useDefaultStore } from "@/store";
|
|
|
|
/* ---------- 对外暴露 ---------- */
|
|
const visible = ref(false);
|
|
const categoryList = ref<any[]>([]);
|
|
|
|
const store = useDefaultStore();
|
|
const isSchoolMode = store.userRole === UserRoleEnum.School;
|
|
|
|
// 多校管理员当前所选学校(单校/超管为 undefined,后端按 scope 自动解析)
|
|
const props = defineProps<{ schoolId?: number | null }>();
|
|
|
|
// 编辑模式:从 appList 中根据 id 取出详情回填(无独立详情接口;SCHOOL 模式列表已合并覆盖值)
|
|
const detailData = ref<Record<string, any>>({});
|
|
const currentId = ref<number | null>(null);
|
|
|
|
// 成功后回调
|
|
let onSuccessCallback: (() => void) | null = null;
|
|
|
|
// useForm 托管提交——currentId 是 ref,闭包内 .value 在 sendForm 时取当前值
|
|
const { form, loading: submitting, send: sendForm, onSuccess, onError } = useForm(
|
|
(dto) => Apis.AppSettings.editApp({
|
|
pathParams: { id: currentId.value },
|
|
data: dto,
|
|
params: props.schoolId != null ? { schoolId: props.schoolId } : undefined,
|
|
}),
|
|
{ initialForm: { id: null, name: '', categoryId: undefined, desc: '', appCode: '', appNature: 'BUSINESS', clients: [], pcUrl: '', pcIcon: '', appUrl: '', appIcon: '', iconKey: '', iconColor: '', officialAccountUrl: '', dingtalkUrl: '' }, resetAfterSubmiting: true }
|
|
);
|
|
|
|
onSuccess(() => {
|
|
Message.success(isSchoolMode ? '保存成功(仅对本校生效)!' : '编辑应用成功!');
|
|
visible.value = false;
|
|
onSuccessCallback?.();
|
|
});
|
|
onError((event) => {
|
|
// 错误提示已由全局 alova 拦截器统一弹出,这里仅记录日志避免二次弹窗
|
|
console.warn('[EditAppContainer] 编辑应用失败:', event?.error?.message || '编辑失败');
|
|
});
|
|
|
|
/* ---------- 打开编辑弹窗 ---------- */
|
|
/**
|
|
* @param id - 应用 ID
|
|
* @param row - 当前行完整数据(来自表格列表),直接用作回填数据
|
|
* @param categories - 应用类型列表
|
|
* @param onSuccess - 编辑成功后的回调
|
|
*/
|
|
function open(id: number, row: any, categories: any[], onSuccess?: () => void) {
|
|
currentId.value = id;
|
|
categoryList.value = categories || [];
|
|
// 直接用行数据回填(appList 接口返回的字段与表单一致)
|
|
detailData.value = { ...row };
|
|
onSuccessCallback = onSuccess || null;
|
|
visible.value = true;
|
|
}
|
|
|
|
/* ---------- 提交:由 useForm 接管 loading 与异常处理 ---------- */
|
|
async function handleSubmit(formData: Record<string, any>) {
|
|
if (!currentId.value) return;
|
|
if (isSchoolMode) {
|
|
// 学校模式:仅 名称/描述/Pc图标 三字段写学校覆盖表(后端 editAppForSchool 只读这三个);
|
|
// categoryId/appCode/appNature 原样回传仅为满足平台级 Update 校验(后端不落平台表),缺失会 400;
|
|
// iconKey/iconColor 不送键(后端 null=不改——学校线不动平台矢量配置)
|
|
form.value = {
|
|
id: currentId.value,
|
|
name: formData.name,
|
|
categoryId: formData.categoryId,
|
|
desc: formData.desc,
|
|
appCode: formData.appCode,
|
|
appNature: formData.appNature,
|
|
pcIcon: formData.pcIcon,
|
|
};
|
|
} else {
|
|
form.value = {
|
|
id: currentId.value,
|
|
name: formData.name,
|
|
categoryId: formData.categoryId,
|
|
desc: formData.desc,
|
|
appCode: formData.appCode,
|
|
appNature: formData.appNature,
|
|
clients: formData.clients,
|
|
pcUrl: formData.pcUrl,
|
|
pcIcon: formData.pcIcon,
|
|
appUrl: formData.appUrl,
|
|
appIcon: formData.appIcon,
|
|
iconKey: formData.iconKey,
|
|
iconColor: formData.iconColor,
|
|
officialAccountUrl: formData.officialAccountUrl,
|
|
dingtalkUrl: formData.dingtalkUrl,
|
|
};
|
|
}
|
|
await sendForm();
|
|
}
|
|
|
|
/* ---------- 重置密钥(非表单提交,直接 MyConfirm + await;学校模式入口已隐藏) ---------- */
|
|
async function handleResetSecret() {
|
|
if (!currentId.value) return;
|
|
try {
|
|
await MyConfirm({
|
|
msg: '确定要重置该应用的 Secret 吗?',
|
|
info: '重置后原 Secret 将立即失效!',
|
|
async onOk() {
|
|
const newSecret = await Apis.AppSettings.resetAppSecret({
|
|
pathParams: { id: currentId.value! },
|
|
});
|
|
Message.success('密钥已重置!新 Secret: ' + (newSecret || ''));
|
|
// 更新表单中的 secret 回显值
|
|
if (newSecret) {
|
|
detailData.value = { ...detailData.value, appSecret: newSecret };
|
|
}
|
|
return true;
|
|
},
|
|
});
|
|
} catch {
|
|
// 用户取消
|
|
}
|
|
}
|
|
|
|
defineExpose({ open, visible });
|
|
</script>
|