Add CodeGraph config; fix analytics, chat, UI
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
This commit is contained in:
parent
fc9455cfce
commit
e575bac340
16
.codegraph/.gitignore
vendored
Normal file
16
.codegraph/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# CodeGraph data files
|
||||
# These are local to each machine and should not be committed
|
||||
|
||||
# Database
|
||||
*.db
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
|
||||
# Cache
|
||||
cache/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Hook markers
|
||||
.dirty
|
||||
143
.codegraph/config.json
Normal file
143
.codegraph/config.json
Normal file
@ -0,0 +1,143 @@
|
||||
{
|
||||
"version": 1,
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
"**/*.py",
|
||||
"**/*.go",
|
||||
"**/*.rs",
|
||||
"**/*.java",
|
||||
"**/*.c",
|
||||
"**/*.h",
|
||||
"**/*.cpp",
|
||||
"**/*.hpp",
|
||||
"**/*.cc",
|
||||
"**/*.cxx",
|
||||
"**/*.cs",
|
||||
"**/*.php",
|
||||
"**/*.rb",
|
||||
"**/*.swift",
|
||||
"**/*.kt",
|
||||
"**/*.kts",
|
||||
"**/*.dart",
|
||||
"**/*.svelte",
|
||||
"**/*.vue",
|
||||
"**/*.liquid",
|
||||
"**/*.pas",
|
||||
"**/*.dpr",
|
||||
"**/*.dpk",
|
||||
"**/*.lpr",
|
||||
"**/*.dfm",
|
||||
"**/*.fmx",
|
||||
"**/*.scala",
|
||||
"**/*.sc"
|
||||
],
|
||||
"exclude": [
|
||||
"**/.git/**",
|
||||
"**/node_modules/**",
|
||||
"**/vendor/**",
|
||||
"**/Pods/**",
|
||||
"**/dist/**",
|
||||
"**/build/**",
|
||||
"**/out/**",
|
||||
"**/bin/**",
|
||||
"**/obj/**",
|
||||
"**/target/**",
|
||||
"**/*.min.js",
|
||||
"**/*.bundle.js",
|
||||
"**/.next/**",
|
||||
"**/.nuxt/**",
|
||||
"**/.svelte-kit/**",
|
||||
"**/.output/**",
|
||||
"**/.turbo/**",
|
||||
"**/.cache/**",
|
||||
"**/.parcel-cache/**",
|
||||
"**/.vite/**",
|
||||
"**/.astro/**",
|
||||
"**/.docusaurus/**",
|
||||
"**/.gatsby/**",
|
||||
"**/.webpack/**",
|
||||
"**/.nx/**",
|
||||
"**/.yarn/cache/**",
|
||||
"**/.pnpm-store/**",
|
||||
"**/storybook-static/**",
|
||||
"**/.expo/**",
|
||||
"**/web-build/**",
|
||||
"**/ios/Pods/**",
|
||||
"**/ios/build/**",
|
||||
"**/android/build/**",
|
||||
"**/android/.gradle/**",
|
||||
"**/__pycache__/**",
|
||||
"**/.venv/**",
|
||||
"**/venv/**",
|
||||
"**/site-packages/**",
|
||||
"**/dist-packages/**",
|
||||
"**/.pytest_cache/**",
|
||||
"**/.mypy_cache/**",
|
||||
"**/.ruff_cache/**",
|
||||
"**/.tox/**",
|
||||
"**/.nox/**",
|
||||
"**/*.egg-info/**",
|
||||
"**/.eggs/**",
|
||||
"**/go/pkg/mod/**",
|
||||
"**/target/debug/**",
|
||||
"**/target/release/**",
|
||||
"**/.gradle/**",
|
||||
"**/.m2/**",
|
||||
"**/generated-sources/**",
|
||||
"**/.kotlin/**",
|
||||
"**/.dart_tool/**",
|
||||
"**/.vs/**",
|
||||
"**/.nuget/**",
|
||||
"**/artifacts/**",
|
||||
"**/publish/**",
|
||||
"**/cmake-build-*/**",
|
||||
"**/CMakeFiles/**",
|
||||
"**/bazel-*/**",
|
||||
"**/vcpkg_installed/**",
|
||||
"**/.conan/**",
|
||||
"**/Debug/**",
|
||||
"**/Release/**",
|
||||
"**/x64/**",
|
||||
"**/.pio/**",
|
||||
"**/release/**",
|
||||
"**/*.app/**",
|
||||
"**/*.asar",
|
||||
"**/DerivedData/**",
|
||||
"**/.build/**",
|
||||
"**/.swiftpm/**",
|
||||
"**/xcuserdata/**",
|
||||
"**/Carthage/Build/**",
|
||||
"**/SourcePackages/**",
|
||||
"**/__history/**",
|
||||
"**/__recovery/**",
|
||||
"**/*.dcu",
|
||||
"**/.composer/**",
|
||||
"**/storage/framework/**",
|
||||
"**/bootstrap/cache/**",
|
||||
"**/.bundle/**",
|
||||
"**/tmp/cache/**",
|
||||
"**/public/assets/**",
|
||||
"**/public/packs/**",
|
||||
"**/.yardoc/**",
|
||||
"**/coverage/**",
|
||||
"**/htmlcov/**",
|
||||
"**/.nyc_output/**",
|
||||
"**/test-results/**",
|
||||
"**/.coverage/**",
|
||||
"**/.idea/**",
|
||||
"**/logs/**",
|
||||
"**/tmp/**",
|
||||
"**/temp/**",
|
||||
"**/_build/**",
|
||||
"**/docs/_build/**",
|
||||
"**/site/**"
|
||||
],
|
||||
"languages": [],
|
||||
"frameworks": [],
|
||||
"maxFileSize": 1048576,
|
||||
"extractDocstrings": true,
|
||||
"trackCallSites": true
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -50,3 +50,5 @@ vite.config.ts.*
|
||||
*.sw?
|
||||
.history
|
||||
node-compile-cache/*
|
||||
.github/*
|
||||
.agents/*
|
||||
@ -59,17 +59,17 @@ const routes: RouteRecordRaw[] = [
|
||||
authority: ['super', 'AC8001'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Order',
|
||||
path: '/order',
|
||||
component: () => import('#/views/operation/order/index.vue'),
|
||||
meta: {
|
||||
affixTab: true,
|
||||
icon: 'lets-icons:order',
|
||||
title: $t('page.operation.order'),
|
||||
authority: ['super', 'AC5002'],
|
||||
},
|
||||
},
|
||||
// {
|
||||
// name: 'Order',
|
||||
// path: '/order',
|
||||
// component: () => import('#/views/operation/order/index.vue'),
|
||||
// meta: {
|
||||
// affixTab: true,
|
||||
// icon: 'lets-icons:order',
|
||||
// title: $t('page.operation.order'),
|
||||
// authority: ['super', 'AC5002'],
|
||||
// },
|
||||
// },
|
||||
{
|
||||
name: 'Activity',
|
||||
path: '/activity',
|
||||
|
||||
@ -4,31 +4,27 @@ import type { AnalysisOverviewItem } from '@vben/common-ui';
|
||||
import type { TabOption } from '@vben/types';
|
||||
import { getstatisticsInfo } from '#/api/core/statistics';
|
||||
import {
|
||||
AnalysisChartCard,
|
||||
AnalysisChartsTabs,
|
||||
AnalysisOverview,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import AnalyticsTrends from './analytics-trends.vue';
|
||||
import AnalyticsVisits from './analytics-visits.vue';
|
||||
import AnalyticsVisitsData from './analytics-visits-data.vue';
|
||||
import AnalyticsVisitsSales from './analytics-visits-sales.vue';
|
||||
import AnalyticsVisitsSource from './analytics-visits-source.vue';
|
||||
|
||||
const overviewItems = ref<AnalysisOverviewItem[]>([
|
||||
{
|
||||
icon: 'lets-icons:user',
|
||||
title: '今日注册',
|
||||
totalTitle: '总用户量',
|
||||
totalValue: 100,
|
||||
value: 100,
|
||||
totalValue: 0,
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
icon: 'icon-park-solid:paper-money',
|
||||
title: '今日充值',
|
||||
totalTitle: '总充值',
|
||||
totalValue: 100,
|
||||
value: 100,
|
||||
totalValue: 0,
|
||||
value: 0,
|
||||
decimals: 2,
|
||||
},
|
||||
{
|
||||
@ -40,7 +36,7 @@ const overviewItems = ref<AnalysisOverviewItem[]>([
|
||||
},
|
||||
{
|
||||
icon: 'iconamoon:sign-percent',
|
||||
title: '次留',
|
||||
title: '昨日次留',
|
||||
totalTitle: 'ARPU',
|
||||
totalValue: 31.02,
|
||||
value: 0.12,
|
||||
@ -66,7 +62,7 @@ onMounted(async () => {
|
||||
}
|
||||
if (overviewItems.value[3]) {
|
||||
overviewItems.value[3].value = data.remain;
|
||||
overviewItems.value[3].totalValue = data.totalRecharge / data.totalRegister;
|
||||
overviewItems.value[3].totalValue = data.totalRegister > 0 ? data.totalRecharge / data.totalRegister : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,111 @@ marked.setOptions({
|
||||
gfm: true,
|
||||
});
|
||||
|
||||
function normalizeMarkdownText(text: string) {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||
|
||||
// 流式接口有时会把 markdown 代码块中的换行和缩进转成转义文本,
|
||||
// 这里在渲染前还原,避免 ```go 仍按普通文本显示。
|
||||
const shouldDecodeEscapes =
|
||||
(!normalized.includes('\n') && /\\[nrt]/.test(normalized))
|
||||
|| /```[\w-]*\\n/.test(normalized)
|
||||
|| /\\n```/.test(normalized)
|
||||
|| /\\n {2,}\S/.test(normalized);
|
||||
|
||||
if (shouldDecodeEscapes) {
|
||||
normalized = normalized
|
||||
.replace(/\\r\\n/g, '\n')
|
||||
.replace(/\\n/g, '\n')
|
||||
.replace(/\\r/g, '\n')
|
||||
.replace(/\\t/g, ' ');
|
||||
}
|
||||
|
||||
return normalized.replace(/\u200b/g, '');
|
||||
}
|
||||
|
||||
function looksLikeCompressedGoSnippet(text: string) {
|
||||
const compactText = text.trim();
|
||||
if (!compactText || compactText.includes('```') || compactText.includes('\n')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const startsWithGoLabel = /^(go|golang)(?=\/\/|\/\*|package\s|import\s|func\s|type\s|const\s|var\s)/i.test(compactText);
|
||||
const hasGoStructure =
|
||||
/func\s+[\w]+\s*\(/.test(compactText)
|
||||
|| /if\s+.+\{/.test(compactText)
|
||||
|| /return\s+/.test(compactText)
|
||||
|| /err\s*:=/.test(compactText);
|
||||
|
||||
return startsWithGoLabel || (hasGoStructure && compactText.includes('{') && compactText.includes('}'));
|
||||
}
|
||||
|
||||
function formatCompressedGoSnippet(text: string) {
|
||||
let normalized = text.trim().replace(/^golang/i, 'go').replace(/^go(?=\/\/|\/\*|package\s|import\s|func\s|type\s|const\s|var\s)/i, '');
|
||||
|
||||
normalized = normalized
|
||||
.replace(/interface\{\}/g, 'interface__EMPTY_BLOCK__')
|
||||
.replace(/(&?[\w.[\]]+)\{/g, '$1__COMPOSITE_OPEN__');
|
||||
|
||||
normalized = normalized
|
||||
.replace(/(\/\/[^\n]*?)(func\s+)/g, '$1\n$2')
|
||||
.replace(/__COMPOSITE_OPEN__\s+(?=(?:"[^"]+"|[A-Za-z_][\w]*)\s*:)/g, '__COMPOSITE_OPEN__\n')
|
||||
.replace(/,\s+(?=(?:"[^"]+"|[A-Za-z_][\w]*)\s*:)/g, ',\n')
|
||||
.replace(/\{\s*/g, '{\n')
|
||||
.replace(/\s*\}/g, '\n}')
|
||||
.replace(/;\s*/g, ';\n')
|
||||
.replace(/\)\s*(if|for|switch|return|var|const|type|func)\b/g, ')\n$1')
|
||||
.replace(/\}\s*(else\b)/g, '}\n$1')
|
||||
.replace(/\}\s*(if|for|switch|return|var|const|type|func)\b/g, '}\n$1')
|
||||
.replace(/\s+(if\s+[^\n{}]+\{)/g, '\n$1')
|
||||
.replace(/\s+(for\s+[^\n{}]+\{)/g, '\n$1')
|
||||
.replace(/\s+(switch\s+[^\n{}]+\{)/g, '\n$1')
|
||||
.replace(/\s+(return\b)/g, '\n$1')
|
||||
.replace(/\s+(defer\b)/g, '\n$1')
|
||||
.replace(/\s+(itemList\s*:=)/g, '\n$1')
|
||||
.replace(/\s+(err\s*:=)/g, '\n$1')
|
||||
.replace(/\s+(err\s*=)/g, '\n$1')
|
||||
.replace(/\s+(player\.(?:SendErrClienRes|PushClientRes|PlayMod\.save|TeLog)\()/g, '\n$1')
|
||||
.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
const lines = normalized
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
let indentLevel = 0;
|
||||
const formattedLines = lines.map((line) => {
|
||||
const restoredLine = line
|
||||
.replace(/__EMPTY_BLOCK__/g, '{}')
|
||||
.replace(/__COMPOSITE_OPEN__/g, '{');
|
||||
|
||||
if (restoredLine.startsWith('}')) {
|
||||
indentLevel = Math.max(indentLevel - 1, 0);
|
||||
}
|
||||
|
||||
const formattedLine = `${' '.repeat(indentLevel)}${restoredLine}`;
|
||||
|
||||
if (restoredLine.endsWith('{')) {
|
||||
indentLevel += 1;
|
||||
}
|
||||
|
||||
return formattedLine;
|
||||
});
|
||||
|
||||
return `\n\n\`\`\`go\n${formattedLines.join('\n')}\n\`\`\``;
|
||||
}
|
||||
|
||||
function optimizeDisplayMarkdown(text: string) {
|
||||
const normalized = normalizeMarkdownText(text);
|
||||
if (looksLikeCompressedGoSnippet(normalized)) {
|
||||
return formatCompressedGoSnippet(normalized);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function scrollBottom() {
|
||||
nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
@ -75,7 +180,7 @@ onBeforeUnmount(() => {
|
||||
});
|
||||
|
||||
function extractStreamText(data: string) {
|
||||
if (!data || data === '[DONE]') {
|
||||
if (data === '[DONE]') {
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -133,27 +238,29 @@ function extractStreamText(data: string) {
|
||||
}
|
||||
|
||||
function renderMarkdown(text: string) {
|
||||
const html = marked.parse(text, { async: false }) as string;
|
||||
const html = marked.parse(optimizeDisplayMarkdown(text), { async: false }) as string;
|
||||
return DOMPurify.sanitize(html);
|
||||
}
|
||||
|
||||
function applyStreamChunk(raw: string, botMsg: Msg) {
|
||||
let evt = 'message';
|
||||
let data = '';
|
||||
const dataLines: string[] = [];
|
||||
|
||||
for (const line of raw.split('\n')) {
|
||||
if (line.startsWith('event:')) evt = line.slice(6).trim();
|
||||
else if (line.startsWith('data:')) {
|
||||
data += (data ? '\n' : '') + line.slice(5).replace(/^ /, '');
|
||||
dataLines.push(line.slice(5).replace(/^ /, ''));
|
||||
}
|
||||
}
|
||||
|
||||
const data = dataLines.join('\n');
|
||||
|
||||
if (evt === 'sources') {
|
||||
botMsg.sources = JSON.parse(data);
|
||||
scrollBottom();
|
||||
} else if (evt === 'token' || evt === 'message') {
|
||||
const chunk = extractStreamText(data);
|
||||
if (chunk) {
|
||||
if (chunk || dataLines.length > 0) {
|
||||
botMsg.text += chunk;
|
||||
}
|
||||
scrollBottom();
|
||||
@ -199,7 +306,7 @@ async function send() {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
const tail = buf.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
|
||||
const tail = buf.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||
if (tail) {
|
||||
applyStreamChunk(tail, botMsg);
|
||||
}
|
||||
@ -328,10 +435,7 @@ function fillQuestion(question: string) {
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['kb-body', `kb-body-${m.role}`, { 'kb-cursor': m.streaming }]">
|
||||
<template v-if="m.role === 'user'">
|
||||
{{ m.text }}
|
||||
</template>
|
||||
<div v-else class="kb-markdown" v-html="renderMarkdown(m.text)"></div>
|
||||
<div class="kb-markdown" v-html="renderMarkdown(m.text)"></div>
|
||||
</div>
|
||||
<details v-if="m.sources && m.sources.length" class="kb-sources">
|
||||
<summary>
|
||||
|
||||
@ -26,7 +26,7 @@ import orderTable from './order-table.vue';
|
||||
// url 也可以是内部路由,在 navTo 方法中识别处理,进行内部跳转
|
||||
// 例如:url: /dashboard/workspace
|
||||
const projectItems: WorkbenchProjectItem[] = [];
|
||||
const gmPermissions:boolean = useAccess().hasAccessByRoles(['super']) || useAccess().hasAccessByRoles(['AC9301']);
|
||||
const gmPermissions:boolean = useAccess().hasAccessByRoles(['super']) || useAccess().hasAccessByCodes(['AC3003']);
|
||||
const chargeDisplay = computed(() => (Number(info.value?.Charge ?? 0)).toFixed(2));
|
||||
const chessBufferCount = computed(() => info.value.ChessBuffer.length);
|
||||
const banStatusText = computed(() => {
|
||||
@ -502,8 +502,8 @@ function formatActLog(type: number, content = ''): [string, string] {
|
||||
<Tabs.TabPane key="1" tab="玩家信息">
|
||||
<div class="player-detail-hero">
|
||||
<UserHeader :avatar="info.Face" :ban="info.Ban">
|
||||
<template #nick_name> nick_name: {{ info.Name || 'N/A' }} </template>
|
||||
<template #user_name> user_name: {{ info.Mac }} </template>
|
||||
<template #nick_name> user_name: {{ info.Name || 'N/A' }} </template>
|
||||
<template #user_name> Mac: {{ info.Mac }} </template>
|
||||
<template #uid> uid: {{ info.Uid }} </template>
|
||||
<template #level>{{ info.Level }}</template>
|
||||
<template #star>{{ info.Star }}</template>
|
||||
|
||||
@ -35,7 +35,7 @@ withDefaults(defineProps<Props>(), {
|
||||
<CardContent class="flex items-center justify-between">
|
||||
<VbenCountToAnimator
|
||||
:end-val="item.value"
|
||||
:start-val="1"
|
||||
:start-val="0"
|
||||
class="text-xl"
|
||||
prefix=""
|
||||
:decimals=item.decimals
|
||||
@ -46,7 +46,7 @@ withDefaults(defineProps<Props>(), {
|
||||
<span>{{ item.totalTitle }}</span>
|
||||
<VbenCountToAnimator
|
||||
:end-val="item.totalValue"
|
||||
:start-val="1"
|
||||
:start-val="0"
|
||||
prefix=""
|
||||
:decimals=item.decimals
|
||||
/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user