From dc470ce82e881996f2c0163eeb42ffa139c9f491 Mon Sep 17 00:00:00 2001 From: JustSong Date: Fri, 31 Jan 2025 19:34:22 +0800 Subject: [PATCH 01/56] feat: show stream & elapsed time in log detail --- common/helper/time.go | 5 + model/log.go | 47 ++-- relay/billing/billing.go | 11 +- relay/controller/helper.go | 21 +- relay/controller/image.go | 11 +- relay/controller/text.go | 3 +- relay/meta/relay_meta.go | 7 +- web/default/src/components/LogsTable.js | 277 ++++++++++++++++++------ 8 files changed, 277 insertions(+), 105 deletions(-) diff --git a/common/helper/time.go b/common/helper/time.go index 302746db..f0bc6021 100644 --- a/common/helper/time.go +++ b/common/helper/time.go @@ -13,3 +13,8 @@ func GetTimeString() string { now := time.Now() return fmt.Sprintf("%s%d", now.Format("20060102150405"), now.UnixNano()%1e9) } + +// CalcElapsedTime return the elapsed time in milliseconds (ms) +func CalcElapsedTime(start time.Time) int64 { + return time.Now().Sub(start).Milliseconds() +} diff --git a/model/log.go b/model/log.go index 1fd7ee84..17525500 100644 --- a/model/log.go +++ b/model/log.go @@ -13,19 +13,22 @@ import ( ) type Log struct { - Id int `json:"id"` - UserId int `json:"user_id" gorm:"index"` - CreatedAt int64 `json:"created_at" gorm:"bigint;index:idx_created_at_type"` - Type int `json:"type" gorm:"index:idx_created_at_type"` - Content string `json:"content"` - Username string `json:"username" gorm:"index:index_username_model_name,priority:2;default:''"` - TokenName string `json:"token_name" gorm:"index;default:''"` - ModelName string `json:"model_name" gorm:"index;index:index_username_model_name,priority:1;default:''"` - Quota int `json:"quota" gorm:"default:0"` - PromptTokens int `json:"prompt_tokens" gorm:"default:0"` - CompletionTokens int `json:"completion_tokens" gorm:"default:0"` - ChannelId int `json:"channel" gorm:"index"` - RequestId string `json:"request_id"` + Id int `json:"id"` + UserId int `json:"user_id" gorm:"index"` + CreatedAt int64 `json:"created_at" gorm:"bigint;index:idx_created_at_type"` + Type int `json:"type" gorm:"index:idx_created_at_type"` + Content string `json:"content"` + Username string `json:"username" gorm:"index:index_username_model_name,priority:2;default:''"` + TokenName string `json:"token_name" gorm:"index;default:''"` + ModelName string `json:"model_name" gorm:"index;index:index_username_model_name,priority:1;default:''"` + Quota int `json:"quota" gorm:"default:0"` + PromptTokens int `json:"prompt_tokens" gorm:"default:0"` + CompletionTokens int `json:"completion_tokens" gorm:"default:0"` + ChannelId int `json:"channel" gorm:"index"` + RequestId string `json:"request_id" gorm:"default:''"` + ElapsedTime int64 `json:"elapsed_time" gorm:"default:0"` // unit is ms + IsStream bool `json:"is_stream" gorm:"default:false"` + SystemPromptReset bool `json:"system_prompt_reset" gorm:"default:false"` } const ( @@ -73,23 +76,13 @@ func RecordTopupLog(ctx context.Context, userId int, content string, quota int) recordLogHelper(ctx, log) } -func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int64, content string) { +func RecordConsumeLog(ctx context.Context, log *Log) { if !config.LogConsumeEnabled { return } - log := &Log{ - UserId: userId, - Username: GetUsernameById(userId), - CreatedAt: helper.GetTimestamp(), - Type: LogTypeConsume, - Content: content, - PromptTokens: promptTokens, - CompletionTokens: completionTokens, - TokenName: tokenName, - ModelName: modelName, - Quota: int(quota), - ChannelId: channelId, - } + log.Username = GetUsernameById(log.UserId) + log.CreatedAt = helper.GetTimestamp() + log.Type = LogTypeConsume recordLogHelper(ctx, log) } diff --git a/relay/billing/billing.go b/relay/billing/billing.go index f1bf197a..8bd086fe 100644 --- a/relay/billing/billing.go +++ b/relay/billing/billing.go @@ -33,7 +33,16 @@ func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int64, totalQ // totalQuota is total quota consumed if totalQuota != 0 { logContent := fmt.Sprintf("%.2f × %.2f", modelRatio, groupRatio) - model.RecordConsumeLog(ctx, userId, channelId, int(totalQuota), 0, modelName, tokenName, totalQuota, logContent) + model.RecordConsumeLog(ctx, &model.Log{ + UserId: userId, + ChannelId: channelId, + PromptTokens: int(totalQuota), + CompletionTokens: 0, + ModelName: modelName, + TokenName: tokenName, + Quota: int(totalQuota), + Content: logContent, + }) model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota) model.UpdateChannelUsedQuota(channelId, totalQuota) } diff --git a/relay/controller/helper.go b/relay/controller/helper.go index 2b83c3d9..3262c017 100644 --- a/relay/controller/helper.go +++ b/relay/controller/helper.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" + "github.com/songquanpeng/one-api/common/helper" "github.com/songquanpeng/one-api/relay/constant/role" "github.com/gin-gonic/gin" @@ -121,12 +122,20 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *meta.M if err != nil { logger.Error(ctx, "error update user quota cache: "+err.Error()) } - var extraLog string - if systemPromptReset { - extraLog = " (注意系统提示词已被重置)" - } - logContent := fmt.Sprintf("%.2f × %.2f × %.2f%s", modelRatio, groupRatio, completionRatio, extraLog) - model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent) + logContent := fmt.Sprintf("%.2f × %.2f × %.2f", modelRatio, groupRatio, completionRatio) + model.RecordConsumeLog(ctx, &model.Log{ + UserId: meta.UserId, + ChannelId: meta.ChannelId, + PromptTokens: promptTokens, + CompletionTokens: completionTokens, + ModelName: textRequest.Model, + TokenName: meta.TokenName, + Quota: int(quota), + Content: logContent, + IsStream: meta.IsStream, + ElapsedTime: helper.CalcElapsedTime(meta.StartTime), + SystemPromptReset: systemPromptReset, + }) model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota) model.UpdateChannelUsedQuota(meta.ChannelId, quota) } diff --git a/relay/controller/image.go b/relay/controller/image.go index 468da566..24e49969 100644 --- a/relay/controller/image.go +++ b/relay/controller/image.go @@ -211,7 +211,16 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus if quota != 0 { tokenName := c.GetString(ctxkey.TokenName) logContent := fmt.Sprintf("%.2f × %.2f", modelRatio, groupRatio) - model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, 0, 0, imageRequest.Model, tokenName, quota, logContent) + model.RecordConsumeLog(ctx, &model.Log{ + UserId: meta.UserId, + ChannelId: meta.ChannelId, + PromptTokens: 0, + CompletionTokens: 0, + ModelName: imageRequest.Model, + TokenName: tokenName, + Quota: int(quota), + Content: logContent, + }) model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota) channelId := c.GetInt(ctxkey.ChannelId) model.UpdateChannelUsedQuota(channelId, quota) diff --git a/relay/controller/text.go b/relay/controller/text.go index 9a47c58b..6a61884d 100644 --- a/relay/controller/text.go +++ b/relay/controller/text.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/songquanpeng/one-api/common/config" "io" "net/http" "github.com/gin-gonic/gin" + + "github.com/songquanpeng/one-api/common/config" "github.com/songquanpeng/one-api/common/logger" "github.com/songquanpeng/one-api/relay" "github.com/songquanpeng/one-api/relay/adaptor" diff --git a/relay/meta/relay_meta.go b/relay/meta/relay_meta.go index bcbe1045..6bf070f3 100644 --- a/relay/meta/relay_meta.go +++ b/relay/meta/relay_meta.go @@ -1,12 +1,15 @@ package meta import ( + "strings" + "time" + "github.com/gin-gonic/gin" + "github.com/songquanpeng/one-api/common/ctxkey" "github.com/songquanpeng/one-api/model" "github.com/songquanpeng/one-api/relay/channeltype" "github.com/songquanpeng/one-api/relay/relaymode" - "strings" ) type Meta struct { @@ -31,6 +34,7 @@ type Meta struct { RequestURLPath string PromptTokens int // only for DoResponse SystemPrompt string + StartTime time.Time } func GetByContext(c *gin.Context) *Meta { @@ -48,6 +52,7 @@ func GetByContext(c *gin.Context) *Meta { APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "), RequestURLPath: c.Request.URL.String(), SystemPrompt: c.GetString(ctxkey.SystemPrompt), + StartTime: time.Now(), } cfg, ok := c.Get(ctxkey.Config) if ok { diff --git a/web/default/src/components/LogsTable.js b/web/default/src/components/LogsTable.js index 8b4cda7b..39bafc2e 100644 --- a/web/default/src/components/LogsTable.js +++ b/web/default/src/components/LogsTable.js @@ -1,21 +1,26 @@ import React, { useEffect, useState } from 'react'; -import { Button, Form, Header, Label, Pagination, Segment, Select, Table } from 'semantic-ui-react'; +import { + Button, + Form, + Header, + Label, + Pagination, + Segment, + Select, + Table, +} from 'semantic-ui-react'; import { API, isAdmin, showError, timestamp2string } from '../helpers'; import { ITEMS_PER_PAGE } from '../constants'; import { renderQuota } from '../helpers/render'; function renderTimestamp(timestamp) { - return ( - <> - {timestamp2string(timestamp)} - - ); + return <>{timestamp2string(timestamp)}; } const MODE_OPTIONS = [ { key: 'all', text: '全部用户', value: 'all' }, - { key: 'self', text: '当前用户', value: 'self' } + { key: 'self', text: '当前用户', value: 'self' }, ]; const LOG_OPTIONS = [ @@ -23,24 +28,87 @@ const LOG_OPTIONS = [ { key: '1', text: '充值', value: 1 }, { key: '2', text: '消费', value: 2 }, { key: '3', text: '管理', value: 3 }, - { key: '4', text: '系统', value: 4 } + { key: '4', text: '系统', value: 4 }, ]; function renderType(type) { switch (type) { case 1: - return ; + return ( + + ); case 2: - return ; + return ( + + ); case 3: - return ; + return ( + + ); case 4: - return ; + return ( + + ); default: - return ; + return ( + + ); } } +function getColorByElapsedTime(elapsedTime) { + if (elapsedTime === undefined || 0) return 'black'; + if (elapsedTime < 1000) return 'green'; + if (elapsedTime < 3000) return 'olive'; + if (elapsedTime < 5000) return 'yellow'; + if (elapsedTime < 10000) return 'orange'; + return 'red'; +} + +function renderDetail(log) { + return ( + <> + 倍率:{log.content} +
+ {log.elapsed_time && ( + + )} + {log.is_stream && ( + <> + + + )} + {log.system_prompt_reset && ( + <> + + + )} +
+ {log.request_id} + + ); +} + const LogsTable = () => { const [logs, setLogs] = useState([]); const [showStat, setShowStat] = useState(false); @@ -57,13 +125,20 @@ const LogsTable = () => { model_name: '', start_timestamp: timestamp2string(0), end_timestamp: timestamp2string(now.getTime() / 1000 + 3600), - channel: '' + channel: '', }); - const { username, token_name, model_name, start_timestamp, end_timestamp, channel } = inputs; + const { + username, + token_name, + model_name, + start_timestamp, + end_timestamp, + channel, + } = inputs; const [stat, setStat] = useState({ quota: 0, - token: 0 + token: 0, }); const handleInputChange = (e, { name, value }) => { @@ -73,7 +148,9 @@ const LogsTable = () => { const getLogSelfStat = async () => { let localStartTimestamp = Date.parse(start_timestamp) / 1000; let localEndTimestamp = Date.parse(end_timestamp) / 1000; - let res = await API.get(`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`); + let res = await API.get( + `/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}` + ); const { success, message, data } = res.data; if (success) { setStat(data); @@ -85,7 +162,9 @@ const LogsTable = () => { const getLogStat = async () => { let localStartTimestamp = Date.parse(start_timestamp) / 1000; let localEndTimestamp = Date.parse(end_timestamp) / 1000; - let res = await API.get(`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`); + let res = await API.get( + `/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}` + ); const { success, message, data } = res.data; if (success) { setStat(data); @@ -201,37 +280,82 @@ const LogsTable = () => {
使用明细(总消耗额度: {showStat && renderQuota(stat.quota)} - {!showStat && 点击查看} + {!showStat && ( + + 点击查看 + + )} )
- - - - - 查询 + + + + + + 查询 + - { - isAdminUser && <> + {isAdminUser && ( + <> - - - + + - } + )}
@@ -245,8 +369,8 @@ const LogsTable = () => { > 时间 - { - isAdminUser && { sortLog('channel'); @@ -255,9 +379,9 @@ const LogsTable = () => { > 渠道 - } - { - isAdminUser && { sortLog('username'); @@ -266,7 +390,7 @@ const LogsTable = () => { > 用户 - } + )} { @@ -328,7 +452,7 @@ const LogsTable = () => { }} width={isAdminUser ? 4 : 6} > - 详情(模型倍率 × 分组倍率 × 补全倍率) + 详情 @@ -344,26 +468,41 @@ const LogsTable = () => { return ( {renderTimestamp(log.created_at)} - { - isAdminUser && ( - {log.channel ? : ''} - ) - } - { - isAdminUser && ( - {log.username ? : ''} - ) - } - {log.token_name ? : ''} + {isAdminUser && ( + + {log.channel ? : ''} + + )} + {isAdminUser && ( + + {log.username ? : ''} + + )} + + {log.token_name ? ( + + ) : ( + '' + )} + {renderType(log.type)} - {log.model_name ? : ''} - {log.prompt_tokens ? log.prompt_tokens : ''} - {log.completion_tokens ? log.completion_tokens : ''} - {log.quota ? renderQuota(log.quota, 6) : ''} - {log.content}{<> -
- {log.request_id} - }
+ + {log.model_name ? ( + + ) : ( + '' + )} + + + {log.prompt_tokens ? log.prompt_tokens : ''} + + + {log.completion_tokens ? log.completion_tokens : ''} + + + {log.quota ? renderQuota(log.quota, 6) : ''} + + {renderDetail(log)}
); })} @@ -382,7 +521,9 @@ const LogsTable = () => { setLogType(value); }} /> - + Date: Fri, 31 Jan 2025 20:02:51 +0800 Subject: [PATCH 02/56] feat: update log table style --- web/default/src/components/LogsTable.js | 55 +++++++++++++++++-------- web/default/src/helpers/render.js | 53 +++++++++++++++++++----- 2 files changed, 79 insertions(+), 29 deletions(-) diff --git a/web/default/src/components/LogsTable.js b/web/default/src/components/LogsTable.js index 39bafc2e..1f528676 100644 --- a/web/default/src/components/LogsTable.js +++ b/web/default/src/components/LogsTable.js @@ -9,13 +9,34 @@ import { Select, Table, } from 'semantic-ui-react'; -import { API, isAdmin, showError, timestamp2string } from '../helpers'; +import { + API, + copy, + isAdmin, + showError, + showSuccess, + showWarning, + timestamp2string, +} from '../helpers'; import { ITEMS_PER_PAGE } from '../constants'; -import { renderQuota } from '../helpers/render'; +import { renderColorLabel, renderQuota } from '../helpers/render'; -function renderTimestamp(timestamp) { - return <>{timestamp2string(timestamp)}; +function renderTimestamp(timestamp, request_id) { + return ( + { + if (await copy(request_id)) { + showSuccess(`已复制请求 ID:${request_id}`); + } else { + showWarning(`请求 ID 复制失败:${request_id}`); + } + }} + style={{ cursor: 'pointer' }} + > + {timestamp2string(timestamp)} + + ); } const MODE_OPTIONS = [ @@ -103,8 +124,6 @@ function renderDetail(log) { )} -
- {log.request_id} ); } @@ -467,7 +486,9 @@ const LogsTable = () => { if (log.deleted) return <>; return ( - {renderTimestamp(log.created_at)} + + {renderTimestamp(log.created_at, log.request_id)} + {isAdminUser && ( {log.channel ? : ''} @@ -475,23 +496,21 @@ const LogsTable = () => { )} {isAdminUser && ( - {log.username ? : ''} + {log.username ? ( + + ) : ( + '' + )} )} - {log.token_name ? ( - - ) : ( - '' - )} + {log.token_name ? renderColorLabel(log.token_name) : ''} {renderType(log.type)} - {log.model_name ? ( - - ) : ( - '' - )} + {log.model_name ? renderColorLabel(log.model_name) : ''} {log.prompt_tokens ? log.prompt_tokens : ''} diff --git a/web/default/src/helpers/render.js b/web/default/src/helpers/render.js index a9c81cc1..b622f8a6 100644 --- a/web/default/src/helpers/render.js +++ b/web/default/src/helpers/render.js @@ -13,16 +13,18 @@ export function renderGroup(group) { } let groups = group.split(','); groups.sort(); - return <> - {groups.map((group) => { - if (group === 'vip' || group === 'pro') { - return ; - } else if (group === 'svip' || group === 'premium') { - return ; - } - return ; - })} - ; + return ( + <> + {groups.map((group) => { + if (group === 'vip' || group === 'pro') { + return ; + } else if (group === 'svip' || group === 'premium') { + return ; + } + return ; + })} + + ); } export function renderNumber(num) { @@ -55,4 +57,33 @@ export function renderQuotaWithPrompt(quota, digits) { return `(等价金额:${renderQuota(quota, digits)})`; } return ''; -} \ No newline at end of file +} + +const colors = [ + 'red', + 'orange', + 'yellow', + 'olive', + 'green', + 'teal', + 'blue', + 'violet', + 'purple', + 'pink', + 'brown', + 'grey', + 'black', +]; + +export function renderColorLabel(text) { + let hash = 0; + for (let i = 0; i < text.length; i++) { + hash = text.charCodeAt(i) + ((hash << 5) - hash); + } + let index = Math.abs(hash % colors.length); + return ( + + ); +} From 0230d366431fc79328aae5bc787d45b6d3d4a1ca Mon Sep 17 00:00:00 2001 From: JustSong Date: Fri, 31 Jan 2025 20:06:43 +0800 Subject: [PATCH 03/56] feat: update log table style --- web/default/src/components/LogsTable.js | 4 +--- web/default/src/helpers/render.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/web/default/src/components/LogsTable.js b/web/default/src/components/LogsTable.js index 1f528676..16d2bc92 100644 --- a/web/default/src/components/LogsTable.js +++ b/web/default/src/components/LogsTable.js @@ -497,9 +497,7 @@ const LogsTable = () => { {isAdminUser && ( {log.username ? ( - + ) : ( '' )} diff --git a/web/default/src/helpers/render.js b/web/default/src/helpers/render.js index b622f8a6..24b70464 100644 --- a/web/default/src/helpers/render.js +++ b/web/default/src/helpers/render.js @@ -82,7 +82,7 @@ export function renderColorLabel(text) { } let index = Math.abs(hash % colors.length); return ( - } + trigger={ + + } content='本渠道被程序自动禁用' basic /> @@ -230,15 +252,35 @@ const ChannelsTable = () => { let time = responseTime / 1000; time = time.toFixed(2) + ' 秒'; if (responseTime === 0) { - return ; + return ( + + ); } else if (responseTime <= 1000) { - return ; + return ( + + ); } else if (responseTime <= 3000) { - return ; + return ( + + ); } else if (responseTime <= 5000) { - return ; + return ( + + ); } else { - return ; + return ( + + ); } }; @@ -277,7 +319,11 @@ const ChannelsTable = () => { newChannels[realIdx].response_time = time * 1000; newChannels[realIdx].test_time = Date.now() / 1000; setChannels(newChannels); - showInfo(`渠道 ${name} 测试成功,模型 ${model},耗时 ${time.toFixed(2)} 秒。`); + showInfo( + `渠道 ${name} 测试成功,模型 ${model},耗时 ${time.toFixed( + 2 + )} 秒,模型输出:${message}` + ); } else { showError(message); } @@ -360,7 +406,6 @@ const ChannelsTable = () => { setLoading(false); }; - return ( <>
@@ -374,20 +419,22 @@ const ChannelsTable = () => { onChange={handleKeywordChange} /> - { - showPrompt && ( - { + {showPrompt && ( + { setShowPrompt(false); setPromptShown(promptID); - }}> - OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为 0。对于支持的渠道类型,请点击余额进行刷新。 -
- 渠道测试仅支持 chat 模型,优先使用 gpt-3.5-turbo,如果该模型不可用则使用你所配置的模型列表中的第一个模型。 -
- 点击下方详情按钮可以显示余额以及设置额外的测试模型。 -
- ) - } + }} + > + OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为 + 0。对于支持的渠道类型,请点击余额进行刷新。 +
+ 渠道测试仅支持 chat 模型,优先使用 + gpt-3.5-turbo,如果该模型不可用则使用你所配置的模型列表中的第一个模型。 +
+ 点击下方详情按钮可以显示余额以及设置额外的测试模型。 +
+ )}
@@ -478,7 +525,11 @@ const ChannelsTable = () => { {renderStatus(channel.status)} { { - manageChannel( - channel.id, - 'priority', - idx, - event.target.value - ); - }}> - - } + trigger={ + { + manageChannel( + channel.id, + 'priority', + idx, + event.target.value + ); + }} + > + + + } content='渠道选择优先级,越高越优先' basic /> @@ -528,7 +590,12 @@ const ChannelsTable = () => { size={'small'} positive onClick={() => { - testChannel(channel.id, channel.name, idx, channel.test_model); + testChannel( + channel.id, + channel.name, + idx, + channel.test_model + ); }} > 测试 @@ -590,14 +657,31 @@ const ChannelsTable = () => { - - - - {/* @@ -627,8 +716,12 @@ const ChannelsTable = () => { (channels.length % ITEMS_PER_PAGE === 0 ? 1 : 0) } /> - - + + diff --git a/web/default/src/components/LogsTable.js b/web/default/src/components/LogsTable.js index 12b8dc60..1ae9fd6e 100644 --- a/web/default/src/components/LogsTable.js +++ b/web/default/src/components/LogsTable.js @@ -21,6 +21,7 @@ import { import { ITEMS_PER_PAGE } from '../constants'; import { renderColorLabel, renderQuota } from '../helpers/render'; +import { Link } from 'react-router-dom'; function renderTimestamp(timestamp, request_id) { return ( @@ -50,6 +51,7 @@ const LOG_OPTIONS = [ { key: '2', text: '消费', value: 2 }, { key: '3', text: '管理', value: 3 }, { key: '4', text: '系统', value: 4 }, + { key: '5', text: '测试', value: 5 }, ]; function renderType(type) { @@ -78,6 +80,12 @@ function renderType(type) { 系统 ); + case 5: + return ( + + ); default: return (