mirror of
https://github.com/xiaoyiweb/YiAi.git
synced 2025-09-26 13:16:39 +08:00
新增gpt-4V上传 修复部分bug
This commit is contained in:
parent
6dc767f009
commit
8db214371a
@ -1,6 +1,6 @@
|
|||||||
# 本地链接生产# xxx填写你的后端服务地址后面/api勿删除
|
# 页面标题
|
||||||
VITE_GLOB_API_URL=https://xxx/api
|
VITE_APP_TITLE = YiAi-Admin
|
||||||
|
# 接口请求地址,会设置到 axios 的 baseURL 参数上 生产地址测试
|
||||||
|
|
||||||
|
VITE_APP_API_BASEURL = https://xxxx/api
|
||||||
VITE_GLOB_OPEN_LONG_REPLY=false
|
VITE_BASE_PATH=/
|
||||||
VITE_GLOB_APP_PWA=false
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "2.4.5",
|
"version": "2.4.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build:test": "vue-tsc --noEmit && vite build --mode test",
|
"build:test": "vue-tsc --noEmit && vite build --mode test",
|
||||||
|
@ -1,305 +1,350 @@
|
|||||||
export const USER_STATUS_OPTIONS = [
|
export const USER_STATUS_OPTIONS = [
|
||||||
{ value: 0, label: '待激活' },
|
{ value: 0, label: "待激活" },
|
||||||
{ value: 1, label: '正常' },
|
{ value: 1, label: "正常" },
|
||||||
{ value: 2, label: '已封禁' },
|
{ value: 2, label: "已封禁" },
|
||||||
{ value: 3, label: '黑名单' },
|
{ value: 3, label: "黑名单" },
|
||||||
]
|
];
|
||||||
|
|
||||||
export const USER_STATUS_MAP = {
|
export const USER_STATUS_MAP = {
|
||||||
0: '待激活',
|
0: "待激活",
|
||||||
1: '正常',
|
1: "正常",
|
||||||
2: '已封禁',
|
2: "已封禁",
|
||||||
3: '黑名单',
|
3: "黑名单",
|
||||||
}
|
};
|
||||||
|
|
||||||
export const USER_STATUS_TYPE_MAP = {
|
export const USER_STATUS_TYPE_MAP = {
|
||||||
0: 'info',
|
0: "info",
|
||||||
1: 'success',
|
1: "success",
|
||||||
2: 'danger',
|
2: "danger",
|
||||||
3: 'danger',
|
3: "danger",
|
||||||
}
|
};
|
||||||
|
|
||||||
// 充值类型map 1: 注册赠送 2: 受邀请赠送 3: 邀请人赠送 4: 购买套餐赠送 5: 管理员赠送 6:扫码支付 7: 绘画失败退款 8: 签到奖励
|
// 充值类型map 1: 注册赠送 2: 受邀请赠送 3: 邀请人赠送 4: 购买套餐赠送 5: 管理员赠送 6:扫码支付 7: 绘画失败退款 8: 签到奖励
|
||||||
export const RECHARGE_TYPE_MAP = {
|
export const RECHARGE_TYPE_MAP = {
|
||||||
1: '注册赠送',
|
1: "注册赠送",
|
||||||
2: '受邀请赠送',
|
2: "受邀请赠送",
|
||||||
3: '邀请人赠送',
|
3: "邀请人赠送",
|
||||||
4: '购买套餐赠送',
|
4: "购买套餐赠送",
|
||||||
5: '管理员赠送',
|
5: "管理员赠送",
|
||||||
6: '扫码支付',
|
6: "扫码支付",
|
||||||
7: '绘画失败退款',
|
7: "绘画失败退款",
|
||||||
8: '签到奖励',
|
8: "签到奖励",
|
||||||
}
|
};
|
||||||
|
|
||||||
// 充值数组
|
// 充值数组
|
||||||
export const RECHARGE_TYPE_OPTIONS = [
|
export const RECHARGE_TYPE_OPTIONS = [
|
||||||
{ value: 1, label: '注册赠送' },
|
{ value: 1, label: "注册赠送" },
|
||||||
{ value: 2, label: '受邀请赠送' },
|
{ value: 2, label: "受邀请赠送" },
|
||||||
{ value: 3, label: '邀请人赠送' },
|
{ value: 3, label: "邀请人赠送" },
|
||||||
{ value: 4, label: '购买套餐赠送' },
|
{ value: 4, label: "购买套餐赠送" },
|
||||||
{ value: 5, label: '管理员赠送' },
|
{ value: 5, label: "管理员赠送" },
|
||||||
{ value: 6, label: '扫码支付' },
|
{ value: 6, label: "扫码支付" },
|
||||||
{ value: 7, label: '绘画失败退款' },
|
{ value: 7, label: "绘画失败退款" },
|
||||||
{ value: 8, label: '签到奖励' },
|
{ value: 8, label: "签到奖励" },
|
||||||
]
|
];
|
||||||
|
|
||||||
export type UserStatus = keyof typeof USER_STATUS_TYPE_MAP
|
export type UserStatus = keyof typeof USER_STATUS_TYPE_MAP;
|
||||||
|
|
||||||
// 是否开启额外赠送
|
// 是否开启额外赠送
|
||||||
export const IS_OPTIONS = {
|
export const IS_OPTIONS = {
|
||||||
0: '关闭',
|
0: "关闭",
|
||||||
1: '开启',
|
1: "开启",
|
||||||
}
|
};
|
||||||
|
|
||||||
// 是否开启额外赠送类型
|
// 是否开启额外赠送类型
|
||||||
export const IS_TYPE_MAP = {
|
export const IS_TYPE_MAP = {
|
||||||
0: 'danger',
|
0: "danger",
|
||||||
1: 'success',
|
1: "success",
|
||||||
}
|
};
|
||||||
|
|
||||||
export const PACKAGE_TYPE_OPTIONS = [
|
export const PACKAGE_TYPE_OPTIONS = [
|
||||||
{ value: 0, label: '禁用' },
|
{ value: 0, label: "禁用" },
|
||||||
{ value: 1, label: '启动' },
|
{ value: 1, label: "启动" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 扣费形式 1: 按次数扣费 2:按Token扣费
|
// 扣费形式 1: 按次数扣费 2:按Token扣费
|
||||||
export const DEDUCTION_TYPE_OPTIONS = [
|
export const DEDUCTION_TYPE_OPTIONS = [
|
||||||
{ value: 1, label: '按次数扣费' },
|
{ value: 1, label: "按次数扣费" },
|
||||||
{ value: 2, label: '按Token扣费' },
|
{ value: 2, label: "按Token扣费" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 扣费形式 map
|
// 扣费形式 map
|
||||||
export const DEDUCTION_TYPE_MAP = {
|
export const DEDUCTION_TYPE_MAP = {
|
||||||
1: '按次数扣费',
|
1: "按次数扣费",
|
||||||
2: '按Token扣费',
|
2: "按Token扣费",
|
||||||
}
|
};
|
||||||
|
|
||||||
export const CRAMI_STATUS_OPTIONS = [
|
export const CRAMI_STATUS_OPTIONS = [
|
||||||
{ value: 0, label: '未使用' },
|
{ value: 0, label: "未使用" },
|
||||||
{ value: 1, label: '已使用' },
|
{ value: 1, label: "已使用" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 图片推荐状态0未推荐1已推荐
|
// 图片推荐状态0未推荐1已推荐
|
||||||
export const RECOMMEND_STATUS_OPTIONS = [
|
export const RECOMMEND_STATUS_OPTIONS = [
|
||||||
{ value: 0, label: '未推荐' },
|
{ value: 0, label: "未推荐" },
|
||||||
{ value: 1, label: '已推荐' },
|
{ value: 1, label: "已推荐" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 0 禁用 1 启用
|
// 0 禁用 1 启用
|
||||||
export const ENABLE_STATUS_OPTIONS = [
|
export const ENABLE_STATUS_OPTIONS = [
|
||||||
{ value: 0, label: '禁用' },
|
{ value: 0, label: "禁用" },
|
||||||
{ value: 1, label: '启用' },
|
{ value: 1, label: "启用" },
|
||||||
{ value: 3, label: '待审核' },
|
{ value: 3, label: "待审核" },
|
||||||
{ value: 4, label: '拒绝共享' },
|
{ value: 4, label: "拒绝共享" },
|
||||||
{ value: 5, label: '通过共享' },
|
{ value: 5, label: "通过共享" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 问题状态 0 未解决 1 已解决
|
// 问题状态 0 未解决 1 已解决
|
||||||
export const QUESTION_STATUS_OPTIONS = [
|
export const QUESTION_STATUS_OPTIONS = [
|
||||||
{ value: '0', label: '未启用' },
|
{ value: "0", label: "未启用" },
|
||||||
{ value: '1', label: '已启用' },
|
{ value: "1", label: "已启用" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 问题状态 0 未解决 1 已解决
|
// 问题状态 0 未解决 1 已解决
|
||||||
export const ORDER_STATUS_OPTIONS = [
|
export const ORDER_STATUS_OPTIONS = [
|
||||||
{ value: 0, label: '待审核' },
|
{ value: 0, label: "待审核" },
|
||||||
{ value: 1, label: '已通过' },
|
{ value: 1, label: "已通过" },
|
||||||
{ value: -1, label: '已拒绝' },
|
{ value: -1, label: "已拒绝" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 0:未推荐 1:已推荐 数组
|
// 0:未推荐 1:已推荐 数组
|
||||||
export const RECOMMEND_STATUS = [
|
export const RECOMMEND_STATUS = [
|
||||||
{ value: 0, label: '未推荐' },
|
{ value: 0, label: "未推荐" },
|
||||||
{ value: 1, label: '已推荐' },
|
{ value: 1, label: "已推荐" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 提现渠道 支付宝 微信
|
// 提现渠道 支付宝 微信
|
||||||
export const WITHDRAW_CHANNEL_OPTIONS = [
|
export const WITHDRAW_CHANNEL_OPTIONS = [
|
||||||
{ value: 1, label: '支付宝' },
|
{ value: 1, label: "支付宝" },
|
||||||
{ value: 2, label: '微信' },
|
{ value: 2, label: "微信" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 1 排队中 2 处理中 3 已完成 4 失败 5 超时
|
// 1 排队中 2 处理中 3 已完成 4 失败 5 超时
|
||||||
export const WITHDRAW_STATUS_OPTIONS = [
|
export const WITHDRAW_STATUS_OPTIONS = [
|
||||||
{ value: 1, label: '正在排队' },
|
{ value: 1, label: "正在排队" },
|
||||||
{ value: 2, label: '正在绘制' },
|
{ value: 2, label: "正在绘制" },
|
||||||
{ value: 3, label: '绘制完成' },
|
{ value: 3, label: "绘制完成" },
|
||||||
{ value: 4, label: '绘制失败' },
|
{ value: 4, label: "绘制失败" },
|
||||||
{ value: 5, label: '绘制超时' },
|
{ value: 5, label: "绘制超时" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 0 禁用 warning 1启用 状态 success
|
// 0 禁用 warning 1启用 状态 success
|
||||||
export const ENABLE_STATUS_TYPE_MAP: QuestionStatusMap = {
|
export const ENABLE_STATUS_TYPE_MAP: QuestionStatusMap = {
|
||||||
0: 'danger',
|
0: "danger",
|
||||||
1: 'success',
|
1: "success",
|
||||||
}
|
};
|
||||||
|
|
||||||
interface QuestionStatusMap {
|
interface QuestionStatusMap {
|
||||||
[key: number]: string
|
[key: number]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 问题状态 0 未解决 1 已解决 映射
|
// 问题状态 0 未解决 1 已解决 映射
|
||||||
export const QUESTION_STATUS_MAP: QuestionStatusMap = {
|
export const QUESTION_STATUS_MAP: QuestionStatusMap = {
|
||||||
'-1': '欠费锁定',
|
"-1": "欠费锁定",
|
||||||
'0': '未启用',
|
"0": "未启用",
|
||||||
'1': '已启用',
|
"1": "已启用",
|
||||||
'3': '待审核',
|
"3": "待审核",
|
||||||
'4': '拒绝共享',
|
"4": "拒绝共享",
|
||||||
'5': '通过共享',
|
"5": "通过共享",
|
||||||
}
|
};
|
||||||
|
|
||||||
// 问题状态 0 被封号 1 正常 映射
|
// 问题状态 0 被封号 1 正常 映射
|
||||||
export const KEY_STATUS_MAP: QuestionStatusMap = {
|
export const KEY_STATUS_MAP: QuestionStatusMap = {
|
||||||
0: '被封禁',
|
0: "被封禁",
|
||||||
1: '工作中',
|
1: "工作中",
|
||||||
}
|
};
|
||||||
// 账号类型 5$ 18$ 120$
|
// 账号类型 5$ 18$ 120$
|
||||||
export const ACCOUNT_TYPE_MAP: QuestionStatus = [
|
export const ACCOUNT_TYPE_MAP: QuestionStatus = [
|
||||||
{ value: '5$', label: '5$' },
|
{ value: "5$", label: "5$" },
|
||||||
{ value: '18$', label: '18$' },
|
{ value: "18$", label: "18$" },
|
||||||
{ value: '120$', label: '120$' },
|
{ value: "120$", label: "120$" },
|
||||||
{ value: '其他', label: '其他' },
|
{ value: "其他", label: "其他" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 模型列表
|
// 模型列表
|
||||||
export const MODEL_LIST = [
|
export const MODEL_LIST = [
|
||||||
'gpt-3.5-turbo',
|
"gpt-3.5-turbo",
|
||||||
'gpt-3.5-turbo-1106',
|
"gpt-3.5-turbo-1106",
|
||||||
'gpt-3.5-turbo-16k',
|
"gpt-3.5-turbo-16k",
|
||||||
'gpt-4',
|
"gpt-4",
|
||||||
'gpt-4-0613',
|
"gpt-4-0613",
|
||||||
'gpt-4-32k',
|
"gpt-4-32k",
|
||||||
'gpt-4-32k-0613',
|
"gpt-4-32k-0613",
|
||||||
'gpt-4-1106-preview',
|
"gpt-4-1106-preview",
|
||||||
'gpt-4-vision-preview',
|
"gpt-4-vision-preview",
|
||||||
'gpt-4-all',
|
"gpt-4-all",
|
||||||
'gpt-4-0125-preview',
|
"gpt-4-0125-preview",
|
||||||
]
|
// claude
|
||||||
|
"claude-2.0",
|
||||||
|
"claude-2.1",
|
||||||
|
// gemini
|
||||||
|
"gemini-pro",
|
||||||
|
// 百度文心
|
||||||
|
"ERNIE-Bot",
|
||||||
|
"ERNIE-Bot-4",
|
||||||
|
"ERNIE-Bot-turbo",
|
||||||
|
// 阿里通义
|
||||||
|
"qwen-turbo",
|
||||||
|
"qwen-plus",
|
||||||
|
"qwen-max",
|
||||||
|
"qwen-max-lingcontext",
|
||||||
|
// 腾讯混元
|
||||||
|
"hunyuan",
|
||||||
|
// 清华智谱
|
||||||
|
"chatglm_turbo",
|
||||||
|
"chatglm_pro",
|
||||||
|
"chatglm_std",
|
||||||
|
"chatglm_lite",
|
||||||
|
// 360 智脑
|
||||||
|
"360GPT_S2_V9",
|
||||||
|
// 讯飞星火
|
||||||
|
"SparkDesk",
|
||||||
|
];
|
||||||
|
|
||||||
// 模型列表 0 mj 1 Dall-e
|
// 模型列表 0 mj 1 Dall-e
|
||||||
export const DRAW_MODEL_LIST = [
|
export const DRAW_MODEL_LIST = [
|
||||||
{ value: 'mj', label: 'MidjourneyAi' },
|
{ value: "mj", label: "MidjourneyAi" },
|
||||||
{ value: 'DALL-E2', label: 'DALL-E' },
|
{ value: "DALL-E2", label: "DALL-E" },
|
||||||
]
|
];
|
||||||
// 支付状态列表 status 0:未支付、1:已支付、2、支付失败、3:支付超时
|
// 支付状态列表 status 0:未支付、1:已支付、2、支付失败、3:支付超时
|
||||||
export const PAY_STATUS_OPTIONS = [
|
export const PAY_STATUS_OPTIONS = [
|
||||||
{ value: 0, label: '未支付' },
|
{ value: 0, label: "未支付" },
|
||||||
{ value: 1, label: '已支付' },
|
{ value: 1, label: "已支付" },
|
||||||
{ value: 2, label: '支付失败' },
|
{ value: 2, label: "支付失败" },
|
||||||
{ value: 3, label: '支付超时' },
|
{ value: 3, label: "支付超时" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 支付状态 status 0:未支付、1:已支付、2、支付失败、3:支付超时
|
// 支付状态 status 0:未支付、1:已支付、2、支付失败、3:支付超时
|
||||||
export const PAY_STATUS_MAP: QuestionStatusMap = {
|
export const PAY_STATUS_MAP: QuestionStatusMap = {
|
||||||
0: '未支付',
|
0: "未支付",
|
||||||
1: '已支付',
|
1: "已支付",
|
||||||
2: '支付失败',
|
2: "支付失败",
|
||||||
3: '支付超时',
|
3: "支付超时",
|
||||||
}
|
};
|
||||||
|
|
||||||
// 平台列表 epay: 易支付 hupi:虎皮椒
|
// 平台列表 epay: 易支付 hupi:虎皮椒
|
||||||
export const PAY_PLATFORM_LIST = [
|
export const PAY_PLATFORM_LIST = [
|
||||||
{ value: 'epay', label: '易支付' },
|
{ value: "epay", label: "易支付" },
|
||||||
{ value: 'hupi', label: '虎皮椒' },
|
{ value: "hupi", label: "虎皮椒" },
|
||||||
{ value: 'wechat', label: '微信支付' },
|
{ value: "wechat", label: "微信支付" },
|
||||||
{ value: 'mpay', label: '码支付' },
|
{ value: "mpay", label: "码支付" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 支付对应
|
// 支付对应
|
||||||
export const PAY_PLATFORM_MAP = {
|
export const PAY_PLATFORM_MAP = {
|
||||||
epay: '易支付',
|
epay: "易支付",
|
||||||
hupi: '虎皮椒',
|
hupi: "虎皮椒",
|
||||||
wechat: '微信支付',
|
wechat: "微信支付",
|
||||||
mpay: '码支付',
|
mpay: "码支付",
|
||||||
}
|
};
|
||||||
|
|
||||||
// 绘画状态 1: 等待中 2: 绘制中 3: 绘制完成 4: 绘制失败 5: 绘制超时
|
// 绘画状态 1: 等待中 2: 绘制中 3: 绘制完成 4: 绘制失败 5: 绘制超时
|
||||||
export const DRAW_MJ_STATUS_LIST = [
|
export const DRAW_MJ_STATUS_LIST = [
|
||||||
{ value: 1, label: '等待中' },
|
{ value: 1, label: "等待中" },
|
||||||
{ value: 2, label: '绘制中' },
|
{ value: 2, label: "绘制中" },
|
||||||
{ value: 3, label: '绘制完成' },
|
{ value: 3, label: "绘制完成" },
|
||||||
{ value: 4, label: '绘制失败' },
|
{ value: 4, label: "绘制失败" },
|
||||||
{ value: 5, label: '绘制超时' },
|
{ value: 5, label: "绘制超时" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// App角色 系统 system 用户 user
|
// App角色 系统 system 用户 user
|
||||||
export const APP_ROLE_LIST = [
|
export const APP_ROLE_LIST = [
|
||||||
{ value: 'system', label: '系统' },
|
{ value: "system", label: "系统" },
|
||||||
{ value: 'user', label: '用户' },
|
{ value: "user", label: "用户" },
|
||||||
]
|
];
|
||||||
|
|
||||||
// 绘画状态 1:排队中 2:绘制中 3:绘制完成 4:绘制失败 5:绘制超时
|
// 绘画状态 1:排队中 2:绘制中 3:绘制完成 4:绘制失败 5:绘制超时
|
||||||
export const DRAW_STATUS_MAP = {
|
export const DRAW_STATUS_MAP = {
|
||||||
1: '排队中',
|
1: "排队中",
|
||||||
2: '绘制中',
|
2: "绘制中",
|
||||||
3: '绘制完成',
|
3: "绘制完成",
|
||||||
4: '绘制失败',
|
4: "绘制失败",
|
||||||
5: '绘制超时',
|
5: "绘制超时",
|
||||||
}
|
};
|
||||||
|
|
||||||
export const TYPEORIGINLIST = [
|
export const TYPEORIGINLIST = [
|
||||||
{ value: '百度云检测', label: '百度云检测' },
|
{ value: "百度云检测", label: "百度云检测" },
|
||||||
{ value: '自定义检测', label: '自定义检测' },
|
{ value: "自定义检测", label: "自定义检测" },
|
||||||
{ value: 'NineAI检测', label: 'NineAI检测' },
|
{ value: "NineAI检测", label: "NineAI检测" },
|
||||||
]
|
];
|
||||||
|
|
||||||
export const MODELTYPELIST = [
|
export const MODELTYPELIST = [
|
||||||
{ value: 1, label: 'OpenAi - [chatGpt]' },
|
{ value: 1, label: "OpenAi - [chatGpt]" },
|
||||||
{ value: 2, label: '百度 - [千帆大模型]' },
|
{ value: 2, label: "百度 - [千帆大模型]" },
|
||||||
{ value: 3, label: '清华 - [智谱大模型]' },
|
{ value: 3, label: "清华 - [智谱大模型]" },
|
||||||
]
|
];
|
||||||
|
|
||||||
export const MODELSMAP = {
|
export const MODELSMAP = {
|
||||||
1: 'OPENAI',
|
1: "OPENAI",
|
||||||
2: '百度文心',
|
2: "百度文心",
|
||||||
3: '清华智谱',
|
3: "清华智谱",
|
||||||
}
|
};
|
||||||
|
|
||||||
export const MODELSMAPLIST = {
|
export const MODELSMAPLIST = {
|
||||||
1: [
|
1: [
|
||||||
'gpt-3.5-turbo',
|
"gpt-3.5-turbo",
|
||||||
'gpt-3.5-turbo-1106',
|
"gpt-3.5-turbo-1106",
|
||||||
'gpt-3.5-turbo-16k',
|
"gpt-3.5-turbo-16k",
|
||||||
'gpt-4',
|
"gpt-4",
|
||||||
'gpt-4-0613',
|
"gpt-4-0613",
|
||||||
'gpt-4-32k',
|
"gpt-4-32k",
|
||||||
'gpt-4-32k-0613',
|
"gpt-4-32k-0613",
|
||||||
'gpt-4-1106-preview',
|
"gpt-4-1106-preview",
|
||||||
'gpt-4-vision-preview',
|
"gpt-4-vision-preview",
|
||||||
'gpt-4-all',
|
"gpt-4-all",
|
||||||
'gpt-4-0125-preview',
|
"gpt-4-0125-preview",
|
||||||
|
// claude
|
||||||
|
"claude-2.0",
|
||||||
|
"claude-2.1",
|
||||||
|
// gemini
|
||||||
|
"gemini-pro",
|
||||||
|
// 百度文心
|
||||||
|
"ERNIE-Bot",
|
||||||
|
"ERNIE-Bot-4",
|
||||||
|
"ERNIE-Bot-turbo",
|
||||||
|
// 阿里通义
|
||||||
|
"qwen-turbo",
|
||||||
|
"qwen-plus",
|
||||||
|
"qwen-max",
|
||||||
|
"qwen-max-lingcontext",
|
||||||
|
// 腾讯混元
|
||||||
|
"hunyuan",
|
||||||
|
// 清华智谱
|
||||||
|
"chatglm_turbo",
|
||||||
|
"chatglm_pro",
|
||||||
|
"chatglm_std",
|
||||||
|
"chatglm_lite",
|
||||||
|
// 360 智脑
|
||||||
|
"360GPT_S2_V9",
|
||||||
|
// 讯飞星火
|
||||||
|
"SparkDesk",
|
||||||
],
|
],
|
||||||
2: [
|
2: [
|
||||||
'ERNIE-Bot',
|
"ERNIE-Bot",
|
||||||
'ERNIE-Bot',
|
"ERNIE-Bot",
|
||||||
'ERNIE-Bot-4',
|
"ERNIE-Bot-4",
|
||||||
'ERNIE-Bot-turbo',
|
"ERNIE-Bot-turbo",
|
||||||
'BLOOMZ-7B',
|
"BLOOMZ-7B",
|
||||||
'Llama-2-7b-chat',
|
"Llama-2-7b-chat",
|
||||||
'Llama-2-13b-chat',
|
"Llama-2-13b-chat",
|
||||||
// 'Llama-2-70b-chat',
|
// 'Llama-2-70b-chat',
|
||||||
// 'ChatGLM2-6B-32K',
|
// 'ChatGLM2-6B-32K',
|
||||||
'Qianfan-BLOOMZ-7B-compressed',
|
"Qianfan-BLOOMZ-7B-compressed",
|
||||||
'Qianfan-Chinese-Llama-2-7B',
|
"Qianfan-Chinese-Llama-2-7B",
|
||||||
'AquilaChat-7B',
|
"AquilaChat-7B",
|
||||||
],
|
],
|
||||||
3: [
|
3: ["chatglm_pro", "chatglm_std", "chatglm_lite", "chatglm_lite_32k"],
|
||||||
'chatglm_pro',
|
};
|
||||||
'chatglm_std',
|
|
||||||
'chatglm_lite',
|
|
||||||
'chatglm_lite_32k',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 扣费类型 普通余额还是高级余额 */
|
/* 扣费类型 普通余额还是高级余额 */
|
||||||
export const DEDUCTTYPELIST = [
|
export const DEDUCTTYPELIST = [
|
||||||
{ value: 1, label: '普通余额' },
|
{ value: 1, label: "普通余额" },
|
||||||
{ value: 2, label: '高级余额' },
|
{ value: 2, label: "高级余额" },
|
||||||
]
|
];
|
||||||
|
|
||||||
/* 不同模型在填入key字段的时候 key代表的含义不同 */
|
/* 不同模型在填入key字段的时候 key代表的含义不同 */
|
||||||
export const ModelTypeLabelMap = {
|
export const ModelTypeLabelMap = {
|
||||||
1: 'APIKey',
|
1: "APIKey",
|
||||||
2: 'client_id',
|
2: "client_id",
|
||||||
3: 'AppKey',
|
3: "AppKey",
|
||||||
}
|
};
|
||||||
|
@ -54,6 +54,7 @@ const formPackage = reactive({
|
|||||||
deductType: 1,
|
deductType: 1,
|
||||||
maxRounds: 12,
|
maxRounds: 12,
|
||||||
isTokenBased: false,
|
isTokenBased: false,
|
||||||
|
tokenFeeRatio: 1000,
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = reactive<FormRules>({
|
const rules = reactive<FormRules>({
|
||||||
@ -84,6 +85,9 @@ const rules = reactive<FormRules>({
|
|||||||
trigger: 'change',
|
trigger: 'change',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
tokenFeeRatio: [
|
||||||
|
{ required: true, message: 'token计费比例', trigger: 'change' },
|
||||||
|
],
|
||||||
model: [
|
model: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@ -192,6 +196,7 @@ function handleEditKey(row: any) {
|
|||||||
maxRounds,
|
maxRounds,
|
||||||
isDraw,
|
isDraw,
|
||||||
isTokenBased,
|
isTokenBased,
|
||||||
|
tokenFeeRatio
|
||||||
} = row
|
} = row
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
Object.assign(formPackage, {
|
Object.assign(formPackage, {
|
||||||
@ -211,6 +216,7 @@ function handleEditKey(row: any) {
|
|||||||
maxRounds,
|
maxRounds,
|
||||||
isDraw,
|
isDraw,
|
||||||
isTokenBased,
|
isTokenBased,
|
||||||
|
tokenFeeRatio
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
visible.value = true
|
visible.value = true
|
||||||
@ -393,6 +399,18 @@ onMounted(() => {
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="isTokenBased"
|
||||||
|
align="center"
|
||||||
|
label="设为Token计费"
|
||||||
|
width="120"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="scope.row.isTokenBased ? 'success' : 'danger'">
|
||||||
|
{{ scope.row.isTokenBased ? '是' : '否' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="deductType"
|
prop="deductType"
|
||||||
align="center"
|
align="center"
|
||||||
@ -792,6 +810,21 @@ onMounted(() => {
|
|||||||
<QuestionFilled />
|
<QuestionFilled />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="token计费比例" prop="tokenFeeRatio">
|
||||||
|
<el-input
|
||||||
|
v-model.number="formPackage.tokenFeeRatio"
|
||||||
|
placeholder="请填写token计费比例"
|
||||||
|
style="width: 80%"
|
||||||
|
/>
|
||||||
|
<el-tooltip class="box-item" effect="dark" placement="right">
|
||||||
|
<template #content>
|
||||||
|
<div style="width: 250px">
|
||||||
|
开启 Token 计费后生效,每积分等价于多少 Token
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-icon class="ml-3 cursor-pointer"><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="[1].includes(Number(formPackage.keyType))"
|
v-if="[1].includes(Number(formPackage.keyType))"
|
||||||
|
@ -8,12 +8,14 @@ export function fetchChatAPIProcess<T = any>(
|
|||||||
prompt: string
|
prompt: string
|
||||||
appId?: number
|
appId?: number
|
||||||
options?: { conversationId?: string; parentMessageId?: string; temperature: number }
|
options?: { conversationId?: string; parentMessageId?: string; temperature: number }
|
||||||
|
imageUrl?:string
|
||||||
|
model?:string
|
||||||
signal?: GenericAbortSignal
|
signal?: GenericAbortSignal
|
||||||
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
|
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
|
||||||
) {
|
) {
|
||||||
return post<T>({
|
return post<T>({
|
||||||
url: '/chatgpt/chat-process',
|
url: '/chatgpt/chat-process',
|
||||||
data: { prompt: params.prompt, appId: params?.appId, options: params.options },
|
data: { prompt: params.prompt, appId: params?.appId, options: params.options,imageUrl: params.imageUrl,model: params.model},
|
||||||
signal: params.signal,
|
signal: params.signal,
|
||||||
onDownloadProgress: params.onDownloadProgress,
|
onDownloadProgress: params.onDownloadProgress,
|
||||||
})
|
})
|
||||||
|
BIN
chat/src/assets/file.jpeg
Normal file
BIN
chat/src/assets/file.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
@ -230,6 +230,10 @@ onMounted(() => {
|
|||||||
<NInput v-model:value="loginForm.password" placeholder="请输入您的账户密码" type="password" :maxlength="30" show-password-on="click" tabindex="0" @keyup.enter="handlerSubmit" />
|
<NInput v-model:value="loginForm.password" placeholder="请输入您的账户密码" type="password" :maxlength="30" show-password-on="click" tabindex="0" @keyup.enter="handlerSubmit" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
</Motion>
|
</Motion>
|
||||||
|
<div style="color:red">
|
||||||
|
老用户密码统一重置为112233<br>
|
||||||
|
登录后请自行修改密码
|
||||||
|
</div>
|
||||||
<NFormItem>
|
<NFormItem>
|
||||||
<NButton
|
<NButton
|
||||||
block
|
block
|
||||||
|
@ -1,215 +1,246 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from "pinia";
|
||||||
import { formatChatPre, getLocalState, setLocalState } from './helper'
|
import { formatChatPre, getLocalState, setLocalState } from "./helper";
|
||||||
import { fetchCreateGroupAPI, fetchDelAllGroupAPI, fetchDelGroupAPI, fetchQueryGroupAPI, fetchUpdateGroupAPI } from '@/api/group'
|
import {
|
||||||
import { fetchDelChatLogAPI, fetchDelChatLogByGroupIdAPI, fetchQueryChatLogListAPI } from '@/api/chatLog'
|
fetchCreateGroupAPI,
|
||||||
import { fetchModelBaseConfigAPI } from '@/api/models'
|
fetchDelAllGroupAPI,
|
||||||
import { fetchGetChatPreList } from '@/api/index'
|
fetchDelGroupAPI,
|
||||||
|
fetchQueryGroupAPI,
|
||||||
|
fetchUpdateGroupAPI,
|
||||||
|
} from "@/api/group";
|
||||||
|
import {
|
||||||
|
fetchDelChatLogAPI,
|
||||||
|
fetchDelChatLogByGroupIdAPI,
|
||||||
|
fetchQueryChatLogListAPI,
|
||||||
|
} from "@/api/chatLog";
|
||||||
|
import { fetchModelBaseConfigAPI } from "@/api/models";
|
||||||
|
import { fetchGetChatPreList } from "@/api/index";
|
||||||
|
|
||||||
export const useChatStore = defineStore('chat-store', {
|
export const useChatStore = defineStore("chat-store", {
|
||||||
state: (): Chat.ChatState => getLocalState(),
|
state: (): Chat.ChatState => getLocalState(),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
/* 当前选用模型的配置 */
|
/* 当前选用模型的配置 */
|
||||||
activeConfig: (state) => {
|
activeConfig: (state) => {
|
||||||
const uuid = state.active
|
const uuid = state.active;
|
||||||
if (!uuid)
|
if (!uuid) return {};
|
||||||
return {}
|
const config = state.groupList.find((item) => item.uuid === uuid)?.config;
|
||||||
const config = state.groupList.find(item => item.uuid === uuid)?.config
|
return config ? JSON.parse(config) : state.baseConfig;
|
||||||
return config ? JSON.parse(config) : state.baseConfig
|
},
|
||||||
},
|
|
||||||
|
|
||||||
activeGroupAppId: (state) => {
|
activeGroupAppId: (state) => {
|
||||||
const uuid = state.active
|
const uuid = state.active;
|
||||||
if (!uuid)
|
if (!uuid) return null;
|
||||||
return null
|
return state.groupList.find((item) => item.uuid === uuid)?.appId;
|
||||||
return state.groupList.find(item => item.uuid === uuid)?.appId
|
},
|
||||||
},
|
|
||||||
|
|
||||||
/* 当前选用模型的扣费类型 */
|
/* 当前选用模型的名称 */
|
||||||
activeModelKeyDeductType(state) {
|
activeModelName(state) {
|
||||||
return this.activeConfig?.modelInfo?.deductType
|
return this.activeConfig?.modelInfo?.model;
|
||||||
},
|
},
|
||||||
|
/* 当前选用模型的扣费类型 */
|
||||||
|
activeModelKeyDeductType(state) {
|
||||||
|
return this.activeConfig?.modelInfo?.deductType;
|
||||||
|
},
|
||||||
|
|
||||||
/* 当前选用模型的模型类型 */
|
/* 当前选用模型的模型类型 */
|
||||||
activeModelKeyType(state) {
|
activeModelKeyType(state) {
|
||||||
return this.activeConfig?.modelInfo?.keyType
|
return this.activeConfig?.modelInfo?.keyType;
|
||||||
},
|
},
|
||||||
|
|
||||||
/* 当前选用模型的调用价格 */
|
/* 当前选用模型的调用价格 */
|
||||||
activeModelKeyPrice(state) {
|
activeModelKeyPrice(state) {
|
||||||
return this.activeConfig?.modelInfo?.deduct
|
return this.activeConfig?.modelInfo?.deduct;
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
},
|
actions: {
|
||||||
|
/* 对话组过滤 */
|
||||||
|
setGroupKeyWord(keyWord: string) {
|
||||||
|
this.groupKeyWord = keyWord;
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
/* 计算拿到当前选择的对话组信息 */
|
||||||
/* 对话组过滤 */
|
getChatByGroupInfo() {
|
||||||
setGroupKeyWord(keyWord: string) {
|
if (this.active)
|
||||||
this.groupKeyWord = keyWord
|
return this.groupList.find((item) => item.uuid === this.active) || {};
|
||||||
},
|
},
|
||||||
|
|
||||||
/* 计算拿到当前选择的对话组信息 */
|
/* */
|
||||||
getChatByGroupInfo() {
|
getConfigFromUuid(uuid: any) {
|
||||||
if (this.active)
|
return this.groupList.find((item) => item.uuid === uuid)?.config;
|
||||||
return this.groupList.find(item => item.uuid === this.active) || {}
|
},
|
||||||
},
|
|
||||||
|
|
||||||
/* */
|
/* 新增新的对话组 */
|
||||||
getConfigFromUuid(uuid: any) {
|
async addNewChatGroup(appId = 0) {
|
||||||
return this.groupList.find(item => item.uuid === uuid)?.config
|
const res: any = await fetchCreateGroupAPI({ appId });
|
||||||
},
|
const { id: uuid } = res.data;
|
||||||
|
await this.setActiveGroup(uuid);
|
||||||
|
this.recordState();
|
||||||
|
},
|
||||||
|
|
||||||
/* 新增新的对话组 */
|
/* 查询基础模型配置 兼容老的chatgroup */
|
||||||
async addNewChatGroup(appId = 0) {
|
async getBaseModelConfig() {
|
||||||
const res: any = await fetchCreateGroupAPI({ appId })
|
const res = await fetchModelBaseConfigAPI();
|
||||||
const { id: uuid } = res.data
|
this.baseConfig = res?.data;
|
||||||
await this.setActiveGroup(uuid)
|
},
|
||||||
this.recordState()
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 查询基础模型配置 兼容老的chatgroup */
|
/* 查询我的对话组 */
|
||||||
async getBaseModelConfig() {
|
async queryMyGroup() {
|
||||||
const res = await fetchModelBaseConfigAPI()
|
const res: any = await fetchQueryGroupAPI();
|
||||||
this.baseConfig = res?.data
|
this.groupList = [
|
||||||
},
|
...res.data.map((item: any) => {
|
||||||
|
const {
|
||||||
|
id: uuid,
|
||||||
|
title,
|
||||||
|
isSticky,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
appId,
|
||||||
|
config,
|
||||||
|
appLogo,
|
||||||
|
} = item;
|
||||||
|
return {
|
||||||
|
uuid,
|
||||||
|
title,
|
||||||
|
isEdit: false,
|
||||||
|
appId,
|
||||||
|
config,
|
||||||
|
isSticky,
|
||||||
|
appLogo,
|
||||||
|
createdAt,
|
||||||
|
updatedAt: new Date(updatedAt).getTime(),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const isHasActive = this.groupList.some(
|
||||||
|
(item) => Number(item.uuid) === Number(this.active)
|
||||||
|
);
|
||||||
|
if (!this.active || !isHasActive)
|
||||||
|
this.groupList.length && this.setActiveGroup(this.groupList[0].uuid);
|
||||||
|
},
|
||||||
|
|
||||||
/* 查询我的对话组 */
|
/* 修改对话组信息 */
|
||||||
async queryMyGroup() {
|
async updateGroupInfo(params: {
|
||||||
const res: any = await fetchQueryGroupAPI()
|
groupId: number;
|
||||||
this.groupList = [...res.data.map((item: any) => {
|
title?: string;
|
||||||
const { id: uuid, title, isSticky, createdAt, updatedAt, appId, config, appLogo } = item
|
isSticky?: boolean;
|
||||||
return { uuid, title, isEdit: false, appId, config, isSticky, appLogo, createdAt, updatedAt: new Date(updatedAt).getTime() }
|
}) {
|
||||||
})]
|
await fetchUpdateGroupAPI(params);
|
||||||
const isHasActive = this.groupList.some(item => Number(item.uuid) === Number(this.active))
|
},
|
||||||
if (!this.active || !isHasActive)
|
|
||||||
this.groupList.length && this.setActiveGroup(this.groupList[0].uuid)
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 修改对话组信息 */
|
/* 变更对话组 */
|
||||||
async updateGroupInfo(params: { groupId: number; title?: string; isSticky?: boolean }) {
|
async setActiveGroup(uuid: number) {
|
||||||
await fetchUpdateGroupAPI(params)
|
this.active = uuid;
|
||||||
},
|
if (this.active) await this.queryActiveChatLogList();
|
||||||
|
else this.chatList = [];
|
||||||
|
|
||||||
/* 变更对话组 */
|
this.groupList.forEach((item) => (item.isEdit = false));
|
||||||
async setActiveGroup(uuid: number) {
|
this.recordState();
|
||||||
this.active = uuid
|
},
|
||||||
if (this.active)
|
|
||||||
await this.queryActiveChatLogList()
|
|
||||||
|
|
||||||
else
|
/* 删除对话组 */
|
||||||
this.chatList = []
|
async deleteGroup(params: Chat.History) {
|
||||||
|
const curIndex = this.groupList.findIndex(
|
||||||
|
(item) => item.uuid === params.uuid
|
||||||
|
);
|
||||||
|
const { uuid: groupId } = params;
|
||||||
|
await fetchDelGroupAPI({ groupId });
|
||||||
|
await this.queryMyGroup();
|
||||||
|
if (this.groupList.length === 0) await this.setActiveGroup(0);
|
||||||
|
|
||||||
this.groupList.forEach(item => (item.isEdit = false))
|
if (curIndex > 0 && curIndex < this.groupList.length)
|
||||||
this.recordState()
|
await this.setActiveGroup(this.groupList[curIndex].uuid);
|
||||||
},
|
|
||||||
|
|
||||||
/* 删除对话组 */
|
if (curIndex === 0 && this.groupList.length > 0)
|
||||||
async deleteGroup(params: Chat.History) {
|
await this.setActiveGroup(this.groupList[0].uuid);
|
||||||
const curIndex = this.groupList.findIndex(item => item.uuid === params.uuid)
|
|
||||||
const { uuid: groupId } = params
|
|
||||||
await fetchDelGroupAPI({ groupId })
|
|
||||||
await this.queryMyGroup()
|
|
||||||
if (this.groupList.length === 0)
|
|
||||||
await this.setActiveGroup(0)
|
|
||||||
|
|
||||||
if (curIndex > 0 && curIndex < this.groupList.length)
|
if (
|
||||||
await this.setActiveGroup(this.groupList[curIndex].uuid)
|
curIndex > this.groupList.length ||
|
||||||
|
(curIndex === 0 && this.groupList.length === 0)
|
||||||
|
)
|
||||||
|
await this.setActiveGroup(0);
|
||||||
|
|
||||||
if (curIndex === 0 && this.groupList.length > 0)
|
if (curIndex > 0 && curIndex === this.groupList.length)
|
||||||
await this.setActiveGroup(this.groupList[0].uuid)
|
await this.setActiveGroup(this.groupList[curIndex - 1].uuid);
|
||||||
|
|
||||||
if (curIndex > this.groupList.length || (curIndex === 0 && this.groupList.length === 0))
|
this.recordState();
|
||||||
await this.setActiveGroup(0)
|
},
|
||||||
|
|
||||||
if (curIndex > 0 && curIndex === this.groupList.length)
|
/* 删除全部非置顶对话组 */
|
||||||
await this.setActiveGroup(this.groupList[curIndex - 1].uuid)
|
async delAllGroup() {
|
||||||
|
if (!this.active || !this.groupList.length) return;
|
||||||
|
await fetchDelAllGroupAPI();
|
||||||
|
await this.queryMyGroup();
|
||||||
|
if (this.groupList.length === 0) await this.setActiveGroup(0);
|
||||||
|
else await this.setActiveGroup(this.groupList[0].uuid);
|
||||||
|
},
|
||||||
|
|
||||||
this.recordState()
|
/* 查询当前对话组的聊天记录 */
|
||||||
},
|
async queryActiveChatLogList() {
|
||||||
|
if (!this.active || Number(this.active) === 0) return;
|
||||||
|
const res: any = await fetchQueryChatLogListAPI({ groupId: this.active });
|
||||||
|
this.chatList = res.data;
|
||||||
|
},
|
||||||
|
|
||||||
/* 删除全部非置顶对话组 */
|
/* 添加一条虚拟的对话记录 */
|
||||||
async delAllGroup() {
|
addGroupChat(data) {
|
||||||
if (!this.active || !this.groupList.length)
|
this.chatList = [...this.chatList, data];
|
||||||
return
|
},
|
||||||
await fetchDelAllGroupAPI()
|
|
||||||
await this.queryMyGroup()
|
|
||||||
if (this.groupList.length === 0)
|
|
||||||
await this.setActiveGroup(0)
|
|
||||||
|
|
||||||
else
|
/* 动态修改对话记录 */
|
||||||
await this.setActiveGroup(this.groupList[0].uuid)
|
updateGroupChat(index: number, data: Chat.Chat) {
|
||||||
},
|
this.chatList[index] = { ...this.chatList[index], ...data };
|
||||||
|
},
|
||||||
|
|
||||||
/* 查询当前对话组的聊天记录 */
|
/* 修改其中部分内容 */
|
||||||
async queryActiveChatLogList() {
|
updateGroupChatSome(index: number, data: Partial<Chat.Chat>) {
|
||||||
if (!this.active || Number(this.active) === 0)
|
this.chatList[index] = { ...this.chatList[index], ...data };
|
||||||
return
|
},
|
||||||
const res: any = await fetchQueryChatLogListAPI({ groupId: this.active })
|
|
||||||
this.chatList = res.data
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 添加一条虚拟的对话记录 */
|
/* 删除一条对话记录 */
|
||||||
addGroupChat(data) {
|
async deleteChatById(chatId: number | undefined) {
|
||||||
this.chatList = [...this.chatList, data]
|
console.log(chatId);
|
||||||
},
|
if (!chatId) return;
|
||||||
|
await fetchDelChatLogAPI({ id: chatId });
|
||||||
|
await this.queryActiveChatLogList();
|
||||||
|
},
|
||||||
|
|
||||||
/* 动态修改对话记录 */
|
/* 查询快问预设 */
|
||||||
updateGroupChat(index: number, data: Chat.Chat) {
|
async queryChatPre() {
|
||||||
this.chatList[index] = { ...this.chatList[index], ...data }
|
const res: any = await fetchGetChatPreList();
|
||||||
},
|
if (!res.data) return;
|
||||||
|
this.chatPreList = formatChatPre(res.data);
|
||||||
|
},
|
||||||
|
|
||||||
/* 修改其中部分内容 */
|
/* 设置使用上下文 */
|
||||||
updateGroupChatSome(index: number, data: Partial<Chat.Chat>) {
|
setUsingContext(context: boolean) {
|
||||||
this.chatList[index] = { ...this.chatList[index], ...data }
|
this.usingContext = context;
|
||||||
},
|
this.recordState();
|
||||||
|
},
|
||||||
|
|
||||||
/* 删除一条对话记录 */
|
/* 设置使用联网 */
|
||||||
async deleteChatById(chatId: number | undefined) {
|
setUsingNetwork(context: boolean) {
|
||||||
console.log(chatId)
|
this.usingNetwork = context;
|
||||||
if (!chatId)
|
this.recordState();
|
||||||
return
|
},
|
||||||
await fetchDelChatLogAPI({ id: chatId })
|
|
||||||
await this.queryActiveChatLogList()
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 查询快问预设 */
|
/* 删除当前对话组的全部内容 */
|
||||||
async queryChatPre() {
|
async clearChatByGroupId() {
|
||||||
const res: any = await fetchGetChatPreList()
|
if (!this.active) return;
|
||||||
if (!res.data)
|
|
||||||
return
|
|
||||||
this.chatPreList = formatChatPre(res.data)
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 设置使用上下文 */
|
await fetchDelChatLogByGroupIdAPI({ groupId: this.active });
|
||||||
setUsingContext(context: boolean) {
|
await this.queryActiveChatLogList();
|
||||||
this.usingContext = context
|
},
|
||||||
this.recordState()
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 设置使用联网 */
|
recordState() {
|
||||||
setUsingNetwork(context: boolean) {
|
setLocalState(this.$state);
|
||||||
this.usingNetwork = context
|
},
|
||||||
this.recordState()
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 删除当前对话组的全部内容 */
|
clearChat() {
|
||||||
async clearChatByGroupId() {
|
this.chatList = [];
|
||||||
if (!this.active)
|
this.groupList = [];
|
||||||
return
|
this.active = 0;
|
||||||
|
this.recordState();
|
||||||
await fetchDelChatLogByGroupIdAPI({ groupId: this.active })
|
},
|
||||||
await this.queryActiveChatLogList()
|
},
|
||||||
},
|
});
|
||||||
|
|
||||||
recordState() {
|
|
||||||
setLocalState(this.$state)
|
|
||||||
},
|
|
||||||
|
|
||||||
clearChat() {
|
|
||||||
this.chatList = []
|
|
||||||
this.groupList = []
|
|
||||||
this.active = 0
|
|
||||||
this.recordState()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
@ -12,7 +12,9 @@ import {
|
|||||||
NTooltip,
|
NTooltip,
|
||||||
useDialog,
|
useDialog,
|
||||||
useMessage,
|
useMessage,
|
||||||
|
NAlert
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
|
import type { MessageRenderMessage } from 'naive-ui'
|
||||||
|
|
||||||
import html2canvas from 'html2canvas'
|
import html2canvas from 'html2canvas'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
@ -86,7 +88,7 @@ const theme = computed(() => appStore.theme)
|
|||||||
|
|
||||||
const globaelConfig = computed(() => authStore.globalConfig)
|
const globaelConfig = computed(() => authStore.globalConfig)
|
||||||
const isSetBeian = computed(
|
const isSetBeian = computed(
|
||||||
() => globaelConfig.value?.companyName && globaelConfig.value?.filingNumber,
|
() => globaelConfig.value?.companyName && globaelConfig.value?.filingNumber
|
||||||
)
|
)
|
||||||
const { addGroupChat, updateGroupChat, updateGroupChatSome } = useChat()
|
const { addGroupChat, updateGroupChat, updateGroupChatSome } = useChat()
|
||||||
const tradeStatus = computed(() => route.query.trade_status as string)
|
const tradeStatus = computed(() => route.query.trade_status as string)
|
||||||
@ -101,13 +103,13 @@ const dataSources = computed(() => chatStore.chatList)
|
|||||||
|
|
||||||
/* 当前所有的ai回复信息列表 方便拿到上下文 */
|
/* 当前所有的ai回复信息列表 方便拿到上下文 */
|
||||||
const conversationList = computed(() =>
|
const conversationList = computed(() =>
|
||||||
dataSources.value.filter(item => !item.inversion && !item.error),
|
dataSources.value.filter((item) => !item.inversion && !item.error)
|
||||||
)
|
)
|
||||||
|
|
||||||
/* 当前上下文有id的最后一条 防止停止回答的时候 上一条的id是空 接不上上下文 */
|
/* 当前上下文有id的最后一条 防止停止回答的时候 上一条的id是空 接不上上下文 */
|
||||||
const lastContext = computed(() => {
|
const lastContext = computed(() => {
|
||||||
const hasIdCoversationList = conversationList.value.filter(
|
const hasIdCoversationList = conversationList.value.filter(
|
||||||
item => item.conversationOptions?.parentMessageId,
|
(item) => item.conversationOptions?.parentMessageId
|
||||||
)
|
)
|
||||||
return hasIdCoversationList[hasIdCoversationList.length - 1]
|
return hasIdCoversationList[hasIdCoversationList.length - 1]
|
||||||
?.conversationOptions
|
?.conversationOptions
|
||||||
@ -120,17 +122,22 @@ const firstScroll = ref<boolean>(true)
|
|||||||
const tipsRef = ref<any>(null)
|
const tipsRef = ref<any>(null)
|
||||||
const tipText = ref('')
|
const tipText = ref('')
|
||||||
const tipsHeight = ref<any>(null)
|
const tipsHeight = ref<any>(null)
|
||||||
|
const dataBase64 = ref(null)
|
||||||
|
const fileName = ref('')
|
||||||
|
const isImageFile = ref(false)
|
||||||
|
const showDeleteIcon = ref(false)
|
||||||
|
|
||||||
/* 当前选中的对话组 */
|
/* 当前选中的对话组 */
|
||||||
const activeGroupId = computed(() => chatStore.active)
|
const activeGroupId = computed(() => chatStore.active)
|
||||||
/* 当前对话组的详细信息 */
|
/* 当前对话组的详细信息 */
|
||||||
const activeGroupInfo = computed(() =>
|
const activeGroupInfo = computed(() =>
|
||||||
chatStore.groupList.find((item: any) => item.uuid === chatStore.active),
|
chatStore.groupList.find((item: any) => item.uuid === chatStore.active)
|
||||||
)
|
)
|
||||||
/* 当前选用的模型的类型 1: openai 2: 百度 */
|
/* 当前选用的模型的类型 1: openai 2: 百度 */
|
||||||
const activeModelKeyType = computed(() => Number(chatStore?.activeModelKeyType))
|
const activeModelKeyType = computed(() => Number(chatStore?.activeModelKeyType))
|
||||||
/* 当前对话组是否是应用 */
|
/* 当前对话组是否是应用 */
|
||||||
const activeAppId = computed(() =>
|
const activeAppId = computed(() =>
|
||||||
activeGroupInfo?.value ? activeGroupInfo.value.appId : 0,
|
activeGroupInfo?.value ? activeGroupInfo.value.appId : 0
|
||||||
)
|
)
|
||||||
/* 粘贴板的文字 */
|
/* 粘贴板的文字 */
|
||||||
const clipboardText = computed(() => useGlobalStore.clipboardText)
|
const clipboardText = computed(() => useGlobalStore.clipboardText)
|
||||||
@ -144,43 +151,37 @@ watch(clipboardText, (val) => {
|
|||||||
watch(
|
watch(
|
||||||
activeAppId,
|
activeAppId,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val)
|
if (val) queryAppDetail(val)
|
||||||
queryAppDetail(val)
|
|
||||||
else appDetail.value = null
|
else appDetail.value = null
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
activeGroupId,
|
activeGroupId,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val)
|
if (val) firstScroll.value = true
|
||||||
firstScroll.value = true
|
if (inputRef.value && !isMobile.value) inputRef.value?.focus()
|
||||||
if (inputRef.value && !isMobile.value)
|
|
||||||
inputRef.value?.focus()
|
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
dataSources,
|
dataSources,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val.length === 0)
|
if (val.length === 0) return
|
||||||
return
|
|
||||||
if (firstScroll.value) {
|
if (firstScroll.value) {
|
||||||
firstScroll.value = false
|
firstScroll.value = false
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const modelName = computed(() => {
|
const modelName = computed(() => {
|
||||||
if (!chatStore.activeConfig)
|
if (!chatStore.activeConfig) return
|
||||||
return
|
|
||||||
const { modelTypeInfo, modelInfo } = chatStore.activeConfig
|
const { modelTypeInfo, modelInfo } = chatStore.activeConfig
|
||||||
if (!modelTypeInfo || !modelInfo)
|
if (!modelTypeInfo || !modelInfo) return
|
||||||
return
|
|
||||||
return `${modelInfo.modelName}`
|
return `${modelInfo.modelName}`
|
||||||
})
|
})
|
||||||
function handleOpenModelDialog() {
|
function handleOpenModelDialog() {
|
||||||
@ -202,14 +203,52 @@ let curFile: File | null
|
|||||||
|
|
||||||
async function handleFileSelect(event: any) {
|
async function handleFileSelect(event: any) {
|
||||||
const file = event?.target?.files[0]
|
const file = event?.target?.files[0]
|
||||||
if (file.size <= 5 * 1024 * 1024)
|
if (!file) return
|
||||||
|
if (file.size <= 10 * 1024 * 1024) {
|
||||||
await handleSetFile(file)
|
await handleSetFile(file)
|
||||||
else ms.error('上传文件失败,上传大小不能超过5M')
|
} else {
|
||||||
|
return ms.error('上传文件失败,上传大小不能超过10M')
|
||||||
|
}
|
||||||
|
let trimmedFileName = file.name
|
||||||
|
const maxLength = 8 // 最大长度限制
|
||||||
|
const extension = trimmedFileName.split('.').pop() // 获取文件扩展名
|
||||||
|
|
||||||
|
if (trimmedFileName.length > maxLength) {
|
||||||
|
// 截取文件名并添加省略号,同时保留扩展名
|
||||||
|
trimmedFileName =
|
||||||
|
trimmedFileName.substring(0, maxLength - extension.length - 1) +
|
||||||
|
'….' +
|
||||||
|
extension
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName.value = trimmedFileName // 更新文件名
|
||||||
|
console.log(file.type)
|
||||||
|
// 检查文件类型
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
// 处理图像文件
|
||||||
|
isImageFile.value = true
|
||||||
|
handleSetFile(file)
|
||||||
|
} else if (
|
||||||
|
file.type.startsWith('application/') ||
|
||||||
|
file.type.startsWith('text/')
|
||||||
|
) {
|
||||||
|
// 处理文件类型
|
||||||
|
isImageFile.value = false
|
||||||
|
handleSetFile(file)
|
||||||
|
} else {
|
||||||
|
// 处理其他类型的文件或显示错误消息
|
||||||
|
ms.error('上传文件失败,不支持此类型文件')
|
||||||
|
console.log('不支持的文件类型')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSetFile(file: File) {
|
async function handleSetFile(file: File) {
|
||||||
curFile = file
|
curFile = file
|
||||||
uploadFile()
|
const reader = new FileReader()
|
||||||
|
reader.onload = (event: any) => {
|
||||||
|
dataBase64.value = event.target?.result as string
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadBtn() {
|
function uploadBtn() {
|
||||||
@ -233,11 +272,13 @@ async function uploadFile() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
prompt.value = `请分析一下上传的这个文件:${res?.data?.data}`
|
return res?.data?.data
|
||||||
ms.success('上传成功,输入内容后发送', { duration: 5000, closable: true })
|
} catch (error) {
|
||||||
}
|
ms.error('网络异常,发送失败')
|
||||||
catch (error) {
|
return null
|
||||||
ms.error('网络异常,上传失败')
|
} finally {
|
||||||
|
dataBase64.value = null
|
||||||
|
curFile = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,9 +312,9 @@ function handleScrollBtm() {
|
|||||||
/* 发送消息 */
|
/* 发送消息 */
|
||||||
async function handleSubmit(index?: number) {
|
async function handleSubmit(index?: number) {
|
||||||
if (
|
if (
|
||||||
chatStore.groupList.length === 0
|
chatStore.groupList.length === 0 ||
|
||||||
|| loading.value
|
loading.value ||
|
||||||
|| !typingStatusEnd.value
|
!typingStatusEnd.value
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
let message = ''
|
let message = ''
|
||||||
@ -287,12 +328,10 @@ async function handleSubmit(index?: number) {
|
|||||||
|
|
||||||
function parseTextToJSON(input: string) {
|
function parseTextToJSON(input: string) {
|
||||||
const startIndex = input.indexOf(',"text":"') + 10
|
const startIndex = input.indexOf(',"text":"') + 10
|
||||||
if (startIndex === -1)
|
if (startIndex === -1) return { text: '' }
|
||||||
return { text: '' }
|
|
||||||
|
|
||||||
let endIndex = input.indexOf('","delta"', startIndex)
|
let endIndex = input.indexOf('","delta"', startIndex)
|
||||||
if (endIndex === -1)
|
if (endIndex === -1) endIndex = input.length - 1
|
||||||
endIndex = input.length - 1
|
|
||||||
else endIndex = endIndex - 10
|
else endIndex = endIndex - 10
|
||||||
|
|
||||||
const text = input.substring(startIndex, endIndex)
|
const text = input.substring(startIndex, endIndex)
|
||||||
@ -301,15 +340,17 @@ function parseTextToJSON(input: string) {
|
|||||||
|
|
||||||
/* 按钮发送消息 */
|
/* 按钮发送消息 */
|
||||||
async function onConversation(msg?: string) {
|
async function onConversation(msg?: string) {
|
||||||
|
let imageUrl = null
|
||||||
|
if (dataBase64.value || curFile) {
|
||||||
|
imageUrl = await uploadFile()
|
||||||
|
}
|
||||||
let message = msg || prompt.value
|
let message = msg || prompt.value
|
||||||
if (tipText.value && !message.includes(tipText.value))
|
if (tipText.value && !message.includes(tipText.value))
|
||||||
message = `${tipText.value}\n${message}`
|
message = `${tipText.value}\n${message}`
|
||||||
|
|
||||||
if (loading.value)
|
if (loading.value) return
|
||||||
return
|
|
||||||
|
|
||||||
if (!message || message.trim() === '')
|
if (!message || message.trim() === '') return
|
||||||
return
|
|
||||||
|
|
||||||
controller = new AbortController()
|
controller = new AbortController()
|
||||||
|
|
||||||
@ -319,6 +360,7 @@ async function onConversation(msg?: string) {
|
|||||||
text: message,
|
text: message,
|
||||||
inversion: true,
|
inversion: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
imageUrl,
|
||||||
conversationOptions: null,
|
conversationOptions: null,
|
||||||
requestOptions: { prompt: message, options: null },
|
requestOptions: { prompt: message, options: null },
|
||||||
})
|
})
|
||||||
@ -369,12 +411,10 @@ async function onConversation(msg?: string) {
|
|||||||
if (cacheResText.length - i > 150) {
|
if (cacheResText.length - i > 150) {
|
||||||
currentText += cacheResText.substring(i, i + 10)
|
currentText += cacheResText.substring(i, i + 10)
|
||||||
i += 10
|
i += 10
|
||||||
}
|
} else if (cacheResText.length - i > 200) {
|
||||||
else if (cacheResText.length - i > 200) {
|
|
||||||
currentText += cacheResText.substring(i)
|
currentText += cacheResText.substring(i)
|
||||||
i += cacheResText.length - i
|
i += cacheResText.length - i
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
currentText += cacheResText[i]
|
currentText += cacheResText[i]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@ -396,8 +436,8 @@ async function onConversation(msg?: string) {
|
|||||||
const curLen = currentText ? currentText.length : 0
|
const curLen = currentText ? currentText.length : 0
|
||||||
const cacheResLen = cacheResText ? cacheResText.length : 0
|
const cacheResLen = cacheResText ? cacheResText.length : 0
|
||||||
if (
|
if (
|
||||||
!isStreamIn.value
|
!isStreamIn.value &&
|
||||||
&& (curLen === cacheResLen || curLen > cacheResLen)
|
(curLen === cacheResLen || curLen > cacheResLen)
|
||||||
) {
|
) {
|
||||||
typingStatusEnd.value = true
|
typingStatusEnd.value = true
|
||||||
updateGroupChatSome(dataSources.value.length - 1, {
|
updateGroupChatSome(dataSources.value.length - 1, {
|
||||||
@ -413,12 +453,12 @@ async function onConversation(msg?: string) {
|
|||||||
authStore.updateUserBanance(userBanance)
|
authStore.updateUserBanance(userBanance)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
dataSources.value.length === 2
|
dataSources.value.length === 2 &&
|
||||||
&& !activeGroupInfo?.value?.appId
|
!activeGroupInfo?.value?.appId
|
||||||
) {
|
) {
|
||||||
const lengthStr = isMobile.value ? 10 : 20
|
const lengthStr = isMobile.value ? 10 : 20
|
||||||
const title
|
const title =
|
||||||
= dataSources.value[1].text.length > lengthStr
|
dataSources.value[1].text.length > lengthStr
|
||||||
? dataSources.value[1].text.slice(0, lengthStr)
|
? dataSources.value[1].text.slice(0, lengthStr)
|
||||||
: dataSources.value[1].text
|
: dataSources.value[1].text
|
||||||
chatStore
|
chatStore
|
||||||
@ -433,8 +473,7 @@ async function onConversation(msg?: string) {
|
|||||||
/* 有多余的再请求下一帧 */
|
/* 有多余的再请求下一帧 */
|
||||||
if (cacheResText.length && cacheResText.length > currentText.length) {
|
if (cacheResText.length && cacheResText.length > currentText.length) {
|
||||||
requestAnimationFrame(update)
|
requestAnimationFrame(update)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
requestAnimationFrame(update)
|
requestAnimationFrame(update)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
@ -447,6 +486,8 @@ async function onConversation(msg?: string) {
|
|||||||
prompt: message,
|
prompt: message,
|
||||||
appId: activeGroupInfo.value ? activeGroupInfo.value.appId : 0,
|
appId: activeGroupInfo.value ? activeGroupInfo.value.appId : 0,
|
||||||
options,
|
options,
|
||||||
|
imageUrl,
|
||||||
|
model: chatStore?.activeModelName,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
onDownloadProgress: ({ event }) => {
|
onDownloadProgress: ({ event }) => {
|
||||||
const xhr = event.target
|
const xhr = event.target
|
||||||
@ -456,16 +497,14 @@ async function onConversation(msg?: string) {
|
|||||||
if ([1].includes(activeModelKeyType.value)) {
|
if ([1].includes(activeModelKeyType.value)) {
|
||||||
const lastIndex = responseText.lastIndexOf(
|
const lastIndex = responseText.lastIndexOf(
|
||||||
'\n',
|
'\n',
|
||||||
responseText.length - 2,
|
responseText.length - 2
|
||||||
)
|
)
|
||||||
let chunk = responseText
|
let chunk = responseText
|
||||||
if (lastIndex !== -1)
|
if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
|
||||||
chunk = responseText.substring(lastIndex)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(chunk)
|
data = JSON.parse(chunk)
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
/* 二次解析 */
|
/* 二次解析 */
|
||||||
// const parseData = parseTextToJSON(responseText)
|
// const parseData = parseTextToJSON(responseText)
|
||||||
// TODO 如果出现类似超时错误 会连接上次的内容一起发出来导致无法解析 后端需要处理 下
|
// TODO 如果出现类似超时错误 会连接上次的内容一起发出来导致无法解析 后端需要处理 下
|
||||||
@ -489,8 +528,7 @@ async function onConversation(msg?: string) {
|
|||||||
const parseData = JSON.parse(line)
|
const parseData = JSON.parse(line)
|
||||||
cacheResult += parseData.result
|
cacheResult += parseData.result
|
||||||
tem = parseData
|
tem = parseData
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
console.log('Json parse 2 3 type error: ')
|
console.log('Json parse 2 3 type error: ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -502,8 +540,7 @@ async function onConversation(msg?: string) {
|
|||||||
/* 如果出现输出内容不一致就需要处理了 */
|
/* 如果出现输出内容不一致就需要处理了 */
|
||||||
if (activeModelKeyType.value === 1) {
|
if (activeModelKeyType.value === 1) {
|
||||||
cacheResText = data.text
|
cacheResText = data.text
|
||||||
if (data?.userBanance)
|
if (data?.userBanance) userBanance = data?.userBanance
|
||||||
userBanance = data?.userBanance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([2, 3].includes(activeModelKeyType.value)) {
|
if ([2, 3].includes(activeModelKeyType.value)) {
|
||||||
@ -512,24 +549,21 @@ async function onConversation(msg?: string) {
|
|||||||
isStreamIn.value = !is_end
|
isStreamIn.value = !is_end
|
||||||
data?.userBanance && (userBanance = data?.userBanance)
|
data?.userBanance && (userBanance = data?.userBanance)
|
||||||
}
|
}
|
||||||
}
|
} catch (error) {}
|
||||||
catch (error) {}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
await fetchChatAPIOnce()
|
await fetchChatAPIOnce()
|
||||||
}
|
} catch (error: any) {
|
||||||
catch (error: any) {
|
|
||||||
useGlobalStore.updateIsChatIn(false)
|
useGlobalStore.updateIsChatIn(false)
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
isStreamIn.value = false
|
isStreamIn.value = false
|
||||||
if (
|
if (
|
||||||
error.code === 402
|
error.code === 402 ||
|
||||||
|| error?.message.includes('余额不足')
|
error?.message.includes('余额不足') ||
|
||||||
|| error?.message.includes('免费额度已经使用完毕')
|
error?.message.includes('免费额度已经使用完毕')
|
||||||
) {
|
) {
|
||||||
if (isLogin.value)
|
if (isLogin.value) useGlobalStore.updateGoodsDialog(true)
|
||||||
useGlobalStore.updateGoodsDialog(true)
|
|
||||||
else authStore.setLoginDialog(true)
|
else authStore.setLoginDialog(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,10 +602,10 @@ async function onConversation(msg?: string) {
|
|||||||
requestOptions: { prompt: message, options: { ...options } },
|
requestOptions: { prompt: message, options: { ...options } },
|
||||||
})
|
})
|
||||||
scrollToBottomIfAtBottom()
|
scrollToBottomIfAtBottom()
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
isStreamIn.value = false
|
isStreamIn.value = false
|
||||||
|
imageUrl = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,16 +623,14 @@ function handleRefresh() {
|
|||||||
ms.success('感谢你的购买、祝您使用愉快~', { duration: 5000 })
|
ms.success('感谢你的购买、祝您使用愉快~', { duration: 5000 })
|
||||||
authStore.getUserInfo()
|
authStore.getUserInfo()
|
||||||
router.replace({ name: 'Chat', query: {} })
|
router.replace({ name: 'Chat', query: {} })
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ms.error('您还没有购买成功哦~')
|
ms.error('您还没有购买成功哦~')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导出 */
|
/* 导出 */
|
||||||
function handleExport() {
|
function handleExport() {
|
||||||
if (loading.value)
|
if (loading.value) return
|
||||||
return
|
|
||||||
|
|
||||||
const d = dialog.warning({
|
const d = dialog.warning({
|
||||||
title: t('chat.exportImage'),
|
title: t('chat.exportImage'),
|
||||||
@ -627,11 +659,9 @@ function handleExport() {
|
|||||||
d.loading = false
|
d.loading = false
|
||||||
ms.success(t('chat.exportSuccess'))
|
ms.success(t('chat.exportSuccess'))
|
||||||
Promise.resolve()
|
Promise.resolve()
|
||||||
}
|
} catch (error: any) {
|
||||||
catch (error: any) {
|
|
||||||
ms.error(t('chat.exportFailed'))
|
ms.error(t('chat.exportFailed'))
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
d.loading = false
|
d.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -640,8 +670,7 @@ function handleExport() {
|
|||||||
|
|
||||||
/* 删除 */
|
/* 删除 */
|
||||||
function handleDelete({ chatId }: Chat.Chat) {
|
function handleDelete({ chatId }: Chat.Chat) {
|
||||||
if (loading.value)
|
if (loading.value) return
|
||||||
return
|
|
||||||
|
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: t('chat.deleteMessage'),
|
title: t('chat.deleteMessage'),
|
||||||
@ -655,8 +684,7 @@ function handleDelete({ chatId }: Chat.Chat) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleClear() {
|
function handleClear() {
|
||||||
if (loading.value)
|
if (loading.value) return
|
||||||
return
|
|
||||||
|
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: t('chat.clearChat'),
|
title: t('chat.clearChat'),
|
||||||
@ -676,8 +704,7 @@ function handleEnter(event: KeyboardEvent) {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
handleSubmit()
|
handleSubmit()
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (event.key === 'Enter' && event.ctrlKey) {
|
if (event.key === 'Enter' && event.ctrlKey) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
handleSubmit()
|
handleSubmit()
|
||||||
@ -695,17 +722,16 @@ function handleStop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const placeholder = computed(() => {
|
const placeholder = computed(() => {
|
||||||
if (isMobile.value)
|
if (isMobile.value) return t('chat.placeholderMobile')
|
||||||
return t('chat.placeholderMobile')
|
|
||||||
return t('chat.placeholder')
|
return t('chat.placeholder')
|
||||||
})
|
})
|
||||||
|
|
||||||
const buttonDisabled = computed(() => {
|
const buttonDisabled = computed(() => {
|
||||||
return (
|
return (
|
||||||
loading.value
|
loading.value ||
|
||||||
|| !prompt.value
|
!prompt.value ||
|
||||||
|| prompt.value.trim() === ''
|
prompt.value.trim() === '' ||
|
||||||
|| !typingStatusEnd.value
|
!typingStatusEnd.value
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
// 改动:发送后添加loading圈圈
|
// 改动:发送后添加loading圈圈
|
||||||
@ -719,8 +745,7 @@ function getTipsRefHeight() {
|
|||||||
|
|
||||||
function onInputeTip() {
|
function onInputeTip() {
|
||||||
tipsHeight.value = 'auto'
|
tipsHeight.value = 'auto'
|
||||||
if (!tipText.value)
|
if (!tipText.value) tipsHeight.value = 0
|
||||||
tipsHeight.value = 0
|
|
||||||
|
|
||||||
nextTick(() => getTipsRefHeight())
|
nextTick(() => getTipsRefHeight())
|
||||||
}
|
}
|
||||||
@ -728,24 +753,20 @@ function onInputeTip() {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
chatStore.queryChatPre()
|
chatStore.queryChatPre()
|
||||||
|
|
||||||
if (token.value)
|
if (token.value) otherLoginByToken(token.value)
|
||||||
otherLoginByToken(token.value)
|
|
||||||
|
|
||||||
if (tradeStatus.value)
|
if (tradeStatus.value) handleRefresh()
|
||||||
handleRefresh()
|
|
||||||
|
|
||||||
nextTick(async () => {
|
nextTick(async () => {
|
||||||
await chatStore.queryActiveChatLogList()
|
await chatStore.queryActiveChatLogList()
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
if (inputRef.value && !isMobile.value)
|
if (inputRef.value && !isMobile.value) inputRef.value?.focus()
|
||||||
inputRef.value?.focus()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const darkMode = computed(() => appStore.theme === 'dark')
|
const darkMode = computed(() => appStore.theme === 'dark')
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (loading.value)
|
if (loading.value) controller.abort()
|
||||||
controller.abort()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -793,6 +814,7 @@ onUnmounted(() => {
|
|||||||
:inversion="item.inversion"
|
:inversion="item.inversion"
|
||||||
:error="item.error"
|
:error="item.error"
|
||||||
:loading="item.loading"
|
:loading="item.loading"
|
||||||
|
:imageUrl="item.imageUrl"
|
||||||
@regenerate="handleSubmit(index)"
|
@regenerate="handleSubmit(index)"
|
||||||
@delete="handleDelete(item)"
|
@delete="handleDelete(item)"
|
||||||
/>
|
/>
|
||||||
@ -825,10 +847,10 @@ onUnmounted(() => {
|
|||||||
'text-[#3076fd]': usingContext,
|
'text-[#3076fd]': usingContext,
|
||||||
'text-[#a8071a]': !usingContext,
|
'text-[#a8071a]': !usingContext,
|
||||||
}"
|
}"
|
||||||
><SvgIcon
|
><SvgIcon
|
||||||
class="text-lg"
|
class="text-lg"
|
||||||
style="width: 1em; height: 1em"
|
style="width: 1em; height: 1em"
|
||||||
icon="ri:chat-history-line"
|
icon="ri:chat-history-line"
|
||||||
/></span>
|
/></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
@ -852,10 +874,11 @@ onUnmounted(() => {
|
|||||||
class="shrink0 flex h-8 w-8 items-center justify-center rounded border transition hover:bg-[#eef0f3] dark:border-neutral-700 dark:hover:bg-[#33373c]"
|
class="shrink0 flex h-8 w-8 items-center justify-center rounded border transition hover:bg-[#eef0f3] dark:border-neutral-700 dark:hover:bg-[#33373c]"
|
||||||
@click="openChatPre"
|
@click="openChatPre"
|
||||||
>
|
>
|
||||||
<span><SvgIcon
|
<span
|
||||||
class="text-lg"
|
><SvgIcon
|
||||||
style="width: 1em; height: 1em"
|
class="text-lg"
|
||||||
icon="noto:open-book"
|
style="width: 1em; height: 1em"
|
||||||
|
icon="noto:open-book"
|
||||||
/></span>
|
/></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
@ -950,7 +973,7 @@ onUnmounted(() => {
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<span class="text-base text-slate-500 dark:text-slate-400">
|
<span class="text-base text-slate-500 dark:text-slate-400">
|
||||||
<!-- <SvgIcon icon="streamline-emojis:wrapped-gift-1" /> -->
|
<!-- <SvgIcon icon="streamline-emojis:wrapped-gift-1" /> -->
|
||||||
<img :src="modelSvg" class="h-8" alt="">
|
<img :src="modelSvg" class="h-8" alt="" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<span style="color: #3076fd">{{ modelName }}</span>
|
<span style="color: #3076fd">{{ modelName }}</span>
|
||||||
@ -1007,7 +1030,10 @@ onUnmounted(() => {
|
|||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<NTooltip
|
<NTooltip
|
||||||
v-if="
|
v-if="
|
||||||
chatStore.activeConfig.modelInfo.model === 'gpt-4-all'
|
!dataBase64 &&
|
||||||
|
(chatStore.activeConfig.modelInfo.model === 'gpt-4-all' ||
|
||||||
|
chatStore.activeConfig.modelInfo.model ===
|
||||||
|
'gpt-4-vision-preview')
|
||||||
"
|
"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
@ -1025,24 +1051,70 @@ onUnmounted(() => {
|
|||||||
:multiple="false"
|
:multiple="false"
|
||||||
type="file"
|
type="file"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
accept="text/plain,image/*, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/pdf"
|
:accept="
|
||||||
@change="handleFileSelect($event)"
|
chatStore.activeConfig.modelInfo.model ===
|
||||||
>
|
'gpt-4-vision-preview'
|
||||||
<SvgIcon
|
? 'image/*'
|
||||||
class="text-lg"
|
: 'text/plain,image/*, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/pdf'
|
||||||
style="width: 1em; height: 1em"
|
"
|
||||||
icon="mingcute:upload-line"
|
@change="handleFileSelect($event)" />
|
||||||
/></span>
|
<SvgIcon icon="mingcute:upload-line"
|
||||||
|
/></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
上传
|
上传
|
||||||
</NTooltip>
|
</NTooltip>
|
||||||
|
<!-- 预览容器 -->
|
||||||
|
<div
|
||||||
|
v-if="dataBase64"
|
||||||
|
class="relative flex items-start justify-start"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="group"
|
||||||
|
@mouseover="showDeleteIcon = true"
|
||||||
|
@mouseleave="showDeleteIcon = false"
|
||||||
|
>
|
||||||
|
<!-- 根据 isImageFile 的值显示不同内容 -->
|
||||||
|
<template v-if="isImageFile">
|
||||||
|
<!-- 图片预览 -->
|
||||||
|
<img
|
||||||
|
:src="dataBase64"
|
||||||
|
class="max-w-full max-h-10 border border-gray-300 rounded-lg"
|
||||||
|
alt="预览图片"
|
||||||
|
/>
|
||||||
|
<!-- 清除图标 -->
|
||||||
|
<SvgIcon
|
||||||
|
class="close-icon"
|
||||||
|
icon="gg:close-o"
|
||||||
|
@click="dataBase64 = ''"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<!-- 非图片文件预览(例如文件图标) -->
|
||||||
|
<div
|
||||||
|
style="white-space: nowrap; padding: 0.25rem"
|
||||||
|
class="flex items-center justify-center border border-gray-300 rounded-lg h-8 hover:bg-gray-100 text-gray-700 dark:hover:bg-gray-700 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
<span>{{ fileName }}</span>
|
||||||
|
<!-- 清除图标 -->
|
||||||
|
<SvgIcon
|
||||||
|
class="close-icon"
|
||||||
|
icon="gg:close-o"
|
||||||
|
@click="dataBase64 = ''"
|
||||||
|
/>
|
||||||
|
<!-- 替换为适当的文件图标 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<div
|
<div
|
||||||
class="flex items-center text-neutral-400 cursor-pointer hover:text-[#3076fd]"
|
class="flex items-center text-neutral-400 cursor-pointer hover:text-[#3076fd]"
|
||||||
>
|
>
|
||||||
<span class="ml-2 mr-2 text-xs" @click="toggleUsingNetwork">{{ usingNetwork ? '关闭' : '开启' }}联网访问</span>
|
<span class="ml-2 mr-2 text-xs" @click="toggleUsingNetwork"
|
||||||
|
>{{ usingNetwork ? '关闭' : '开启' }}联网访问</span
|
||||||
|
>
|
||||||
<NTooltip trigger="hover" :disabled="isMobile">
|
<NTooltip trigger="hover" :disabled="isMobile">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<SvgIcon
|
<SvgIcon
|
||||||
@ -1101,9 +1173,10 @@ onUnmounted(() => {
|
|||||||
class="ml-2 transition-all text-[#aeaeae] hover:text-[#60606d]"
|
class="ml-2 transition-all text-[#aeaeae] hover:text-[#60606d]"
|
||||||
href="https://beian.miit.gov.cn"
|
href="https://beian.miit.gov.cn"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{{ globaelConfig?.filingNumber }}</a>
|
>{{ globaelConfig?.filingNumber }}</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<NModal v-model:show="showProgressModal" :mask-closable="false">
|
<!-- <NModal v-model:show="showProgressModal" :mask-closable="false">
|
||||||
<NCard
|
<NCard
|
||||||
style="width: 80%"
|
style="width: 80%"
|
||||||
title="上传文件中"
|
title="上传文件中"
|
||||||
@ -1119,7 +1192,7 @@ onUnmounted(() => {
|
|||||||
processing
|
processing
|
||||||
/>
|
/>
|
||||||
</NCard>
|
</NCard>
|
||||||
</NModal>
|
</NModal> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -1137,4 +1210,31 @@ onUnmounted(() => {
|
|||||||
.shrink0 {
|
.shrink0 {
|
||||||
flex-shrink: 0 !important;
|
flex-shrink: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: -0;
|
||||||
|
right: 0;
|
||||||
|
color: #ff6347;
|
||||||
|
font-size: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
animation: scaleAnim 2s infinite ease-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
font-weight: 800;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scaleAnim {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -15,6 +15,7 @@ interface Props {
|
|||||||
text?: string
|
text?: string
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
asRawText?: boolean
|
asRawText?: boolean
|
||||||
|
imageUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emit {
|
interface Emit {
|
||||||
@ -83,6 +84,12 @@ const text = computed(() => {
|
|||||||
if (!props.asRawText) return mdi.render(value)
|
if (!props.asRawText) return mdi.render(value)
|
||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
|
const imageUrl = computed(() => props.imageUrl)
|
||||||
|
|
||||||
|
const isImageUrl = computed(() => {
|
||||||
|
if (!imageUrl.value) return false
|
||||||
|
return /\.(jpg|jpeg|png|gif)$/i.test(imageUrl.value)
|
||||||
|
})
|
||||||
|
|
||||||
function highlightBlock(str: string, lang?: string) {
|
function highlightBlock(str: string, lang?: string) {
|
||||||
return `<pre class="code-block-wrapper ${
|
return `<pre class="code-block-wrapper ${
|
||||||
@ -122,7 +129,11 @@ defineExpose({ textRef })
|
|||||||
v-html="text"
|
v-html="text"
|
||||||
/>
|
/>
|
||||||
<div v-else class="w-full whitespace-pre-wrap" v-text="text" />
|
<div v-else class="w-full whitespace-pre-wrap" v-text="text" />
|
||||||
<span v-if="loading" class="dark:text-white w-[4px] h-[20px] block animate-blink" style="display:none"/>
|
<span
|
||||||
|
v-if="loading"
|
||||||
|
class="dark:text-white w-[4px] h-[20px] block animate-blink"
|
||||||
|
style="display: none"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 小易改动:注册掉底部的内容 -->
|
<!-- 小易改动:注册掉底部的内容 -->
|
||||||
<!-- <div style="margin-top: 0.5rem"> -->
|
<!-- <div style="margin-top: 0.5rem"> -->
|
||||||
@ -170,6 +181,28 @@ defineExpose({ textRef })
|
|||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="whitespace-pre-wrap" v-text="text" />
|
<div class="whitespace-pre-wrap" v-text="text" />
|
||||||
|
<a v-if="imageUrl && isImageUrl" :href="imageUrl" target="_blank">
|
||||||
|
<img
|
||||||
|
:src="imageUrl"
|
||||||
|
alt="图片"
|
||||||
|
class="h-auto rounded-md mb-1"
|
||||||
|
:class="{ 'max-w-full': isMobile, 'max-w-sm': !isMobile }"
|
||||||
|
style="margin-top: 0.5rem"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
:href="imageUrl"
|
||||||
|
target="_blank"
|
||||||
|
:class="{ 'file-2': isMobile, 'file-1': !isMobile }"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="@/assets/file.jpeg"
|
||||||
|
alt="文件"
|
||||||
|
class="h-auto rounded-md mb-1"
|
||||||
|
:class="{ 'file-2': isMobile, 'file-1': !isMobile }"
|
||||||
|
v-if="imageUrl && !isImageUrl"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
<div v-if="false" style="margin-left: 0.5rem">
|
<div v-if="false" style="margin-left: 0.5rem">
|
||||||
<NButton class="ml-2" text color="#FFF" @click="handleCopy">
|
<NButton class="ml-2" text color="#FFF" @click="handleCopy">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@ -251,4 +284,17 @@ defineExpose({ textRef })
|
|||||||
html.dark pre code.hljs {
|
html.dark pre code.hljs {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-1 {
|
||||||
|
display: inline;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
width: 120px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
.file-2 {
|
||||||
|
display: inline;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
width: 90px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -15,6 +15,7 @@ interface Props {
|
|||||||
inversion?: boolean
|
inversion?: boolean
|
||||||
error?: boolean
|
error?: boolean
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
|
imageUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emit {
|
interface Emit {
|
||||||
@ -53,7 +54,9 @@ const options = computed(() => {
|
|||||||
common.unshift({
|
common.unshift({
|
||||||
label: asRawText.value ? t('chat.preview') : t('chat.showRawText'),
|
label: asRawText.value ? t('chat.preview') : t('chat.showRawText'),
|
||||||
key: 'toggleRenderType',
|
key: 'toggleRenderType',
|
||||||
icon: iconRender({ icon: asRawText.value ? 'ic:outline-code-off' : 'ic:outline-code' }),
|
icon: iconRender({
|
||||||
|
icon: asRawText.value ? 'ic:outline-code-off' : 'ic:outline-code',
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,8 +104,14 @@ function handleRegenerate() {
|
|||||||
>
|
>
|
||||||
<AvatarComponent :image="inversion" />
|
<AvatarComponent :image="inversion" />
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-hidden text-sm " :class="[inversion ? 'items-end' : 'items-start']">
|
<div
|
||||||
<p class="text-xs text-[#b4bbc4]" :class="[inversion ? 'text-right' : 'text-left']">
|
class="overflow-hidden text-sm"
|
||||||
|
:class="[inversion ? 'items-end' : 'items-start']"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="text-xs text-[#b4bbc4]"
|
||||||
|
:class="[inversion ? 'text-right' : 'text-left']"
|
||||||
|
>
|
||||||
{{ dateTime }}
|
{{ dateTime }}
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
@ -119,11 +128,12 @@ function handleRegenerate() {
|
|||||||
@regenerate="handleRegenerate"
|
@regenerate="handleRegenerate"
|
||||||
@copy="handleCopy"
|
@copy="handleCopy"
|
||||||
@delete="handleDetele"
|
@delete="handleDetele"
|
||||||
|
:imageUrl="imageUrl"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<button
|
<button
|
||||||
v-if="!inversion"
|
v-if="!inversion"
|
||||||
class=" flex mb-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
|
class="flex mb-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
|
||||||
@click="handleRegenerate"
|
@click="handleRegenerate"
|
||||||
>
|
>
|
||||||
<SvgIcon icon="ri:restart-line" />
|
<SvgIcon icon="ri:restart-line" />
|
||||||
@ -134,7 +144,9 @@ function handleRegenerate() {
|
|||||||
:options="options"
|
:options="options"
|
||||||
@select="handleSelect"
|
@select="handleSelect"
|
||||||
>
|
>
|
||||||
<button class="transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-200">
|
<button
|
||||||
|
class="transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-200"
|
||||||
|
>
|
||||||
<SvgIcon icon="ri:more-2-fill" />
|
<SvgIcon icon="ri:more-2-fill" />
|
||||||
</button>
|
</button>
|
||||||
</NDropdown>
|
</NDropdown>
|
||||||
|
BIN
service/.DS_Store
vendored
BIN
service/.DS_Store
vendored
Binary file not shown.
BIN
service/src/.DS_Store
vendored
BIN
service/src/.DS_Store
vendored
Binary file not shown.
@ -32,7 +32,7 @@ async function bootstrap() {
|
|||||||
|
|
||||||
createSwagger(app);
|
createSwagger(app);
|
||||||
const server = await app.listen(PORT, () => {
|
const server = await app.listen(PORT, () => {
|
||||||
Logger.log(`服务启动成功: http://localhost:${PORT}/nineai/swagger/docs 作者:小易 QQ:805239273`, 'Main');
|
Logger.log(`服务启动成功: http://localhost:${PORT}/nineai/swagger/docs`, 'Main');
|
||||||
});
|
});
|
||||||
server.timeout = 5 * 60 * 1000;
|
server.timeout = 5 * 60 * 1000;
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,9 @@ export class ChatLogEntity extends BaseEntity {
|
|||||||
@Column({ comment: '图片信息的string', nullable: true, type: 'text' })
|
@Column({ comment: '图片信息的string', nullable: true, type: 'text' })
|
||||||
fileInfo: string;
|
fileInfo: string;
|
||||||
|
|
||||||
|
@Column({ comment: '上传图片的信息', nullable: true, type: 'text' })
|
||||||
|
imageUrl: string;
|
||||||
|
|
||||||
@Column({ comment: 'role system user assistant', nullable: true })
|
@Column({ comment: 'role system user assistant', nullable: true })
|
||||||
role: string;
|
role: string;
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ export class ChatLogService {
|
|||||||
}
|
}
|
||||||
const list = await this.chatLogEntity.find({ where });
|
const list = await this.chatLogEntity.find({ where });
|
||||||
return list.map((item) => {
|
return list.map((item) => {
|
||||||
const { prompt, role, answer, createdAt, model, conversationOptions, requestOptions, id } = item;
|
const { prompt, role, answer, createdAt, model, conversationOptions, requestOptions, id,imageUrl} = item;
|
||||||
let parseConversationOptions: any = null
|
let parseConversationOptions: any = null
|
||||||
let parseRequestOptions: any = null
|
let parseRequestOptions: any = null
|
||||||
try {
|
try {
|
||||||
@ -228,6 +228,8 @@ export class ChatLogService {
|
|||||||
error: false,
|
error: false,
|
||||||
conversationOptions: parseConversationOptions,
|
conversationOptions: parseConversationOptions,
|
||||||
requestOptions: parseRequestOptions,
|
requestOptions: parseRequestOptions,
|
||||||
|
imageUrl,
|
||||||
|
model
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ export class ChatgptService implements OnModuleInit {
|
|||||||
/* 不同场景会变更其信息 */
|
/* 不同场景会变更其信息 */
|
||||||
let setSystemMessage = systemMessage;
|
let setSystemMessage = systemMessage;
|
||||||
const { parentMessageId } = options;
|
const { parentMessageId } = options;
|
||||||
const { prompt } = body;
|
const { prompt ,imageUrl,model:activeModel} = body;
|
||||||
const { groupId, usingNetwork } = options;
|
const { groupId, usingNetwork } = options;
|
||||||
// const { model = 3 } = options;
|
// const { model = 3 } = options;
|
||||||
/* 获取当前对话组的详细配置信息 */
|
/* 获取当前对话组的详细配置信息 */
|
||||||
@ -260,6 +260,8 @@ export class ChatgptService implements OnModuleInit {
|
|||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
type: DeductionKey.CHAT_TYPE,
|
type: DeductionKey.CHAT_TYPE,
|
||||||
prompt,
|
prompt,
|
||||||
|
imageUrl,
|
||||||
|
activeModel,
|
||||||
answer: '',
|
answer: '',
|
||||||
promptTokens: prompt_tokens,
|
promptTokens: prompt_tokens,
|
||||||
completionTokens: 0,
|
completionTokens: 0,
|
||||||
@ -318,6 +320,8 @@ export class ChatgptService implements OnModuleInit {
|
|||||||
const { context: messagesHistory } = await this.nineStore.buildMessageFromParentMessageId(usingNetwork ? netWorkPrompt : prompt, {
|
const { context: messagesHistory } = await this.nineStore.buildMessageFromParentMessageId(usingNetwork ? netWorkPrompt : prompt, {
|
||||||
parentMessageId,
|
parentMessageId,
|
||||||
systemMessage,
|
systemMessage,
|
||||||
|
imageUrl,
|
||||||
|
activeModel,
|
||||||
maxModelToken: maxToken,
|
maxModelToken: maxToken,
|
||||||
maxResponseTokens: maxTokenRes,
|
maxResponseTokens: maxTokenRes,
|
||||||
maxRounds: addOneIfOdd(rounds),
|
maxRounds: addOneIfOdd(rounds),
|
||||||
@ -328,6 +332,8 @@ export class ChatgptService implements OnModuleInit {
|
|||||||
maxTokenRes,
|
maxTokenRes,
|
||||||
apiKey: modelKey,
|
apiKey: modelKey,
|
||||||
model,
|
model,
|
||||||
|
imageUrl,
|
||||||
|
activeModel,
|
||||||
temperature,
|
temperature,
|
||||||
proxyUrl: proxyResUrl,
|
proxyUrl: proxyResUrl,
|
||||||
onProgress: (chat) => {
|
onProgress: (chat) => {
|
||||||
@ -386,6 +392,8 @@ export class ChatgptService implements OnModuleInit {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
name: undefined,
|
name: undefined,
|
||||||
usage: null,
|
usage: null,
|
||||||
|
imageUrl,
|
||||||
|
activeModel,
|
||||||
parentMessageId: parentMessageId,
|
parentMessageId: parentMessageId,
|
||||||
conversationId: response?.conversationId,
|
conversationId: response?.conversationId,
|
||||||
};
|
};
|
||||||
@ -449,6 +457,8 @@ export class ChatgptService implements OnModuleInit {
|
|||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
type: DeductionKey.CHAT_TYPE,
|
type: DeductionKey.CHAT_TYPE,
|
||||||
prompt,
|
prompt,
|
||||||
|
imageUrl,
|
||||||
|
activeModel,
|
||||||
answer: '',
|
answer: '',
|
||||||
promptTokens: prompt_tokens,
|
promptTokens: prompt_tokens,
|
||||||
completionTokens: 0,
|
completionTokens: 0,
|
||||||
|
@ -102,7 +102,11 @@ export function sendMessageFromOpenAi(messagesHistory, inputs ){
|
|||||||
|
|
||||||
|
|
||||||
export function getTokenCount(text: string) {
|
export function getTokenCount(text: string) {
|
||||||
if(!text) return 0;
|
if (!text) return 0;
|
||||||
|
// 确保text是字符串类型
|
||||||
|
if (typeof text !== 'string') {
|
||||||
|
text = String(text);
|
||||||
|
}
|
||||||
text = text.replace(/<\|endoftext\|>/g, '')
|
text = text.replace(/<\|endoftext\|>/g, '')
|
||||||
return tokenizer.encode(text).length
|
return tokenizer.encode(text).length
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,44 @@
|
|||||||
import Keyv from 'keyv'
|
import Keyv from 'keyv';
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { get_encoding } from '@dqbd/tiktoken'
|
import { get_encoding } from '@dqbd/tiktoken';
|
||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
const tokenizer = get_encoding('cl100k_base')
|
const tokenizer = get_encoding('cl100k_base');
|
||||||
|
|
||||||
|
|
||||||
export type Role = 'user' | 'assistant' | 'function'
|
|
||||||
|
|
||||||
|
export type Role = 'user' | 'assistant' | 'function';
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
store: Keyv
|
store: Keyv;
|
||||||
namespace: string
|
namespace: string;
|
||||||
expires?: number
|
expires?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessageInfo {
|
export interface MessageInfo {
|
||||||
id: string
|
id: string;
|
||||||
text: string
|
text: string;
|
||||||
role: Role
|
role: Role;
|
||||||
name?: string
|
name?: string;
|
||||||
|
imageUrl?: string;
|
||||||
|
activeModel?: string;
|
||||||
usage: {
|
usage: {
|
||||||
prompt_tokens?: number
|
prompt_tokens?: number;
|
||||||
completion_tokens?: number
|
completion_tokens?: number;
|
||||||
total_tokens?: number
|
total_tokens?: number;
|
||||||
}
|
};
|
||||||
parentMessageId?: string
|
parentMessageId?: string;
|
||||||
conversationId?: string
|
conversationId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BuildMessageOptions {
|
export interface BuildMessageOptions {
|
||||||
systemMessage?: string
|
systemMessage?: string;
|
||||||
parentMessageId: string
|
parentMessageId: string;
|
||||||
maxRounds?: number
|
maxRounds?: number;
|
||||||
maxModelToken?: number
|
maxModelToken?: number;
|
||||||
maxResponseTokens?: number
|
maxResponseTokens?: number;
|
||||||
name?: string
|
name?: string;
|
||||||
|
imageUrl?: string;
|
||||||
|
activeModel?: string;
|
||||||
|
model?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export interface BuildMessageRes {
|
// export interface BuildMessageRes {
|
||||||
@ -43,12 +46,12 @@ export interface BuildMessageOptions {
|
|||||||
// numTokens: number
|
// numTokens: number
|
||||||
// maxTokens: number
|
// maxTokens: number
|
||||||
// }
|
// }
|
||||||
export type BuildMessageRes = any[]
|
export type BuildMessageRes = any[];
|
||||||
|
|
||||||
export interface NineStoreInterface {
|
export interface NineStoreInterface {
|
||||||
getData(id: string): Promise<string>;
|
getData(id: string): Promise<string>;
|
||||||
setData(message: MessageInfo, expires?: number): Promise<void>;
|
setData(message: MessageInfo, expires?: number): Promise<void>;
|
||||||
getUuid(): string
|
getUuid(): string;
|
||||||
buildMessageFromParentMessageId(string, opt?: BuildMessageOptions): Promise<any>;
|
buildMessageFromParentMessageId(string, opt?: BuildMessageOptions): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,115 +61,165 @@ export class NineStore implements NineStoreInterface {
|
|||||||
private expires: number;
|
private expires: number;
|
||||||
|
|
||||||
constructor(options: Options) {
|
constructor(options: Options) {
|
||||||
const { store, namespace, expires } = this.formatOptions(options)
|
const { store, namespace, expires } = this.formatOptions(options);
|
||||||
this.store = store
|
this.store = store;
|
||||||
this.namespace = namespace
|
this.namespace = namespace;
|
||||||
this.expires = expires
|
this.expires = expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
public formatOptions(options: Options){
|
public formatOptions(options: Options) {
|
||||||
const { store, expires = 1000 * 60 * 60 * 24 * 3, namespace = 'chat'} = options
|
const { store, expires = 1000 * 60 * 60 * 24 * 3, namespace = 'chat' } = options;
|
||||||
return { store, namespace, expires }
|
return { store, namespace, expires };
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateKey(key){
|
public generateKey(key: any) {
|
||||||
return this.namespace ? `${this.namespace}-${key}` : key
|
return this.namespace ? `${this.namespace}-${key}` : key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getData(id: string ): Promise<any> {
|
public async getData(id: string): Promise<any> {
|
||||||
const res = await this.store.get(id)
|
const res = await this.store.get(id);
|
||||||
return res
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setData(message, expires = this.expires): Promise<void> {
|
public async setData(message: MessageInfo, expires = this.expires): Promise<void> {
|
||||||
await this.store.set(message.id, message, expires)
|
await this.store.set(message.id, message, expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 通过传入prompt和parentMessageId 递归往上拿到历史记录组装为模型需要的上下文、
|
* @desc 通过传入prompt和parentMessageId 递归往上拿到历史记录组装为模型需要的上下文、
|
||||||
* 可以传入maxRounds限制最大轮次的对话 传入maxModelToken, maxResponseTokens 则通过计算上下文占用的token计算出最大容量
|
* 可以传入maxRounds限制最大轮次的对话 传入maxModelToken, maxResponseTokens 则通过计算上下文占用的token计算出最大容量
|
||||||
*/
|
*/
|
||||||
public async buildMessageFromParentMessageId(text: string, options: BuildMessageOptions){
|
public async buildMessageFromParentMessageId(text: string, options: BuildMessageOptions) {
|
||||||
let { maxRounds, maxModelToken, maxResponseTokens, systemMessage = '', name } = options
|
let { maxRounds, maxModelToken, maxResponseTokens, systemMessage = '', name, imageUrl, model, activeModel } = options;
|
||||||
let { parentMessageId } = options
|
let { parentMessageId } = options;
|
||||||
let messages = []
|
let messages = [];
|
||||||
let nextNumTokensEstimate = 0
|
let nextNumTokensEstimate = 0;
|
||||||
|
// messages.push({ role: 'system', content: systemMessage, name })
|
||||||
if (systemMessage) {
|
if (systemMessage) {
|
||||||
messages.push({ role: 'system', content: systemMessage })
|
const specialModels = ['gemini-pro', 'ERNIE', 'qwen', 'SparkDesk', 'hunyuan'];
|
||||||
|
const isSpecialModel = activeModel && specialModels.some((specialModel) => activeModel.includes(specialModel));
|
||||||
|
if (isSpecialModel) {
|
||||||
|
messages.push({ role: 'user', content: systemMessage, name });
|
||||||
|
messages.push({ role: 'assistant', content: '好的', name });
|
||||||
|
} else {
|
||||||
|
messages.push({ role: 'system', content: systemMessage, name });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const systemMessageOffset = messages.length
|
const systemMessageOffset = messages.length;
|
||||||
let round = 0
|
let round = 0;
|
||||||
let nextMessages = text ? messages.concat([{ role: 'user', content: text, name }]) : messages
|
// 特殊处理 gpt-4-vision-preview 模型
|
||||||
do {
|
if (activeModel === 'gpt-4-vision-preview' && imageUrl) {
|
||||||
// let parentId = '1bf30262-8f25-4a03-88ad-9d42d55e6f0b'
|
const content = [
|
||||||
/* 没有parentMessageId就没有历史 直接返回 */
|
{
|
||||||
if(!parentMessageId){
|
type: 'text',
|
||||||
break;
|
text: text,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'image_url',
|
||||||
|
image_url: {
|
||||||
|
url: imageUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
messages.push({ role: 'user', content: content, name });
|
||||||
|
} else {
|
||||||
|
// 处理 gpt-4-all 模型
|
||||||
|
if (model === 'gpt-4-all' && imageUrl) {
|
||||||
|
text = imageUrl + '\n' + text;
|
||||||
}
|
}
|
||||||
const parentMessage = await this.getData(parentMessageId)
|
messages.push({ role: 'user', content: text, name });
|
||||||
|
}
|
||||||
|
// Logger.debug(`发送的参数:${messages}`)
|
||||||
|
|
||||||
if(!parentMessage){
|
let nextMessages = messages;
|
||||||
|
do {
|
||||||
|
// let parentId = '1bf30262-8f25-4a03-88ad-9d42d55e6f0b'
|
||||||
|
/* 没有parentMessageId就没有历史 直接返回 */
|
||||||
|
if (!parentMessageId) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const { text, name, role } = parentMessage
|
const parentMessage = await this.getData(parentMessageId);
|
||||||
|
if (!parentMessage) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const { text, name, role, imageUrl } = parentMessage;
|
||||||
|
let content = text; // 默认情况下使用text作为content
|
||||||
|
|
||||||
|
// 特别处理包含 imageUrl 的消息
|
||||||
|
if (role === 'user' && imageUrl) {
|
||||||
|
if (activeModel === 'gpt-4-vision-preview') {
|
||||||
|
content = [
|
||||||
|
{ type: 'text', text: text },
|
||||||
|
{ type: 'image_url', image_url: { url: imageUrl } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 将本轮消息插入到列表中 */
|
/* 将本轮消息插入到列表中 */
|
||||||
nextMessages = nextMessages.slice(0, systemMessageOffset).concat([
|
nextMessages = nextMessages.slice(0, systemMessageOffset).concat([
|
||||||
{ role, content: text, name },
|
{ role, content, name }, // 使用调整后的content
|
||||||
...nextMessages.slice(systemMessageOffset)
|
...nextMessages.slice(systemMessageOffset),
|
||||||
])
|
]);
|
||||||
round++
|
|
||||||
|
// Logger.debug(`nextMessages:${JSON.stringify(nextMessages, null, 2)}`);
|
||||||
|
|
||||||
|
round++;
|
||||||
/* 如果超出了限制的最大轮次 就退出 不包含本次发送的本身 */
|
/* 如果超出了限制的最大轮次 就退出 不包含本次发送的本身 */
|
||||||
if(maxRounds && round >= maxRounds){
|
if (maxRounds && round >= maxRounds) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* 如果传入maxModelToken, maxResponseTokens 则判断是否超过边界 */
|
/* 如果传入maxModelToken, maxResponseTokens 则判断是否超过边界 */
|
||||||
if(maxModelToken && maxResponseTokens){
|
if (maxModelToken && maxResponseTokens) {
|
||||||
const maxNumTokens = maxModelToken - maxResponseTokens // 模型最大token限制减去限制回复剩余空间
|
const maxNumTokens = maxModelToken - maxResponseTokens; // 模型最大token限制减去限制回复剩余空间
|
||||||
/* 当前的对话历史列表合并的总token容量 */
|
/* 当前的对话历史列表合并的总token容量 */
|
||||||
nextNumTokensEstimate = await this._getTokenCount(nextMessages)
|
nextNumTokensEstimate = await this._getTokenCount(nextMessages);
|
||||||
/* 200是添加的一个安全区间 防止少量超过 待优化 */
|
/* 200是添加的一个安全区间 防止少量超过 待优化 */
|
||||||
const isValidPrompt = nextNumTokensEstimate + 200 <= maxNumTokens
|
const isValidPrompt = nextNumTokensEstimate + 200 <= maxNumTokens;
|
||||||
/* 如果大于这个区间了说明本轮加入之后导致超过限制、则递归删除头部的对话轮次来保证不出边界 */
|
/* 如果大于这个区间了说明本轮加入之后导致超过限制、则递归删除头部的对话轮次来保证不出边界 */
|
||||||
if(!isValidPrompt){
|
if (!isValidPrompt) {
|
||||||
nextMessages = this._recursivePruning(nextMessages, maxNumTokens, systemMessage)
|
nextMessages = this._recursivePruning(nextMessages, maxNumTokens, systemMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parentMessageId = parentMessage.parentMessageId
|
parentMessageId = parentMessage.parentMessageId;
|
||||||
} while (true);
|
} while (true);
|
||||||
const maxTokens = Math.max(
|
const maxTokens = Math.max(1, Math.min(maxModelToken - nextNumTokensEstimate, maxResponseTokens));
|
||||||
1,
|
|
||||||
Math.min(maxModelToken - nextNumTokensEstimate, maxResponseTokens)
|
|
||||||
)
|
|
||||||
// Logger.debug(`本轮调用:模型:${model}`)
|
// Logger.debug(`本轮调用:模型:${model}`)
|
||||||
console.log('本次携带上下文的长度',nextMessages.length, nextNumTokensEstimate )
|
console.log('本次携带上下文的长度', nextMessages.length, nextNumTokensEstimate);
|
||||||
return { context: nextMessages, round: nextMessages.length, historyToken:nextNumTokensEstimate }
|
return { context: nextMessages, round: nextMessages.length, historyToken: nextNumTokensEstimate };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getTokenCount(messages: any[]) {
|
protected _getTokenCount(messages: any[]) {
|
||||||
let text = messages.reduce( (pre: string, cur: any) => {
|
let text = messages.reduce((pre: string, cur: any) => {
|
||||||
return pre+=cur.content
|
// 检查cur.content是否为数组
|
||||||
}, '')
|
if (Array.isArray(cur.content)) {
|
||||||
text = text.replace(/<\|endoftext\|>/g, '')
|
// 提取并连接数组中的文本元素
|
||||||
return tokenizer.encode(text).length
|
const contentText = cur.content
|
||||||
}
|
.filter((item: { type: string }) => item.type === 'text')
|
||||||
|
.map((item: { text: any }) => item.text)
|
||||||
|
.join(' ');
|
||||||
|
return pre + contentText;
|
||||||
|
} else {
|
||||||
|
// 如果不是数组,则直接添加
|
||||||
|
return pre + (cur.content || '');
|
||||||
|
}
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
text = text.replace(/<\|endoftext\|>/g, '');
|
||||||
|
return tokenizer.encode(text).length;
|
||||||
|
}
|
||||||
|
|
||||||
/* 递归删除 当token超过模型限制容量 删除到在限制区域内 */
|
/* 递归删除 当token超过模型限制容量 删除到在限制区域内 */
|
||||||
protected _recursivePruning(
|
protected _recursivePruning(messages: MessageInfo[], maxNumTokens: number, systemMessage: string) {
|
||||||
messages: MessageInfo[],
|
const currentTokens = this._getTokenCount(messages);
|
||||||
maxNumTokens: number,
|
|
||||||
systemMessage: string
|
|
||||||
) {
|
|
||||||
const currentTokens = this._getTokenCount(messages)
|
|
||||||
if (currentTokens <= maxNumTokens) {
|
if (currentTokens <= maxNumTokens) {
|
||||||
return messages
|
return messages;
|
||||||
}
|
}
|
||||||
/* 有系统预设则跳过第一条删除 没有则直接删除 */
|
/* 有系统预设则跳过第一条删除 没有则直接删除 */
|
||||||
messages.splice(systemMessage ? 1 : 0, 1)
|
messages.splice(systemMessage ? 1 : 0, 1);
|
||||||
return this._recursivePruning(messages, maxNumTokens, systemMessage)
|
return this._recursivePruning(messages, maxNumTokens, systemMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUuid(){
|
public getUuid() {
|
||||||
return uuidv4()
|
return uuidv4();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ export class MidjourneyService {
|
|||||||
private redisCacheService: RedisCacheService,
|
private redisCacheService: RedisCacheService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
||||||
private lockPrompt = [];
|
private lockPrompt = [];
|
||||||
|
|
||||||
/* 睡眠 xs */
|
/* 睡眠 xs */
|
||||||
@ -111,6 +110,9 @@ export class MidjourneyService {
|
|||||||
await this.updateDrawData(jobData, drawRes);
|
await this.updateDrawData(jobData, drawRes);
|
||||||
/* 存完解锁当前文件 */
|
/* 存完解锁当前文件 */
|
||||||
this.lockPrompt = this.lockPrompt.filter((item) => item !== drawInfo.randomDrawId);
|
this.lockPrompt = this.lockPrompt.filter((item) => item !== drawInfo.randomDrawId);
|
||||||
|
|
||||||
|
/* 只有在画成功后才扣分*/
|
||||||
|
this.drawSuccess(jobData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -160,16 +162,16 @@ export class MidjourneyService {
|
|||||||
const { id, content, channel_id, attachments = [], timestamp, durationSpent } = drawRes;
|
const { id, content, channel_id, attachments = [], timestamp, durationSpent } = drawRes;
|
||||||
const { filename, url, proxy_url, width, height, size } = attachments[0];
|
const { filename, url, proxy_url, width, height, size } = attachments[0];
|
||||||
/* 将图片存入cos */
|
/* 将图片存入cos */
|
||||||
const mjNotSaveImg = await this.globalConfigService.getConfigs(['mjNotSaveImg'])
|
const mjNotSaveImg = await this.globalConfigService.getConfigs(['mjNotSaveImg']);
|
||||||
let cosUrl = ''
|
let cosUrl = '';
|
||||||
if(!Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0){
|
if (!Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0) {
|
||||||
Logger.debug(`------> 开始上传图片!!!`, 'MidjourneyService');
|
Logger.debug(`------> 开始上传图片!!!`, 'MidjourneyService');
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
cosUrl = await this.uploadService.uploadFileFromUrl({ filename, url });
|
cosUrl = await this.uploadService.uploadFileFromUrl({ filename, url });
|
||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
Logger.debug(`本次图片上传耗时为${(endDate.getTime() - startDate.getTime()) / 1000}秒`, 'MidjourneyService');
|
Logger.debug(`本次图片上传耗时为${(endDate.getTime() - startDate.getTime()) / 1000}秒`, 'MidjourneyService');
|
||||||
}else{
|
} else {
|
||||||
console.log('本次不存图片了')
|
console.log('本次不存图片了');
|
||||||
}
|
}
|
||||||
/* 记录当前图片存储方式 方便后续对不同平台图片压缩 */
|
/* 记录当前图片存储方式 方便后续对不同平台图片压缩 */
|
||||||
const cosType = await this.uploadService.getUploadType();
|
const cosType = await this.uploadService.getUploadType();
|
||||||
@ -181,11 +183,11 @@ export class MidjourneyService {
|
|||||||
fileInfo: JSON.stringify({ width, height, size, filename, cosUrl, cosType }),
|
fileInfo: JSON.stringify({ width, height, size, filename, cosUrl, cosType }),
|
||||||
extend: this.removeEmoji(JSON.stringify(drawRes)),
|
extend: this.removeEmoji(JSON.stringify(drawRes)),
|
||||||
durationSpent,
|
durationSpent,
|
||||||
isSaveImg: !Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0,
|
isSaveImg: !Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0,
|
||||||
};
|
};
|
||||||
await this.midjourneyEntity.update({ id: jobData.id }, drawInfo);
|
await this.midjourneyEntity.update({ id: jobData.id }, drawInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('TODO->存储图片失败, ', jobData,error);
|
console.log('TODO->存储图片失败, ', jobData, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,17 +736,15 @@ export class MidjourneyService {
|
|||||||
take: size,
|
take: size,
|
||||||
skip: (page - 1) * size,
|
skip: (page - 1) * size,
|
||||||
});
|
});
|
||||||
const mjProxyImgUrl = await this.globalConfigService.getConfigs(['mjProxyImgUrl'])
|
const mjProxyImgUrl = await this.globalConfigService.getConfigs(['mjProxyImgUrl']);
|
||||||
rows.forEach((item: any) => {
|
rows.forEach((item: any) => {
|
||||||
try {
|
try {
|
||||||
const { extend, isSaveImg, fileInfo } = item;
|
const { extend, isSaveImg, fileInfo } = item;
|
||||||
const originUrl = JSON.parse(extend)?.attachments[0]?.url
|
const originUrl = JSON.parse(extend)?.attachments[0]?.url;
|
||||||
item.fileInfo = this.formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl);
|
item.fileInfo = this.formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl);
|
||||||
item.isGroup = JSON.parse(extend)?.components[0]?.components[0].label === "U1";
|
item.isGroup = JSON.parse(extend)?.components[0]?.components[0].label === 'U1';
|
||||||
item.originUrl = originUrl
|
item.originUrl = originUrl;
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const countQueue = await this.midjourneyEntity.count({ where: { isDelete: 0, status: In([1, 2]) } });
|
const countQueue = await this.midjourneyEntity.count({ where: { isDelete: 0, status: In([1, 2]) } });
|
||||||
const data: any = { rows: formatCreateOrUpdateDate(rows), count, countQueue };
|
const data: any = { rows: formatCreateOrUpdateDate(rows), count, countQueue };
|
||||||
@ -755,15 +755,15 @@ export class MidjourneyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 格式化fileinfo 对于不同平台的图片进行压缩 */
|
/* 格式化fileinfo 对于不同平台的图片进行压缩 */
|
||||||
formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl) {
|
formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl) {
|
||||||
if (!fileInfo) return {};
|
if (!fileInfo) return {};
|
||||||
let parseFileInfo: any = null
|
let parseFileInfo: any = null;
|
||||||
try {
|
try {
|
||||||
parseFileInfo = JSON.parse(fileInfo);
|
parseFileInfo = JSON.parse(fileInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
parseFileInfo = null
|
parseFileInfo = null;
|
||||||
}
|
}
|
||||||
if(!parseFileInfo) return;
|
if (!parseFileInfo) return;
|
||||||
const { url, filename, size, cosUrl, width, height } = parseFileInfo;
|
const { url, filename, size, cosUrl, width, height } = parseFileInfo;
|
||||||
const targetSize = 310; // 目标宽度或高度
|
const targetSize = 310; // 目标宽度或高度
|
||||||
|
|
||||||
@ -786,10 +786,10 @@ export class MidjourneyService {
|
|||||||
}
|
}
|
||||||
parseFileInfo.thumbImg = thumbImg;
|
parseFileInfo.thumbImg = thumbImg;
|
||||||
/* 如果配置了不存储图片 则 isSaceImg 为false的则需要使用反代地址拼接 */
|
/* 如果配置了不存储图片 则 isSaceImg 为false的则需要使用反代地址拼接 */
|
||||||
if(!isSaveImg){
|
if (!isSaveImg) {
|
||||||
const proxyImgUrl = `${mjProxyImgUrl}/mj/pipe?url=${originUrl}`
|
const proxyImgUrl = `${mjProxyImgUrl}/mj/pipe?url=${originUrl}`;
|
||||||
parseFileInfo.thumbImg = proxyImgUrl
|
parseFileInfo.thumbImg = proxyImgUrl;
|
||||||
parseFileInfo.cosUrl = proxyImgUrl
|
parseFileInfo.cosUrl = proxyImgUrl;
|
||||||
}
|
}
|
||||||
return parseFileInfo;
|
return parseFileInfo;
|
||||||
}
|
}
|
||||||
@ -859,8 +859,8 @@ export class MidjourneyService {
|
|||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
const count = await this.midjourneyEntity.count({ where: { userId: id, isDelete: 0, status: In([1, 2]) } });
|
const count = await this.midjourneyEntity.count({ where: { userId: id, isDelete: 0, status: In([1, 2]) } });
|
||||||
const mjLimitCount = await this.globalConfigService.getConfigs(['mjLimitCount'])
|
const mjLimitCount = await this.globalConfigService.getConfigs(['mjLimitCount']);
|
||||||
const max = mjLimitCount ? Number(mjLimitCount) : 2
|
const max = mjLimitCount ? Number(mjLimitCount) : 2;
|
||||||
if (count >= max) {
|
if (count >= max) {
|
||||||
throw new HttpException(`当前管理员限制单用户同时最多能执行${max}个任务`, HttpStatus.BAD_REQUEST);
|
throw new HttpException(`当前管理员限制单用户同时最多能执行${max}个任务`, HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
@ -870,11 +870,21 @@ export class MidjourneyService {
|
|||||||
async drawFailed(jobData) {
|
async drawFailed(jobData) {
|
||||||
const { id, userId, action } = jobData;
|
const { id, userId, action } = jobData;
|
||||||
/* 退还余额 放大图片(类型2)是1 其他都是4 */
|
/* 退还余额 放大图片(类型2)是1 其他都是4 */
|
||||||
const amount = action === 2 ? 1 : 4;
|
// const amount = action === 2 ? 1 : 4;
|
||||||
await this.userBalanceService.refundMjBalance(userId, amount);
|
// await this.userBalanceService.refundMjBalance(userId, amount);
|
||||||
await this.midjourneyEntity.update({ id }, { status: 4 });
|
await this.midjourneyEntity.update({ id }, { status: 4 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 绘图成功扣费 */
|
||||||
|
async drawSuccess(jobData) {
|
||||||
|
const { id, userId, action } = jobData;
|
||||||
|
/* 扣除余额 放大图片(类型2)是1 其他都是4 */
|
||||||
|
const amount = action === 2 ? 1 : 4;
|
||||||
|
Logger.debug(`绘画完成,执行扣费,扣除费用:${amount}积分。`);
|
||||||
|
await this.userBalanceService.refundMjBalance(userId, -amount);
|
||||||
|
await this.midjourneyEntity.update({ id }, { status: 3 });
|
||||||
|
}
|
||||||
|
|
||||||
/* 获取绘画列表 */
|
/* 获取绘画列表 */
|
||||||
async getList(params: GetListDto) {
|
async getList(params: GetListDto) {
|
||||||
const { page = 1, size = 20, rec, userId, status } = params;
|
const { page = 1, size = 20, rec, userId, status } = params;
|
||||||
@ -902,17 +912,15 @@ export class MidjourneyService {
|
|||||||
skip: (page - 1) * size,
|
skip: (page - 1) * size,
|
||||||
select: ['fileInfo', 'extend', 'prompt', 'createdAt', 'id', 'extend', 'fullPrompt', 'rec', 'isSaveImg'],
|
select: ['fileInfo', 'extend', 'prompt', 'createdAt', 'id', 'extend', 'fullPrompt', 'rec', 'isSaveImg'],
|
||||||
});
|
});
|
||||||
const mjProxyImgUrl = await this.globalConfigService.getConfigs(['mjProxyImgUrl'])
|
const mjProxyImgUrl = await this.globalConfigService.getConfigs(['mjProxyImgUrl']);
|
||||||
rows.forEach((item: any) => {
|
rows.forEach((item: any) => {
|
||||||
try {
|
try {
|
||||||
const { extend, isSaveImg, fileInfo } = item;
|
const { extend, isSaveImg, fileInfo } = item;
|
||||||
const originUrl = JSON.parse(extend)?.attachments[0]?.url
|
const originUrl = JSON.parse(extend)?.attachments[0]?.url;
|
||||||
item.fileInfo = this.formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl);
|
item.fileInfo = this.formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl);
|
||||||
item.isGroup = JSON.parse(extend)?.components[0]?.components[0].label === "U1";
|
item.isGroup = JSON.parse(extend)?.components[0]?.components[0].label === 'U1';
|
||||||
item.originUrl = originUrl
|
item.originUrl = originUrl;
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Number(size) === 999) {
|
if (Number(size) === 999) {
|
||||||
@ -931,10 +939,10 @@ export class MidjourneyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
async getFullPrompt(id: number){
|
async getFullPrompt(id: number) {
|
||||||
const m = await this.midjourneyEntity.findOne({where: {id}})
|
const m = await this.midjourneyEntity.findOne({ where: { id } });
|
||||||
if(!m) return ''
|
if (!m) return '';
|
||||||
const { fullPrompt } = m
|
const { fullPrompt } = m;
|
||||||
return fullPrompt;
|
return fullPrompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -953,27 +961,25 @@ export class MidjourneyService {
|
|||||||
skip: (page - 1) * size,
|
skip: (page - 1) * size,
|
||||||
});
|
});
|
||||||
|
|
||||||
const userIds = rows.map((item: any) => item.userId).filter( id => id < 100000);
|
const userIds = rows.map((item: any) => item.userId).filter((id) => id < 100000);
|
||||||
const userInfos = await this.userEntity.find({ where: { id: In(userIds) }, select: ['id', 'username', 'avatar', 'email'] });
|
const userInfos = await this.userEntity.find({ where: { id: In(userIds) }, select: ['id', 'username', 'avatar', 'email'] });
|
||||||
rows.forEach((item: any) => {
|
rows.forEach((item: any) => {
|
||||||
item.userInfo = userInfos.find((user) => user.id === item.userId);
|
item.userInfo = userInfos.find((user) => user.id === item.userId);
|
||||||
});
|
});
|
||||||
const mjProxyImgUrl = await this.globalConfigService.getConfigs(['mjProxyImgUrl'])
|
const mjProxyImgUrl = await this.globalConfigService.getConfigs(['mjProxyImgUrl']);
|
||||||
rows.forEach((item: any) => {
|
rows.forEach((item: any) => {
|
||||||
try {
|
try {
|
||||||
const { extend, isSaveImg, fileInfo } = item;
|
const { extend, isSaveImg, fileInfo } = item;
|
||||||
const originUrl = JSON.parse(extend)?.attachments[0]?.url
|
const originUrl = JSON.parse(extend)?.attachments[0]?.url;
|
||||||
item.fileInfo = this.formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl);
|
item.fileInfo = this.formatFileInfo(fileInfo, isSaveImg, mjProxyImgUrl, originUrl);
|
||||||
// item.isGroup = JSON.parse(extend)?.components[0]?.components.length === 5;
|
// item.isGroup = JSON.parse(extend)?.components[0]?.components.length === 5;
|
||||||
item.isGroup = JSON.parse(extend)?.components[0]?.components[0].label === "U1";
|
item.isGroup = JSON.parse(extend)?.components[0]?.components[0].label === 'U1';
|
||||||
item.originUrl = originUrl
|
item.originUrl = originUrl;
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
if (req.user.role !== 'super') {
|
if (req.user.role !== 'super') {
|
||||||
rows.forEach((item: any) => {
|
rows.forEach((item: any) => {
|
||||||
if(item.userInfo && item.userInfo.email){
|
if (item.userInfo && item.userInfo.email) {
|
||||||
item.userInfo.email = item.userInfo.email.replace(/(.{2}).+(.{2}@.+)/, '$1****$2');
|
item.userInfo.email = item.userInfo.email.replace(/(.{2}).+(.{2}@.+)/, '$1****$2');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1021,38 +1027,38 @@ export class MidjourneyService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setPrompt(req: Request, body){
|
async setPrompt(req: Request, body) {
|
||||||
try {
|
try {
|
||||||
const { prompt, status, isCarryParams, title, order, id, aspect } = body
|
const { prompt, status, isCarryParams, title, order, id, aspect } = body;
|
||||||
if(id){
|
if (id) {
|
||||||
return await this.mjPromptsEntity.update({id}, {prompt, status, isCarryParams, order, aspect})
|
return await this.mjPromptsEntity.update({ id }, { prompt, status, isCarryParams, order, aspect });
|
||||||
}else{
|
} else {
|
||||||
return await this.mjPromptsEntity.save({prompt, status, isCarryParams, title, order, aspect})
|
return await this.mjPromptsEntity.save({ prompt, status, isCarryParams, title, order, aspect });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('error: ', error);
|
console.log('error: ', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async delPrompt(req: Request, body){
|
async delPrompt(req: Request, body) {
|
||||||
const {id} = body
|
const { id } = body;
|
||||||
if(!id) {
|
if (!id) {
|
||||||
throw new HttpException('非法操作!', HttpStatus.BAD_REQUEST);
|
throw new HttpException('非法操作!', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
return await this.mjPromptsEntity.delete({id})
|
return await this.mjPromptsEntity.delete({ id });
|
||||||
}
|
}
|
||||||
|
|
||||||
async queryPrompt(){
|
async queryPrompt() {
|
||||||
return await this.mjPromptsEntity.find({
|
return await this.mjPromptsEntity.find({
|
||||||
order: { order: 'DESC' },
|
order: { order: 'DESC' },
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async proxyImg(params){
|
async proxyImg(params) {
|
||||||
const { url } = params
|
const { url } = params;
|
||||||
if(!url) return
|
if (!url) return;
|
||||||
const response = await axios.get(url, { responseType: 'arraybuffer' });
|
const response = await axios.get(url, { responseType: 'arraybuffer' });
|
||||||
const base64 = Buffer.from(response.data).toString('base64');
|
const base64 = Buffer.from(response.data).toString('base64');
|
||||||
return base64
|
return base64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,4 +56,6 @@ export class SetModelDto {
|
|||||||
//设置token计费
|
//设置token计费
|
||||||
@ApiProperty({ example: true, description: '是否使用token计费', required: false })
|
@ApiProperty({ example: true, description: '是否使用token计费', required: false })
|
||||||
isTokenBased: boolean;
|
isTokenBased: boolean;
|
||||||
|
@ApiProperty({ example: true, description: 'token计费比例', required: false })
|
||||||
|
tokenFeeRatio: number;
|
||||||
}
|
}
|
||||||
|
@ -66,4 +66,7 @@ export class ModelsEntity extends BaseEntity {
|
|||||||
|
|
||||||
@Column({ comment: '是否使用token计费: 0:不是 1:是', default: 0 })
|
@Column({ comment: '是否使用token计费: 0:不是 1:是', default: 0 })
|
||||||
isTokenBased: boolean;
|
isTokenBased: boolean;
|
||||||
|
|
||||||
|
@Column({ comment: 'token计费比例', default: 0 })
|
||||||
|
tokenFeeRatio: number;
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ export class QueueService implements OnApplicationBootstrap {
|
|||||||
/* 绘图和图生图扣除余额4 */
|
/* 绘图和图生图扣除余额4 */
|
||||||
this.jobIds.push(job.id);
|
this.jobIds.push(job.id);
|
||||||
/* 扣费 */
|
/* 扣费 */
|
||||||
await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
// await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ export class QueueService implements OnApplicationBootstrap {
|
|||||||
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
||||||
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
||||||
/* 扣费 */
|
/* 扣费 */
|
||||||
await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 1, 1);
|
// await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 1, 1);
|
||||||
this.jobIds.push(job.id);
|
this.jobIds.push(job.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ export class QueueService implements OnApplicationBootstrap {
|
|||||||
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
||||||
this.jobIds.push(job.id);
|
this.jobIds.push(job.id);
|
||||||
/* 扣费 */
|
/* 扣费 */
|
||||||
await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
// await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ export class QueueService implements OnApplicationBootstrap {
|
|||||||
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
||||||
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
||||||
this.jobIds.push(job.id);
|
this.jobIds.push(job.id);
|
||||||
await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
// await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ export class QueueService implements OnApplicationBootstrap {
|
|||||||
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
||||||
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
||||||
this.jobIds.push(job.id);
|
this.jobIds.push(job.id);
|
||||||
await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
// await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ export class QueueService implements OnApplicationBootstrap {
|
|||||||
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
const timeout = (await this.globalConfigService.getConfigs(['mjTimeoutMs'])) || 200000;
|
||||||
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
const job = await this.mjDrawQueue.add('mjDraw', { id: res.id, action, userId: req.user.id }, { delay: 1000, timeout: +timeout });
|
||||||
this.jobIds.push(job.id);
|
this.jobIds.push(job.id);
|
||||||
await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
// await this.userBalanceService.deductFromBalance(req.user.id, 'mjDraw', 4, 4);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,8 +300,8 @@ export class UserBalanceService {
|
|||||||
/* 记录修改使用的token */
|
/* 记录修改使用的token */
|
||||||
const updateBalance = { [updateKey]: b[updateKey] - amount < 0 ? 0 : b[updateKey] - amount, [useKey]: b[useKey] + UseAmount };
|
const updateBalance = { [updateKey]: b[updateKey] - amount < 0 ? 0 : b[updateKey] - amount, [useKey]: b[useKey] + UseAmount };
|
||||||
/* 记录修改使用的次数 mj不需要 */
|
/* 记录修改使用的次数 mj不需要 */
|
||||||
useKey === 'useModel3Token' && (updateBalance['useModel3Count'] = b['useModel3Count'] + 1);
|
useKey === 'useModel3Token' && (updateBalance['useModel3Count'] = b['useModel3Count'] + amount);
|
||||||
useKey === 'useModel4Token' && (updateBalance['useModel4Count'] = b['useModel4Count'] + 1);
|
useKey === 'useModel4Token' && (updateBalance['useModel4Count'] = b['useModel4Count'] + amount);
|
||||||
const result = await this.userBalanceEntity.update({ userId }, updateBalance);
|
const result = await this.userBalanceEntity.update({ userId }, updateBalance);
|
||||||
if (result.affected === 0) {
|
if (result.affected === 0) {
|
||||||
throw new HttpException('消费余额失败!', HttpStatus.BAD_REQUEST);
|
throw new HttpException('消费余额失败!', HttpStatus.BAD_REQUEST);
|
||||||
@ -552,7 +552,9 @@ export class UserBalanceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* MJ绘画失败退款 */
|
/* MJ绘画失败退款 */
|
||||||
async refundMjBalance(userId, amount) {}
|
async refundMjBalance(userId, amount) {
|
||||||
|
return await this.deductFromBalance(userId, 'mjDraw', -amount);
|
||||||
|
}
|
||||||
|
|
||||||
/* V1.5升级将旧版本余额并入到新表 */
|
/* V1.5升级将旧版本余额并入到新表 */
|
||||||
async upgradeBalance() {
|
async upgradeBalance() {
|
||||||
|
BIN
service/templates/.DS_Store
vendored
Normal file
BIN
service/templates/.DS_Store
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user