Commit c856c3a6 authored by shirlyn.guo's avatar shirlyn.guo 👌🏻

Merge branch 'master' of https://gitlab.gsstcloud.com/poc/poc-fe into shirlyn

parents fc6952f6 4afb6b26
......@@ -26,6 +26,7 @@ export default [
ConversationMessageItemInfo: 'readonly',
MittEvents: 'readonly',
I18n: 'readonly',
google: 'readonly',
},
parser: vueParser,
parserOptions: {
......
......@@ -3,12 +3,12 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_n5pnpk71gp.css" />
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_j7wtxbx0oer.css" />
<title>Model Link</title>
</head>
......
......@@ -40,7 +40,7 @@
"spark-md5": "^3.0.2",
"type-fest": "^4.26.1",
"validator": "^13.12.0",
"vue": "^3.5.12",
"vue": "^3.5.13",
"vue-i18n": "^9.14.0",
"vue-router": "^4.4.5"
},
......
This diff is collapsed.
......@@ -43,3 +43,38 @@ export function fetchGetMonthSendMessageCount<T>(agentId: string, channel: strin
export function fetchGetMonthConsumePointCount<T>(agentId: string, channel: string[]) {
return request.post<T>(`/agentDataAnalyzeRest/getPointUsageCount.json?agentId=${agentId}&channel=${channel}`)
}
/**
* @params { agentId: 应用Id timeRange: { rangType: 类型, startTime: 开始时间, endTime: 结束时间 }}
* @returns API渠道积分使用情况
*/
export function fetchGetAPIChannelPointUsageCount<T>(payload: {
agentId: string
timeRange: {
rangType: 'today' | 'week' | 'month' | 'customize'
startTime: string
endTime: string
}
}) {
return request.post<T>('/agentDataAnalyzeRest/getApiChannelPointUsageCount.json', payload)
}
/**
* @params { reportRequestType:导出类型 agentId: 应用Id timeRange: { rangType: 类型, startTime: 开始时间, endTime: 结束时间 }}
* @returns API调用积分消耗报表导出
*/
export function fetchReportAPIChannelPointUsage<T>(payload: {
reportRequestType: string
agentId: string
timeRange: {
rangType: 'today' | 'week' | 'month' | 'customize'
startTime: string
endTime: string
}
}) {
return request.post<T>('/formReportingRest/report.json', payload, {
ignoreErrorCheck: true,
responseType: 'blob',
timeout: 0,
})
}
......@@ -187,3 +187,17 @@ export function fetchGetAutoPlayByAgentId<T>(agentId: string) {
export function fetchUpdateAutoPlay<T>(agentId: string, autoPlay: 'Y' | 'N') {
return request.post<T>(`/agentApplicationRest/enableAutoPlay.json?agentId=${agentId}&autoPlay=${autoPlay}`)
}
/**
* @returns 获取API配置
*/
export function fetchGetAPIProfile<T>() {
return request.post<T>('/agentApplicationRest/getApiProfile.json')
}
/**
* @returns 重新生成API配置
*/
export function fetchResetAPIProfile<T>() {
return request.post<T>('/agentApplicationRest/resetApiProfile.json')
}
......@@ -4,12 +4,12 @@ export function fetchUserEquityInfo<T>() {
return request.post<T>('/equityRest/getCurrentEquity.json')
}
export function fetchCreateEquityOrder<T>(payload: object) {
return request.post<T>('/equityOrderRest/createOrder.json', payload)
export function fetchCreateEquityOrder<T>(payload: object, controller: AbortController) {
return request.post<T>('/equityOrderRest/createOrder.json', payload, { signal: controller.signal })
}
export function fetchGetEquityPayQrCode<T>(payload: object) {
return request.post<T>('/payRest/pay.json', null, { params: payload })
export function fetchGetEquityPayQrCode<T>(payload: object, controller: AbortController) {
return request.post<T>('/payRest/pay.json', null, { params: payload, signal: controller.signal })
}
export function fetchGetPayStatus<T>(payOrderSn: string) {
......
import { request } from '@/utils/request'
/**
* @query channels 渠道列表
* @returns 平台积分使用情况
*/
export function fetchGetPlatformPointUsage<T>(channels: string[]) {
return request.post<T>(`/dataStatisticsRest/platformPointUsage.json?channels=${channels}`)
}
/**
* @param { channel: 渠道列表 timeRange: { rangType: 类型, startTime: 开始时间, endTime: 结束时间 } }
* @returns 平台积分使用趋势
*/
export function fetchGetPlatformPointTrend<T>(payload: {
channel: string[]
timeRange: { rangType: string; startTime: string; endTime: string }
}) {
return request.post<T>('/dataStatisticsRest/platformPointTrend.json', payload)
}
/**
* @query channels 渠道列表
* @returns 平台应用使用情况
*/
export function fetchGetPlatformAgentUsage<T>(channels: string[]) {
return request.post<T>(`/dataStatisticsRest/platformAgentUsage.json?channels=${channels}`)
}
/**
* @params { timeRange: { rangType: 类型, startTime: 开始时间, endTime: 结束时间 } }
* @returns 平台应用使用明细
*/
export function fetchGetPlatformAgentUsageDetail<T>(payload: {
timeRange: {
rangType: 'month' | 'week' | 'customize'
startTime: string
endTime: string
}
pagingInfo: {
pageNo: number
pageSize: number
}
}) {
return request.post<T>('/dataStatisticsRest/platformAgentUsageDetail.json', payload)
}
import { request } from '@/utils/request'
export function fetchLogin<T>(payload: {
loginChannel: 'MEMBER_PLATFOMR_SMS' | 'MEMBER_PLATFOMR_EMAIL' | 'MEMBER_PLATFOMR_PW'
loginChannel: 'MEMBER_PLATFOMR_SMS' | 'MEMBER_PLATFOMR_EMAIL' | 'MEMBER_PLATFOMR_PW' | 'MEMBER_PLATFOMR_GOOGLE'
account: string
password?: string
authCode?: string
......@@ -38,3 +38,7 @@ export function fetchUserPasswordUpdate<T>(authCode: string, password: string) {
params: { authCode, password },
})
}
export function fetchGoogleClientId<T>() {
return request.post<T>('/googleConfigRest/getClientId.json')
}
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { RangType } from './type.d'
const emit = defineEmits<{
updateSearchList: [currentTime: RangType, startTime: string, endTime: string]
}>()
const { t } = useI18n()
const currentTime = ref(RangType.week)
const selectDateRange = ref<[string, string]>(['', ''])
const rangTypeOptionList = [
{
label: t('common_module.last_week'),
value: RangType.week,
},
{
label: t('common_module.last_month'),
value: RangType.month,
},
{
label: t('common_module.custom'),
value: RangType.customize,
},
]
const disableSelectDate = (ts: number, type: 'start' | 'end', range: [number, number] | null) => {
if (type === 'start' && range !== null) {
return range[1] - ts >= 86400000 * 365 || ts > Date.now()
}
if (type === 'end' && range !== null) {
return ts - range[0] >= 86400000 * 365 || ts > Date.now()
}
return ts > Date.now()
}
onMounted(() => {
emit('updateSearchList', currentTime.value, '', '')
})
function handleUpdateRangType(rangType: string) {
if (rangType !== RangType.customize) {
selectDateRange.value = ['', '']
emit('updateSearchList', currentTime.value, '', '')
}
}
function handleUpdateDateRange(_value: [number, number], formattedValue: [string, string]) {
if (formattedValue[0] && formattedValue[1]) {
selectDateRange.value = formattedValue
emit('updateSearchList', currentTime.value, selectDateRange.value[0], selectDateRange.value[1])
}
}
</script>
<template>
<div class="flex gap-3">
<n-select
v-model:value="currentTime"
:options="rangTypeOptionList"
class="w-[124px]! time-range-select"
@update:value="handleUpdateRangType"
/>
<n-date-picker
v-show="currentTime === 'customize'"
type="daterange"
:is-date-disabled="disableSelectDate"
clearable
class="w-[250px]! customize-date-picker"
@update:value="handleUpdateDateRange"
/>
</div>
</template>
<style scoped lang="scss">
:deep(.time-range-select .n-base-selection),
:deep(.customize-date-picker.n-date-picker .n-input) {
--n-height: 36px !important;
}
</style>
export enum RangType {
week = 'week',
month = 'month',
customize = 'customize',
}
......@@ -12,6 +12,8 @@ const isShowModal = ref(false)
const modalOptions = reactive({
title: t('common_module.tip'),
content: '',
cancelText: t('common_module.cancel_btn_text'),
confirmText: t('common_module.confirm_btn_text'),
})
let _modalStatusResolve = (_value?: any) => {}
......@@ -33,9 +35,11 @@ function handleConfirm() {
_modalStatusResolve(true)
}
function handleShowModal(content: string, title?: string) {
function handleShowModal(content: string, title?: string, cancelText?: string, confirmText?: string) {
modalOptions.content = content
title && (modalOptions.title = title)
cancelText && (modalOptions.cancelText = cancelText)
confirmText && (modalOptions.confirmText = confirmText)
isShowModal.value = true
......@@ -63,11 +67,11 @@ function handleShowModal(content: string, title?: string) {
<div class="mt-[50px] text-end">
<n-button color="#F5F5F5" round class="!px-[34px] !py-[10px] !text-[14px] !text-[#333]" @click="handleCancel">
{{ t('common_module.cancel_btn_text') }}
{{ modalOptions.cancelText || t('common_module.cancel_btn_text') }}
</n-button>
<n-button color="#6F77FF" round class="!ml-[12px] !px-[34px] !py-[10px] !text-[14px]" @click="handleConfirm">
{{ t('common_module.confirm_btn_text') }}
{{ modalOptions.confirmText || t('common_module.confirm_btn_text') }}
</n-button>
</div>
</div>
......
......@@ -40,6 +40,11 @@ const menuOptions = computed<MenuOption[]>(() => {
key: 'ApplicationsSquare',
icon: () => h('i', { class: 'iconfont icon-square' }),
},
{
label: () => h('div', {}, t('router_title_module.data_statistic')),
key: 'Statistic',
icon: () => h('i', { class: 'iconfont icon-statistics' }),
},
],
},
]
......@@ -144,7 +149,14 @@ function handleNavigateToEquity() {
<i class="iconfont icon-gift text-[14px] text-[#000dff]"></i>
<n-ellipsis>
<span class="ml-[5px] truncate">{{ userStore.equityInfo.points || '-' }}</span>
<!-- <n-number-animation
v-if="userStore.equityInfo.points"
:from="0"
:to="userStore.equityInfo.points"
:precision="2"
/> -->
<span v-if="userStore.equityInfo.points">{{ userStore.equityInfo.points }}</span>
<span v-else class="ml-[5px]">-</span>
</n-ellipsis>
</span>
......
......@@ -112,6 +112,7 @@ common_module:
forever_effective: 'Forever Effective'
year: ' Year'
month: ' Month'
today: 'Today'
alipay: 'Alipay'
wechat: 'WeChat'
analysis: 'Analysis'
......@@ -126,6 +127,14 @@ common_module:
unlimited_duration: 'Unlimited duration'
buy_now: 'Buy now'
payment_success: 'Payment success'
back: 'Back'
time: 'Time(s)'
export_data: 'Export Data'
not_generated: 'Not generated'
export_successfully: 'Export successfully'
export_failed: 'Export failed'
copy_link: 'Copy link'
or: 'or'
dialogue_module:
continue_question_message: 'You can keep asking questions'
......@@ -176,6 +185,7 @@ router_title_module:
application_square: 'Application square'
personal_settings: 'Personal settings'
order_manage: 'Order Management'
data_statistic: 'Data statistic'
login_module:
app_welcome_words: 'Hi, welcome to Model Link'
......@@ -374,6 +384,13 @@ personal_space_module:
removal_prompt_title: 'Are you sure you want to remove the app from the app plaza'
removal_prompt_content: 'After removal, platform users will not be able to experience this application through the app plaza. If you need to relist it, please reconfigure it'
successfully_configured_published: 'Configuration successful, published to App Store'
api_call: 'API call'
api_call_desc: 'You can use apis to connect to agents interfaces'
interface_document: 'Interface document'
click_to_generate: 'Click to generate'
agentId: 'AgentId'
api_call_details: 'API call details'
api_call_datetime: 'Call datetime'
agent_sale_module:
application_square_release_setting: 'The application plaza publishes the configuration'
......@@ -414,6 +431,8 @@ personal_space_module:
upload_knowledge_document_btn_text: 'Import file'
batch_delete_knowledge_document_btn_text: 'Batch deletion'
not_find_knowledge_document_message: 'No knowledge base file found'
not_all_files_train_complete_tip: 'Some of the files are not valid. Please remove the invalid files and try adding again.'
cannot_add_tip_when_file_is_training: 'Currently, the files are being trained and cannot be added'
create_knowledge_modal_title: 'Create a knowledge base'
edit_knowledge_modal_title: 'Edit knowledge base'
......@@ -557,6 +576,8 @@ equity_module:
point_recharge: 'Point recharge'
get_points_for_interacting_with_the_ai: 'Get {points} points for interacting with the AI'
no_time_limit_when_used_up: 'No time limit, when used up'
agents_created_exceeds_tip: 'The number of agents created exceeds the current package benefits, please upgrade the package'
documents_uploaded_exceeds_tip: 'The number of documents uploaded exceeds the current package benefits, please upgrade the package(The remaining {count} can be uploaded)'
order_manage_module:
package_name: 'PackageName'
......@@ -590,5 +611,26 @@ analysis_module:
usage_channel: 'Usage channel'
index: 'Index'
agent_square: 'Agent square'
api: 'Api'
api: 'API'
link_share: 'Link share'
statistic_module:
data_statistic: 'Data statistic'
point_statistic: 'Point statistic'
agent_statistic: 'Agent statistic'
platform_point_usage: 'Platform point usage'
platform_point_trend: 'Platform point trend'
today_usage: 'Today usage'
current_week_usage: 'Current week usage'
current_month_usage: 'Current month usage'
current_year_usage: 'Current year usage'
create_agent_count: 'Create agent count'
usage_agent_count: 'Usage agent count'
unpublish_agent_count: 'Unpublish agent count'
published_agent_count: 'Published agent count'
platform_agent_usage_detail: 'Platform agent usage detail'
agent_title: 'Agent title'
agent_status: 'Agent status'
owner: 'Owner'
usage_count: 'Usage count'
last_usage_time: 'Last usage time'
......@@ -111,6 +111,7 @@ common_module:
forever_effective: '永远有效'
year: '年'
month: '个月'
today: '今日'
alipay: '支付宝'
wechat: '微信'
analysis: '分析'
......@@ -125,6 +126,14 @@ common_module:
unlimited_duration: '无期限'
buy_now: '立即购买'
payment_success: '支付成功'
back: '返回'
time: '次'
export_data: '导出数据'
not_generated: '未生成'
export_successfully: '导出成功'
export_failed: '导出失败'
copy_link: '复制链接'
or: '或'
dialogue_module:
continue_question_message: '你可以继续提问'
......@@ -175,6 +184,7 @@ router_title_module:
application_square: '应用广场'
personal_settings: '个人设置'
order_manage: '订单管理'
data_statistic: '数据统计'
login_module:
app_welcome_words: 'Hi, 欢迎使用Model Link'
......@@ -372,6 +382,13 @@ personal_space_module:
removal_prompt_title: '确定要下架在应用广场的应用?'
removal_prompt_content: '下架后,平台用户将无法通过应用广场体验本应用,如需再次上架请重新配置。'
successfully_configured_published: '配置成功,已发布至应用广场'
api_call: 'API调用'
api_call_desc: '可使用API对接应用接口'
interface_document: '接口文档'
click_to_generate: '点击生成'
agentId: '应用ID'
api_call_details: 'API调用明细'
api_call_datetime: '调用时间'
agent_sale_module:
application_square_release_setting: '应用广场发布配置'
......@@ -412,6 +429,8 @@ personal_space_module:
upload_knowledge_document_btn_text: '导入文件'
batch_delete_knowledge_document_btn_text: '批量删除'
not_find_knowledge_document_message: '未找到知识库文件'
not_all_files_train_complete_tip: '存在不可用文件,请删除文件后再添加'
cannot_add_tip_when_file_is_training: '文件正在学习中,不可添加'
create_knowledge_modal_title: '创建知识库'
edit_knowledge_modal_title: '编辑知识库'
......@@ -555,6 +574,8 @@ equity_module:
point_recharge: '积分充值'
get_points_for_interacting_with_the_ai: '获得{points}积分,用于和AI互动行动'
no_time_limit_when_used_up: '无期限,用完即止'
agents_created_exceeds_tip: '创建应用数量超出当前套餐权益,请升级套餐'
documents_uploaded_exceeds_tip: '上传文件超出当前套餐权益,请升级套餐(剩余可上传{count}个)'
order_manage_module:
package_name: '套餐名称'
......@@ -588,5 +609,28 @@ analysis_module:
usage_channel: '使用渠道'
index: '首页'
agent_square: '应用广场'
api: 'api调用'
api: 'API调用'
link_share: '网页链接'
statistic_module:
data_statistic: '数据统计'
point_statistic: '积分统计'
agent_statistic: '应用统计'
platform_point_usage: '平台积分使用情况'
platform_point_trend: '平台积分使用趋势'
today_usage: '今日消耗积分'
current_week_usage: '本周消耗积分'
current_month_usage: '本月消耗积分'
current_year_usage: '本年消耗积分'
create_agent_count: '创建的应用'
usage_agent_count: '使用的应用'
unpublish_agent_count: '未发布的应用'
published_agent_count: '已发布的应用'
platform_agent_usage_detail: '各应用使用明细表'
agent_title: '应用标题'
agent_status: '应用状态'
owner: '开发者'
usage_count: '使用次数'
last_usage_time: '最后一次使用时间'
......@@ -111,6 +111,7 @@ common_module:
forever_effective: '永遠有效'
year: '年'
month: '個月'
today: '今日'
alipay: '支付寶'
wechat: '微信'
analysis: '分析'
......@@ -125,6 +126,14 @@ common_module:
unlimited_duration: '無期限'
buy_now: '立即購買'
payment_success: '支付成功'
back: '返回'
time: '次'
export_data: '導出數據'
not_generated: '未生成'
export_successfully: '導出成功'
export_failed: '導出失敗'
copy_link: '複製鏈接'
or: '或'
dialogue_module:
continue_question_message: '你可以繼續提問'
......@@ -175,6 +184,7 @@ router_title_module:
application_square: '應用廣場'
personal_settings: '個人設置'
order_manage: '訂單管理'
data_statistic: '數據統計'
login_module:
app_welcome_words: 'Hi, 歡迎使用Model Link'
......@@ -372,6 +382,13 @@ personal_space_module:
removal_prompt_title: '確定要下架在應用廣場的應用?'
removal_prompt_content: '下架後,平臺用戶將無法通過應用廣場體驗本應用,如需再次上架請重新配置。'
successfully_configured_published: '配置成功,已發布至應用廣場'
api_call: 'API調用'
api_call_desc: '可使用API對接應用接口'
interface_document: '接口文檔'
click_to_generate: '點擊生成'
agentId: '應用ID'
api_call_details: 'API調用明細'
api_call_datetime: '調用時間'
agent_sale_module:
application_square_release_setting: '應用廣場發佈配寘'
......@@ -412,6 +429,8 @@ personal_space_module:
upload_knowledge_document_btn_text: '導入文件'
batch_delete_knowledge_document_btn_text: '批量刪除'
not_find_knowledge_document_message: '未找到知識庫文件'
not_all_files_train_complete_tip: '存在不可用文件,請刪除文件後再添加'
cannot_add_tip_when_file_is_training: '文件正在學習中,不可添加'
create_knowledge_modal_title: '創建知識庫'
edit_knowledge_modal_title: '編輯知識庫'
......@@ -555,6 +574,8 @@ equity_module:
point_recharge: '积分充值'
get_points_for_interacting_with_the_ai: '獲得{points}積分,用於和AI互動行動'
no_time_limit_when_used_up: '無期限,用完即止'
agents_created_exceeds_tip: '創建應用數量超出當前套餐權益,請升級套餐'
documents_uploaded_exceeds_tip: '上傳文件超出當前套餐權益,請升級套餐(剩餘可上傳{count}個)'
order_manage_module:
package_name: '套餐名稱'
......@@ -588,5 +609,26 @@ analysis_module:
usage_channel: '使用渠道'
index: '首頁'
agent_square: '應用廣場'
api: 'api調用'
api: 'API調用'
link_share: '網頁鏈接'
statistic_module:
data_statistic: '數據統計'
point_statistic: '積分統計'
agent_statistic: '應用統計'
platform_point_usage: '平台積分使用情況'
platform_point_trend: '平台積分使用趨勢'
today_usage: '今日消耗積分'
current_week_usage: '本週消耗積分'
current_month_usage: '本月消耗積分'
current_year_usage: '本年消耗積分'
create_agent_count: '創建的應用'
usage_agent_count: '使用的應用'
unpublish_agent_count: '未發佈的應用'
published_agent_count: '已發佈的應用'
platform_agent_usage_detail: '各應用使用明細表'
agent_title: '應用標題'
agent_status: '應用狀態'
owner: '開發者'
usage_count: '使用次數'
last_usage_time: '最後一次使用時間'
import { type RouteRecordRaw } from 'vue-router'
import Layout from '@/layout/index.vue'
export default [
{
path: '/statistic-layout',
name: 'StatisticLayout',
meta: {
rank: 1001,
title: '',
},
component: Layout,
redirect: '/statistic',
children: [
{
path: '/statistic',
name: 'Statistic',
meta: {
rank: 1001,
title: 'router_title_module.data_statistic',
},
component: () => import('@/views/statistics/statistics.vue'),
redirect: '/statistic/point-statistic',
children: [
{
path: '/statistic/point-statistic',
name: 'PointStatistic',
meta: {
rank: 1001,
title: 'router_title_module.data_statistic',
belong: 'Statistic',
},
component: () => import('@/views/statistics/point-statistic/point-static.vue'),
},
{
path: '/statistic/agent-statistic',
name: 'AgentStatistic',
meta: {
rank: 1001,
title: 'router_title_module.data_statistic',
belong: 'Statistic',
},
component: () => import('@/views/statistics/agent-statistic/agent-statistic.vue'),
},
],
},
],
},
] as RouteRecordRaw[]
......@@ -53,6 +53,10 @@ export const useUserStore = defineStore('user-store', {
this.token = ''
this.userInfo = createDefaultUserInfoFactory()
if (window.google) {
window.google.accounts.id.disableAutoSelect()
}
ss.remove(UserStoreStorageKeyEnum.isLogin)
ss.remove(UserStoreStorageKeyEnum.token)
ss.remove(UserStoreStorageKeyEnum.userInfo)
......
<script setup lang="ts">
import { fetchCreateEquityOrder, fetchGetEquityPayQrCode, fetchGetPayStatus } from '@/apis/equity'
import { useUserStore } from '@/store/modules/user'
import { computed, onMounted, ref, watch, watchEffect } from 'vue'
import { computed, onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
type VersionName = 'BasicVersion' | 'ProfessionalVersion' | 'FlagshipVersion' | 'EnterpriseVersion' | null
......@@ -24,7 +24,7 @@ const packageList = ref({
title: t('equity_module.flagship_version'),
list: [
{ name: t('equity_module.monthly_pass'), currentPrice: 29.9, originalPrice: 39.9, discountedPrice: 10 },
{ name: t('equity_module.annual_card'), currentPrice: 299, originalPrice: 238.9, discountedPrice: 59.9 },
{ name: t('equity_module.annual_card'), currentPrice: 299, originalPrice: 358.9, discountedPrice: 59.9 },
],
},
})
......@@ -37,7 +37,8 @@ const payInfo = ref({
payOrderSn: '',
})
const isPayQrcodeInvalid = ref(false)
const payStatusTimer = ref<NodeJS.Timeout | null>(null)
let payStatusTimer: NodeJS.Timeout | null = null
let controller: AbortController | null = null
const isShowPayQrCode = computed(() => {
return !!(
......@@ -53,7 +54,17 @@ const currentPackageInfo = computed(() => {
return null
})
watch(currentSelectVersion, () => {
watch(currentSelectVersion, (newVal) => {
if ((!newVal && payStatusTimer !== null) || !isShowPayQrCode.value) {
clearPayStatusTimer()
}
if (newVal && ['ProfessionalVersion', 'FlagshipVersion'].includes(newVal)) {
getEquityPayQrCode()
} else {
clearPayStatusTimer()
}
clearPayInfo()
currentSelectPackage.value = 0
......@@ -66,12 +77,6 @@ watch(currentPackageInfo, (info) => {
}
})
watchEffect(() => {
if (isShowPayQrCode.value) {
getEquityPayQrCode()
}
})
onMounted(() => {
if (currentPackageInfo.value) {
currentPayPrice.value = currentPackageInfo.value.list[0].currentPrice
......@@ -89,9 +94,13 @@ function clearPayInfo() {
}
function getEquityPayQrCode() {
controller && controller.abort()
clearPayStatusTimer()
clearPayInfo()
controller = new AbortController()
const payload = {
type: '',
domain: 'equity',
......@@ -116,7 +125,7 @@ function getEquityPayQrCode() {
break
}
fetchCreateEquityOrder<string>(payload).then((orderRes) => {
fetchCreateEquityOrder<string>(payload, controller).then((orderRes) => {
if (orderRes.code !== 0) return ''
const payQrCodePayload = {
......@@ -133,26 +142,28 @@ function getEquityPayQrCode() {
break
}
fetchGetEquityPayQrCode<{ packageExtra: string; payOrderSn: string }>(payQrCodePayload).then((qrcodeRes) => {
if (orderRes.code !== 0) return ''
fetchGetEquityPayQrCode<{ packageExtra: string; payOrderSn: string }>(payQrCodePayload, controller!).then(
(qrcodeRes) => {
if (orderRes.code !== 0) return ''
isPayQrcodeInvalid.value = false
isPayQrcodeInvalid.value = false
payInfo.value = {
url: qrcodeRes.data.packageExtra.replace('code_url=', ''),
payOrderSn: qrcodeRes.data.payOrderSn,
}
payInfo.value = {
url: qrcodeRes.data.packageExtra.replace('code_url=', ''),
payOrderSn: qrcodeRes.data.payOrderSn,
}
/* 延迟自动取消支付查询 */
setTimeout(
() => {
isPayQrcodeInvalid.value = true
},
10 * 60 * 1000,
)
/* 延迟自动取消支付查询 */
setTimeout(
() => {
isPayQrcodeInvalid.value = true
},
10 * 60 * 1000,
)
getPayStatus()
})
getPayStatus()
},
)
})
}
......@@ -175,15 +186,17 @@ function getPayStatus() {
})
}
payStatusTimer.value = setInterval(() => {
clearPayStatusTimer()
payStatusTimer = setInterval(() => {
request()
}, 1000)
}
function clearPayStatusTimer() {
if (payStatusTimer.value) {
clearInterval(payStatusTimer.value)
payStatusTimer.value = null
if (payStatusTimer !== null) {
clearInterval(payStatusTimer)
payStatusTimer = null
}
}
......@@ -200,6 +213,10 @@ function handlePackageSwitch(packageKey: typeof currentSelectPackage.value, pric
getEquityPayQrCode()
}
function handlePayQrCodeRefresh() {
getEquityPayQrCode()
}
</script>
<template>
......@@ -226,21 +243,21 @@ function handlePackageSwitch(packageKey: typeof currentSelectPackage.value, pric
</div>
<div class="font-600 mt-[20px] text-[20px] text-[#482801]">
{{ packageItem.currentPrice }}<span class="text-[14px]"> CNY$/{{ t('equity_module.month') }}</span>
{{ packageItem.currentPrice }}<span class="text-[14px]"> /{{ t('equity_module.month') }}</span>
</div>
<div
v-show="currentSelectPackage === index"
class="mt-[6px] text-center text-[14px] text-[#482801] line-through"
>
{{ packageItem.originalPrice }} CNY$
{{ packageItem.originalPrice }}
</div>
<div
v-show="currentSelectPackage !== index"
class="absolute bottom-0 w-full bg-[#ffebd2] py-[6px] text-center text-[12px] text-[#9A5705]"
>
{{ t('equity_module.discounted') }}{{ packageItem.discountedPrice }}CNY$
{{ t('equity_module.discounted') }}{{ packageItem.discountedPrice }}
</div>
<div
......@@ -301,7 +318,10 @@ function handlePackageSwitch(packageKey: typeof currentSelectPackage.value, pric
<div v-show="isPayQrcodeInvalid" class="flex-center absolute inset-0 flex">
<div class="absolute inset-0 bg-[rgba(0,0,0,0.6)] blur-sm"></div>
<i class="iconfont icon-huanyihuan font-600 z-1 relative text-[30px] text-[#fff]"></i>
<i
class="iconfont icon-huanyihuan font-600 z-1 relative cursor-pointer text-[30px] text-[#fff]"
@click="handlePayQrCodeRefresh"
></i>
</div>
</Transition>
</div>
......@@ -316,7 +336,7 @@ function handlePackageSwitch(packageKey: typeof currentSelectPackage.value, pric
<span class="font-600 text-[16px]">
{{ t('common_module.alipay') }} {{ t('equity_module.scan_code_payment') }}
<span class="text-[24px] text-[#F25744]">{{ currentPayPrice }}</span
>CNY$
>
</span>
</template>
<template v-else-if="currentPaymentMethod === 'wechatpay'">
......@@ -324,7 +344,7 @@ function handlePackageSwitch(packageKey: typeof currentSelectPackage.value, pric
<span class="font-600 text-[16px]">
{{ t('common_module.wechat') }} {{ t('equity_module.scan_code_payment') }}
<span class="text-[24px] text-[#F25744]">{{ currentPayPrice }}</span
>CNY$
>
</span>
</template>
</div>
......
......@@ -5,14 +5,16 @@ import {
fetchGetPayStatus,
fetchGetPointsGiftPackageList,
} from '@/apis/equity'
import { useSystemLanguageStore } from '@/store/modules/system-language'
import { useUserStore } from '@/store/modules/user'
import { ref, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
const isShowPointRechargeModal = defineModel<boolean>('isShowPointRechargeModal', { default: false })
const { t } = useI18n()
const userStore = useUserStore()
const systemLanguageStore = useSystemLanguageStore()
const currentSelectPackage = ref<0 | 1 | 2>(0) // 1:100积分 2:350积分 3:600积分
const currentPaymentMethod = ref<'alipay' | 'wechatpay'>('alipay')
......@@ -41,8 +43,11 @@ const payInfo = ref({
payOrderSn: '',
})
const isPayQrcodeInvalid = ref(false)
const payStatusTimer = ref<NodeJS.Timeout | null>(null)
const currentPayPrice = ref(0)
let payStatusTimer: NodeJS.Timeout | null = null
let controller: AbortController | null = null
const isEnglishLanguage = computed(() => systemLanguageStore.currentLanguageInfo.key === 'en')
watch(isShowPointRechargeModal, (newVal) => {
if (newVal) {
......@@ -82,9 +87,13 @@ function getPointsGiftPackageList() {
}
function getEquityPayQrCode() {
controller && controller.abort()
clearPayStatusTimer()
clearPayInfo()
controller = new AbortController()
const payload = {
type: '',
domain: 'point',
......@@ -106,7 +115,7 @@ function getEquityPayQrCode() {
break
}
fetchCreateEquityOrder<string>(payload).then((orderRes) => {
fetchCreateEquityOrder<string>(payload, controller).then((orderRes) => {
if (orderRes.code !== 0) return ''
const payQrCodePayload = {
......@@ -123,26 +132,28 @@ function getEquityPayQrCode() {
break
}
fetchGetEquityPayQrCode<{ packageExtra: string; payOrderSn: string }>(payQrCodePayload).then((qrcodeRes) => {
if (orderRes.code !== 0) return ''
fetchGetEquityPayQrCode<{ packageExtra: string; payOrderSn: string }>(payQrCodePayload, controller!).then(
(qrcodeRes) => {
if (orderRes.code !== 0) return ''
isPayQrcodeInvalid.value = false
isPayQrcodeInvalid.value = false
payInfo.value = {
url: qrcodeRes.data.packageExtra.replace('code_url=', ''),
payOrderSn: qrcodeRes.data.payOrderSn,
}
payInfo.value = {
url: qrcodeRes.data.packageExtra.replace('code_url=', ''),
payOrderSn: qrcodeRes.data.payOrderSn,
}
/* 延迟自动取消支付查询 */
setTimeout(
() => {
isPayQrcodeInvalid.value = true
},
10 * 60 * 1000,
)
/* 延迟自动取消支付查询 */
setTimeout(
() => {
isPayQrcodeInvalid.value = true
},
10 * 60 * 1000,
)
getPayStatus()
})
getPayStatus()
},
)
})
}
......@@ -165,16 +176,10 @@ function getPayStatus() {
})
}
const timerId = setInterval(() => {
if (!payInfo.value.payOrderSn) {
clearInterval(timerId)
payStatusTimer.value = null
return
}
clearPayStatusTimer()
payStatusTimer = setInterval(() => {
request()
}, 1000)
payStatusTimer.value = timerId
}
function handlePaymentMethodSwitch(method: 'alipay' | 'wechatpay') {
......@@ -200,9 +205,9 @@ function clearPayInfo() {
}
function clearPayStatusTimer() {
if (payStatusTimer.value !== null) {
clearInterval(payStatusTimer.value)
payStatusTimer.value = null
if (payStatusTimer !== null) {
clearInterval(payStatusTimer)
payStatusTimer = null
}
}
......@@ -213,6 +218,10 @@ function onModalAfterLeave() {
clearPayStatusTimer()
clearPayInfo()
}
function handlePayQrCodeRefresh() {
getEquityPayQrCode()
}
</script>
<template>
......@@ -225,7 +234,7 @@ function onModalAfterLeave() {
:on-after-leave="onModalAfterLeave"
>
<div v-if="!pointsGiftPackageListLoading">
<ul class="mt-[10px] flex h-[255px]">
<ul class="mt-[10px] flex" :class="isEnglishLanguage ? 'h-[285px]' : 'h-[255px]'">
<li
v-for="(packageItem, packageIndex) in pointsGiftPackageList"
:key="packageItem.id"
......@@ -256,13 +265,15 @@ function onModalAfterLeave() {
{{ t('common_module.buy_now') }}
</button>
<ul class="ml-[20px] mt-[26px]">
<li class="mb-[8px] flex items-center text-[14px] text-[#482801]">
<ul class="ml-[20px] mt-[26px] pr-[8px]">
<li class="mb-[8px] flex text-[14px] text-[#482801]">
<span
class="mr-[5px] inline-block h-[6px] w-[6px] rounded-full"
class="mr-[5px] mt-[8px] inline-block h-[6px] w-[6px] shrink-0 rounded-full"
:class="currentSelectPackage === packageIndex ? 'bg-[#FFEAC8]' : 'bg-[#EBD8BE]'"
></span>
{{ t('equity_module.get_points_for_interacting_with_the_ai', { points: packageItem.points }) }}
<div class="text-start">
{{ t('equity_module.get_points_for_interacting_with_the_ai', { points: packageItem.points }) }}
</div>
</li>
<li class="mb-[8px] flex items-center text-[14px] text-[#482801]">
<span
......@@ -319,7 +330,10 @@ function onModalAfterLeave() {
<div v-show="isPayQrcodeInvalid" class="flex-center absolute inset-0 flex">
<div class="absolute inset-0 bg-[rgba(0,0,0,0.6)] blur-sm"></div>
<i class="iconfont icon-huanyihuan font-600 z-1 relative text-[30px] text-[#fff]"></i>
<i
class="iconfont icon-huanyihuan font-600 z-1 relative cursor-pointer text-[30px] text-[#fff]"
@click="handlePayQrCodeRefresh"
></i>
</div>
</Transition>
</div>
......@@ -331,19 +345,19 @@ function onModalAfterLeave() {
<div class="mt-[10px] flex items-center justify-center">
<template v-if="currentPaymentMethod === 'alipay'">
<img class="mr-[5px] mt-[6px] h-[16px] w-[16px]" src="@/assets/images/equity/alipay-icon.png" alt="alipay" />
<span class="font-600 text-[16px]"
>{{ t('common_module.alipay') }} {{ t('equity_module.scan_code_payment')
}}<span class="text-[24px] text-[#F25744]">{{ currentPayPrice }}</span
></span
>
<span class="font-600 text-[16px]">
{{ t('common_module.alipay') }} {{ t('equity_module.scan_code_payment') }}
<span class="text-[24px] text-[#F25744]">{{ currentPayPrice }}</span>
</span>
</template>
<template v-else-if="currentPaymentMethod === 'wechatpay'">
<img class="mr-[5px] mt-[6px] h-[16px] w-[16px]" src="@/assets/images/equity/wechat-icon.png" alt="alipay" />
<span class="font-600 text-[16px]"
>{{ t('common_module.wechat') }} {{ t('equity_module.scan_code_payment')
}}<span class="text-[24px] text-[#F25744]">{{ currentPayPrice }}</span
></span
>
<span class="font-600 text-[16px]">
{{ t('common_module.wechat') }} {{ t('equity_module.scan_code_payment') }}
<span class="text-[24px] text-[#F25744]"> {{ currentPayPrice }}</span>
</span>
</template>
</div>
</div>
......
......@@ -20,6 +20,11 @@ const equityInfo = computed(() => {
return userStore.equityInfo
})
/* created */
;(function () {
userStore.fetchUpdateEquityInfo()
})()
function handlePointRecharge() {
currentSelectVersion.value = null
......@@ -71,17 +76,18 @@ function handlePointRecharge() {
</div>
</div>
<div class="ml-[58px] mt-[4px] flex items-end">
<div class="font-600 text-[24px]"><n-number-animation :from="0" :to="equityInfo.points" /></div>
<div class="font-600 text-[24px]">
<n-number-animation :from="0" :to="equityInfo.points" :precision="2" />
</div>
<span class="font-600 mb-[3px] ml-[7px] text-[14px]">{{ t('equity_module.points') }}</span>
</div>
</div>
<div
class="flex cursor-pointer select-none items-center justify-end text-[15px] text-[#0B7DFF]"
@click="handlePointRecharge"
>
<span>{{ t('equity_module.top_up_immediately') }}</span>
<i class="iconfont icon-left ml-[2px] inline-block rotate-180 text-[12px]"></i>
<div class="flex select-none items-center justify-end text-[15px] text-[#0B7DFF]">
<span class="cursor-pointer" @click="handlePointRecharge">
<span>{{ t('equity_module.top_up_immediately') }}</span>
<i class="iconfont icon-left ml-[2px] inline-block rotate-180 text-[12px]"></i>
</span>
</div>
</section>
<section
......
......@@ -6,6 +6,8 @@ import { nanoid } from 'nanoid'
import fetchEventStreamSource from '../utils/fetch-event-stream-source'
import { throttle } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/store/modules/user'
import { ChannelType } from '@/enums/channel'
interface Props {
currentSessionId: string
......@@ -32,6 +34,7 @@ const currentFetchEventSourceController = defineModel<AbortController | null>('c
const { t } = useI18n()
const inputFileRef = useTemplateRef<HTMLInputElement | null>('inputFileRef')
const userStore = useUserStore()
let fileUploadController = shallowRef<AbortController | null>(null)
......@@ -164,6 +167,7 @@ function questionSubmit() {
agentId: currentAgentApplication.value.agentId, //应用ID
input: questionContent.value.trim(), //提问文本
fileUrls: currentInputFileInfo.value.url ? [currentInputFileInfo.value.url] : [],
channel: ChannelType.index,
},
{
onmessage: (message) => {
......@@ -189,6 +193,8 @@ function questionSubmit() {
nextTick(() => {
emit('historyRecordListUpdate')
userStore.fetchUpdateEquityInfo()
})
},
onerror: (err) => {
......@@ -339,7 +345,7 @@ defineExpose({
<Transition name="application-select-menu">
<ul
v-show="isShowApplicationSelectMenu"
class="absolute -top-[10px] left-1/2 w-[200px] -translate-x-1/2 -translate-y-full rounded-[6px] bg-[#fff] p-[10px] pb-[4px] pr-0 shadow-md"
class="absolute -top-[10px] left-0 w-[240px] -translate-y-full rounded-[6px] bg-[#fff] p-[10px] pb-[4px] pr-0 shadow-md"
>
<n-virtual-list
style="max-height: 200px"
......@@ -350,7 +356,7 @@ defineExpose({
>
<template #default="{ item }">
<li
class="relative mb-[6px] flex cursor-pointer items-center overflow-hidden rounded-[4px] bg-[#f3f3f5] px-[10px] py-[4px] transition hover:bg-[#E7E7E7]"
class="relative mb-[6px] flex cursor-pointer items-center overflow-hidden rounded-[4px] bg-[#f3f3f5] py-[4px] pl-[10px] pr-[16px] transition hover:bg-[#E7E7E7]"
@click="handleApplicationChange(item)"
>
<div
......@@ -363,6 +369,12 @@ defineExpose({
{{ item.agentTitle }}
</n-ellipsis>
</div>
<div class="pl-[8px] text-[12px] text-[#0B7DFF]">
<template v-if="item.points && item.points !== 0">
{{ item.points }}{{ t('equity_module.points2') }}/{{ t('common_module.time') }}
</template>
<template v-else-if="item.points && item.points === 0">{{ t('common_module.free') }}</template>
</div>
<i
v-if="item.agentId === currentAgentApplication.agentId"
......
......@@ -186,7 +186,7 @@ defineExpose({
</div> -->
<div class="px-[4px]">
<div class="h-[40px]">
<div class="h-[40px]" :class="[isHistoryListEdit ? 'sticky top-0 z-10' : '']">
<Transition name="history-record-edit" mode="out-in">
<h2 v-if="!isHistoryListEdit" class="flex items-center justify-between">
<span class="flex items-center">
......
......@@ -5,6 +5,7 @@ export interface AgentApplicationRecordItem {
agentDesc: string
creator: string
publishedTime: string
points: number
}
export interface MessageItemInterface {
......
......@@ -5,27 +5,29 @@ import { Mail, Lock, Iphone, Down, User } from '@icon-park/vue-next'
import isMobilePhone from 'validator/es/lib/isMobilePhone'
import isEmail from 'validator/es/lib/isEmail'
import { ss } from '@/utils/storage'
import { fetchEmailCode, fetchLogin, fetchSMSCode } from '@/apis/user'
import { fetchEmailCode, fetchLogin, fetchSMSCode, fetchGoogleClientId } from '@/apis/user'
import SparkMD5 from 'spark-md5'
import { useUserStore } from '@/store/modules/user'
import type { UserInfo } from '@/store/types/user'
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import LanguageSetting from '@/components/language-setting/language-setting.vue'
import { useSystemLanguageStore } from '@/store/modules/system-language'
enum StorageKeyEnum {
smsCountdownTime = 'SMS_COUNTDOWN_TIME',
emailCountdownTime = 'MAIL_COUNTDOWN_TIME',
}
type LoginMethod = 'password' | 'sms' | 'email'
type LoginMethod = 'password' | 'sms' | 'email' | 'google'
interface LoginPayload {
loginChannel: 'MEMBER_PLATFOMR_SMS' | 'MEMBER_PLATFOMR_EMAIL' | 'MEMBER_PLATFOMR_PW'
loginChannel: 'MEMBER_PLATFOMR_SMS' | 'MEMBER_PLATFOMR_EMAIL' | 'MEMBER_PLATFOMR_PW' | 'MEMBER_PLATFOMR_GOOGLE'
account: string
password?: string
authCode?: string
}
const userStore = useUserStore()
const systemLanguageStore = useSystemLanguageStore()
const router = useRouter()
const route = useRoute()
const { t } = useI18n()
......@@ -34,6 +36,7 @@ const passwordLoginFormRef = useTemplateRef<FormInst>('passwordLoginFormRef')
const smsLoginFormRef = useTemplateRef<FormInst>('smsLoginFormRef')
const emailLoginFormRef = useTemplateRef<FormInst>('emailLoginFormRef')
const countdownRef = useTemplateRef<CountdownInst>('countdownRef')
const googleLoginBtnRef = useTemplateRef<HTMLDivElement>('googleLoginBtnRef')
const currentLoginMethod = ref<LoginMethod>('sms')
const showCardReserveAnimation = ref(false)
......@@ -52,6 +55,9 @@ const emailLoginForm = ref({
email: '',
code: '',
})
const googleLoginForm = ref({
credential: '',
})
const passwordLoginFormRules = shallowReadonly<FormRules>({
account: { required: true, message: t('login_module.please_enter_your_account_number'), trigger: 'blur' },
......@@ -90,6 +96,13 @@ const emailLoginFormRules = shallowReadonly<FormRules>({
code: { required: true, message: t('login_module.please_enter_the_verification_code') },
})
const currentPhoneNumberArea = ref<'+86' | '+852'>('+852')
const countdownActive = ref(true)
const isShowCountdown = ref(false)
const countdownDuration = ref<number>(60000)
const googleLoginBtnLoading = ref(false)
const phoneNumberAreaOptions = computed(() => {
return [
{
......@@ -102,11 +115,6 @@ const phoneNumberAreaOptions = computed(() => {
},
]
})
const currentPhoneNumberArea = ref<'+86' | '+852'>('+852')
const countdownActive = ref(true)
const isShowCountdown = ref(false)
const countdownDuration = ref<number>(60000)
watchEffect(() => {
let timeStringDraft = ''
......@@ -135,6 +143,25 @@ watchEffect(() => {
}
})
/* created */
;(function () {
if (!window.google) {
const script = document.createElement('script')
script.src = `https://accounts.google.com/gsi/client?hl=${systemLanguageStore.currentLanguage}` // 加载客户端库
script.async = true
script.onload = () => {
getGoogleLoginClientId().then((clientId) => {
clientId && initializeGoogleSignIn(clientId)
})
}
document.head.appendChild(script)
} else {
getGoogleLoginClientId().then((clientId) => {
clientId && initializeGoogleSignIn(clientId)
})
}
})()
function onlyAllowNumber(value: string) {
return !value || /^\d+$/.test(value)
}
......@@ -160,7 +187,7 @@ function onCountdownFinish() {
function getInputPhoneNumber() {
return currentPhoneNumberArea.value !== '+86'
? `${currentPhoneNumberArea.value}${smsLoginForm.value.phoneNumber}`
? encodeURIComponent(`${currentPhoneNumberArea.value}${smsLoginForm.value.phoneNumber}`)
: smsLoginForm.value.phoneNumber
}
......@@ -195,7 +222,7 @@ function handleLoginSubmit(method: LoginMethod) {
payload = {
loginChannel: 'MEMBER_PLATFOMR_SMS',
account: getInputPhoneNumber(),
account: smsLoginForm.value.phoneNumber,
authCode: smsLoginForm.value.code,
}
......@@ -218,6 +245,17 @@ function handleLoginSubmit(method: LoginMethod) {
})
}
break
case 'google':
{
payload = {
loginChannel: 'MEMBER_PLATFOMR_GOOGLE',
account: '',
authCode: googleLoginForm.value.credential,
}
resolve(true)
}
break
}
}).then(() => {
loginBtnLoading.value = true
......@@ -301,6 +339,43 @@ function handleEmailCodeGain() {
},
)
}
function initializeGoogleSignIn(clientId: string) {
window.google.accounts.id.initialize({
client_id: encodeURIComponent(clientId),
auto_select: false,
callback: (res: { credential: string; clientId: string }) => {
googleLoginForm.value.credential = res.credential
handleLoginSubmit('google')
},
})
if (googleLoginBtnRef.value) {
window.google.accounts.id.renderButton(googleLoginBtnRef.value, {
size: 'medium',
text: 'signin_with',
logo_alignment: 'left',
locale: systemLanguageStore.currentLanguage,
})
googleLoginBtnLoading.value = true
}
// window.google.accounts.id.prompt((notification: any) => {
// if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
// console.log('💥💥💥💥💥💥登录失败💥💥💥💥💥💥')
// }
// })
}
function getGoogleLoginClientId() {
return fetchGoogleClientId<string>().then((res) => {
if (res.code !== 0) return ''
return res.data
})
}
</script>
<template>
......@@ -313,7 +388,7 @@ function handleEmailCodeGain() {
<LanguageSetting arrow-direction="bottom" btn-bg-color="#f4f5f5" />
</div>
<div class="absolute right-[14%] top-1/2 h-[458px] w-[390px] -translate-y-1/2">
<div class="absolute right-[14%] top-1/2 w-[390px] -translate-y-1/2">
<div
class="h-full w-full rounded-[10px] bg-[#fff] px-[29px] shadow-2xl"
:class="{ 'animate-card-reverse': showCardReserveAnimation }"
......@@ -532,8 +607,8 @@ function handleEmailCodeGain() {
</n-form>
</div>
<div class="absolute bottom-[22px] left-0 w-full">
<div class="mb-[32px]">
<div class="w-full pb-[34px]">
<div class="h-[134px]">
<div class="mb-[12px] text-center text-[12px] text-[#999999]">
{{ t('login_module.other_login_methods') }}
</div>
......@@ -562,6 +637,14 @@ function handleEmailCodeGain() {
<Lock theme="outline" size="17" fill="#666666" :stroke-width="3" />
</button>
</div>
<div v-show="googleLoginBtnLoading" class="py-[10px] text-center text-[12px] text-[#999999]">
{{ t('common_module.or') }}
</div>
<div v-show="googleLoginBtnLoading" class="flex-center flex">
<div ref="googleLoginBtnRef"></div>
</div>
</div>
<!-- <div class="text-center">
......
......@@ -9,6 +9,7 @@ import { fetchEventStreamSource } from '../utils/fetch-event-stream-source'
import { UploadStatus } from '@/enums/upload-status'
import { ChannelType } from '@/enums/channel'
import { useDialogueFile } from '@/composables/useDialogueFile'
import { useUserStore } from '@/store/modules/user'
const { t } = useI18n()
......@@ -29,6 +30,8 @@ const emit = defineEmits<{
clearAllMessage: []
}>()
const userStore = useUserStore()
const { uploadFileList, handleLimitUpload, handleUpload, handleRemoveFile } = useDialogueFile()
const multiModelDialogueList = defineModel<MultiModelDialogueItem[]>('multiModelDialogueList', { required: true })
......@@ -185,6 +188,7 @@ function handleQuestionSubmit() {
onFinally: () => {
modelItem.controller = null
modelItem.isAnswerResponseWait = false
userStore.fetchUpdateEquityInfo()
},
})
})
......
......@@ -13,7 +13,6 @@ interface Props {
modelListOptions: SelectOption[]
totalNum: number
isCurrent: boolean
modelPoints: number
}
const { t } = useI18n()
......@@ -139,7 +138,6 @@ function scrollToBottom() {
v-model:model-config="modelConfig"
:model-list-options="modelListOptions"
:is-current="isCurrent"
:model-points="modelPoints"
@update-config="(modelConfig) => emit('updateConfig', modelConfig)"
@reset-conversation="emit('resetConversation')"
/>
......
......@@ -9,7 +9,6 @@ import { useSystemLanguageStore } from '@/store/modules/system-language'
interface Props {
modelListOptions: SelectOption[]
isCurrent: boolean
modelPoints: number
}
const { t } = useI18n()
......@@ -26,7 +25,6 @@ const systemLanguageStore = useSystemLanguageStore()
const modelConfig = defineModel<MultiModelDialogueItem>('modelConfig', { required: true })
const currentDiversityMode = ref('balance')
const modelConsumePoints = ref(props.modelPoints || 0)
const currentModelNickName = computed({
get: () => (modelConfig.value.modelNickName === '' ? undefined : modelConfig.value.modelNickName),
......@@ -44,22 +42,24 @@ const isEnLanguage = computed(() => {
})
const totalConsumePoints = computed(() => (communicationTurn: number) => {
if (!modelConsumePoints.value) {
const modelConsumePoints = modelConfig.value.modelPoints
if (!modelConsumePoints) {
return t('common_module.free')
}
switch (communicationTurn) {
case 1:
return t('common_module.points_per_time', { count: (modelConsumePoints.value * 1).toFixed(1) })
return t('common_module.points_per_time', { count: (modelConsumePoints * 1).toFixed(1) })
case 5:
return t('common_module.points_per_time', { count: (modelConsumePoints.value * 1).toFixed(1) })
return t('common_module.points_per_time', { count: (modelConsumePoints * 1).toFixed(1) })
case 10:
return t('common_module.points_per_time', { count: (modelConsumePoints.value * 2).toFixed(1) })
return t('common_module.points_per_time', { count: (modelConsumePoints * 2).toFixed(1) })
case 15:
return t('common_module.points_per_time', { count: (modelConsumePoints.value * 3).toFixed(1) })
return t('common_module.points_per_time', { count: (modelConsumePoints * 3).toFixed(1) })
}
return t('common_module.points_per_time', { count: modelConsumePoints.value.toFixed(1) })
return t('common_module.points_per_time', { count: modelConsumePoints.toFixed(1) })
})
watch(
......@@ -139,7 +139,7 @@ function modelListRenderTag({ option }: { option: SelectOption }) {
// 更新大模型
function handleUpdateLargeModel(_value: string, option: SelectOption) {
modelConfig.value.icon = option.icon as string
modelConsumePoints.value = (option.points || 0) as number
modelConfig.value.modelPoints = (option.points || 0) as number
emit('resetConversation')
handleUpdateAgentConfig()
}
......
......@@ -34,7 +34,6 @@ const isFullPageLoading = ref(false)
const agentId = ref('')
const agentApplicationConfig = ref<PersonalAppConfigState>(defaultPersonalAppConfigState())
const multiModelDialogueList = ref<MultiModelDialogueItem[]>([])
const currentModelPoints = ref(0)
let modelListOptions = reactive<SelectOption[]>([])
......@@ -92,6 +91,7 @@ function modelDialogueFactory() {
topP: 0.1,
temperature: 1,
communicationTurn: 5,
modelPoints: 0,
agentSystem: '',
controller: null,
isAnswerResponseWait: false,
......@@ -154,7 +154,7 @@ async function handleGetLargeModelInfo() {
if (res.code === 0) {
multiModelDialogueList.value[0].icon = res.data.icon
currentModelPoints.value = res.data.models?.[0].points || 0
multiModelDialogueList.value[0].modelPoints = res.data.models?.[0].points || 0
isFullPageLoading.value = false
}
}
......@@ -265,6 +265,7 @@ function handleClearAllMessage() {
.ctWarning(t('common_module.dialogue_module.clear_message_dialog_content'), t('common_module.tip'))
.then(() => {
handleBlockMessageResponse()
userStore.fetchUpdateEquityInfo()
window.$message.success(t('common_module.clear_success_message'))
})
}
......@@ -316,7 +317,6 @@ function handleBlockMessageResponse() {
:key="modelDialogueItem.id"
:model-dialogue-item="modelDialogueItem"
:is-current="modelDialogueItem.id === multiModelDialogueList[0].id"
:model-points="currentModelPoints"
:model-list-options="modelListOptions"
:total-num="multiModelDialogueList.length"
@update-config="handleSavePersonalAppConfig"
......
......@@ -19,6 +19,7 @@ export interface MultiModelDialogueItem {
temperature: number
agentSystem: string
communicationTurn: number
modelPoints: number
controller: AbortController | null
isAnswerResponseWait: boolean
messageList: Map<string, MessageItemInterface>
......
......@@ -40,6 +40,8 @@ const currentPlayMessageItem = ref<ConversationMessageItem | null>(null)
const currentPlayAudioFragmentSerialNo = ref(0)
const currentSoundCtl = shallowRef<Howl | null>(null)
const isSoundCtlCreated = ref(false)
const isAnswerResponseLoading = ref(false)
const createContinueQuestionsException = ref(false)
const isEnLanguage = computed(() => {
return systemLanguageStore.currentLanguageInfo.key === 'en'
......@@ -107,12 +109,18 @@ function handleClearAllMessage() {
}
async function handleCreateContinueQuestions(replyTextContent: string) {
const res = await fetchCreateContinueQuestions<string[]>({ input: replyTextContent })
createContinueQuestionsException.value = false
if (res.code === 0) {
continuousQuestionList.value = res.data
handleUpdatePageScroll()
}
fetchCreateContinueQuestions<string[]>({ input: replyTextContent })
.then((res) => {
if (res.code === 0) {
continuousQuestionList.value = res.data
handleUpdatePageScroll()
}
})
.catch(() => {
createContinueQuestionsException.value = true
})
}
function handleUpdateContinueQuestionStatus(status: 'default' | 'close') {
......@@ -316,6 +324,8 @@ function handleAudioPause(isClearMessageList = false) {
:message-list="messageList"
:continuous-question-status="continuousQuestionStatus"
:continuous-question-list="continuousQuestionList"
:is-answer-response-loading="isAnswerResponseLoading"
:create-continue-questions-exception="createContinueQuestionsException"
@audio-play="handleAudioPlay"
@audio-pause="handleAudioPause"
/>
......@@ -324,6 +334,7 @@ function handleAudioPause(isClearMessageList = false) {
<FooterInput
ref="footerInputRef"
v-model:is-answer-response-loading="isAnswerResponseLoading"
:continuous-question-status="continuousQuestionStatus"
:message-list="messageList"
:answer-audio-auto-play="answerAudioAutoPlay"
......
......@@ -11,6 +11,7 @@ import { useDialogueFile } from '@/composables/useDialogueFile'
import { TEXTTOSPEECH_WS_URL } from '@/config/base-url'
import WebSocketCtr from '@/utils/web-socket-ctr'
import { ChannelType } from '@/enums/channel'
import { useUserStore } from '@/store/modules/user'
interface Props {
messageList: Map<string, ConversationMessageItem>
......@@ -36,10 +37,12 @@ const emit = defineEmits<{
}>()
const personalAppConfigStore = usePersonalAppConfigStore()
const userStore = useUserStore()
const { uploadFileList, handleLimitUpload, handleUpload, handleRemoveFile } = useDialogueFile()
const messageTipModalRef = useTemplateRef<InstanceType<typeof OverwriteMessageTipModal>>('messageTipModalRef')
const isAnswerResponseLoading = defineModel<boolean>('isAnswerResponseLoading', { required: true })
const emitter = inject<Emitter<MittEvents>>('emitter')
......@@ -182,6 +185,7 @@ function handleMessageSend() {
})
})
isAnswerResponseLoading.value = true
inputMessageContent.value = ''
isAnswerResponseWait.value = true
......@@ -226,6 +230,7 @@ function handleMessageSend() {
isAnswerResponseLoading: false,
})
isAnswerResponseLoading.value = false
isCreateContinueQuestions.value && emit('createContinueQuestions', replyTextContent)
emit('updatePageScroll')
blockMessageResponse()
......@@ -261,6 +266,7 @@ function handleMessageSend() {
},
onFinally: () => {
controller = null
userStore.fetchUpdateEquityInfo()
},
})
}
......@@ -285,6 +291,7 @@ function handleClearAllMessage() {
function blockMessageResponse() {
controller?.abort()
isAnswerResponseWait.value = false
userStore.fetchUpdateEquityInfo()
}
function handleSelectFile(cb: () => void) {
......
......@@ -8,6 +8,8 @@ interface Props {
messageList: Map<string, ConversationMessageItem>
continuousQuestionStatus: 'default' | 'close'
continuousQuestionList: string[]
isAnswerResponseLoading: boolean
createContinueQuestionsException: boolean
}
const props = defineProps<Props>()
......@@ -23,7 +25,8 @@ const isShowContinueQuestion = computed(() => {
return (
props.continuousQuestionStatus === 'default' &&
props.messageList.size > 1 &&
!Array.from(props.messageList.entries()).pop()?.[1].isAnswerResponseLoading
!props.isAnswerResponseLoading &&
!props.createContinueQuestionsException
)
})
......
......@@ -4,6 +4,7 @@ import { FormInst, InputInst } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { Emitter } from 'mitt'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import { useThrottleFn } from '@vueuse/core'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import { Help, People, RightOne } from '@icon-park/vue-next'
......@@ -11,6 +12,7 @@ import UploadImage from '@/components/upload-image/upload-image.vue'
import AutoConfigModal from './components/auto-config-modal.vue'
import OptimizeSystemModal from './components/optimize-system-modal.vue'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import { useUserStore } from '@/store/modules/user'
import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import {
fetchCreateAgentAvatar,
......@@ -28,7 +30,10 @@ import AgentRoleSetting from './components/agent-role-setting.vue'
const { t } = useI18n()
const router = useRouter()
const personalAppConfigStore = usePersonalAppConfigStore()
const userStore = useUserStore()
const { baseInfo, commConfig, commModelConfig, knowledgeConfig } = storeToRefs(personalAppConfigStore)
......@@ -90,6 +95,8 @@ watch(
)
onMounted(async () => {
await handleEquityInfoValidate()
emitter?.on('isGetAgentDetail', () => {
;['preamble', 'featuredQuestions']
.filter((property) => {
......@@ -342,6 +349,32 @@ function handleStopGenerate() {
generateAgentSystemController?.abort()
isFullScreenLoading.value = false
}
async function handleEquityInfoValidate() {
if (!userStore.isLogin) {
router.push({
name: 'Login',
query: { redirect: encodeURIComponent(router.currentRoute.value.fullPath) },
})
}
await userStore.fetchUpdateEquityInfo()
if (
userStore.equityInfo.usedAgentCount >= userStore.equityInfo.maxAgentCount &&
userStore.equityInfo.maxAgentCount !== 0 &&
!baseInfo.value.agentId
) {
window.$message
.ctWarning(t('equity_module.agents_created_exceeds_tip'), '', t('common_module.back'))
.then(() => {
router.replace({ name: 'Equity' })
})
.catch(() => {
router.back()
})
}
}
</script>
<template>
......
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Computer, PreviewOpen, AllApplication, SettingOne } from '@icon-park/vue-next'
import { Computer, PreviewOpen, AllApplication, SettingOne, Api } from '@icon-park/vue-next'
import useTableScrollY from '@/composables/useTableScrollY'
import { copyToClip } from '@/utils/copy'
import { formatDateTime } from '@/utils/date-formatter'
......@@ -14,7 +14,8 @@ import {
fetchRemoveSalePublishApplication,
} from '@/apis/agent-application'
import { defaultPersonalAppConfigState, usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import { ApplicationMallInfo } from '../../personal-app/types'
import { ApplicationMallInfo } from '../../../personal-app/types'
import ApiCallDrawer from './components/api-call-drawer.vue'
const { t } = useI18n()
......@@ -34,6 +35,7 @@ const applicationMallInfo = ref<ApplicationMallInfo>({
isSale: '',
launchTime: '',
})
const isShowAPICallDrawer = ref(false)
watch(
() => saleApplicationsInfo.value,
......@@ -76,6 +78,9 @@ function handleClickChannelPublishTableAction(actionType: string) {
case 'unSaleApplication':
handleUnSaleApplication()
break
case 'APIConfiguration':
handleAPIConfiguration()
break
}
}
......@@ -139,10 +144,14 @@ function handleGetApplicationInfo() {
function handleToApplicationSquare() {
router.push({ name: 'ApplicationsSquare' })
}
function handleAPIConfiguration() {
isShowAPICallDrawer.value = true
}
</script>
<template>
<div ref="pageContentWrapRef" class="h-full w-full overflow-hidden p-5">
<div id="drawer-target" ref="pageContentWrapRef" class="relative h-full w-full overflow-hidden p-5">
<table class="w-full text-left" :max-height="tableContentY">
<thead>
<tr>
......@@ -279,25 +288,6 @@ function handleToApplicationSquare() {
</div>
</div>
<div v-show="applicationMallInfo?.isSale === 'Y'" class="flex">
<!-- <div
class="flex cursor-pointer items-center justify-start rounded-[5px] border-[1px] border-[#000DFF] bg-[#f7f7f9] px-[12px] py-[4px] text-[14px] text-[#000DFF] hover:opacity-80"
@click="handleClickChannelPublishTableAction('accessPage')"
>
<PreviewOpen theme="outline" size="16" fill="#000DFF" :stroke-width="4" class="mr-[6px]" />
<span>{{
t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.access_page')
}}</span>
</div>
<div
class="hover:text-theme-color hover:border-theme-color ml-[16px] flex cursor-pointer items-center justify-start rounded-md border bg-[#f7f7f9] px-[12px] py-[4px] text-[14px]"
@click="handleClickChannelPublishTableAction('copyLink')"
>
<CustomIcon icon="pepicons-pop:share-android-circle" class="mr-[6px]" />
<span>{{
t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.share_link')
}}</span>
</div> -->
<div
class="hover:text-theme-color hover:border-theme-color flex cursor-pointer items-center justify-start rounded-md border bg-[#f7f7f9] px-[12px] py-[4px] text-[14px]"
@click="handleClickChannelPublishTableAction('modifySetting')"
......@@ -321,9 +311,46 @@ function handleToApplicationSquare() {
</div>
</td>
</tr>
<tr>
<td class="border-[1px] border-[#efeff5]">
<div class="flex items-center justify-start p-[12px]">
<Api theme="filled" size="24" fill="#000dff" :stroke-width="3" />
<div class="ml-[12px]">
<div class="flex">
<span class="flex items-center gap-[4px]">
{{ t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.api_call') }}
</span>
</div>
<div class="text-[#84868c]">
{{ t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.api_call_desc') }}
</div>
</div>
</div>
</td>
<td class="border-[1px] border-[#efeff5] p-[12px]">
<span class="text-[#84868c]">---</span>
</td>
<td class="border-[1px] border-[#efeff5] p-[12px]">
<div class="flex">
<div
class="flex cursor-pointer items-center justify-start rounded-[5px] border-[1px] border-[#000DFF] bg-[#f7f7f9] px-[12px] py-[4px] text-[14px] text-[#000DFF] hover:opacity-80"
@click="handleClickChannelPublishTableAction('APIConfiguration')"
>
<SettingOne theme="outline" size="16" fill="#000dff" :stroke-width="3" class="mr-[3px]" />
<span>
{{ t('common_module.config') }}
</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<ApiCallDrawer v-model:is-show="isShowAPICallDrawer" />
<SaleApplicationsConfigurationModal
v-model="isShowSaleApplicationsConfigurationModal"
:sale-applications-data="saleApplicationsInfo"
......
import i18n from '@/locales'
import { ApiCallDetailItem } from './type'
const t = i18n.global.t
export function createApiCallDetailColumns() {
return [
{
title: () => (
<span>
{t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.api_call_datetime')}
</span>
),
key: 'dateTime',
align: 'center',
ellipsis: {
tooltip: true,
},
render(row: ApiCallDetailItem) {
return row.dateTime || '--'
},
},
{
title: () => <span>{t('analysis_module.consume_points')}</span>,
key: 'count',
align: 'center',
ellipsis: {
tooltip: true,
},
render(row: ApiCallDetailItem) {
return row.count.toFixed(2) || '--'
},
},
]
}
export interface ApiProfileInfo {
apiKey: string
apiSecret: string
}
export interface ApiCallDetailItem {
dateTime: Date
count: number
}
......@@ -5,7 +5,7 @@ import { Emitter } from 'mitt'
import { useI18n } from 'vue-i18n'
import PageNarBar from './components/page-narbar.vue'
import AgentConfig from './components/agent-config/agent-config.vue'
import AgentPublish from './components/agent-publish.vue'
import AgentPublish from './components/agent-publish/agent-publish.vue'
import AgentAnalysis from './components/agent-analysis/agent-analysis.vue'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import { PersonalAppConfigState } from '@/store/types/personal-app-config'
......
......@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n'
import { Search, MoreOne, Star } from '@icon-park/vue-next'
import { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
import { formatDateTime } from '@/utils/date-formatter'
import {
fetchApplicationsCollectionStatusChange,
fetchDeleteApplication,
......@@ -18,10 +17,12 @@ import { defaultPersonalAppConfigState } from '@/store/modules/personal-app-conf
import searchEmptyImage from '@/assets/images/search-empty.png'
import applicationEmptyImage from '@/assets/images/application-empty.png'
import { debounce } from 'lodash-es'
import { useUserStore } from '@/store/modules/user'
const { t } = useI18n()
const cardContentWrapRef = useTemplateRef<HTMLDivElement>('cardContentWrapRef')
const userStore = useUserStore()
const selectedPublishStatusValue = ref('')
const isShowSaleApplicationsConfigurationModal = ref(false)
......@@ -144,6 +145,8 @@ function handleDeletePersonalApp(agentId: string) {
window.$message.success(t('common_module.delete_success_message'))
agentAppList.value.length === 1 && (pagingInfo.value.pageNo = pagingInfo.value.pageNo - 1)
getApplicationList()
userStore.fetchUpdateEquityInfo()
})
})
}
......
......@@ -16,11 +16,14 @@ import {
} from '@/apis/knowledge'
import useTableScrollY from '@/composables/useTableScrollY'
import EditKnowledgeModal, { KnowledgeFormDataInterface } from './components/edit-knowledge-modal.vue'
import { useUserStore } from '@/store/modules/user'
const { t } = useI18n()
const router = useRouter()
const userStore = useUserStore()
const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 48)
const knowledgeDocumentColumn = createKnowledgeDocumentColumn(handleClickKnowledgeDocumentTableAction)
......@@ -146,6 +149,7 @@ async function handleDeleteKnowledgeDocument(kdId: number) {
if (res.code === 0) {
window.$message.success(t('common_module.delete_success_message'))
await userStore.fetchUpdateEquityInfo()
await handleGetKnowledgeDocumentList()
}
})
......@@ -192,6 +196,7 @@ async function handleBatchDelDocument() {
if (res.code === 0) {
window.$message.success(t('common_module.delete_success_message'))
await userStore.fetchUpdateEquityInfo()
await handleGetKnowledgeDocumentList()
}
})
......
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { UploadFileInfo } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { UploadOne } from '@icon-park/vue-next'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import { fetchUploadKnowledgeDocument } from '@/apis/knowledge'
import { useUserStore } from '@/store/modules/user'
interface Emit {
(e: 'next', value: number[]): void
......@@ -29,9 +31,14 @@ interface FileItem {
const { t } = useI18n()
const router = useRouter()
const userStore = useUserStore()
const emit = defineEmits<Emit>()
const uploadFileList = ref<FileItem[]>([])
const isExceedKnowledgeCount = ref(false)
const uploadFileIcon = (type: string) => {
return `https://gsst-poe-sit.gz.bcebos.com/icon/${type}.svg`
......@@ -53,8 +60,49 @@ const uploadFileSize = computed(() => (fileSize: number) => {
return (fileSize / Math.pow(binarySize, unit)).toPrecision(3) + ' ' + sizes[unit]
})
onMounted(async () => {
await handleGetEquityInfo()
})
async function handleGetEquityInfo() {
await userStore.fetchUpdateEquityInfo()
}
// 上传文件前限制
function handleLimitUpload(data: { file: UploadFileInfo }) {
function handleLimitUpload(data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) {
// 上传数量达到上限
if (isExceedKnowledgeCount.value) {
return false
}
// 获取文件上传时多选的个数
let uploadKnowledgeCount = 0
const knowledgeFileList = document.getElementsByClassName('upload-knowledge-file')
if (knowledgeFileList && knowledgeFileList.length > 0) {
const knowledgeFile = knowledgeFileList[0].getElementsByTagName('input')
if (knowledgeFile && knowledgeFile.length > 0 && knowledgeFile[0].files && knowledgeFile[0].files.length > 0) {
uploadKnowledgeCount = knowledgeFile[0].files.length
}
}
const enableKnowledgeCount = Math.max(
0,
userStore.equityInfo.maxKnowledgeCount - userStore.equityInfo.usedKnowledgeCount - uploadFileList.value.length,
)
if (userStore.equityInfo.maxKnowledgeCount !== 0 && enableKnowledgeCount < uploadKnowledgeCount) {
isExceedKnowledgeCount.value = true
window.$message
.ctWarning(t('equity_module.documents_uploaded_exceeds_tip', { count: enableKnowledgeCount }), '')
.then(() => {
router.push({ name: 'Equity' })
})
.catch(() => {})
return false
}
const allowTypeList = ['md', 'doc', 'docx', 'pdf', 'txt']
const fileType = (data.file.file && data.file.file?.name.split('.')?.pop()?.toLowerCase()) || ''
......@@ -144,6 +192,26 @@ function handleDropFile(e: DragEvent) {
const files = e.dataTransfer?.files as FileList
const file = files[0]
isExceedKnowledgeCount.value = false
const dropKnowledgeCount = files.length
const enableKnowledgeCount = Math.max(
0,
userStore.equityInfo.maxKnowledgeCount - userStore.equityInfo.usedKnowledgeCount - uploadFileList.value.length,
)
if (userStore.equityInfo.maxKnowledgeCount !== 0 && enableKnowledgeCount < dropKnowledgeCount) {
isExceedKnowledgeCount.value = true
window.$message
.ctWarning(t('equity_module.documents_uploaded_exceeds_tip', { count: enableKnowledgeCount }), '')
.then(() => {
router.push({ name: 'Equity' })
})
.catch(() => {})
return
}
const allowTypeList = ['md', 'doc', 'docx', 'pdf', 'txt']
if (file && file.name) {
......@@ -175,12 +243,13 @@ function handleNextStep() {
directory-dnd
:show-file-list="false"
:disabled="uploadFileList.length >= 5"
class="upload-knowledge-file"
accept=".doc, .pdf, .docx, .txt, .md"
@before-upload="handleLimitUpload"
@change="handleUpload"
@drop="handleDropFile"
>
<NUploadDragger>
<NUploadDragger @click="isExceedKnowledgeCount = false">
<div class="mb-3 flex justify-center">
<UploadOne theme="outline" size="36" fill="#333" />
</div>
......
......@@ -71,6 +71,8 @@ async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormD
const res = await fetchCreateKnowledge<{ id: number }>({
knowledgeName: createKnowledgeData.knowledgeName,
desc: createKnowledgeData.knowledgeDesc,
}).finally(() => {
createKnowledgeBtnLoading.value = false
})
if (res.code === 0) {
......@@ -82,7 +84,6 @@ async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormD
},
})
showCreateKnowledgeModal.value = false
createKnowledgeBtnLoading.value = false
}
}
</script>
......
......@@ -50,6 +50,8 @@ const userStore = useUserStore()
const { uploadFileList, handleLimitUpload, handleUpload, handleRemoveFile } = useDialogueFile()
const isAnswerResponseLoading = defineModel<boolean>('isAnswerResponseLoading', { required: true })
const emitter = inject<Emitter<MittEvents>>('emitter')
const inputMessageContent = ref('')
......@@ -175,6 +177,7 @@ function handleMessageSend() {
const input = inputMessageContent.value
let replyTextContent = ''
isAnswerResponseLoading.value = true
isAnswerResponseWait.value = true
inputMessageContent.value = ''
currentReplyContentSentenceExtractIndex.value = 0
......@@ -204,6 +207,7 @@ function handleMessageSend() {
isAnswerResponseLoading: false,
})
isAnswerResponseLoading.value = false
isCreateContinueQuestions.value && emit('createContinueQuestions', replyTextContent)
emit('updatePageScroll')
blockMessageResponse()
......@@ -239,6 +243,7 @@ function handleMessageSend() {
},
onFinally: () => {
controller = null
userStore.fetchUpdateEquityInfo()
},
})
}
......@@ -263,6 +268,7 @@ function handleClearAllMessage() {
function blockMessageResponse() {
controller?.abort()
isAnswerResponseWait.value = false
userStore.fetchUpdateEquityInfo()
}
function handleToLogin() {
......
......@@ -10,6 +10,8 @@ interface Props {
agentApplicationConfig: PersonalAppConfigState
continuousQuestionStatus: 'default' | 'close'
continuousQuestionList: string[]
isAnswerResponseLoading: boolean
createContinueQuestionsException: boolean
}
const props = defineProps<Props>()
......@@ -25,7 +27,8 @@ const isShowContinueQuestion = computed(() => {
return (
props.continuousQuestionStatus === 'default' &&
props.messageList.size > 1 &&
!Array.from(props.messageList.entries()).pop()?.[1].isAnswerResponseLoading
!props.isAnswerResponseLoading &&
!props.createContinueQuestionsException
)
})
......
......@@ -48,6 +48,8 @@ const currentPlayMessageItem = ref<ConversationMessageItem | null>(null)
const currentPlayAudioFragmentSerialNo = ref(0)
const currentSoundCtl = shallowRef<Howl | null>(null)
const isSoundCtlCreated = ref(false)
const isAnswerResponseLoading = ref(false)
const createContinueQuestionsException = ref(false)
const isEnableDocumentParse = computed(() => {
return agentApplicationConfig.value.knowledgeConfig.isDocumentParsing === 'Y'
......@@ -184,12 +186,18 @@ function handleClearAllMessage() {
}
async function handleCreateContinueQuestions(replyTextContent: string) {
const res = await fetchCreateContinueQuestions<string[]>({ input: replyTextContent })
createContinueQuestionsException.value = false
if (res.code === 0) {
continueQuestionList.value = res.data
handleUpdatePageScroll()
}
fetchCreateContinueQuestions<string[]>({ input: replyTextContent })
.then((res) => {
if (res.code === 0) {
continueQuestionList.value = res.data
handleUpdatePageScroll()
}
})
.catch(() => {
createContinueQuestionsException.value = true
})
}
function handleResetContinueQuestionList() {
......@@ -316,6 +324,8 @@ function handleAudioPause(isClearMessageList = false) {
:message-list="messageList"
:continuous-question-status="continuousQuestionStatus"
:continuous-question-list="continueQuestionList"
:is-answer-response-loading="isAnswerResponseLoading"
:create-continue-questions-exception="createContinueQuestionsException"
@audio-play="handleAudioPlay"
@audio-pause="handleAudioPause"
/>
......@@ -325,6 +335,7 @@ function handleAudioPause(isClearMessageList = false) {
<div class="footer-operation px-4">
<FooterInput
ref="footerInputRef"
v-model:is-answer-response-loading="isAnswerResponseLoading"
:message-list="messageList"
:dialogs-id="dialogsId"
:agent-id="agentApplicationConfig.baseInfo.agentId"
......
......@@ -51,6 +51,8 @@ const currentPlayMessageItem = ref<ConversationMessageItem | null>(null)
const currentPlayAudioFragmentSerialNo = ref(0)
const currentSoundCtl = shallowRef<Howl | null>(null)
const isSoundCtlCreated = ref(false)
const isAnswerResponseLoading = ref(false)
const createContinueQuestionsException = ref(false)
const isEnableDocumentParse = computed(() => {
return agentApplicationConfig.value.knowledgeConfig.isDocumentParsing === 'Y'
......@@ -205,12 +207,18 @@ function handleClearAllMessage() {
}
async function handleCreateContinueQuestions(replyTextContent: string) {
const res = await fetchCreateContinueQuestions<string[]>({ input: replyTextContent })
createContinueQuestionsException.value = false
if (res.code === 0) {
continueQuestionList.value = res.data
handleUpdatePageScroll()
}
fetchCreateContinueQuestions<string[]>({ input: replyTextContent })
.then((res) => {
if (res.code === 0) {
continueQuestionList.value = res.data
handleUpdatePageScroll()
}
})
.catch(() => {
createContinueQuestionsException.value = true
})
}
function handleResetContinueQuestionList() {
......@@ -339,6 +347,8 @@ function handleAudioPause(isClearMessageList = false) {
:message-list="messageList"
:continuous-question-status="continuousQuestionStatus"
:continuous-question-list="continueQuestionList"
:is-answer-response-loading="isAnswerResponseLoading"
:create-continue-questions-exception="createContinueQuestionsException"
@audio-play="handleAudioPlay"
@audio-pause="handleAudioPause"
/>
......@@ -348,6 +358,7 @@ function handleAudioPause(isClearMessageList = false) {
<div class="px-5">
<FooterInput
ref="footerInputRef"
v-model:is-answer-response-loading="isAnswerResponseLoading"
:message-list="messageList"
:dialogs-id="dialogsId"
:agent-id="agentApplicationConfig.baseInfo.agentId"
......
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import ContentTitle from '../components/content-title.vue'
import { IPlatformAgentUsage, IPlatformAgentUsageDetailItem } from '../type'
import { createPlatformAgentUsageDetail } from './columns'
import CustomPagination from '@/components/custom-pagination/custom-pagination.vue'
import { fetchGetPlatformAgentUsage, fetchGetPlatformAgentUsageDetail } from '@/apis/statistic'
import { ChannelType } from '@/enums/channel'
import { usePagination } from '@/composables/usePagination'
import useTableScrollY from '@/composables/useTableScrollY'
import DataSelectSearch from '@/components/date-select-search/data-select-search.vue'
import { RangType } from '@/components/date-select-search/type.d'
const { t } = useI18n()
const { pageContentWrapRef, tableContentY } = useTableScrollY(46 + 124 + 28 + 36 + 20 + 48 + 28 + 40)
const { paginationData, handlePageNoChange, handlePageSizeChange } = usePagination()
const platformAgentUsage = ref<IPlatformAgentUsage>({
createCount: 0,
usageCount: 0,
unPublishCount: 0,
publishCount: 0,
})
const currentTime = ref<RangType>(RangType.week)
const selectDateRange = ref<[string, string]>(['', ''])
const platformAgentUsageDetailLoading = ref(false)
const platformAgentUsageDetailColumns = createPlatformAgentUsageDetail()
const platformAgentUsageDetailData = ref<IPlatformAgentUsageDetailItem[]>([])
watch([() => paginationData.pageNo, () => paginationData.pageSize], () => {
handleGetPlatformAgentUsageDetail(currentTime.value, selectDateRange.value[0], selectDateRange.value[1])
})
onMounted(() => {
handleGetPlatformAgentUsage()
})
async function handleGetPlatformAgentUsage() {
const channels = [
ChannelType.api,
ChannelType.index,
ChannelType.link_share,
ChannelType.mall,
ChannelType.preview,
ChannelType.multi_preview,
]
const res = await fetchGetPlatformAgentUsage<IPlatformAgentUsage>(channels)
if (res.code === 0) {
platformAgentUsage.value = res.data
}
}
async function handleGetPlatformAgentUsageDetail(rangType: RangType, startTime: string, endTime: string) {
currentTime.value = rangType
selectDateRange.value = [startTime, endTime]
platformAgentUsageDetailLoading.value = true
const res = await fetchGetPlatformAgentUsageDetail<IPlatformAgentUsageDetailItem[]>({
timeRange: {
rangType,
startTime,
endTime,
},
pagingInfo: paginationData,
})
if (res.code === 0) {
platformAgentUsageDetailData.value = res.data
paginationData.totalRows = res.pagingInfo?.totalRows || 0
paginationData.totalPages = res.pagingInfo?.totalPages || 0
platformAgentUsageDetailLoading.value = false
}
}
</script>
<template>
<div ref="pageContentWrapRef" class="mx-6 h-full rounded-[10px] bg-white pt-[46px]">
<div class="text-font-color mb-7 flex w-full justify-around gap-2 px-[34px]">
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-create_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformAgentUsage.createCount" />
</div>
<span class="mt-3">{{ t('statistic_module.create_agent_count') }}</span>
</div>
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-usage_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformAgentUsage.usageCount" />
</div>
<span class="mt-3">{{ t('statistic_module.usage_agent_count') }}</span>
</div>
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-unpublish_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformAgentUsage.unPublishCount" />
</div>
<span class="mt-3">{{ t('statistic_module.unpublish_agent_count') }}</span>
</div>
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-publish_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformAgentUsage.publishCount" />
</div>
<span class="mt-3">{{ t('statistic_module.published_agent_count') }}</span>
</div>
</div>
<div class="mx-[34px] flex items-start justify-between">
<ContentTitle :title="t('statistic_module.platform_agent_usage_detail')" />
<DataSelectSearch @update-search-list="handleGetPlatformAgentUsageDetail" />
</div>
<div class="mx-[34px] mt-5 flex flex-1 flex-col">
<div :style="{ height: tableContentY + 48 + 'px' }">
<n-data-table
:columns="platformAgentUsageDetailColumns"
:bordered="false"
:bottom-bordered="false"
:max-height="tableContentY"
:loading="platformAgentUsageDetailLoading"
:data="platformAgentUsageDetailData"
:scroll-x="1505"
>
<template #empty>
<div class="flex items-center justify-center" :style="{ height: tableContentY + 'px' }">
<div class="flex flex-col items-center justify-center">
<div class="bg-px-empty_list-png mb-5 h-[68px] w-[68px]" />
<p class="select-none text-[#84868c]">{{ t('common_module.empty_data') }}</p>
</div>
</div>
</template>
</n-data-table>
</div>
<div v-show="tableContentY > 0" class="mt-5 flex justify-end">
<CustomPagination
:paging-info="paginationData"
@update-page-no="handlePageNoChange"
@update-page-size="handlePageSizeChange"
/>
</div>
</div>
</div>
</template>
import i18n from '@/locales'
import { IPlatformAgentUsageDetailItem } from '../type'
import { formatDateTime } from '@/utils/date-formatter'
const t = i18n.global.t
export function createPlatformAgentUsageDetail() {
return [
{
title: () => <span>{t('statistic_module.agent_title')}</span>,
key: 'agentTitle',
align: 'left',
width: '369px',
ellipsis: {
tooltip: true,
},
render(row: IPlatformAgentUsageDetailItem) {
return row.agentTitle || '--'
},
},
{
title: () => <span>{t('analysis_module.consume_points')}</span>,
key: 'totalPoint',
align: 'left',
width: '188px',
ellipsis: {
tooltip: true,
},
render(row: IPlatformAgentUsageDetailItem) {
return row.totalPoint.toFixed(1) || '--'
},
},
{
title: () => <span>{t('statistic_module.agent_status')}</span>,
key: 'publishStatus',
align: 'left',
width: '160px',
ellipsis: {
tooltip: true,
},
render(row: IPlatformAgentUsageDetailItem) {
return row.publishStatus === 'Y' ? t('common_module.published') : t('common_module.unpublished')
},
},
{
title: () => <span>{t('statistic_module.owner')}</span>,
key: 'owner',
align: 'left',
width: '236px',
ellipsis: {
tooltip: true,
},
render(row: IPlatformAgentUsageDetailItem) {
return row.owner || '--'
},
},
{
title: () => <span>{t('statistic_module.usage_count')}</span>,
key: 'usageCount',
align: 'left',
width: '200px',
ellipsis: {
tooltip: true,
},
render(row: IPlatformAgentUsageDetailItem) {
return row.usageCount || '--'
},
},
{
title: () => <span>{t('statistic_module.last_usage_time')}</span>,
key: 'lastUsageTime',
align: 'left',
width: '360px',
ellipsis: {
tooltip: true,
},
render(row: IPlatformAgentUsageDetailItem) {
return row.lastUsageTime ? formatDateTime(row.lastUsageTime) : '--'
},
},
]
}
<script setup lang="ts">
interface Props {
title: string
}
defineProps<Props>()
</script>
<template>
<div class="flex items-start">
<span class="bg-theme-color mr-[5px] mt-1.5 inline-block h-[14px] w-1 flex-shrink-0 rounded-sm" />
<span class="text-[16px]">{{ title }}</span>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import ContentTitle from '../../components/content-title.vue'
import { IPlatformPointUsage } from '../../type'
import { fetchGetPlatformPointUsage } from '@/apis/statistic'
interface Props {
channel: string[]
}
const { t } = useI18n()
const props = defineProps<Props>()
const platformPointUsage = ref<IPlatformPointUsage>({
today: 0,
week: 0,
month: 0,
year: 0,
})
watch(
() => props.channel,
() => {
handleGetPlatformPointUsage()
},
)
async function handleGetPlatformPointUsage() {
const res = await fetchGetPlatformPointUsage<IPlatformPointUsage>(props.channel)
if (res.code === 0) {
platformPointUsage.value = res.data
}
}
</script>
<template>
<div class="w-full rounded-[20px] bg-white py-5">
<ContentTitle :title="t('statistic_module.platform_point_usage')" class="ml-[34px]" />
<div class="text-font-color mt-6.5 mb-2.5 flex w-full justify-around gap-2 px-[34px]">
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-today_point_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformPointUsage.today" :precision="1" />
</div>
<span class="mt-3">{{ t('statistic_module.today_usage') }}</span>
</div>
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-week_point_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformPointUsage.week" :precision="1" />
</div>
<span class="mt-3">{{ t('statistic_module.current_week_usage') }}</span>
</div>
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-month_point_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformPointUsage.month" :precision="1" />
</div>
<span class="mt-3">{{ t('statistic_module.current_month_usage') }}</span>
</div>
<div
class="relative flex h-[124px] max-w-[312px] flex-1 flex-col justify-center rounded-[10px] bg-[#F8F9FB] px-10"
>
<div class="bg-px-statistics-year_point_icon-png absolute bottom-0 right-0 h-[93px] w-[90px]" />
<div class="font-600 text-[30px]">
<n-number-animation :from="0" :to="platformPointUsage.year" :precision="1" />
</div>
<span class="mt-3">{{ t('statistic_module.current_year_usage') }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import ContentTitle from '../../components/content-title.vue'
import { IPlatformPointTrendItem } from '../../type'
import { type EChartsOption } from '@/utils/echarts'
import CustomEchart from '@/components/custom-echart/custom-echart.vue'
import DataSelectSearch from '@/components/date-select-search/data-select-search.vue'
import { RangType } from '@/components/date-select-search/type.d'
import { ChannelText, ChannelType } from '@/enums/channel'
import { fetchGetPlatformPointTrend } from '@/apis/statistic'
interface Props {
channel: string[]
}
const { t } = useI18n()
const props = defineProps<Props>()
const pointUsageTrendRef = useTemplateRef<InstanceType<typeof CustomEchart> | null>('pointUsageTrendRef')
const currentTime = ref<RangType>(RangType.week)
const selectDateRange = ref<[string, string]>(['', ''])
const defaultEchartOption: EChartsOption = {
legend: {
top: '36px',
},
tooltip: {
trigger: 'axis',
textStyle: {
fontWeight: 'normal',
},
},
xAxis: { type: 'category' },
yAxis: { gridIndex: 0 },
grid: { top: '100px', left: '110px', right: '70px', bottom: '40px' },
color: ['#e94540', '#6ea2ff', '#ba68ff', '#ff61c1', '#2db70e', '#eda030'],
series: [
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' },
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' },
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' },
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' },
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' },
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' },
},
],
}
const pointUsageTrendEchartsOption = reactive<EChartsOption>({ ...defaultEchartOption })
watch(
() => props.channel,
() => {
handleGetPlatformPointTrend(currentTime.value, selectDateRange.value[0], selectDateRange.value[1])
},
)
async function handleGetPlatformPointTrend(rangType: RangType, startTime: string, endTime: string) {
if (!props.channel.length) {
return
}
currentTime.value = rangType
selectDateRange.value = [startTime, endTime]
pointUsageTrendRef.value?.echartShowLoading()
const res = await fetchGetPlatformPointTrend<IPlatformPointTrendItem[]>({
channel: props.channel,
timeRange: {
rangType,
startTime,
endTime,
},
})
if (res.code === 0) {
const dataSourceSort = handleDataSourceSort(res.data)
const dateList = [...new Set(dataSourceSort.flatMap((item) => item.pointUsages.map((usage) => usage.date)))].sort()
const dataSource: [string[], ...[string, ...number[]][]] = [['product', ...dateList]]
dataSourceSort.forEach((item) => {
const row: [string, ...number[]] = [t(ChannelText[item.channel])]
dateList.forEach((date) => {
const count = item.pointUsages.find((usage) => usage.date === date)?.count || 0
row.push(count)
})
dataSource.push(row)
})
pointUsageTrendEchartsOption.dataset = {
source: dataSource,
}
pointUsageTrendRef.value?.echartHideLoading()
}
}
function handleDataSourceSort(dataSource: IPlatformPointTrendItem[]) {
const channelSortMap = {
[ChannelType.preview]: 1,
[ChannelType.multi_preview]: 2,
[ChannelType.mall]: 3,
[ChannelType.index]: 4,
[ChannelType.link_share]: 5,
[ChannelType.api]: 6,
}
return dataSource.sort((a, b) => channelSortMap[a.channel] - channelSortMap[b.channel])
}
</script>
<template>
<div class="relative h-[455px] w-full rounded-[20px] bg-white py-5">
<div class="absolute left-0 right-0 z-10">
<div class="mx-[34px] flex items-start justify-between">
<ContentTitle :title="t('statistic_module.platform_point_trend')" />
<DataSelectSearch @update-search-list="handleGetPlatformPointTrend" />
</div>
</div>
<div class="h-full w-full">
<CustomEchart ref="pointUsageTrendRef" :option="pointUsageTrendEchartsOption" />
</div>
</div>
</template>
<script setup lang="ts">
import { computed, h, onMounted, readonly, ref } from 'vue'
import { NTag, TreeSelectOption } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import PointUsagePanel from './components/point-usage-panel.vue'
import PointUsageTrend from './components/point-usage-trend.vue'
import { ChannelType } from '@/enums/channel'
import { useSystemLanguageStore } from '@/store/modules/system-language'
const { t } = useI18n()
const systemLanguageStore = useSystemLanguageStore()
const channelOptionList = [
{
label: t('analysis_module.agent_debug'),
key: 'agent_preview',
children: [
{
label: t('analysis_module.debug'),
key: ChannelType.preview,
},
{
label: t('analysis_module.multi_debug'),
key: ChannelType.multi_preview,
},
],
},
{
label: t('analysis_module.usage_channel'),
key: 'usage_channel',
children: [
{
label: t('analysis_module.index'),
key: ChannelType.index,
},
{
label: t('analysis_module.agent_square'),
key: ChannelType.mall,
},
{
label: t('analysis_module.link_share'),
key: ChannelType.link_share,
},
{
label: t('analysis_module.api'),
key: ChannelType.api,
},
],
},
]
const channelGroupList = readonly(channelOptionList.map((channelItem) => channelItem.key))
const allChannelList = handleGetAllChannelList(channelOptionList)
const selectChannelList = ref<string[]>([])
onMounted(() => {
selectChannelList.value = allChannelList
})
const isEnLanguage = computed(() => {
return systemLanguageStore.currentLanguageInfo.key === 'en'
})
function handleUpdateChannelList(channelList: string[]) {
if (channelList.length === 0) {
selectChannelList.value = allChannelList
return
}
selectChannelList.value = channelList
}
function handleRenderChannelLabel({ option }: { option: TreeSelectOption }) {
return h('span', { style: { marginRight: '24px' } }, { default: () => option.label as string })
}
function handleRenderSelectChannel({ option }: { option: TreeSelectOption }) {
if (
selectChannelList.value.length === allChannelList.length &&
selectChannelList.value.every((item) => allChannelList.includes(item))
) {
return option.key === allChannelList?.[0]
? h('span', { class: 'mb-[3px]' }, t('analysis_module.all_channels'))
: null
}
return h(NTag, { class: 'mb-[3px] px-[10px]!' }, { default: () => option.label as string })
}
function handleGetAllChannelList(options: TreeSelectOption[]) {
let channelList: string[] = []
function recurseChannelList(channels: TreeSelectOption[]) {
if (Array.isArray(channels)) {
channels.forEach((channel) => {
if (!channel.children || channel.children.length === 0) {
channelList.push(channel.key as string)
} else {
recurseChannelList(channel.children)
}
})
}
}
recurseChannelList(options)
return channelList
}
</script>
<template>
<div class="flex h-full w-full flex-col overflow-y-auto overflow-x-hidden px-6">
<div class="flex justify-end pr-[34px]">
<div class="max-w-[300px]" :class="isEnLanguage ? 'min-w-[124px]' : 'min-w-[154px]'">
<n-tree-select
v-model:value="selectChannelList"
multiple
cascade
checkable
check-strategy="child"
placement="bottom-end"
max-tag-count="responsive"
class="tree-tag"
:options="channelOptionList"
:menu-props="{ style: { marginTop: '6px', borderRadius: '5px' } }"
:consistent-menu-width="false"
:ellipsis-tag-popover-props="{ show: false }"
:default-expanded-keys="channelGroupList"
:render-label="handleRenderChannelLabel"
:render-tag="handleRenderSelectChannel"
@update:value="handleUpdateChannelList"
>
</n-tree-select>
</div>
</div>
<div class="mt-5 flex flex-1 flex-col gap-5">
<PointUsagePanel :channel="selectChannelList" />
<PointUsageTrend :channel="selectChannelList" />
</div>
</div>
</template>
<style scoped lang="scss">
@include custom-scrollbar(6px);
:deep(.tree-tag .n-base-selection) {
--n-height: 36px !important;
--n-color: #f3f5f8 !important;
--n-color-active: #f3f5f8 !important;
--n-border: 1px solid #ccc !important;
.n-base-selection-tag-wrapper .n-tag {
margin-right: 10px !important;
background: #fff !important;
border-radius: 5px !important;
--n-border: none !important;
}
.n-base-selection-tag-wrapper {
padding: 0;
}
}
</style>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
const { t } = useI18n()
const currentRoute = useRoute()
const router = useRouter()
const routerNameValue = ref(currentRoute.name)
const statisticModuleList = [
{
routeName: 'PointStatistic',
label: 'statistic_module.point_statistic',
},
{
routeName: 'AgentStatistic',
label: 'statistic_module.agent_statistic',
},
]
watch(
() => currentRoute.fullPath,
() => {
routerNameValue.value = currentRoute.name
},
)
function handleChangeRoute(routeName: string) {
router.replace({ name: routeName })
}
</script>
<template>
<div class="m-auto flex h-full max-w-[1669px] flex-col pb-2 pt-1">
<div class="flex flex-col px-6">
<div class="mb-5 flex items-center">
<img src="@/assets/images/statistics/statistics-icon.png" />
<p class="ml-[5px] text-lg">{{ t('statistic_module.data_statistic') }}</p>
</div>
<ul class="mb-5 flex">
<li
v-for="statisticModuleItem in statisticModuleList"
:key="statisticModuleItem.routeName"
class="rounded-theme hover:bg-theme-color ml-2.5 h-8 cursor-pointer select-none px-[14px] leading-8 transition-colors duration-300 first:ml-0 hover:text-white"
:class="[
routerNameValue === statisticModuleItem.routeName
? 'bg-theme-color text-white'
: 'text-gray-font-color border-transparent',
]"
@click="handleChangeRoute(statisticModuleItem.routeName)"
>
{{ t(statisticModuleItem.label) }}
</li>
</ul>
</div>
<div class="h-full overflow-hidden">
<RouterView v-slot="{ Component }">
<Transition appear name="fade-slide" mode="out-in">
<Component :is="Component" />
</Transition>
</RouterView>
</div>
</div>
</template>
<style lang="scss" scoped>
.fade-slide-leave-active,
.fade-slide-enter-active {
transition: all 0.3s;
}
.fade-slide-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-slide-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>
import { ChannelType } from '@/enums/channel'
export interface IPlatformPointUsage {
today: number
week: number
month: number
year: number
}
export interface IPlatformPointTrendItem {
channel: ChannelType
pointUsages: {
date: string
count: number
}[]
}
export interface IPlatformAgentUsage {
createCount: number
usageCount: number
unPublishCount: number
publishCount: number
}
export interface IPlatformAgentUsageDetailItem {
agentTitle: string
owner: string
publishStatus: 'Y' | 'N'
totalPoint: number
usageCount: number
lastUsageTime: Date
}
......@@ -3,9 +3,11 @@ declare interface Window {
$loadingBar: import('naive-ui').LoadingBarProviderInst
$dialog: import('naive-ui').DialogProviderInst
$message: import('naive-ui').MessageProviderInst & {
ctWarning: (message: string, title?: string) => Promise<boolean | Error>
ctWarning: (message: string, title?: string, cancelText?: string, confirmText?: string) => Promise<boolean | Error>
}
$notification: import('naive-ui').NotificationProviderInst
google: any
}
declare namespace JSX {
......
......@@ -112,6 +112,7 @@ declare namespace I18n {
forever_effective: string
year: string
month: string
today: string
alipay: string
wechat: string
analysis: string
......@@ -125,6 +126,14 @@ declare namespace I18n {
support: string
buy_now: string
payment_success: string
back: string
time: string
export_data: string
not_generated: string
export_successfully: string
export_failed: string
copy_link: string
or: string
dialogue_module: {
continue_question_message: string
......@@ -182,6 +191,7 @@ declare namespace I18n {
application_square: string
personal_settings: string
order_manage: string
data_statistic: string
}
login_module: {
......@@ -376,6 +386,13 @@ declare namespace I18n {
removal_prompt_title: string
removal_prompt_content: string
successfully_configured_published: string
api_call: string
api_call_desc: string
interface_document: string
click_to_generate: string
agentId: string
api_call_details: string
api_call_datetime: string
}
}
......@@ -420,6 +437,8 @@ declare namespace I18n {
upload_knowledge_document_btn_text: string
batch_delete_knowledge_document_btn_text: string
not_find_knowledge_document_message: string
not_all_files_train_complete_tip: string
cannot_add_tip_when_file_is_training: string
create_knowledge_modal_title: string
edit_knowledge_modal_title: string
......@@ -571,6 +590,8 @@ declare namespace I18n {
point_recharge: string
get_points_for_interacting_with_the_ai: string
no_time_limit_when_used_up: string
agents_created_exceeds_tip: string
documents_uploaded_exceeds_tip: string
}
order_manage_module: {
......@@ -609,5 +630,27 @@ declare namespace I18n {
api: string
link_share: string
}
statistic_module: {
data_statistic: string
point_statistic: string
agent_statistic: string
platform_point_usage: string
platform_point_trend: string
today_usage: string
current_week_usage: string
current_month_usage: string
current_year_usage: string
create_agent_count: string
usage_agent_count: string
unpublish_agent_count: string
published_agent_count: string
platform_agent_usage_detail: string
agent_title: string
agent_status: string
owner: string
usage_count: string
last_usage_time: string
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment