This commit is contained in:
cc 2026-01-21 08:44:43 +08:00
parent cf1c329342
commit 47b0bd8e3b
12 changed files with 263 additions and 57 deletions

View File

@ -21,8 +21,15 @@ Page({
async loadModules() {
try {
const result = await request({ url: '/portal/api/modules' });
if (result && result.success && result.data && Array.isArray(result.data.modules) && result.data.modules.length) {
this.setData({ modules: result.data.modules });
if (Array.isArray(result) && result.length) {
const defaultMap = new Map(DEFAULT_MODULES.map((item) => [item.path, item]));
const merged = DEFAULT_MODULES.map((item) => {
const remote = result.find((entry) => entry && entry.path === item.path);
return remote ? { ...item, ...remote } : item;
});
if (merged.length) {
this.setData({ modules: merged });
}
}
} catch (e) {}
},

View File

@ -1,4 +1,12 @@
const { request } = require('../../utils/request');
const { request, normalizeResponse } = require('../../utils/request');
const TASK_STATUS_TEXT = {
0: '待开始',
1: '进行中',
2: '已完成',
3: '已暂停',
4: '已取消'
};
Page({
data: {
@ -10,12 +18,16 @@ Page({
},
async loadData() {
try {
const stats = await request({ url: '/api/prompt/dashboard' });
const running = await request({ url: '/api/prompt/tasks?status=1&page_size=5' });
this.setData({
stats: stats && stats.success ? stats.data : null,
running: running && running.list ? running.list : (running && running.data && running.data.list ? running.data.list : [])
});
const statsResp = await request({ url: '/api/prompt/dashboard' });
const runningResp = await request({ url: '/api/prompt/tasks?status=1&page_size=5' });
const stats = normalizeResponse(statsResp).payload || null;
const runningPayload = normalizeResponse(runningResp).payload;
const runningList = runningPayload && runningPayload.list ? runningPayload.list : (Array.isArray(runningPayload) ? runningPayload : []);
const running = runningList.map(item => ({
...item,
statusText: TASK_STATUS_TEXT[item.status] || ''
}));
this.setData({ stats, running });
} catch (e) {}
}
});

View File

@ -22,6 +22,14 @@
<view class="module-title">已完成</view>
<view class="badge badge--success">{{stats.completed_tasks}}</view>
</view>
<view class="card card--inner module-card" style="margin:0;">
<view class="module-title">模板</view>
<view class="badge">{{stats.total_templates}}</view>
</view>
<view class="card card--inner module-card" style="margin:0;">
<view class="module-title">对话</view>
<view class="badge">{{stats.total_conversations}}</view>
</view>
</view>
<view wx:else>暂无统计数据</view>
</view>
@ -32,7 +40,7 @@
<view wx:for="{{running}}" wx:key="id" class="card card--inner list-item">
<view class="row" style="justify-content: space-between;">
<view class="module-title">{{item.title}}</view>
<view class="badge">{{item.status}}</view>
<view class="badge">{{item.statusText || item.status}}</view>
</view>
</view>
</view>

View File

@ -1,4 +1,48 @@
const { request, buildResultView } = require('../../utils/request');
const { request, buildResultView, normalizeResponse } = require('../../utils/request');
const RESULT_PREVIEW_LIMIT = 240;
function buildResultState(text) {
const content = text || '';
return { resultHasMore: content.length > RESULT_PREVIEW_LIMIT, resultExpanded: false };
}
function buildImportCards(payload) {
const cards = [];
if (!payload || typeof payload !== 'object') return cards;
if (payload.task_id) {
cards.push({ label: '关联任务', value: `#${payload.task_id}` });
}
if (payload.existing_task) {
const name = payload.existing_task.title || payload.existing_task.id || '已存在任务';
cards.push({ label: '已存在任务', value: String(name) });
if (payload.existing_task.conversation_count !== undefined) {
cards.push({ label: '对话数量', value: String(payload.existing_task.conversation_count) });
}
}
if (Array.isArray(payload.conversations)) {
cards.push({ label: '解析对话', value: String(payload.conversations.length) });
}
if (payload.summary) {
const summary = payload.summary;
if (summary.total_rounds !== undefined) {
cards.push({ label: '总轮数', value: String(summary.total_rounds) });
}
if (summary.user_messages !== undefined) {
cards.push({ label: '用户消息', value: String(summary.user_messages) });
}
if (summary.bot_messages !== undefined) {
cards.push({ label: '机器人消息', value: String(summary.bot_messages) });
}
if (Array.isArray(summary.keywords) && summary.keywords.length) {
cards.push({ label: '关键词', value: summary.keywords.join('、') });
}
if (Array.isArray(summary.main_topics) && summary.main_topics.length) {
cards.push({ label: '主要话题', value: summary.main_topics.join('、') });
}
}
return cards;
}
Page({
data: {
@ -9,20 +53,32 @@ Page({
task_name: '',
robot_id: '',
result: '',
resultCards: []
resultCards: [],
resultExpanded: false,
resultHasMore: false,
importCards: []
},
onInput(e) {
const key = e.currentTarget.dataset.key;
this.setData({ [key]: e.detail.value });
},
toggleResult() {
this.setData({ resultExpanded: !this.data.resultExpanded });
},
setResult(result) {
const view = buildResultView(result);
const payload = normalizeResponse(result).payload;
const importCards = buildImportCards(payload);
const state = buildResultState(view.text);
this.setData({ result: view.text, resultCards: view.cards, importCards, ...state });
},
async parse() {
const result = await request({
url: '/api/prompt/import',
method: 'POST',
data: { content: this.data.content, check_only: true }
});
const view = buildResultView(result);
this.setData({ result: view.text, resultCards: view.cards });
this.setResult(result);
},
async confirm() {
const payload = {
@ -34,7 +90,6 @@ Page({
robot_id: this.data.robot_id ? Number(this.data.robot_id) : undefined
};
const result = await request({ url: '/api/prompt/import', method: 'POST', data: payload });
const view = buildResultView(result);
this.setData({ result: view.text, resultCards: view.cards });
this.setResult(result);
}
});

View File

@ -26,9 +26,20 @@
<view class="card result-card" wx:if="{{result}}">
<view class="result-header">
<view class="result-title">结果</view>
<view class="badge">原始数据</view>
<view class="result-actions">
<view class="badge">原始数据</view>
<view class="result-toggle" wx:if="{{resultHasMore}}" bindtap="toggleResult">{{resultExpanded ? '收起' : '展开'}}</view>
</view>
</view>
<view class="result-hint">已为你保留完整响应</view>
<view wx:if="{{importCards.length}}" class="list">
<view wx:for="{{importCards}}" wx:key="label" class="card card--inner list-item">
<view class="row" style="justify-content: space-between;">
<view class="module-title">{{item.label}}</view>
<view class="badge">{{item.value}}</view>
</view>
</view>
</view>
<view wx:if="{{resultCards.length}}" class="list">
<view wx:for="{{resultCards}}" wx:key="label" class="card card--inner list-item">
<view class="row" style="justify-content: space-between;">
@ -37,6 +48,6 @@
</view>
</view>
</view>
<view class="result">{{result}}</view>
<view class="result {{resultExpanded ? 'result--expanded' : 'result--collapsed'}}">{{result}}</view>
</view>
</view>

View File

@ -1,17 +1,23 @@
const { request, buildResultView } = require('../../utils/request');
const { request, buildResultView, normalizeResponse } = require('../../utils/request');
const RESULT_PREVIEW_LIMIT = 240;
const ROBOT_STATUS_TEXT = {
0: '禁用',
1: '启用'
};
function safeParseResult(raw) {
if (raw === null || raw === undefined) return null;
if (typeof raw === 'string') {
const normalized = normalizeResponse(raw);
const payload = normalized.payload;
if (payload === null || payload === undefined) return null;
if (typeof payload === 'string') {
try {
return JSON.parse(raw);
return JSON.parse(payload);
} catch (e) {
return null;
}
}
return raw;
return payload;
}
function pickList(obj) {
@ -42,10 +48,12 @@ function buildRobotCards(parsed) {
return list.map(item => {
const name = item.name || item.title || item.robot_name || '';
const desc = item.description || item.desc || item.detail || '';
const statusText = ROBOT_STATUS_TEXT[item.status] || '';
return {
id: item.id || item.robot_id || item.robotId || '',
name: name || (item.id || item.robot_id ? `机器人 ${item.id || item.robot_id}` : '机器人'),
status: item.status,
statusText,
avatar: item.avatar || item.avatar_url || '',
description: desc ? String(desc).slice(0, 80) : ''
};

View File

@ -44,7 +44,8 @@
<view class="badge" wx:if="{{item.id}}">#{{item.id}}</view>
</view>
<view class="row meta-row">
<view class="meta-item" wx:if="{{item.status !== undefined && item.status !== null && item.status !== ''}}">状态:{{item.status}}</view>
<view class="meta-item" wx:if="{{item.statusText}}">状态:{{item.statusText}}</view>
<view class="meta-item" wx:elif="{{item.status !== undefined && item.status !== null && item.status !== ''}}">状态:{{item.status}}</view>
</view>
<view class="subtle" wx:if="{{item.description}}">{{item.description}}</view>
</view>

View File

@ -1,17 +1,26 @@
const { request, downloadFile, buildResultView } = require('../../utils/request');
const { request, downloadFile, buildResultView, normalizeResponse } = require('../../utils/request');
const RESULT_PREVIEW_LIMIT = 240;
const TASK_STATUS_TEXT = {
0: '待开始',
1: '进行中',
2: '已完成',
3: '已暂停',
4: '已取消'
};
function safeParseResult(raw) {
if (raw === null || raw === undefined) return null;
if (typeof raw === 'string') {
const normalized = normalizeResponse(raw);
const payload = normalized.payload;
if (payload === null || payload === undefined) return null;
if (typeof payload === 'string') {
try {
return JSON.parse(raw);
return JSON.parse(payload);
} catch (e) {
return null;
}
}
return raw;
return payload;
}
function pickList(obj) {
@ -41,14 +50,21 @@ function buildTaskCards(parsed) {
const list = toArray(parsed).filter(isTaskLike).slice(0, 6);
return list.map(item => {
const title = item.title || item.name || item.subject || item.task_name || '';
const content = item.content || item.description || item.desc || item.detail || '';
const description = item.description || item.implement_path || item.implement_rules || item.initial_prompt || item.content || item.desc || item.detail || '';
const robot = item.robot || {};
const tags = Array.isArray(item.tags) ? item.tags.join('、') : '';
const statusText = TASK_STATUS_TEXT[item.status] || '';
return {
id: item.id || item.task_id || item.taskId || '',
title: title || (item.id || item.task_id ? `任务 ${item.id || item.task_id}` : '任务'),
status: item.status,
statusText,
priority: item.priority,
progress: item.progress,
robotId: item.robot_id || item.robotId,
contentPreview: content ? String(content).slice(0, 80) : ''
robotName: robot.name || '',
tags,
contentPreview: description ? String(description).slice(0, 80) : ''
};
});
}

View File

@ -73,9 +73,13 @@
<view class="badge" wx:if="{{item.id}}">#{{item.id}}</view>
</view>
<view class="row meta-row">
<view class="meta-item" wx:if="{{item.status !== undefined && item.status !== null && item.status !== ''}}">状态:{{item.status}}</view>
<view class="meta-item" wx:if="{{item.statusText}}">状态:{{item.statusText}}</view>
<view class="meta-item" wx:elif="{{item.status !== undefined && item.status !== null && item.status !== ''}}">状态:{{item.status}}</view>
<view class="meta-item" wx:if="{{item.priority !== undefined && item.priority !== null && item.priority !== ''}}">优先级:{{item.priority}}</view>
<view class="meta-item" wx:if="{{item.robotId !== undefined && item.robotId !== null && item.robotId !== ''}}">机器人:{{item.robotId}}</view>
<view class="meta-item" wx:if="{{item.progress !== undefined && item.progress !== null && item.progress !== ''}}">进度:{{item.progress}}%</view>
<view class="meta-item" wx:if="{{item.robotName}}">机器人:{{item.robotName}}</view>
<view class="meta-item" wx:elif="{{item.robotId !== undefined && item.robotId !== null && item.robotId !== ''}}">机器人:{{item.robotId}}</view>
<view class="meta-item" wx:if="{{item.tags}}">标签:{{item.tags}}</view>
</view>
<view class="subtle" wx:if="{{item.contentPreview}}">{{item.contentPreview}}</view>
</view>

View File

@ -1,17 +1,23 @@
const { request, buildResultView } = require('../../utils/request');
const { request, buildResultView, normalizeResponse } = require('../../utils/request');
const RESULT_PREVIEW_LIMIT = 240;
const TEMPLATE_STATUS_TEXT = {
0: '禁用',
1: '启用'
};
function safeParseResult(raw) {
if (raw === null || raw === undefined) return null;
if (typeof raw === 'string') {
const normalized = normalizeResponse(raw);
const payload = normalized.payload;
if (payload === null || payload === undefined) return null;
if (typeof payload === 'string') {
try {
return JSON.parse(raw);
return JSON.parse(payload);
} catch (e) {
return null;
}
}
return raw;
return payload;
}
function pickList(obj) {
@ -42,10 +48,14 @@ function buildTemplateCards(parsed) {
return list.map(item => {
const name = item.name || item.title || item.template_name || '';
const content = item.content || item.body || item.desc || item.description || '';
const statusText = TEMPLATE_STATUS_TEXT[item.status] || '';
return {
id: item.id || item.template_id || item.templateId || '',
name: name || (item.id || item.template_id ? `模板 ${item.id || item.template_id}` : '模板'),
category: item.category || item.group || '',
usageCount: item.usage_count !== undefined && item.usage_count !== null ? item.usage_count : '',
status: item.status,
statusText,
contentPreview: content ? String(content).slice(0, 80) : ''
};
});

View File

@ -41,6 +41,9 @@
</view>
<view class="row meta-row">
<view class="meta-item" wx:if="{{item.category}}">分类:{{item.category}}</view>
<view class="meta-item" wx:if="{{item.usageCount !== '' && item.usageCount !== null && item.usageCount !== undefined}}">使用次数:{{item.usageCount}}</view>
<view class="meta-item" wx:if="{{item.statusText}}">状态:{{item.statusText}}</view>
<view class="meta-item" wx:elif="{{item.status !== undefined && item.status !== null && item.status !== ''}}">状态:{{item.status}}</view>
</view>
<view class="subtle" wx:if="{{item.contentPreview}}">{{item.contentPreview}}</view>
</view>

View File

@ -85,51 +85,122 @@ function downloadFile(options) {
});
}
function normalizeResponse(raw) {
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
const hasData = Object.prototype.hasOwnProperty.call(raw, 'data');
const hasCode = typeof raw.code === 'number';
const hasSuccess = typeof raw.success === 'boolean';
const hasMessage = typeof raw.message === 'string';
if (hasData && (hasCode || hasSuccess || hasMessage)) {
return {
payload: raw.data,
meta: {
code: raw.code,
success: raw.success,
message: raw.message
}
};
}
}
return { payload: raw, meta: null };
}
function pickArray(obj) {
if (!obj || typeof obj !== 'object') return null;
const keys = ['list', 'items', 'records', 'rows', 'data', 'result'];
for (let i = 0; i < keys.length; i += 1) {
const value = obj[keys[i]];
if (Array.isArray(value)) return value;
}
return null;
}
function buildListCards(list, limit) {
return list.slice(0, limit).map((item, index) => {
if (item && typeof item === 'object') {
const keys = Object.keys(item);
const titleKey = keys.find(k => /name|title|id|task_id|book_id|book_name/i.test(k)) || keys[0] || `item_${index + 1}`;
const titleValue = item[titleKey] !== undefined && item[titleKey] !== null ? item[titleKey] : titleKey;
return { label: String(titleValue), value: `#${index + 1}` };
}
return { label: `item_${index + 1}`, value: String(item) };
});
}
function buildResultView(raw) {
const result = { text: '', cards: [] };
if (raw === null || raw === undefined) return result;
if (typeof raw === 'string') {
result.text = raw;
const normalized = normalizeResponse(raw);
const payload = normalized.payload;
if (payload === undefined && normalized.meta && normalized.meta.message) {
result.text = normalized.meta.message;
} else if (typeof payload === 'string') {
result.text = payload;
} else {
try {
result.text = JSON.stringify(raw, null, 2);
result.text = JSON.stringify(payload, null, 2);
} catch (e) {
result.text = String(raw);
result.text = String(payload);
}
}
let parsed = raw;
if (typeof raw === 'string') {
let parsed = payload;
if (typeof payload === 'string') {
try {
parsed = JSON.parse(raw);
parsed = JSON.parse(payload);
} catch (e) {
parsed = null;
}
}
if (parsed && typeof parsed === 'object') {
const metaCards = [];
if (normalized.meta) {
if (typeof normalized.meta.code === 'number') {
metaCards.push({ label: 'code', value: String(normalized.meta.code) });
}
if (typeof normalized.meta.success === 'boolean') {
metaCards.push({ label: 'success', value: String(normalized.meta.success) });
}
if (normalized.meta.message) {
metaCards.push({ label: 'message', value: String(normalized.meta.message) });
}
}
if (Array.isArray(parsed)) {
result.cards = parsed.slice(0, 6).map((item, index) => {
if (item && typeof item === 'object') {
const keys = Object.keys(item);
const titleKey = keys.find(k => /name|title|id/i.test(k)) || keys[0] || `item_${index + 1}`;
const titleValue = item[titleKey] !== undefined && item[titleKey] !== null ? item[titleKey] : titleKey;
return { label: String(titleValue), value: `#${index + 1}` };
}
return { label: `item_${index + 1}`, value: String(item) };
});
result.cards = metaCards.concat(buildListCards(parsed, 6));
return result;
}
const list = pickArray(parsed);
if (list) {
const headerCards = [];
if (parsed.total !== undefined && parsed.total !== null) {
headerCards.push({ label: 'total', value: String(parsed.total) });
}
if (parsed.page !== undefined && parsed.page !== null) {
headerCards.push({ label: 'page', value: String(parsed.page) });
}
if (parsed.page_size !== undefined && parsed.page_size !== null) {
headerCards.push({ label: 'page_size', value: String(parsed.page_size) });
}
if (parsed.pageSize !== undefined && parsed.pageSize !== null) {
headerCards.push({ label: 'pageSize', value: String(parsed.pageSize) });
}
result.cards = metaCards.concat(headerCards, buildListCards(list, 6));
return result;
}
const keys = Object.keys(parsed);
result.cards = keys.slice(0, 8).map(key => ({
result.cards = metaCards.concat(keys.slice(0, 8).map(key => ({
label: key,
value: typeof parsed[key] === 'object' ? '对象' : String(parsed[key])
}));
})));
}
return result;
}
module.exports = { request, uploadFile, downloadFile, buildUrl, buildResultView };
module.exports = { request, uploadFile, downloadFile, buildUrl, buildResultView, normalizeResponse };