初次提交
This commit is contained in:
commit
363baeb59a
|
|
@ -0,0 +1,12 @@
|
|||
const { API_BASE_URL } = require('./config');
|
||||
|
||||
App({
|
||||
globalData: {
|
||||
token: '',
|
||||
apiBase: API_BASE_URL || ''
|
||||
},
|
||||
onLaunch() {
|
||||
const token = wx.getStorageSync('token') || '';
|
||||
this.globalData.token = token;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"pages": [
|
||||
"pages/login/login",
|
||||
"pages/home/home",
|
||||
"pages/console/console",
|
||||
"pages/console/password",
|
||||
"pages/console/finance",
|
||||
"pages/console/oil",
|
||||
"pages/console/electric",
|
||||
"pages/console/payrecord",
|
||||
"pages/console/dayrecord",
|
||||
"pages/console/remind",
|
||||
"pages/console/report",
|
||||
"pages/console/ai",
|
||||
"pages/console/didi",
|
||||
"pages/console/qianggou",
|
||||
"pages/console/system",
|
||||
"pages/console/search",
|
||||
"pages/console/books",
|
||||
"pages/console/tool",
|
||||
"pages/prompt/prompt",
|
||||
"pages/prompt/dashboard",
|
||||
"pages/prompt/robots",
|
||||
"pages/prompt/tasks",
|
||||
"pages/prompt/templates",
|
||||
"pages/prompt/import",
|
||||
"pages/prompt/tool"
|
||||
],
|
||||
"window": {
|
||||
"navigationBarTitleText": "MXT 小程序",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f5f6f8"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#8a8a8a",
|
||||
"selectedColor": "#2b7cff",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/home",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/console/console",
|
||||
"text": "控制台"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/prompt/prompt",
|
||||
"text": "提示词库"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
page { background-color: #f4f6fb; color: #1f2937; font-size: 28rpx; }
|
||||
.container { padding: 24rpx 24rpx 32rpx; }
|
||||
.card { background: #fff; border-radius: 20rpx; padding: 24rpx; margin-bottom: 20rpx; box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06); border: 1rpx solid rgba(15, 23, 42, 0.04); }
|
||||
.card--inner { background: #f8fafc; box-shadow: none; border: 1rpx solid #eef2f7; }
|
||||
.card-title { font-size: 30rpx; font-weight: 600; margin-bottom: 12rpx; color: #0f172a; }
|
||||
.section-title { font-size: 26rpx; font-weight: 500; color: #6b7280; margin: 18rpx 0 10rpx; }
|
||||
.row { display: flex; align-items: center; gap: 16rpx; flex-wrap: wrap; }
|
||||
.row .input { flex: 1 1 200rpx; }
|
||||
.row .btn { flex: 0 0 auto; }
|
||||
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16rpx; }
|
||||
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
.module-card { padding: 20rpx; display: flex; flex-direction: column; gap: 10rpx; }
|
||||
.module-title { font-size: 28rpx; font-weight: 600; color: #111827; }
|
||||
.module-desc { font-size: 24rpx; color: #94a3b8; }
|
||||
.label { color: #6b6b6b; margin-bottom: 8rpx; display: block; }
|
||||
.input { background: #f8fafc; border-radius: 12rpx; padding: 18rpx; font-size: 28rpx; border: 1rpx solid #e5e7eb; }
|
||||
.textarea { background: #f8fafc; border-radius: 12rpx; padding: 18rpx; min-height: 160rpx; font-size: 26rpx; border: 1rpx solid #e5e7eb; }
|
||||
.btn { background: #2b7cff; color: #fff; border-radius: 12rpx; padding: 0 24rpx; min-height: 72rpx; text-align: center; display: inline-flex; align-items: center; justify-content: center; font-size: 28rpx; }
|
||||
.btn::after { border: none; }
|
||||
.btn-secondary { background: #eef3ff; color: #2b7cff; }
|
||||
.btn-danger { background: #ff4d4f; color: #fff; }
|
||||
.badge { display: inline-block; background: #eef3ff; color: #2b7cff; border-radius: 999rpx; padding: 6rpx 16rpx; font-size: 22rpx; }
|
||||
.badge--success { background: #e8f7ee; color: #16a34a; }
|
||||
.badge--warn { background: #fff4e5; color: #d97706; }
|
||||
.result { background: #0b1220; color: #e2e8f0; border-radius: 12rpx; padding: 16rpx; white-space: pre-wrap; font-size: 24rpx; line-height: 1.6; max-height: 360rpx; overflow: auto; }
|
||||
.result-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx; }
|
||||
.result-title { font-size: 28rpx; font-weight: 600; color: #0f172a; }
|
||||
.result-hint { font-size: 22rpx; color: #94a3b8; margin-bottom: 12rpx; }
|
||||
.list { display: flex; flex-direction: column; gap: 12rpx; }
|
||||
.list-item { margin: 0; }
|
||||
.action-row { margin-top: 16rpx; gap: 12rpx; }
|
||||
.pagination { justify-content: space-between; }
|
||||
.subtle { color: #94a3b8; font-size: 24rpx; }
|
||||
.page-header { margin-bottom: 20rpx; }
|
||||
.page-title { font-size: 34rpx; font-weight: 700; color: #0f172a; }
|
||||
.page-subtitle { font-size: 24rpx; color: #94a3b8; margin-top: 6rpx; }
|
||||
.section-hint { font-size: 24rpx; color: #94a3b8; margin-top: 6rpx; }
|
||||
.file-row { align-items: center; gap: 12rpx; }
|
||||
.file-name { color: #64748b; font-size: 24rpx; }
|
||||
.preview-image { width: 100%; border-radius: 12rpx; box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.08); }
|
||||
.result-card { border: 1rpx solid rgba(148, 163, 184, 0.2); }
|
||||
.result-actions { display: flex; align-items: center; gap: 12rpx; }
|
||||
.result-toggle { background: #0b1220; color: #e2e8f0; border-radius: 999rpx; padding: 6rpx 16rpx; font-size: 22rpx; }
|
||||
.result--collapsed { max-height: 360rpx; overflow: hidden; }
|
||||
.result--expanded { max-height: none; overflow: visible; }
|
||||
.result-entity { border: 1rpx solid #e5e7eb; }
|
||||
.meta-row { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 8rpx; }
|
||||
.meta-item { font-size: 24rpx; color: #64748b; background: #f1f5f9; padding: 6rpx 12rpx; border-radius: 10rpx; }
|
||||
.result-avatar { width: 48rpx; height: 48rpx; border-radius: 50%; background: #e2e8f0; flex: 0 0 auto; }
|
||||
.input:focus, .textarea:focus { border-color: #93c5fd; box-shadow: 0 0 0 4rpx rgba(59, 130, 246, 0.12); }
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
const API_BASE_URL = 'https://mxt.wecog.cn';
|
||||
|
||||
module.exports = { API_BASE_URL };
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,26 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
message: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
this.setData({ message: e.detail.value });
|
||||
},
|
||||
async send() {
|
||||
if (!this.data.message) {
|
||||
const view = buildResultView('请输入内容');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/ai/chat',
|
||||
method: 'POST',
|
||||
data: { message: this.data.message }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "AI 对话"
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">AI 对话</view>
|
||||
<view class="page-subtitle">快速提问与结果预览</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">AI 对话</view>
|
||||
<textarea class="textarea" value="{{message}}" bindinput="onInput" placeholder="请输入内容"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="send">发送</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">回答</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
const { request, uploadFile, downloadFile, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
status: '',
|
||||
keyword: '',
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
bookId: '',
|
||||
bookPage: 1,
|
||||
filePath: '',
|
||||
path: '',
|
||||
creator: '',
|
||||
taskBookId: '',
|
||||
days: '',
|
||||
push_times: '',
|
||||
channel: '',
|
||||
email: '',
|
||||
wechat_remark: '',
|
||||
taskId: '',
|
||||
result: '',
|
||||
resultCards: [],
|
||||
imagePath: ''
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async chooseFile() {
|
||||
try {
|
||||
const res = await wx.chooseMessageFile({ count: 1, type: 'file' });
|
||||
const file = res.tempFiles && res.tempFiles[0];
|
||||
if (file && file.path) this.setData({ filePath: file.path });
|
||||
} catch (e) {}
|
||||
},
|
||||
async listBooks() {
|
||||
const qs = [];
|
||||
if (this.data.status) qs.push(`status=${encodeURIComponent(this.data.status)}`);
|
||||
if (this.data.keyword) qs.push(`keyword=${encodeURIComponent(this.data.keyword)}`);
|
||||
qs.push(`page=${encodeURIComponent(this.data.page)}`);
|
||||
qs.push(`pageSize=${encodeURIComponent(this.data.pageSize)}`);
|
||||
const result = await request({ url: `/api/console/books?${qs.join('&')}` });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async importByPath() {
|
||||
const result = await request({
|
||||
url: '/api/console/books',
|
||||
method: 'POST',
|
||||
data: { path: this.data.path, creator: this.data.creator }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async uploadPDF() {
|
||||
if (!this.data.filePath) {
|
||||
const view = buildResultView('请选择PDF文件');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await uploadFile({
|
||||
url: '/api/console/books',
|
||||
filePath: this.data.filePath,
|
||||
name: 'file',
|
||||
formData: { creator: this.data.creator }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async bookDetail() {
|
||||
const result = await request({ url: `/api/console/books/${encodeURIComponent(this.data.bookId)}` });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async deleteBook() {
|
||||
const result = await request({ url: `/api/console/books/${encodeURIComponent(this.data.bookId)}`, method: 'DELETE' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async previewPage() {
|
||||
if (!this.data.bookId || !this.data.bookPage) {
|
||||
const view = buildResultView('请输入书籍ID和页码');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const tempPath = await downloadFile({ url: `/api/console/books/${encodeURIComponent(this.data.bookId)}/page/${encodeURIComponent(this.data.bookPage)}` });
|
||||
const view = buildResultView('下载成功');
|
||||
this.setData({ imagePath: tempPath, result: view.text, resultCards: view.cards });
|
||||
} catch (e) {
|
||||
const view = buildResultView('下载失败');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
},
|
||||
async listTasks() {
|
||||
const qs = [];
|
||||
if (this.data.status) qs.push(`status=${encodeURIComponent(this.data.status)}`);
|
||||
if (this.data.taskBookId) qs.push(`book_id=${encodeURIComponent(this.data.taskBookId)}`);
|
||||
qs.push(`page=${encodeURIComponent(this.data.page)}`);
|
||||
qs.push(`pageSize=${encodeURIComponent(this.data.pageSize)}`);
|
||||
const result = await request({ url: `/api/console/read_task?${qs.join('&')}` });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async createTask() {
|
||||
const data = {
|
||||
book_id: Number(this.data.taskBookId),
|
||||
days: this.data.days ? Number(this.data.days) : undefined,
|
||||
push_times: this.data.push_times,
|
||||
channel: this.data.channel,
|
||||
email: this.data.email,
|
||||
wechat_remark: this.data.wechat_remark,
|
||||
auto_start: true
|
||||
};
|
||||
const result = await request({ url: '/api/console/read_task', method: 'POST', data });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async updateTask() {
|
||||
const data = {
|
||||
status: undefined,
|
||||
days: this.data.days ? Number(this.data.days) : undefined,
|
||||
push_times: this.data.push_times,
|
||||
channel: this.data.channel,
|
||||
email: this.data.email,
|
||||
wechat_remark: this.data.wechat_remark,
|
||||
auto_start: true
|
||||
};
|
||||
const result = await request({ url: `/api/console/read_task/${encodeURIComponent(this.data.taskId)}`, method: 'PUT', data });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async deleteTask() {
|
||||
const result = await request({ url: `/api/console/read_task/${encodeURIComponent(this.data.taskId)}`, method: 'DELETE' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async pushOnce() {
|
||||
const result = await request({ url: `/api/console/read_task/${encodeURIComponent(this.data.taskId)}/push_once`, method: 'POST', data: {} });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async previewNext() {
|
||||
const result = await request({ url: `/api/console/read_task/${encodeURIComponent(this.data.taskId)}/next` });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "读书任务"
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">书籍管理</view>
|
||||
<view class="page-subtitle">列表、导入、预览与读书任务</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">书籍列表</view>
|
||||
<view class="section-title">状态/关键词</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="status" value="{{status}}" bindinput="onInput" />
|
||||
<input class="input" data-key="keyword" value="{{keyword}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">页码/每页</view>
|
||||
<view class="row">
|
||||
<input class="input" type="number" data-key="page" value="{{page}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="pageSize" value="{{pageSize}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row pagination">
|
||||
<view class="badge">页码 {{page || 1}}</view>
|
||||
<view class="badge">每页 {{pageSize || 10}}</view>
|
||||
</view>
|
||||
<view class="section-hint">修改上方页码/每页后点击查询</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="listBooks">查询</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">导入书籍</view>
|
||||
<view class="section-title">服务器路径</view>
|
||||
<input class="input" data-key="path" value="{{path}}" bindinput="onInput" />
|
||||
<view class="section-title">创建人</view>
|
||||
<input class="input" data-key="creator" value="{{creator}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="importByPath">路径导入</button>
|
||||
<button class="btn btn-secondary" bindtap="chooseFile">选择PDF</button>
|
||||
<button class="btn" bindtap="uploadPDF">上传导入</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">书籍详情/预览</view>
|
||||
<view class="section-title">书籍ID/页码</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="bookId" value="{{bookId}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="bookPage" value="{{bookPage}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="bookDetail">详情</button>
|
||||
<button class="btn btn-secondary" bindtap="previewPage">预览页</button>
|
||||
<button class="btn btn-danger" bindtap="deleteBook">删除书籍</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">读书任务</view>
|
||||
<view class="section-title">书籍ID/任务ID</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="taskBookId" value="{{taskBookId}}" bindinput="onInput" placeholder="book_id" />
|
||||
<input class="input" data-key="taskId" value="{{taskId}}" bindinput="onInput" placeholder="task_id" />
|
||||
</view>
|
||||
<view class="section-title">天数/推送时间</view>
|
||||
<view class="row">
|
||||
<input class="input" type="number" data-key="days" value="{{days}}" bindinput="onInput" />
|
||||
<input class="input" data-key="push_times" value="{{push_times}}" bindinput="onInput" placeholder="08:00,20:00" />
|
||||
</view>
|
||||
<view class="section-title">渠道/邮箱/微信备注</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="channel" value="{{channel}}" bindinput="onInput" />
|
||||
<input class="input" data-key="email" value="{{email}}" bindinput="onInput" />
|
||||
</view>
|
||||
<input class="input" data-key="wechat_remark" value="{{wechat_remark}}" bindinput="onInput" placeholder="微信备注" />
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="listTasks">任务列表</button>
|
||||
<button class="btn" bindtap="createTask">创建任务</button>
|
||||
<button class="btn btn-secondary" bindtap="updateTask">更新任务</button>
|
||||
<button class="btn btn-secondary" bindtap="pushOnce">手动推送</button>
|
||||
<button class="btn btn-secondary" bindtap="previewNext">预览下一批</button>
|
||||
<button class="btn btn-danger" bindtap="deleteTask">删除任务</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card" wx:if="{{imagePath}}">
|
||||
<view class="card-title">预览</view>
|
||||
<image class="preview-image" mode="widthFix" src="{{imagePath}}" />
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
const { modules } = require('../../utils/console_ops');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
modules: [],
|
||||
routeMap: {
|
||||
password: '/pages/console/password',
|
||||
finance: '/pages/console/finance',
|
||||
oil: '/pages/console/oil',
|
||||
electric: '/pages/console/electric',
|
||||
payrecord: '/pages/console/payrecord',
|
||||
dayrecord: '/pages/console/dayrecord',
|
||||
remind: '/pages/console/remind',
|
||||
report: '/pages/console/report',
|
||||
ai: '/pages/console/ai',
|
||||
didi: '/pages/console/didi',
|
||||
qianggou: '/pages/console/qianggou',
|
||||
system: '/pages/console/system',
|
||||
search: '/pages/console/search',
|
||||
books: '/pages/console/books'
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.setData({ modules });
|
||||
},
|
||||
goModule(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
const route = this.data.routeMap[key] || `/pages/console/tool?module=${key}`;
|
||||
wx.navigateTo({ url: route });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "功能控制台"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">控制台</view>
|
||||
<view class="page-subtitle">管理与工具集合</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">控制台模块</view>
|
||||
<view class="grid">
|
||||
<view wx:for="{{modules}}" wx:key="key" class="card card--inner module-card" style="margin:0;" data-key="{{item.key}}" bindtap="goModule">
|
||||
<view class="module-title">{{item.title}}</view>
|
||||
<view wx:if="{{item.desc || item.description}}" class="module-desc">{{item.desc || item.description}}</view>
|
||||
<view class="badge">进入</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
content: '',
|
||||
range: 'week',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
start: '',
|
||||
end: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async createRecord() {
|
||||
if (!this.data.content) {
|
||||
const view = buildResultView('请输入日记内容');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/dayrecord',
|
||||
method: 'POST',
|
||||
data: { content: this.data.content }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards, content: '' });
|
||||
},
|
||||
async loadList() {
|
||||
const qs = [];
|
||||
if (this.data.range) qs.push(`range=${encodeURIComponent(this.data.range)}`);
|
||||
if (this.data.page) qs.push(`page=${encodeURIComponent(this.data.page)}`);
|
||||
if (this.data.pageSize) qs.push(`pageSize=${encodeURIComponent(this.data.pageSize)}`);
|
||||
if (this.data.start) qs.push(`start=${encodeURIComponent(this.data.start)}`);
|
||||
if (this.data.end) qs.push(`end=${encodeURIComponent(this.data.end)}`);
|
||||
const url = `/api/console/dayrecord?${qs.join('&')}`;
|
||||
const result = await request({ url });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async analyze() {
|
||||
const result = await request({
|
||||
url: '/api/console/dayrecord/analyze',
|
||||
method: 'POST',
|
||||
data: {
|
||||
range_type: this.data.range,
|
||||
start_date: this.data.start,
|
||||
end_date: this.data.end
|
||||
}
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async loadAnalysisHistory() {
|
||||
const result = await request({
|
||||
url: `/api/console/dayrecord/analysis-history?page=${encodeURIComponent(this.data.page)}&pageSize=5`
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "日记管理"
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">日记</view>
|
||||
<view class="page-subtitle">记录、查询与分析</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">新增日记</view>
|
||||
<textarea class="textarea" data-key="content" value="{{content}}" bindinput="onInput" placeholder="请输入日记内容"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="createRecord">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">查询</view>
|
||||
<view class="section-title">范围(week/month/custom)</view>
|
||||
<input class="input" data-key="range" value="{{range}}" bindinput="onInput" />
|
||||
<view class="section-title">页码/每页</view>
|
||||
<view class="row">
|
||||
<input class="input" type="number" data-key="page" value="{{page}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="pageSize" value="{{pageSize}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row pagination">
|
||||
<view class="badge">页码 {{page || 1}}</view>
|
||||
<view class="badge">每页 {{pageSize || 10}}</view>
|
||||
</view>
|
||||
<view class="section-hint">修改上方页码/每页后点击查询日记</view>
|
||||
<view class="section-title">开始/结束日期</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="start" value="{{start}}" bindinput="onInput" />
|
||||
<input class="input" data-key="end" value="{{end}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="loadList">查询日记</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">AI 分析</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="analyze">分析</button>
|
||||
<button class="btn btn-secondary" bindtap="loadAnalysisHistory">分析历史</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
token: '',
|
||||
email: '',
|
||||
saleTime: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async setToken() {
|
||||
const result = await request({
|
||||
url: '/api/console/didi/token',
|
||||
method: 'POST',
|
||||
data: { token: this.data.token }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async goods() {
|
||||
const result = await request({ url: '/api/console/didi/goods' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async start() {
|
||||
const result = await request({
|
||||
url: '/api/console/didi/start',
|
||||
method: 'POST',
|
||||
data: { email: this.data.email }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async stop() {
|
||||
const result = await request({ url: '/api/console/didi/stop', method: 'POST', data: {} });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async status() {
|
||||
const result = await request({ url: '/api/console/didi/status' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async check() {
|
||||
const result = await request({ url: '/api/console/didi/check' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async seckill() {
|
||||
const result = await request({
|
||||
url: '/api/console/didi/seckill',
|
||||
method: 'POST',
|
||||
data: { saleTime: Number(this.data.saleTime), email: this.data.email }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "滴滴秒杀"
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">调度/商品</view>
|
||||
<view class="page-subtitle">Token 校验与秒杀调度</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">Token</view>
|
||||
<textarea class="textarea" data-key="token" value="{{token}}" bindinput="onInput" placeholder="请输入Token"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="setToken">设置Token</button>
|
||||
<button class="btn btn-secondary" bindtap="check">校验</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">调度/商品</view>
|
||||
<view class="section-title">邮箱(可选)</view>
|
||||
<input class="input" data-key="email" value="{{email}}" bindinput="onInput" />
|
||||
<view class="section-title">秒杀时间(秒)</view>
|
||||
<input class="input" type="number" data-key="saleTime" value="{{saleTime}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="goods">商品列表</button>
|
||||
<button class="btn btn-secondary" bindtap="start">启动调度</button>
|
||||
<button class="btn btn-secondary" bindtap="stop">停止调度</button>
|
||||
<button class="btn btn-secondary" bindtap="status">状态</button>
|
||||
<button class="btn btn-secondary" bindtap="seckill">手动秒杀</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
province: '',
|
||||
power: '',
|
||||
hours: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async calculate() {
|
||||
if (!this.data.power || !this.data.hours) {
|
||||
const view = buildResultView('请填写完整参数');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/electric',
|
||||
method: 'POST',
|
||||
data: {
|
||||
province: this.data.province,
|
||||
power: this.data.power,
|
||||
hours: this.data.hours
|
||||
}
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "电费计算"
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">电费计算</view>
|
||||
<view class="page-subtitle">按功率与时长估算</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">电费计算</view>
|
||||
<view class="section-title">省份</view>
|
||||
<input class="input" data-key="province" value="{{province}}" bindinput="onInput" />
|
||||
<view class="section-title">功率</view>
|
||||
<input class="input" data-key="power" value="{{power}}" bindinput="onInput" />
|
||||
<view class="section-title">使用时长</view>
|
||||
<input class="input" type="number" data-key="hours" value="{{hours}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="calculate">计算</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
rate: '',
|
||||
money: '',
|
||||
days: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async calculate() {
|
||||
const { rate, money, days } = this.data;
|
||||
if (!rate || !money || !days) {
|
||||
const view = buildResultView('请填写完整参数');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/finance',
|
||||
method: 'POST',
|
||||
data: { rate, money, days }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "理财计算"
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">理财计算</view>
|
||||
<view class="page-subtitle">按利率与本金估算</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">理财计算</view>
|
||||
<view class="section-title">利率</view>
|
||||
<input class="input" data-key="rate" value="{{rate}}" bindinput="onInput" />
|
||||
<view class="section-title">本金</view>
|
||||
<input class="input" data-key="money" value="{{money}}" bindinput="onInput" />
|
||||
<view class="section-title">天数</view>
|
||||
<input class="input" type="number" data-key="days" value="{{days}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="calculate">计算</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
province: '',
|
||||
oilType: '',
|
||||
consumption: '',
|
||||
distance: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async calculate() {
|
||||
if (!this.data.distance) {
|
||||
const view = buildResultView('请输入行驶公里数');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/oil',
|
||||
method: 'POST',
|
||||
data: {
|
||||
province: this.data.province,
|
||||
oilType: this.data.oilType,
|
||||
consumption: this.data.consumption,
|
||||
distance: this.data.distance
|
||||
}
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "油费计算"
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">油费计算</view>
|
||||
<view class="page-subtitle">里程与油耗估算</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">油费计算</view>
|
||||
<view class="section-title">省份</view>
|
||||
<input class="input" data-key="province" value="{{province}}" bindinput="onInput" />
|
||||
<view class="section-title">油号</view>
|
||||
<input class="input" data-key="oilType" value="{{oilType}}" bindinput="onInput" />
|
||||
<view class="section-title">油耗</view>
|
||||
<input class="input" data-key="consumption" value="{{consumption}}" bindinput="onInput" />
|
||||
<view class="section-title">里程(km)</view>
|
||||
<input class="input" type="number" data-key="distance" value="{{distance}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="calculate">计算</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
length: 16,
|
||||
type: 'mix',
|
||||
plaintext: '',
|
||||
ciphertext: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async generate() {
|
||||
const result = await request({
|
||||
url: '/api/console/password/generate',
|
||||
method: 'POST',
|
||||
data: { length: Number(this.data.length) || 16, type: this.data.type || 'mix' }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async encrypt() {
|
||||
if (!this.data.plaintext) {
|
||||
const view = buildResultView('请输入明文');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/password/encrypt',
|
||||
method: 'POST',
|
||||
data: { plaintext: this.data.plaintext }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async decrypt() {
|
||||
if (!this.data.ciphertext) {
|
||||
const view = buildResultView('请输入密文');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/password/decrypt',
|
||||
method: 'POST',
|
||||
data: { ciphertext: this.data.ciphertext }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "密码工具"
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">安全工具</view>
|
||||
<view class="page-subtitle">生成密码与加解密</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">生成密码</view>
|
||||
<view class="section-title">长度</view>
|
||||
<input class="input" type="number" data-key="length" value="{{length}}" bindinput="onInput" />
|
||||
<view class="section-title">类型(mix/num/alpha)</view>
|
||||
<input class="input" data-key="type" value="{{type}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="generate">生成</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">加密/解密</view>
|
||||
<view class="section-title">明文</view>
|
||||
<textarea class="textarea" data-key="plaintext" value="{{plaintext}}" bindinput="onInput" placeholder="请输入明文"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="encrypt">加密</button>
|
||||
</view>
|
||||
<view class="section-title" style="margin-top:20rpx;">密文</view>
|
||||
<textarea class="textarea" data-key="ciphertext" value="{{ciphertext}}" bindinput="onInput" placeholder="请输入密文"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="decrypt">解密</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
records: '',
|
||||
limit: 10,
|
||||
range: 'week',
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
start: '',
|
||||
end: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async createRecord() {
|
||||
if (!this.data.records) {
|
||||
const view = buildResultView('请输入记账内容');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/payrecord',
|
||||
method: 'POST',
|
||||
data: { records: this.data.records }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards, records: '' });
|
||||
},
|
||||
async loadList() {
|
||||
const result = await request({
|
||||
url: `/api/console/payrecord?limit=${encodeURIComponent(this.data.limit)}`
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async loadStats() {
|
||||
const qs = [];
|
||||
if (this.data.range) qs.push(`range=${encodeURIComponent(this.data.range)}`);
|
||||
if (this.data.page) qs.push(`page=${encodeURIComponent(this.data.page)}`);
|
||||
if (this.data.pageSize) qs.push(`pageSize=${encodeURIComponent(this.data.pageSize)}`);
|
||||
if (this.data.start) qs.push(`start=${encodeURIComponent(this.data.start)}`);
|
||||
if (this.data.end) qs.push(`end=${encodeURIComponent(this.data.end)}`);
|
||||
const url = `/api/console/payrecord/stats?${qs.join('&')}`;
|
||||
const result = await request({ url });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "记账管理"
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">账单</view>
|
||||
<view class="page-subtitle">新增、列表与统计</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">新增记账</view>
|
||||
<textarea class="textarea" data-key="records" value="{{records}}" bindinput="onInput" placeholder="请输入记账内容"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="createRecord">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">列表</view>
|
||||
<view class="section-title">条数限制</view>
|
||||
<input class="input" type="number" data-key="limit" value="{{limit}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="loadList">查询列表</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">统计</view>
|
||||
<view class="section-title">范围(week/month/custom)</view>
|
||||
<input class="input" data-key="range" value="{{range}}" bindinput="onInput" />
|
||||
<view class="section-title">页码/每页</view>
|
||||
<view class="row">
|
||||
<input class="input" type="number" data-key="page" value="{{page}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="pageSize" value="{{pageSize}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row pagination">
|
||||
<view class="badge">页码 {{page || 1}}</view>
|
||||
<view class="badge">每页 {{pageSize || 10}}</view>
|
||||
</view>
|
||||
<view class="section-hint">修改上方页码/每页后点击查询统计</view>
|
||||
<view class="section-title">开始/结束日期(YYYY-MM-DD)</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="start" value="{{start}}" bindinput="onInput" />
|
||||
<input class="input" data-key="end" value="{{end}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="loadStats">查询统计</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
action: 'start',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
this.setData({ action: e.detail.value });
|
||||
},
|
||||
async status() {
|
||||
const result = await request({ url: '/api/console/qianggou' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async setAction() {
|
||||
const result = await request({
|
||||
url: '/api/console/qianggou',
|
||||
method: 'POST',
|
||||
data: { action: this.data.action }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "抢购控制"
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">抢购控制</view>
|
||||
<view class="page-subtitle">操作与状态管理</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">抢购控制</view>
|
||||
<view class="section-title">动作(start/stop)</view>
|
||||
<input class="input" value="{{action}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="setAction">执行</button>
|
||||
<button class="btn btn-secondary" bindtap="status">状态</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
content: '',
|
||||
email: '',
|
||||
id: '',
|
||||
timeTodo: '',
|
||||
things: '',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
minutes: 30,
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async createRemind() {
|
||||
if (!this.data.content) {
|
||||
const view = buildResultView('请输入提醒内容');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const result = await request({
|
||||
url: '/api/console/remind',
|
||||
method: 'POST',
|
||||
data: { content: this.data.content, email: this.data.email }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards, content: '' });
|
||||
},
|
||||
async loadList() {
|
||||
const result = await request({
|
||||
url: `/api/console/remind?page=${encodeURIComponent(this.data.page)}&pageSize=${encodeURIComponent(this.data.pageSize)}`
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async updateRemind() {
|
||||
const result = await request({
|
||||
url: '/api/console/remind',
|
||||
method: 'PUT',
|
||||
data: { id: Number(this.data.id), timeTodo: this.data.timeTodo, things: this.data.things }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async deleteRemind() {
|
||||
const result = await request({
|
||||
url: `/api/console/remind?id=${encodeURIComponent(this.data.id)}`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async doneRemind() {
|
||||
const result = await request({
|
||||
url: `/api/console/remind/${encodeURIComponent(this.data.id)}/done`,
|
||||
method: 'PUT'
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async delayRemind() {
|
||||
const result = await request({
|
||||
url: `/api/console/remind/${encodeURIComponent(this.data.id)}/delay`,
|
||||
method: 'PUT',
|
||||
data: { minutes: Number(this.data.minutes) || 30 }
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async archiveRemind() {
|
||||
const result = await request({
|
||||
url: `/api/console/remind/${encodeURIComponent(this.data.id)}/archive`,
|
||||
method: 'PUT'
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async history() {
|
||||
const result = await request({
|
||||
url: `/api/console/remind/history?action=archive&page=${encodeURIComponent(this.data.page)}&pageSize=${encodeURIComponent(this.data.pageSize)}`
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "提醒任务"
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">提醒事项</view>
|
||||
<view class="page-subtitle">创建、管理与历史</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">创建提醒</view>
|
||||
<textarea class="textarea" data-key="content" value="{{content}}" bindinput="onInput" placeholder="提醒内容(需含'提醒我')"></textarea>
|
||||
<view class="section-title">邮箱(可选)</view>
|
||||
<input class="input" data-key="email" value="{{email}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="createRemind">创建</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">管理</view>
|
||||
<view class="section-title">任务ID</view>
|
||||
<input class="input" data-key="id" value="{{id}}" bindinput="onInput" />
|
||||
<view class="section-title">提醒时间/事项</view>
|
||||
<input class="input" data-key="timeTodo" value="{{timeTodo}}" bindinput="onInput" placeholder="提醒时间" />
|
||||
<input class="input" data-key="things" value="{{things}}" bindinput="onInput" placeholder="提醒事项" />
|
||||
<view class="section-title">延期分钟</view>
|
||||
<input class="input" type="number" data-key="minutes" value="{{minutes}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="updateRemind">更新</button>
|
||||
<button class="btn btn-secondary" bindtap="doneRemind">完成</button>
|
||||
<button class="btn btn-secondary" bindtap="delayRemind">延期</button>
|
||||
<button class="btn btn-secondary" bindtap="archiveRemind">归档</button>
|
||||
<button class="btn btn-danger" bindtap="deleteRemind">删除</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">列表/历史</view>
|
||||
<view class="section-title">页码/每页</view>
|
||||
<view class="row">
|
||||
<input class="input" type="number" data-key="page" value="{{page}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="pageSize" value="{{pageSize}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row pagination">
|
||||
<view class="badge">页码 {{page || 1}}</view>
|
||||
<view class="badge">每页 {{pageSize || 10}}</view>
|
||||
</view>
|
||||
<view class="section-hint">修改上方页码/每页后点击列表或历史</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="loadList">列表</button>
|
||||
<button class="btn btn-secondary" bindtap="history">历史</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
type: 'week',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
include: 'diary,pay,remind',
|
||||
mask_money: 'false',
|
||||
mask_names: 'false',
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async generate() {
|
||||
const body = {
|
||||
type: this.data.type,
|
||||
start_date: this.data.start_date,
|
||||
end_date: this.data.end_date,
|
||||
include: this.data.include ? this.data.include.split(',').map(s => s.trim()).filter(Boolean) : [],
|
||||
mask: {
|
||||
money: String(this.data.mask_money).toLowerCase() === 'true',
|
||||
names: String(this.data.mask_names).toLowerCase() === 'true'
|
||||
}
|
||||
};
|
||||
const result = await request({
|
||||
url: '/api/console/report/generate',
|
||||
method: 'POST',
|
||||
data: body
|
||||
});
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async history() {
|
||||
const qs = [];
|
||||
if (this.data.type) qs.push(`type=${encodeURIComponent(this.data.type)}`);
|
||||
qs.push(`page=${encodeURIComponent(this.data.page)}`);
|
||||
qs.push(`pageSize=${encodeURIComponent(this.data.pageSize)}`);
|
||||
const result = await request({ url: `/api/console/report/history?${qs.join('&')}` });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "报告中心"
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">报告</view>
|
||||
<view class="page-subtitle">生成与历史查询</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">生成报告</view>
|
||||
<view class="section-title">类型(week/month/custom)</view>
|
||||
<input class="input" data-key="type" value="{{type}}" bindinput="onInput" />
|
||||
<view class="section-title">开始/结束日期(自定义)</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="start_date" value="{{start_date}}" bindinput="onInput" />
|
||||
<input class="input" data-key="end_date" value="{{end_date}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">包含内容(逗号分隔)</view>
|
||||
<input class="input" data-key="include" value="{{include}}" bindinput="onInput" />
|
||||
<view class="section-title">脱敏金额/姓名(true/false)</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="mask_money" value="{{mask_money}}" bindinput="onInput" />
|
||||
<input class="input" data-key="mask_names" value="{{mask_names}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="generate">生成</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">历史记录</view>
|
||||
<view class="section-title">页码/每页</view>
|
||||
<view class="row">
|
||||
<input class="input" type="number" data-key="page" value="{{page}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="pageSize" value="{{pageSize}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row pagination">
|
||||
<view class="badge">页码 {{page || 1}}</view>
|
||||
<view class="badge">每页 {{pageSize || 10}}</view>
|
||||
</view>
|
||||
<view class="section-hint">修改上方页码/每页后点击查询历史</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="history">查询历史</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
q: '',
|
||||
type: '',
|
||||
start: '',
|
||||
end: '',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
async search() {
|
||||
if (!this.data.q) {
|
||||
const view = buildResultView('请输入关键词');
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const qs = [];
|
||||
qs.push(`q=${encodeURIComponent(this.data.q)}`);
|
||||
if (this.data.type) qs.push(`type=${encodeURIComponent(this.data.type)}`);
|
||||
if (this.data.start) qs.push(`start=${encodeURIComponent(this.data.start)}`);
|
||||
if (this.data.end) qs.push(`end=${encodeURIComponent(this.data.end)}`);
|
||||
qs.push(`page=${encodeURIComponent(this.data.page)}`);
|
||||
qs.push(`pageSize=${encodeURIComponent(this.data.pageSize)}`);
|
||||
const result = await request({ url: `/api/console/search?${qs.join('&')}` });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "全局搜索"
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">全局搜索</view>
|
||||
<view class="page-subtitle">按条件查询数据</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">全局搜索</view>
|
||||
<view class="section-title">关键词</view>
|
||||
<input class="input" data-key="q" value="{{q}}" bindinput="onInput" />
|
||||
<view class="section-title">类型</view>
|
||||
<input class="input" data-key="type" value="{{type}}" bindinput="onInput" />
|
||||
<view class="section-title">开始/结束日期</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="start" value="{{start}}" bindinput="onInput" />
|
||||
<input class="input" data-key="end" value="{{end}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">页码/每页</view>
|
||||
<view class="row">
|
||||
<input class="input" type="number" data-key="page" value="{{page}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="pageSize" value="{{pageSize}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row pagination">
|
||||
<view class="badge">页码 {{page || 1}}</view>
|
||||
<view class="badge">每页 {{pageSize || 10}}</view>
|
||||
</view>
|
||||
<view class="section-hint">修改上方页码/每页后点击搜索</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="search">搜索</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
async ping() {
|
||||
const result = await request({ url: '/api/console/ping' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async features() {
|
||||
const result = await request({ url: '/api/console/features' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
},
|
||||
async status() {
|
||||
const result = await request({ url: '/api/console/status' });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ result: view.text, resultCards: view.cards });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "系统状态"
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">系统状态</view>
|
||||
<view class="page-subtitle">探活与功能状态</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">系统状态</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="ping">探活</button>
|
||||
<button class="btn btn-secondary" bindtap="features">功能列表</button>
|
||||
<button class="btn btn-secondary" bindtap="status">系统状态</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
const { modules } = require('../../utils/console_ops');
|
||||
const { request, uploadFile, downloadFile, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
moduleKey: '',
|
||||
moduleTitle: '',
|
||||
ops: [],
|
||||
opIndex: 0,
|
||||
formData: {},
|
||||
resultText: '',
|
||||
resultTextCards: [],
|
||||
filePath: '',
|
||||
imagePath: ''
|
||||
},
|
||||
onLoad(options) {
|
||||
const moduleKey = options.module || '';
|
||||
const module = modules.find(m => m.key === moduleKey) || modules[0];
|
||||
this.setData({
|
||||
moduleKey: module ? module.key : '',
|
||||
moduleTitle: module ? module.title : '',
|
||||
ops: module ? module.ops : []
|
||||
});
|
||||
this.resetForm();
|
||||
},
|
||||
onOpChange(e) {
|
||||
this.setData({ opIndex: Number(e.detail.value) || 0, resultText: '', resultTextCards: [], imagePath: '' });
|
||||
this.resetForm();
|
||||
},
|
||||
resetForm() {
|
||||
const op = this.currentOp();
|
||||
const formData = {};
|
||||
if (op && op.fields) {
|
||||
op.fields.forEach(f => {
|
||||
if (f.default !== undefined) formData[f.key] = f.default;
|
||||
});
|
||||
}
|
||||
this.setData({ formData, filePath: '' });
|
||||
},
|
||||
currentOp() {
|
||||
return this.data.ops[this.data.opIndex];
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
const value = e.detail.value;
|
||||
const formData = { ...this.data.formData, [key]: value };
|
||||
this.setData({ formData });
|
||||
},
|
||||
async chooseFile() {
|
||||
try {
|
||||
const res = await wx.chooseMessageFile({ count: 1, type: 'file' });
|
||||
const file = res.tempFiles && res.tempFiles[0];
|
||||
if (file && file.path) {
|
||||
this.setData({ filePath: file.path });
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
buildUrl(op, formData) {
|
||||
let url = op.path;
|
||||
const query = [];
|
||||
if (op.fields) {
|
||||
op.fields.forEach(f => {
|
||||
const val = formData[f.key];
|
||||
if (f.in === 'path' && val !== undefined && val !== '') {
|
||||
url = url.replace(`{${f.key}}`, encodeURIComponent(String(val)));
|
||||
}
|
||||
if (f.in === 'query' && val !== undefined && val !== '') {
|
||||
query.push(`${encodeURIComponent(f.key)}=${encodeURIComponent(String(val))}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (query.length > 0) {
|
||||
url += (url.includes('?') ? '&' : '?') + query.join('&');
|
||||
}
|
||||
return url;
|
||||
},
|
||||
buildBody(op, formData) {
|
||||
if (!op.fields) return {};
|
||||
const body = {};
|
||||
op.fields.forEach(f => {
|
||||
if (f.in === 'path' || f.in === 'query') return;
|
||||
if (f.key === 'body_json' && formData.body_json) return;
|
||||
const val = formData[f.key];
|
||||
if (val !== undefined && val !== '') {
|
||||
body[f.key] = val;
|
||||
}
|
||||
});
|
||||
if (formData.body_json) {
|
||||
try {
|
||||
const parsed = JSON.parse(formData.body_json);
|
||||
return parsed;
|
||||
} catch (e) {}
|
||||
}
|
||||
if (body.include && typeof body.include === 'string') {
|
||||
body.include = body.include.split(',').map(s => s.trim()).filter(Boolean);
|
||||
}
|
||||
if (body.mask_money || body.mask_names) {
|
||||
body.mask = {
|
||||
money: String(body.mask_money).toLowerCase() === 'true',
|
||||
names: String(body.mask_names).toLowerCase() === 'true'
|
||||
};
|
||||
delete body.mask_money;
|
||||
delete body.mask_names;
|
||||
}
|
||||
if (body.auto_start !== undefined) {
|
||||
body.auto_start = String(body.auto_start).toLowerCase() === 'true';
|
||||
}
|
||||
if (body.saleTime) {
|
||||
body.saleTime = Number(body.saleTime);
|
||||
}
|
||||
return body;
|
||||
},
|
||||
async runOp() {
|
||||
const op = this.currentOp();
|
||||
if (!op) return;
|
||||
const formData = this.data.formData || {};
|
||||
this.setData({ resultText: '', resultTextCards: [], imagePath: '' });
|
||||
|
||||
if (op.type === 'upload') {
|
||||
if (!this.data.filePath) {
|
||||
const view = buildResultView('请先选择文件');
|
||||
this.setData({ resultText: view.text, resultTextCards: view.cards });
|
||||
return;
|
||||
}
|
||||
const formDataPayload = { ...formData };
|
||||
delete formDataPayload.file;
|
||||
const result = await uploadFile({ url: op.path, filePath: this.data.filePath, name: 'file', formData: formDataPayload });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ resultText: view.text, resultTextCards: view.cards });
|
||||
return;
|
||||
}
|
||||
|
||||
const url = this.buildUrl(op, formData);
|
||||
if (op.responseType === 'file') {
|
||||
try {
|
||||
const tempPath = await downloadFile({ url });
|
||||
const view = buildResultView('下载成功');
|
||||
this.setData({ imagePath: tempPath, resultText: view.text, resultTextCards: view.cards });
|
||||
} catch (e) {
|
||||
const view = buildResultView('下载失败');
|
||||
this.setData({ resultText: view.text, resultTextCards: view.cards });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const body = this.buildBody(op, formData);
|
||||
try {
|
||||
const result = await request({ url, method: op.method, data: op.method === 'GET' ? undefined : body });
|
||||
const view = buildResultView(result);
|
||||
this.setData({ resultText: view.text, resultTextCards: view.cards });
|
||||
} catch (e) {
|
||||
const view = buildResultView(e.message || '请求失败');
|
||||
this.setData({ resultText: view.text, resultTextCards: view.cards });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "控制台操作"
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">通用工具</view>
|
||||
<view class="page-subtitle">动态参数与执行结果</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">{{moduleTitle}}</view>
|
||||
<picker mode="selector" range="{{ops}}" range-key="name" value="{{opIndex}}" bindchange="onOpChange">
|
||||
<view class="input">{{ops[opIndex].name}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="card" wx:if="{{ops.length}}">
|
||||
<view class="card-title">参数</view>
|
||||
<block wx:for="{{ops[opIndex].fields}}" wx:key="key">
|
||||
<view class="section-title">{{item.label}}</view>
|
||||
<textarea wx:if="{{item.type === 'textarea' || item.type === 'json'}}" class="textarea" data-key="{{item.key}}" bindinput="onInput" placeholder="请输入{{item.label}}" value="{{formData[item.key]}}"></textarea>
|
||||
<input wx:elif="{{item.type === 'number'}}" class="input" data-key="{{item.key}}" bindinput="onInput" type="number" placeholder="请输入{{item.label}}" value="{{formData[item.key]}}" />
|
||||
<input wx:elif="{{item.type === 'text'}}" class="input" data-key="{{item.key}}" bindinput="onInput" placeholder="请输入{{item.label}}" value="{{formData[item.key]}}" />
|
||||
<view wx:elif="{{item.type === 'file'}}" class="row file-row">
|
||||
<button class="btn btn-secondary" bindtap="chooseFile">选择文件</button>
|
||||
<text class="file-name">{{filePath || '未选择'}}</text>
|
||||
</view>
|
||||
</block>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="runOp">执行</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{resultText}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">执行结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</view>
|
||||
<view wx:if="{{resultTextCards.length}}" class="list">
|
||||
<view wx:for="{{resultTextCards}}" 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 class="result">{{resultText}}</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{imagePath}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">预览</view>
|
||||
<view class="badge">图片</view>
|
||||
</view>
|
||||
<image class="preview-image" mode="widthFix" src="{{imagePath}}" />
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
const { request } = require('../../utils/request');
|
||||
|
||||
const DEFAULT_MODULES = [
|
||||
{ name: '功能控制台', path: '/pages/console/console' },
|
||||
{ name: '提示词库', path: '/pages/prompt/prompt' }
|
||||
];
|
||||
|
||||
const TAB_PAGES = new Set([
|
||||
'/pages/home/home',
|
||||
'/pages/console/console',
|
||||
'/pages/prompt/prompt'
|
||||
]);
|
||||
|
||||
Page({
|
||||
data: {
|
||||
modules: DEFAULT_MODULES
|
||||
},
|
||||
onLoad() {
|
||||
this.loadModules();
|
||||
},
|
||||
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 });
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
goModule(e) {
|
||||
const path = e.currentTarget.dataset.path;
|
||||
if (!path) return;
|
||||
if (TAB_PAGES.has(path)) {
|
||||
wx.switchTab({ url: path });
|
||||
return;
|
||||
}
|
||||
wx.navigateTo({ url: path });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "首页"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">首页</view>
|
||||
<view class="page-subtitle">常用功能快速入口</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">功能入口</view>
|
||||
<view class="grid">
|
||||
<view wx:for="{{modules}}" wx:key="name" class="card card--inner module-card" style="margin:0;" data-path="{{item.path}}" bindtap="goModule">
|
||||
<view class="module-title">{{item.name || item.title || '模块'}}</view>
|
||||
<view wx:if="{{item.desc || item.description}}" class="module-desc">{{item.desc || item.description}}</view>
|
||||
<view class="badge">进入</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1 @@
|
|||
.grid .module-card { padding: 20rpx; }
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
const { loginWithWeChat } = require('../../utils/auth');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
loading: false,
|
||||
error: ''
|
||||
},
|
||||
async handleLogin() {
|
||||
this.setData({ loading: true, error: '' });
|
||||
try {
|
||||
await loginWithWeChat(null);
|
||||
wx.reLaunch({ url: '/pages/home/home' });
|
||||
} catch (err) {
|
||||
this.setData({ error: err.message || '登录失败' });
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
async handleLoginWithProfile() {
|
||||
this.setData({ loading: true, error: '' });
|
||||
wx.getUserProfile({
|
||||
desc: '用于完善用户资料',
|
||||
success: async (res) => {
|
||||
try {
|
||||
await loginWithWeChat(res.userInfo || null);
|
||||
wx.reLaunch({ url: '/pages/home/home' });
|
||||
} catch (err) {
|
||||
this.setData({ error: err.message || '登录失败' });
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
this.setData({ loading: false, error: '未授权用户信息' });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">欢迎使用</view>
|
||||
<view class="page-subtitle">登录后可同步并管理你的工作台</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">微信登录</view>
|
||||
<view class="section-title">请选择登录方式</view>
|
||||
<view class="row" style="flex-direction: column; gap: 16rpx;">
|
||||
<button class="btn" bindtap="handleLogin" loading="{{loading}}">微信登录</button>
|
||||
<button class="btn btn-secondary" bindtap="handleLoginWithProfile" loading="{{loading}}">授权头像昵称并登录</button>
|
||||
</view>
|
||||
<view wx:if="{{error}}" class="section-title" style="color:#ff4d4f;">{{error}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1 @@
|
|||
.container { padding-top: 80rpx; }
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
const { request } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
stats: null,
|
||||
running: []
|
||||
},
|
||||
onShow() {
|
||||
this.loadData();
|
||||
},
|
||||
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 : [])
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "仪表盘"
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">概览</view>
|
||||
<view class="page-subtitle">运行状态与任务统计</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">统计</view>
|
||||
<view wx:if="{{stats}}" class="grid">
|
||||
<view class="card card--inner module-card" style="margin:0;">
|
||||
<view class="module-title">机器人</view>
|
||||
<view class="badge">{{stats.total_robots}}</view>
|
||||
</view>
|
||||
<view class="card card--inner module-card" style="margin:0;">
|
||||
<view class="module-title">总任务</view>
|
||||
<view class="badge">{{stats.total_tasks}}</view>
|
||||
</view>
|
||||
<view class="card card--inner module-card" style="margin:0;">
|
||||
<view class="module-title">进行中</view>
|
||||
<view class="badge badge--warn">{{stats.running_tasks}}</view>
|
||||
</view>
|
||||
<view class="card card--inner module-card" style="margin:0;">
|
||||
<view class="module-title">已完成</view>
|
||||
<view class="badge badge--success">{{stats.completed_tasks}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:else>暂无统计数据</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">进行中任务</view>
|
||||
<view wx:if="{{running.length}}" class="list">
|
||||
<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>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:else>暂无进行中任务</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
content: '',
|
||||
check_only: 'false',
|
||||
overwrite: 'false',
|
||||
task_id: '',
|
||||
task_name: '',
|
||||
robot_id: '',
|
||||
result: '',
|
||||
resultCards: []
|
||||
},
|
||||
onInput(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.setData({ [key]: e.detail.value });
|
||||
},
|
||||
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 });
|
||||
},
|
||||
async confirm() {
|
||||
const payload = {
|
||||
content: this.data.content,
|
||||
check_only: String(this.data.check_only).toLowerCase() === 'true',
|
||||
overwrite: String(this.data.overwrite).toLowerCase() === 'true',
|
||||
task_id: this.data.task_id ? Number(this.data.task_id) : undefined,
|
||||
task_name: this.data.task_name,
|
||||
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 });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "对话导入"
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">导入对话</view>
|
||||
<view class="page-subtitle">批量解析与导入数据</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">对话导入</view>
|
||||
<view class="section-title">内容</view>
|
||||
<textarea class="textarea" data-key="content" value="{{content}}" bindinput="onInput"></textarea>
|
||||
<view class="section-title">check_only/overwrite(true/false)</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="check_only" value="{{check_only}}" bindinput="onInput" />
|
||||
<input class="input" data-key="overwrite" value="{{overwrite}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">任务ID/任务名/机器人ID</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="task_id" value="{{task_id}}" bindinput="onInput" />
|
||||
<input class="input" data-key="task_name" value="{{task_name}}" bindinput="onInput" />
|
||||
<input class="input" data-key="robot_id" value="{{robot_id}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="parse">解析</button>
|
||||
<button class="btn" bindtap="confirm">导入</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</view>
|
||||
<view class="badge">原始数据</view>
|
||||
</view>
|
||||
<view class="result-hint">已为你保留完整响应</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
const { modules } = require('../../utils/prompt_ops');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
modules: [],
|
||||
routeMap: {
|
||||
dashboard: '/pages/prompt/dashboard',
|
||||
robots: '/pages/prompt/robots',
|
||||
tasks: '/pages/prompt/tasks',
|
||||
templates: '/pages/prompt/templates',
|
||||
import: '/pages/prompt/import'
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.setData({ modules });
|
||||
},
|
||||
goModule(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
const route = this.data.routeMap[key] || `/pages/prompt/tool?module=${key}`;
|
||||
wx.navigateTo({ url: route });
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "提示词库"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">提示词库</view>
|
||||
<view class="page-subtitle">管理任务、机器人与模板</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">提示词库模块</view>
|
||||
<view class="grid">
|
||||
<view wx:for="{{modules}}" wx:key="key" class="card card--inner module-card" style="margin:0;" data-key="{{item.key}}" bindtap="goModule">
|
||||
<view class="module-title">{{item.title}}</view>
|
||||
<view wx:if="{{item.desc || item.description}}" class="module-desc">{{item.desc || item.description}}</view>
|
||||
<view class="badge">进入</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
const RESULT_PREVIEW_LIMIT = 240;
|
||||
|
||||
function safeParseResult(raw) {
|
||||
if (raw === null || raw === undefined) return null;
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function pickList(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 toArray(parsed) {
|
||||
if (!parsed || typeof parsed !== 'object') return [];
|
||||
if (Array.isArray(parsed)) return parsed;
|
||||
const list = pickList(parsed);
|
||||
if (list) return list;
|
||||
return [parsed];
|
||||
}
|
||||
|
||||
function isRobotLike(item) {
|
||||
if (!item || typeof item !== 'object') return false;
|
||||
return item.id !== undefined || item.name || item.description || item.avatar || item.status !== undefined;
|
||||
}
|
||||
|
||||
function buildRobotCards(parsed) {
|
||||
const list = toArray(parsed).filter(isRobotLike).slice(0, 6);
|
||||
return list.map(item => {
|
||||
const name = item.name || item.title || item.robot_name || '';
|
||||
const desc = item.description || item.desc || item.detail || '';
|
||||
return {
|
||||
id: item.id || item.robot_id || item.robotId || '',
|
||||
name: name || (item.id || item.robot_id ? `机器人 ${item.id || item.robot_id}` : '机器人'),
|
||||
status: item.status,
|
||||
avatar: item.avatar || item.avatar_url || '',
|
||||
description: desc ? String(desc).slice(0, 80) : ''
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function buildResultState(text) {
|
||||
const content = text || '';
|
||||
return { resultHasMore: content.length > RESULT_PREVIEW_LIMIT, resultExpanded: false };
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
status: '',
|
||||
avatar: '',
|
||||
body_json: '',
|
||||
result: '',
|
||||
resultCards: [],
|
||||
resultExpanded: false,
|
||||
resultHasMore: false,
|
||||
robotCards: []
|
||||
},
|
||||
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 parsed = safeParseResult(result);
|
||||
const robotCards = buildRobotCards(parsed);
|
||||
const state = buildResultState(view.text);
|
||||
this.setData({ result: view.text, resultCards: view.cards, robotCards, ...state });
|
||||
},
|
||||
async list() {
|
||||
const result = await request({ url: '/api/prompt/robots' });
|
||||
this.setResult(result);
|
||||
},
|
||||
async detail() {
|
||||
const result = await request({ url: `/api/prompt/robots/${encodeURIComponent(this.data.id)}` });
|
||||
this.setResult(result);
|
||||
},
|
||||
async create() {
|
||||
const payload = this.buildPayload();
|
||||
const result = await request({ url: '/api/prompt/robots', method: 'POST', data: payload });
|
||||
this.setResult(result);
|
||||
},
|
||||
async update() {
|
||||
const payload = this.buildPayload();
|
||||
const result = await request({ url: `/api/prompt/robots/${encodeURIComponent(this.data.id)}`, method: 'PUT', data: payload });
|
||||
this.setResult(result);
|
||||
},
|
||||
async remove() {
|
||||
const result = await request({ url: `/api/prompt/robots/${encodeURIComponent(this.data.id)}`, method: 'DELETE' });
|
||||
this.setResult(result);
|
||||
},
|
||||
buildPayload() {
|
||||
if (this.data.body_json) {
|
||||
try {
|
||||
return JSON.parse(this.data.body_json);
|
||||
} catch (e) {}
|
||||
}
|
||||
return {
|
||||
name: this.data.name,
|
||||
description: this.data.description,
|
||||
status: this.data.status ? Number(this.data.status) : undefined,
|
||||
avatar: this.data.avatar
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "机器人管理"
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">机器人管理</view>
|
||||
<view class="page-subtitle">配置机器人信息与状态</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">机器人</view>
|
||||
<view class="section-title">ID</view>
|
||||
<input class="input" data-key="id" value="{{id}}" bindinput="onInput" />
|
||||
<view class="section-title">名称/状态</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="name" value="{{name}}" bindinput="onInput" />
|
||||
<input class="input" data-key="status" value="{{status}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">描述/头像</view>
|
||||
<input class="input" data-key="description" value="{{description}}" bindinput="onInput" />
|
||||
<input class="input" data-key="avatar" value="{{avatar}}" bindinput="onInput" />
|
||||
<view class="section-title">自定义JSON(可选)</view>
|
||||
<textarea class="textarea" data-key="body_json" value="{{body_json}}" bindinput="onInput"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="list">列表</button>
|
||||
<button class="btn btn-secondary" bindtap="detail">详情</button>
|
||||
<button class="btn" bindtap="create">创建</button>
|
||||
<button class="btn btn-secondary" bindtap="update">更新</button>
|
||||
<button class="btn btn-danger" bindtap="remove">删除</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</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="{{robotCards.length}}" class="list">
|
||||
<view wx:for="{{robotCards}}" wx:for-index="idx" wx:key="idx" class="card card--inner list-item result-entity">
|
||||
<view class="row" style="justify-content: space-between;">
|
||||
<view class="row" style="gap: 12rpx;">
|
||||
<image class="result-avatar" wx:if="{{item.avatar}}" src="{{item.avatar}}" mode="aspectFill"></image>
|
||||
<view class="module-title">{{item.name}}</view>
|
||||
</view>
|
||||
<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>
|
||||
<view class="subtle" wx:if="{{item.description}}">{{item.description}}</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result {{resultExpanded ? 'result--expanded' : 'result--collapsed'}}">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
const { request, downloadFile, buildResultView } = require('../../utils/request');
|
||||
|
||||
const RESULT_PREVIEW_LIMIT = 240;
|
||||
|
||||
function safeParseResult(raw) {
|
||||
if (raw === null || raw === undefined) return null;
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function pickList(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 toArray(parsed) {
|
||||
if (!parsed || typeof parsed !== 'object') return [];
|
||||
if (Array.isArray(parsed)) return parsed;
|
||||
const list = pickList(parsed);
|
||||
if (list) return list;
|
||||
return [parsed];
|
||||
}
|
||||
|
||||
function isTaskLike(item) {
|
||||
if (!item || typeof item !== 'object') return false;
|
||||
return item.id !== undefined || item.task_id !== undefined || item.title || item.content || item.robot_id !== undefined;
|
||||
}
|
||||
|
||||
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 || '';
|
||||
return {
|
||||
id: item.id || item.task_id || item.taskId || '',
|
||||
title: title || (item.id || item.task_id ? `任务 ${item.id || item.task_id}` : '任务'),
|
||||
status: item.status,
|
||||
priority: item.priority,
|
||||
robotId: item.robot_id || item.robotId,
|
||||
contentPreview: content ? String(content).slice(0, 80) : ''
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function buildResultState(text) {
|
||||
const content = text || '';
|
||||
return { resultHasMore: content.length > RESULT_PREVIEW_LIMIT, resultExpanded: false };
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
id: '',
|
||||
title: '',
|
||||
robot_id: '',
|
||||
content: '',
|
||||
status: '',
|
||||
priority: '',
|
||||
keyword: '',
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
convId: '',
|
||||
result: '',
|
||||
resultCards: [],
|
||||
resultExpanded: false,
|
||||
resultHasMore: false,
|
||||
taskCards: []
|
||||
},
|
||||
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 parsed = safeParseResult(result);
|
||||
const taskCards = buildTaskCards(parsed);
|
||||
const state = buildResultState(view.text);
|
||||
this.setData({ result: view.text, resultCards: view.cards, taskCards, ...state });
|
||||
},
|
||||
async list() {
|
||||
const qs = [];
|
||||
qs.push(`page=${encodeURIComponent(this.data.page)}`);
|
||||
qs.push(`page_size=${encodeURIComponent(this.data.page_size)}`);
|
||||
if (this.data.robot_id) qs.push(`robot_id=${encodeURIComponent(this.data.robot_id)}`);
|
||||
if (this.data.status) qs.push(`status=${encodeURIComponent(this.data.status)}`);
|
||||
if (this.data.keyword) qs.push(`keyword=${encodeURIComponent(this.data.keyword)}`);
|
||||
const result = await request({ url: `/api/prompt/tasks?${qs.join('&')}` });
|
||||
this.setResult(result);
|
||||
},
|
||||
async detail() {
|
||||
const result = await request({ url: `/api/prompt/tasks/${encodeURIComponent(this.data.id)}?with_conversations=true` });
|
||||
this.setResult(result);
|
||||
},
|
||||
async create() {
|
||||
const payload = this.buildPayload();
|
||||
const result = await request({ url: '/api/prompt/tasks', method: 'POST', data: payload });
|
||||
this.setResult(result);
|
||||
},
|
||||
async update() {
|
||||
const payload = this.buildPayload();
|
||||
const result = await request({ url: `/api/prompt/tasks/${encodeURIComponent(this.data.id)}`, method: 'PUT', data: payload });
|
||||
this.setResult(result);
|
||||
},
|
||||
async remove() {
|
||||
const result = await request({ url: `/api/prompt/tasks/${encodeURIComponent(this.data.id)}`, method: 'DELETE' });
|
||||
this.setResult(result);
|
||||
},
|
||||
async updateStatus() {
|
||||
const result = await request({
|
||||
url: `/api/prompt/tasks/${encodeURIComponent(this.data.id)}/status`,
|
||||
method: 'PUT',
|
||||
data: { status: Number(this.data.status) }
|
||||
});
|
||||
this.setResult(result);
|
||||
},
|
||||
async clearConversations() {
|
||||
const result = await request({ url: `/api/prompt/tasks/${encodeURIComponent(this.data.id)}/conversations`, method: 'DELETE' });
|
||||
this.setResult(result);
|
||||
},
|
||||
async deleteConversation() {
|
||||
const result = await request({ url: `/api/prompt/conversations/${encodeURIComponent(this.data.convId)}`, method: 'DELETE' });
|
||||
this.setResult(result);
|
||||
},
|
||||
async download() {
|
||||
try {
|
||||
const tempPath = await downloadFile({ url: `/api/prompt/tasks/${encodeURIComponent(this.data.id)}/download` });
|
||||
this.setResult(`下载成功: ${tempPath}`);
|
||||
} catch (e) {
|
||||
this.setResult('下载失败');
|
||||
}
|
||||
},
|
||||
buildPayload() {
|
||||
return {
|
||||
title: this.data.title,
|
||||
robot_id: this.data.robot_id ? Number(this.data.robot_id) : undefined,
|
||||
content: this.data.content,
|
||||
status: this.data.status ? Number(this.data.status) : undefined,
|
||||
priority: this.data.priority ? Number(this.data.priority) : undefined
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "任务管理"
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">任务管理</view>
|
||||
<view class="page-subtitle">创建、查询与状态管理</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">任务</view>
|
||||
<view class="section-title">任务ID/机器人ID</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="id" value="{{id}}" bindinput="onInput" />
|
||||
<input class="input" data-key="robot_id" value="{{robot_id}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">标题/优先级/状态</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="title" value="{{title}}" bindinput="onInput" />
|
||||
<input class="input" data-key="priority" value="{{priority}}" bindinput="onInput" />
|
||||
<input class="input" data-key="status" value="{{status}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">内容</view>
|
||||
<textarea class="textarea" data-key="content" value="{{content}}" bindinput="onInput"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn" bindtap="create">创建</button>
|
||||
<button class="btn btn-secondary" bindtap="update">更新</button>
|
||||
<button class="btn btn-secondary" bindtap="detail">详情</button>
|
||||
<button class="btn btn-secondary" bindtap="download">下载</button>
|
||||
<button class="btn btn-danger" bindtap="remove">删除</button>
|
||||
<button class="btn btn-secondary" bindtap="updateStatus">更新状态</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">对话操作</view>
|
||||
<view class="section-title">对话ID</view>
|
||||
<input class="input" data-key="convId" value="{{convId}}" bindinput="onInput" />
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="clearConversations">清空对话</button>
|
||||
<button class="btn btn-secondary" bindtap="deleteConversation">删除单条对话</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">列表</view>
|
||||
<view class="section-title">关键词/状态/页码</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="keyword" value="{{keyword}}" bindinput="onInput" />
|
||||
<input class="input" data-key="status" value="{{status}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="page" value="{{page}}" bindinput="onInput" />
|
||||
<input class="input" type="number" data-key="page_size" value="{{page_size}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="row pagination">
|
||||
<view class="badge">页码 {{page || 1}}</view>
|
||||
<view class="badge">每页 {{page_size || 10}}</view>
|
||||
</view>
|
||||
<view class="section-hint">修改上方页码/每页后点击查询列表</view>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="list">查询列表</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</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="{{taskCards.length}}" class="list">
|
||||
<view wx:for="{{taskCards}}" wx:for-index="idx" wx:key="idx" class="card card--inner list-item result-entity">
|
||||
<view class="row" style="justify-content: space-between;">
|
||||
<view class="module-title">{{item.title}}</view>
|
||||
<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.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>
|
||||
<view class="subtle" wx:if="{{item.contentPreview}}">{{item.contentPreview}}</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result {{resultExpanded ? 'result--expanded' : 'result--collapsed'}}">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
const { request, buildResultView } = require('../../utils/request');
|
||||
|
||||
const RESULT_PREVIEW_LIMIT = 240;
|
||||
|
||||
function safeParseResult(raw) {
|
||||
if (raw === null || raw === undefined) return null;
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function pickList(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 toArray(parsed) {
|
||||
if (!parsed || typeof parsed !== 'object') return [];
|
||||
if (Array.isArray(parsed)) return parsed;
|
||||
const list = pickList(parsed);
|
||||
if (list) return list;
|
||||
return [parsed];
|
||||
}
|
||||
|
||||
function isTemplateLike(item) {
|
||||
if (!item || typeof item !== 'object') return false;
|
||||
return item.id !== undefined || item.name || item.category || item.content;
|
||||
}
|
||||
|
||||
function buildTemplateCards(parsed) {
|
||||
const list = toArray(parsed).filter(isTemplateLike).slice(0, 6);
|
||||
return list.map(item => {
|
||||
const name = item.name || item.title || item.template_name || '';
|
||||
const content = item.content || item.body || item.desc || item.description || '';
|
||||
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 || '',
|
||||
contentPreview: content ? String(content).slice(0, 80) : ''
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function buildResultState(text) {
|
||||
const content = text || '';
|
||||
return { resultHasMore: content.length > RESULT_PREVIEW_LIMIT, resultExpanded: false };
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
id: '',
|
||||
name: '',
|
||||
category: '',
|
||||
content: '',
|
||||
body_json: '',
|
||||
result: '',
|
||||
resultCards: [],
|
||||
resultExpanded: false,
|
||||
resultHasMore: false,
|
||||
templateCards: []
|
||||
},
|
||||
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 parsed = safeParseResult(result);
|
||||
const templateCards = buildTemplateCards(parsed);
|
||||
const state = buildResultState(view.text);
|
||||
this.setData({ result: view.text, resultCards: view.cards, templateCards, ...state });
|
||||
},
|
||||
async list() {
|
||||
const url = this.data.category ? `/api/prompt/templates?category=${encodeURIComponent(this.data.category)}` : '/api/prompt/templates';
|
||||
const result = await request({ url });
|
||||
this.setResult(result);
|
||||
},
|
||||
async detail() {
|
||||
const result = await request({ url: `/api/prompt/templates/${encodeURIComponent(this.data.id)}` });
|
||||
this.setResult(result);
|
||||
},
|
||||
async create() {
|
||||
const payload = this.buildPayload();
|
||||
const result = await request({ url: '/api/prompt/templates', method: 'POST', data: payload });
|
||||
this.setResult(result);
|
||||
},
|
||||
async update() {
|
||||
const payload = this.buildPayload();
|
||||
const result = await request({ url: `/api/prompt/templates/${encodeURIComponent(this.data.id)}`, method: 'PUT', data: payload });
|
||||
this.setResult(result);
|
||||
},
|
||||
async remove() {
|
||||
const result = await request({ url: `/api/prompt/templates/${encodeURIComponent(this.data.id)}`, method: 'DELETE' });
|
||||
this.setResult(result);
|
||||
},
|
||||
buildPayload() {
|
||||
if (this.data.body_json) {
|
||||
try {
|
||||
return JSON.parse(this.data.body_json);
|
||||
} catch (e) {}
|
||||
}
|
||||
return {
|
||||
name: this.data.name,
|
||||
category: this.data.category,
|
||||
content: this.data.content
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "模板库"
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<view class="container">
|
||||
<view class="page-header">
|
||||
<view class="page-title">模板管理</view>
|
||||
<view class="page-subtitle">组织与复用模板内容</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="card-title">模板</view>
|
||||
<view class="section-title">模板ID/分类</view>
|
||||
<view class="row">
|
||||
<input class="input" data-key="id" value="{{id}}" bindinput="onInput" />
|
||||
<input class="input" data-key="category" value="{{category}}" bindinput="onInput" />
|
||||
</view>
|
||||
<view class="section-title">名称</view>
|
||||
<input class="input" data-key="name" value="{{name}}" bindinput="onInput" />
|
||||
<view class="section-title">内容</view>
|
||||
<textarea class="textarea" data-key="content" value="{{content}}" bindinput="onInput"></textarea>
|
||||
<view class="section-title">自定义JSON(可选)</view>
|
||||
<textarea class="textarea" data-key="body_json" value="{{body_json}}" bindinput="onInput"></textarea>
|
||||
<view class="row action-row">
|
||||
<button class="btn btn-secondary" bindtap="list">列表</button>
|
||||
<button class="btn btn-secondary" bindtap="detail">详情</button>
|
||||
<button class="btn" bindtap="create">创建</button>
|
||||
<button class="btn btn-secondary" bindtap="update">更新</button>
|
||||
<button class="btn btn-danger" bindtap="remove">删除</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card result-card" wx:if="{{result}}">
|
||||
<view class="result-header">
|
||||
<view class="result-title">结果</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="{{templateCards.length}}" class="list">
|
||||
<view wx:for="{{templateCards}}" wx:for-index="idx" wx:key="idx" class="card card--inner list-item result-entity">
|
||||
<view class="row" style="justify-content: space-between;">
|
||||
<view class="module-title">{{item.name}}</view>
|
||||
<view class="badge" wx:if="{{item.id}}">#{{item.id}}</view>
|
||||
</view>
|
||||
<view class="row meta-row">
|
||||
<view class="meta-item" wx:if="{{item.category}}">分类:{{item.category}}</view>
|
||||
</view>
|
||||
<view class="subtle" wx:if="{{item.contentPreview}}">{{item.contentPreview}}</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;">
|
||||
<view class="module-title">{{item.label}}</view>
|
||||
<view class="badge">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="result {{resultExpanded ? 'result--expanded' : 'result--collapsed'}}">{{result}}</view>
|
||||
</view>
|
||||
</view>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue