forked from wangziqi/gongxue-base
feat: add import template downloads
This commit is contained in:
@@ -64,7 +64,7 @@ pages/tenant-admin/marketing/index 优惠券、激活码、CRM、分佣摘要
|
||||
pages/tenant-admin/settings/index 品牌、域名、支付、登录、角色模板
|
||||
```
|
||||
|
||||
当前后台页面已经从只读联调推进到第一批运营写操作。题库内容页已接入公共题库采纳、公共题库同步、同步冲突查看、单条采纳平台版本/保留本地版本、导入任务详情、异步任务轮询、导入问题查看、模板预览、导入后复检详情,以及 JSON/CSV/Excel 的 H5 选择文件或粘贴内容、后端预览、字段别名覆盖和同步/异步执行导入第一版;营销、设置和学生页仍以扫描和轻量操作为主。真正权限以后端 permission keys 为准,前端菜单隐藏只做体验优化。
|
||||
当前后台页面已经从只读联调推进到第一批运营写操作。题库内容页已接入公共题库采纳、公共题库同步、同步冲突查看、单条采纳平台版本/保留本地版本、导入任务详情、异步任务轮询、导入问题查看、模板预览/下载、导入后复检详情,以及 JSON/CSV/Excel 的 H5 选择文件或粘贴内容、后端预览、字段别名覆盖和同步/异步执行导入第一版;营销、设置和学生页仍以扫描和轻量操作为主。真正权限以后端 permission keys 为准,前端菜单隐藏只做体验优化。
|
||||
|
||||
## 当前平台后台页面
|
||||
|
||||
|
||||
@@ -97,6 +97,33 @@ function openH5FilePicker(format: ImportSourceFormat) {
|
||||
});
|
||||
}
|
||||
|
||||
function base64ToUint8Array(base64: string) {
|
||||
const binary = atob(base64);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
for (let index = 0; index < binary.length; index += 1) {
|
||||
bytes[index] = binary.charCodeAt(index);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function downloadTemplateFile(template: ImportTemplateItem) {
|
||||
if (process.env.TARO_ENV !== 'h5' || typeof document === 'undefined') {
|
||||
throw new Error('当前端暂不支持直接下载模板,请先使用模板预览。');
|
||||
}
|
||||
if (!template.contentBase64) throw new Error('模板内容为空,请重新加载模板。');
|
||||
const blob = new Blob([base64ToUint8Array(template.contentBase64)], {
|
||||
type: template.mimeType || 'application/octet-stream',
|
||||
});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = template.fileName || `import-template.${template.format || 'json'}`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function safeJsonPreview(value: unknown, maxLength = 360) {
|
||||
try {
|
||||
return JSON.stringify(value, null, 2).slice(0, maxLength);
|
||||
@@ -246,14 +273,15 @@ export default function TenantContentPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function previewTemplate(type = selectedImportType) {
|
||||
async function previewTemplate(type = selectedImportType, format = sourceFormat) {
|
||||
setError('');
|
||||
try {
|
||||
const [mappingPayload, templatePayload] = await Promise.all([
|
||||
loadImportFieldMapping(type),
|
||||
loadImportTemplate(type, 'json'),
|
||||
loadImportTemplate(type, format === 'excel' ? 'csv' : format),
|
||||
]);
|
||||
setSelectedImportType(type);
|
||||
if (format !== 'excel') setSourceFormat(format);
|
||||
setMapping(mappingPayload.item || null);
|
||||
setMappingOverrides(Object.fromEntries(
|
||||
(mappingPayload.item?.fields || [])
|
||||
@@ -266,6 +294,21 @@ export default function TenantContentPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadCurrentTemplate() {
|
||||
setError('');
|
||||
try {
|
||||
const payload = template && template.importType === selectedImportType && template.format === sourceFormat
|
||||
? { item: template }
|
||||
: await loadImportTemplate(selectedImportType, sourceFormat === 'excel' ? 'csv' : sourceFormat);
|
||||
if (!payload.item) throw new Error('模板不存在。');
|
||||
downloadTemplateFile(payload.item);
|
||||
setTemplate(payload.item);
|
||||
Taro.showToast({ title: '已下载', icon: 'success' });
|
||||
} catch (nextError) {
|
||||
setError(nextError instanceof Error ? nextError.message : '模板下载失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function chooseImportFile() {
|
||||
setError('');
|
||||
try {
|
||||
@@ -510,7 +553,7 @@ export default function TenantContentPage() {
|
||||
</View>
|
||||
<View className='admin-tabs'>
|
||||
{importTypes.map(type => (
|
||||
<Button key={type} className={`admin-button ${selectedImportType === type ? 'active' : ''}`} onClick={() => previewTemplate(type)}>{type}</Button>
|
||||
<Button key={type} className={`admin-button ${selectedImportType === type ? 'active' : ''}`} onClick={() => previewTemplate(type, sourceFormat)}>{type}</Button>
|
||||
))}
|
||||
</View>
|
||||
<View className='admin-tabs'>
|
||||
@@ -519,6 +562,7 @@ export default function TenantContentPage() {
|
||||
setSourceFormat(format);
|
||||
setPreviewResult(null);
|
||||
setIssues([]);
|
||||
previewTemplate(selectedImportType, format);
|
||||
}}>{format}</Button>
|
||||
))}
|
||||
</View>
|
||||
@@ -550,6 +594,7 @@ export default function TenantContentPage() {
|
||||
setPreviewResult(null);
|
||||
setIssues([]);
|
||||
}}>填入模板</Button>
|
||||
<Button className='admin-button' onClick={downloadCurrentTemplate}>下载模板</Button>
|
||||
</View>
|
||||
<View className='admin-grid'>
|
||||
<View className='admin-metric'><Text className='admin-metric-label'>当前任务</Text><Text className='admin-metric-value'>{selectedJobId ? selectedJobId.slice(0, 8) : '-'}</Text></View>
|
||||
|
||||
Reference in New Issue
Block a user