Skip to content

feat: New MCP comprehensive example added to the official website #3497

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 11, 2025

Conversation

zzcr
Copy link
Member

@zzcr zzcr commented Jun 11, 2025

feat: 官网新增mcp综合示例,修复 tiny-robot-chat 组件中的提示项和建议项的传递逻辑,优化 Demo 组件的 QR 码展示样式

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive product management interface with CRUD operations, inline editing, and a data grid.
    • Added a right-side interactive panel featuring a customizable AI chat assistant.
    • Enabled mobile remote control via QR code, including secure session linking and step-by-step instructions.
    • Integrated persistent storage utilities for session and local data management.
    • Added a static dataset of products and type definitions for products, users, and forms.
  • Improvements

    • Enhanced the AI chat assistant to allow external customization of prompt and suggestion items.
    • Updated import paths for improved code organization and maintainability.
  • Bug Fixes

    • Corrected the AI assistant's welcome message text.
  • Chores

    • Added new dependencies to support encryption and storage features.

Copy link

coderabbitai bot commented Jun 11, 2025

Walkthrough

This update introduces a comprehensive new feature set centered around a product management demo, including new Vue components, TypeScript interfaces, and a JSON dataset. It also adds a storage utility module, enables external customization of chat prompts via props, updates several import paths, and integrates a new route for the comprehensive demo. Minor corrections and dependency additions are included.

Changes

File(s) Change Summary
examples/sites/package.json Added crypto-js dependency.
examples/sites/src/App.vue Changed import path for globalConversation.
examples/sites/src/components/demo.vue Updated import paths for multiple config modules.
examples/sites/src/components/tiny-robot-chat.vue Corrected welcome text, added props for prompt/suggestion items, updated bindings, and adjusted imports.
examples/sites/src/composable/DifyModelProvider.ts Removed an extraneous blank line.
examples/sites/src/composable/storage.ts Added new utility for reactive storage management and auto-syncing refs.
examples/sites/src/composable/utils.ts Added $local export and new showTinyRobot reactive ref.
examples/sites/src/router.js Added a new route for the "comprehensive" demo and import for its component.
examples/sites/src/views/components-doc/common.vue Updated import paths for components and composables.
examples/sites/src/views/comprehensive/Demo.vue New Vue component for product management with CRUD-like grid and UI.
examples/sites/src/views/comprehensive/index.vue New Vue component with main demo area, right-side chat panel, QR code modal, and session encryption.
examples/sites/src/views/comprehensive/products.json New JSON file containing detailed product data.
examples/sites/src/views/comprehensive/types/index.ts New TypeScript interfaces for User, Product, LoginForm, and ProductForm.
examples/sites/src/views/layout/layout.vue Updated import paths for layout-related components.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MainDemo (index.vue)
    participant DemoComponent (Demo.vue)
    participant TinyRobotChat
    participant Storage
    participant QRModal

    User->>MainDemo: Loads comprehensive route
    MainDemo->>DemoComponent: Renders product management UI
    MainDemo->>TinyRobotChat: Passes promptItems & suggestionPillItems props
    User->>DemoComponent: Adds/Removes/Edits products
    DemoComponent->>Storage: Reads/Writes products (via $local)
    User->>MainDemo: Clicks QR code trigger
    MainDemo->>QRModal: Shows QR code with encrypted session URL
    User->>TinyRobotChat: Interacts with chat panel
Loading

Poem

A hop, a skip, a brand new show—
Products to manage, and chatbots that glow!
With QR codes shining and storage anew,
Interfaces typed, and imports askew.
Rabbits rejoice in this comprehensive spree,
For every new feature brings code jubilee!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

examples/sites/src/components/tiny-robot-chat.vue

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-vue".

(The package "eslint-plugin-vue" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-vue@latest --save-dev

The plugin "eslint-plugin-vue" was referenced from the config file in ".eslintrc.js » @antfu/eslint-config » @antfu/eslint-config-vue".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

examples/sites/src/App.vue

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-vue".

(The package "eslint-plugin-vue" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-vue@latest --save-dev

The plugin "eslint-plugin-vue" was referenced from the config file in ".eslintrc.js » @antfu/eslint-config » @antfu/eslint-config-vue".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

examples/sites/src/components/demo.vue

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-vue".

(The package "eslint-plugin-vue" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-vue@latest --save-dev

The plugin "eslint-plugin-vue" was referenced from the config file in ".eslintrc.js » @antfu/eslint-config » @antfu/eslint-config-vue".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

  • 8 others
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the enhancement New feature or request (功能增强) label Jun 11, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (7)
examples/sites/src/components/demo.vue (1)

78-78: Use alias imports instead of long relative paths

'../views/components-doc/cmp-config' walks two levels up and into another directory. Prefer a Vite alias (e.g. @/views/components-doc/cmp-config) to improve readability and guard against path breakage when files are moved.

examples/sites/src/composable/storage.ts (1)

27-37: Missing proxy traps cause silent data leaks

Only get and set are implemented. Common operations like
delete $session.foo or Object.keys($session) will silently fail.
Add at least deleteProperty, ownKeys, and has traps to keep the
proxy behaviour consistent with normal objects and avoid surprises.

examples/sites/src/views/layout/layout.vue (1)

66-67: Update lazy-loading chunk names if any

Moving float-settings.vue & version-tip.vue to the shared @/components folder may change Vite’s chunk splitting. If you relied on deterministic chunk names for CDN caching, audit the build output.

examples/sites/src/components/tiny-robot-chat.vue (1)

48-51: Props should be optional & typed precisely

promptItems / suggestionPillItems are allowed to be omitted, yet their type is non-nullable. Mark them optional and provide read-only typing to avoid accidental mutation:

-const props = defineProps<{
-  promptItems: any[]
-  suggestionPillItems: any[]
-}>()
+const props = defineProps<{
+  promptItems?: ReadonlyArray<any>
+  suggestionPillItems?: ReadonlyArray<any>
+}>()
examples/sites/src/views/comprehensive/Demo.vue (2)

18-24: Use camelCase for property names.

The property tiny_mcp_config should follow JavaScript naming conventions.

-        :tiny_mcp_config="{
+        :tinyMcpConfig="{
          server,
          business: {
            id: 'product-list',
            description: '商品列表'
          }
        }"

112-120: Extract hardcoded values to constants.

The hardcoded image URL and default values should be extracted for better maintainability.

Add constants at the component level:

const DEFAULT_PRODUCT_IMAGE = 'https://img1.baidu.com/it/u=1559062020,1043707656&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'
const DEFAULT_PRODUCT = {
  image: DEFAULT_PRODUCT_IMAGE,
  price: 10000,
  stock: 100,
  category: 'phones',
  status: 'on'
}

Then update the function:

 const addProductToEdit = async () => {
-  gridRef?.value?.insert({
-    'image': 'https://img1.baidu.com/it/u=1559062020,1043707656&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
-    price: 10000,
-    stock: 100,
-    category: 'phones',
-    status: 'on'
-  })
+  gridRef?.value?.insert({ ...DEFAULT_PRODUCT })
 }
examples/sites/src/views/comprehensive/products.json (1)

1-99: Standardize dataset for consistency and reliability. While the JSON structure is valid, please:

  • Unify all createdAt/updatedAt fields to full ISO 8601 timestamps.
  • Validate unique id values and ensure stock/price entries match realistic demo data.
  • Replace duplicate or potentially unstable external image URLs (e.g., items 6 and 8) with distinct assets or a stable CDN.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1490dcd and ecc861d.

📒 Files selected for processing (14)
  • examples/sites/package.json (1 hunks)
  • examples/sites/src/App.vue (1 hunks)
  • examples/sites/src/components/demo.vue (1 hunks)
  • examples/sites/src/components/tiny-robot-chat.vue (4 hunks)
  • examples/sites/src/composable/DifyModelProvider.ts (0 hunks)
  • examples/sites/src/composable/storage.ts (1 hunks)
  • examples/sites/src/composable/utils.ts (1 hunks)
  • examples/sites/src/router.js (2 hunks)
  • examples/sites/src/views/components-doc/common.vue (1 hunks)
  • examples/sites/src/views/comprehensive/Demo.vue (1 hunks)
  • examples/sites/src/views/comprehensive/index.vue (1 hunks)
  • examples/sites/src/views/comprehensive/products.json (1 hunks)
  • examples/sites/src/views/comprehensive/types/index.ts (1 hunks)
  • examples/sites/src/views/layout/layout.vue (1 hunks)
💤 Files with no reviewable changes (1)
  • examples/sites/src/composable/DifyModelProvider.ts
🔇 Additional comments (5)
examples/sites/package.json (1)

61-61:

❓ Verification inconclusive

Consider pinning crypto-js and auditing for known CVEs

Adding a broad semver range (^4.2.0) risks unintentionally upgrading to a minor/patch that might introduce breaking changes or vulnerabilities. Pin to an exact version or automate dependency-bot scanning to catch issues early.

You can quickly verify CVE status with:


🏁 Script executed:

#!/usr/bin/env bash
npm audit --package-lock-only | grep crypto-js -A3

Length of output: 852


🏁 Script executed:

#!/usr/bin/env bash
# Check for lockfiles in examples/sites
echo "Looking for lockfiles in examples/sites:"
find examples/sites -maxdepth 1 -type f \( -name "package-lock.json" -o -name "yarn.lock" \)

# If a lockfile is found, inspect for crypto-js entries
echo "Inspecting any package-lock.json for crypto-js:"
find examples/sites -maxdepth 1 -type f -name "package-lock.json" -exec grep -H "crypto-js" -A3 {} +

# Show the installed version of crypto-js in examples/sites (if node_modules is present)
echo "Listing crypto-js in examples/sites dependencies:"
npm ls crypto-js --prefix examples/sites --depth=0 || true

Length of output: 631


examples/sites: add lockfile and audit crypto-js

No lockfile (package-lock.json or yarn.lock) was found under examples/sites, so the suggested audit command cannot run successfully. To ensure you’re not introducing vulnerabilities or unintended upgrades:

  • Commit a lockfile in examples/sites (e.g. npm install --package-lock-only).
  • Run npm audit in that directory and confirm the CVE status of crypto-js.
  • Consider pinning crypto-js to an exact version ("crypto-js": "4.2.0") or set up automated dependency scanning to prevent unreviewed minor/patch upgrades.
examples/sites/src/App.vue (1)

20-20: Verify re-export after path change

globalConversation is now imported from ./composable/utils. Ensure the module re-exports the same symbol (type & behaviour) or downstream chat features will break.

examples/sites/src/router.js (1)

22-26: Missing layout & meta for the new “comprehensive” route

The new route mounts Comprehensive directly at the root level, bypassing the common Layout wrapper and omitting meta.title. This may break:

  1. global header / footer that Layout provides
  2. SEO / document.title handling (router.afterEach relies on meta.title)
  3. Consistent theming / padding expected by child views

Please verify that the page really must be full-screen. If not, wrap it in Layout and add a title:

{
-  path: `${context}:all?/zh-CN/:theme/comprehensive`,
-  component: Comprehensive,
-  name: 'comprehensive'
+  path: `${context}:all?/zh-CN/:theme/comprehensive`,
+  component: Layout,
+  name: 'comprehensive',
+  children: [
+    {
+      path: '',
+      component: Comprehensive,
+      meta: { title: '综合示例 | TinyVue' }
+    }
+  ]
}
examples/sites/src/views/components-doc/common.vue (2)

102-108: Validated updated component and composable import paths. The relative imports from ../../components and ../../composable correctly reflect the directory restructure and align with other files.


111-111: Confirmed robotChat import path update. The tiny-robot-chat.vue component import now correctly references the shared component location.

Comment on lines +8 to +14
if (type === 'o' || type === 'b') {
let val = JSON.parse(strVal)
return typeof val === 'string' ? new Date(val) : val
}
if (type === 'n') return +Number(strVal)
if (type === 's') return strVal
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

parse() mis-classifies boolean vs object & drops undefined

  1. if (type === 'o' || type === 'b') conflates booleans with objects.
    JSON.parse('true') works, but treating it as object is misleading.
  2. Prefix 'u' for undefined written by save() is never handled on read.
-  if (type === 'o' || type === 'b') {
+  if (type === 'o') return JSON.parse(strVal)
+  if (type === 'b') return strVal === 'true'
+  if (type === 'u') return undefined

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In examples/sites/src/composable/storage.ts around lines 8 to 14, the parse()
function incorrectly treats booleans as objects by combining 'o' and 'b' types,
and it does not handle the 'u' prefix for undefined values. To fix this,
separate the handling of booleans ('b') from objects ('o') by parsing booleans
directly without JSON.parse, handle the 'u' prefix by returning undefined when
encountered, and ensure objects are parsed correctly with JSON.parse.

Comment on lines +48 to +70
const typeMatcher = { session: $session, local: $local, api: null }

/**
* 用于记录用户行为,并保存到session,local 或api接口(api保存的功能还未实现)
* 示例:useAutoStore("session","key1")
* useAutoStore("session","key2",100)
* useAutoStore("session","key2",$session.key2 || 100)
* @param type 自动存储到的目标
* @param key 存储时的key
* @param defaultValue 默认值。
* @returns 响应式ref
*/
const useAutoStore = (type, key, defaultValue) => {
let refVar = ref(typeMatcher[type][key])
watch(refVar, (curr, prev) => {
typeMatcher[type][key] = curr
})

refVar.value = refVar.value ?? defaultValue

return refVar
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

api branch is unusable and will throw at runtime

typeMatcher.api is null; useAutoStore('api', ...) therefore
dereferences null[key], throwing a TypeError.

-const typeMatcher = { session: $session, local: $local, api: null }
+const typeMatcher = { session: $session, local: $local, api: {} } // TODO: wire real impl

Also guard against unsupported types:

if (!typeMatcher[type])
  throw new Error(`Unsupported auto-store type: ${type}`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const typeMatcher = { session: $session, local: $local, api: null }
/**
* 用于记录用户行为,并保存到session,local 或api接口(api保存的功能还未实现)
* 示例:useAutoStore("session","key1")
* useAutoStore("session","key2",100)
* useAutoStore("session","key2",$session.key2 || 100)
* @param type 自动存储到的目标
* @param key 存储时的key
* @param defaultValue 默认值。
* @returns 响应式ref
*/
const useAutoStore = (type, key, defaultValue) => {
let refVar = ref(typeMatcher[type][key])
watch(refVar, (curr, prev) => {
typeMatcher[type][key] = curr
})
refVar.value = refVar.value ?? defaultValue
return refVar
}
// change api from null to an empty object placeholder
const typeMatcher = { session: $session, local: $local, api: {} } // TODO: wire real impl
/**
* 用于记录用户行为,并保存到session,local 或api接口(api保存的功能还未实现)
* 示例:useAutoStore("session","key1")
* useAutoStore("session","key2",100)
* useAutoStore("session","key2",$session.key2 || 100)
* @param type 自动存储到的目标
* @param key 存储时的key
* @param defaultValue 默认值。
* @returns 响应式ref
*/
const useAutoStore = (type, key, defaultValue) => {
// guard against unsupported storage targets
if (!typeMatcher[type]) {
throw new Error(`Unsupported auto-store type: ${type}`)
}
let refVar = ref(typeMatcher[type][key])
watch(refVar, (curr, prev) => {
typeMatcher[type][key] = curr
})
refVar.value = refVar.value ?? defaultValue
return refVar
}
🤖 Prompt for AI Agents
In examples/sites/src/composable/storage.ts around lines 48 to 70, the
typeMatcher.api is set to null, causing a runtime TypeError when useAutoStore is
called with 'api' because it tries to access null[key]. To fix this, initialize
typeMatcher.api with a valid object or implement a guard clause in useAutoStore
to check if the type exists and is an object before accessing its keys. If the
type is unsupported, throw an informative error or handle it gracefully to
prevent runtime exceptions.

Comment on lines +10 to 13
export { $local } from './storage'

export const showTinyRobot = ref(true)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Duplicate global‐state source: prefer a single store for showTinyRobot

showTinyRobot is already exposed through appData (used in several other files), and now it is re-declared and exported here as a standalone ref. Two independent refs will desynchronise the UI if they are both imported in different places.

Consider re-exporting the existing appData.showTinyRobot instead of creating a new one:

-import { ref, type Ref } from 'vue'
+import { type Ref } from 'vue'
+import { appData } from '../tools/appData'

-export const showTinyRobot = ref(true)
+export const showTinyRobot = appData.showTinyRobot

This keeps a single source of truth and avoids subtle reactivity bugs.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export { $local } from './storage'
export const showTinyRobot = ref(true)
// Replace the existing Vue import
-import { ref, type Ref } from 'vue'
+import { type Ref } from 'vue'
+import { appData } from '../tools/appData'
export { $local } from './storage'
-export const showTinyRobot = ref(true)
+export const showTinyRobot = appData.showTinyRobot
🤖 Prompt for AI Agents
In examples/sites/src/composable/utils.ts around lines 10 to 13, avoid declaring
and exporting a new ref named showTinyRobot since it duplicates the existing
global state in appData. Instead, remove the current showTinyRobot ref
declaration and re-export the existing appData.showTinyRobot to maintain a
single source of truth and prevent UI desynchronization issues.

Comment on lines +72 to 74
const customPromptItems = props.promptItems || defaultPromptItems
const customSuggestionPillItems = props.suggestionPillItems || defaultSuggestionPillItems
</script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fallback arrays are not reactive – wrap them in computed()

customPromptItems / customSuggestionPillItems are evaluated once.
If parent components update the props later, the UI will not react.
Make them computed so they track prop changes:

-import { computed } from 'vue'
+import { computed } from 'vue'

-const customPromptItems = props.promptItems || defaultPromptItems
-const customSuggestionPillItems = props.suggestionPillItems || defaultSuggestionPillItems
+const customPromptItems = computed(
+  () => props.promptItems?.length ? props.promptItems : defaultPromptItems
+)
+const customSuggestionPillItems = computed(
+  () => props.suggestionPillItems?.length ? props.suggestionPillItems : defaultSuggestionPillItems
+)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const customPromptItems = props.promptItems || defaultPromptItems
const customSuggestionPillItems = props.suggestionPillItems || defaultSuggestionPillItems
</script>
<script setup lang="ts">
import { computed } from 'vue'
const customPromptItems = computed(
() => props.promptItems?.length ? props.promptItems : defaultPromptItems
)
const customSuggestionPillItems = computed(
() => props.suggestionPillItems?.length ? props.suggestionPillItems : defaultSuggestionPillItems
)
</script>
🤖 Prompt for AI Agents
In examples/sites/src/components/tiny-robot-chat.vue around lines 72 to 74, the
fallback arrays for customPromptItems and customSuggestionPillItems are assigned
directly, making them non-reactive to prop changes. To fix this, wrap these
assignments in computed() so they update reactively when the props change,
ensuring the UI reflects the latest data from parent components.

Comment on lines +122 to +135
const removeProduct = () => {
const selectedRows = gridRef?.value?.getSelectRecords()
if (selectedRows.length === 0) {
TinyModal.confirm({
message: '请选择要删除的商品',
title: '删除商品',
status: 'warning'
})
return
}
if (selectedRows.length > 0) {
gridRef?.value?.removeSelecteds()
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove redundant condition check.

The second condition check is unnecessary since the function already returns early when no rows are selected.

 const removeProduct = () => {
   const selectedRows = gridRef?.value?.getSelectRecords()
   if (selectedRows.length === 0) {
     TinyModal.confirm({
       message: '请选择要删除的商品',
       title: '删除商品',
       status: 'warning'
     })
     return
   }
-  if (selectedRows.length > 0) {
-    gridRef?.value?.removeSelecteds()
-  }
+  gridRef?.value?.removeSelecteds()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const removeProduct = () => {
const selectedRows = gridRef?.value?.getSelectRecords()
if (selectedRows.length === 0) {
TinyModal.confirm({
message: '请选择要删除的商品',
title: '删除商品',
status: 'warning'
})
return
}
if (selectedRows.length > 0) {
gridRef?.value?.removeSelecteds()
}
}
const removeProduct = () => {
const selectedRows = gridRef?.value?.getSelectRecords()
if (selectedRows.length === 0) {
TinyModal.confirm({
message: '请选择要删除的商品',
title: '删除商品',
status: 'warning'
})
return
}
gridRef?.value?.removeSelecteds()
}
🤖 Prompt for AI Agents
In examples/sites/src/views/comprehensive/Demo.vue around lines 122 to 135, the
second condition checking if selectedRows.length > 0 is redundant because the
function already returns early when selectedRows.length is 0. Remove the if
statement and directly call gridRef?.value?.removeSelecteds() after the early
return to simplify the code.

Comment on lines +137 to +146
const saveProduct = () => {
setTimeout(() => {
const data = gridRef?.value?.getTableData()
$local.products = data.tableData
TinyModal.message({
message: '保存成功',
status: 'success'
})
}, 1000)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling and remove unnecessary setTimeout.

The current implementation always shows success regardless of the actual save result and uses an arbitrary timeout.

-const saveProduct = () => {
-  setTimeout(() => {
+const saveProduct = async () => {
+  try {
     const data = gridRef?.value?.getTableData()
     $local.products = data.tableData
     TinyModal.message({
       message: '保存成功',
       status: 'success'
     })
-  }, 1000)
+  } catch (error) {
+    TinyModal.message({
+      message: '保存失败,请重试',
+      status: 'error'
+    })
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const saveProduct = () => {
setTimeout(() => {
const data = gridRef?.value?.getTableData()
$local.products = data.tableData
TinyModal.message({
message: '保存成功',
status: 'success'
})
}, 1000)
}
const saveProduct = async () => {
try {
const data = gridRef?.value?.getTableData()
$local.products = data.tableData
TinyModal.message({
message: '保存成功',
status: 'success'
})
} catch (error) {
TinyModal.message({
message: '保存失败,请重试',
status: 'error'
})
}
}
🤖 Prompt for AI Agents
In examples/sites/src/views/comprehensive/Demo.vue around lines 137 to 146, the
saveProduct function uses a setTimeout with a fixed delay and always shows a
success message without checking if the save operation actually succeeded.
Remove the setTimeout and instead implement proper error handling by performing
the save operation synchronously or with a promise, then show the success
message only if the save succeeds, and handle errors by showing an appropriate
error message.

Comment on lines +29 to +37
export interface ProductForm {
name: string
price: number
description: string
image: string
category: string
stock: number
status: 'on' | 'off'
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Align field optionality between Product and ProductForm interfaces.

The description field is optional in the Product interface but required in the ProductForm interface. This inconsistency could lead to type errors.

 export interface ProductForm {
   name: string
   price: number
-  description: string
+  description?: string
   image: string
   category: string
   stock: number
   status: 'on' | 'off'
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface ProductForm {
name: string
price: number
description: string
image: string
category: string
stock: number
status: 'on' | 'off'
}
export interface ProductForm {
name: string
price: number
description?: string
image: string
category: string
stock: number
status: 'on' | 'off'
}
🤖 Prompt for AI Agents
In examples/sites/src/views/comprehensive/types/index.ts around lines 29 to 37,
the description field is required in the ProductForm interface but optional in
the Product interface, causing inconsistency. Update the ProductForm interface
to make the description field optional by adding a question mark (?) after the
field name to align with the Product interface.

Comment on lines +92 to +103
watch(
() => globalConversation.sessionId,
(newVal) => {
if (newVal) {
const encryptedId = CryptoJS.AES.encrypt(newVal, 'secret-session-id').toString()

const secretId = encodeURIComponent(encryptedId)
sessionUrl.value = 'http://39.108.160.245?id=' + secretId
}
},
{ immediate: true }
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Critical security issue: Remove hardcoded encryption key and URL.

Hardcoding encryption keys in client-side code exposes them to anyone who can view the source. Additionally, the URL should be configurable.

Consider these improvements:

  1. Move encryption to the backend
  2. Use environment variables for the URL
  3. If client-side encryption is necessary, use a public key approach
 watch(
   () => globalConversation.sessionId,
   (newVal) => {
     if (newVal) {
-      const encryptedId = CryptoJS.AES.encrypt(newVal, 'secret-session-id').toString()
-
-      const secretId = encodeURIComponent(encryptedId)
-      sessionUrl.value = 'http://39.108.160.245?id=' + secretId
+      // Generate session URL through a secure backend API
+      // or use a configuration service
+      sessionUrl.value = generateSecureSessionUrl(newVal)
     }
   },
   { immediate: true }
 )

I'll verify if there's a configuration service or environment variable system in place:


🏁 Script executed:

#!/bin/bash
# Search for environment variable usage or configuration patterns
rg -A 3 "import\.meta\.env|process\.env|config\." --type vue --type ts --type js

# Look for any existing session or encryption utilities
fd -e ts -e js -e vue | xargs rg -l "session|encrypt|crypto" | head -20

Length of output: 1315


🏁 Script executed:

#!/bin/bash
# Search for environment variable patterns in .js, .ts, and .vue files
rg -n "import\.meta\.env" -g "*.{js,ts,vue}"
rg -n "process\.env" -g "*.{js,ts,vue}"
rg -n "dotenv" -g "*.{js,ts,vue}"
rg -n "VITE_" -g "*.{js,ts,vue}"

# List any .env* files in the repository
fd -t f -g ".env*"

Length of output: 74319


Security: Remove hard-coded AES key & URL in client code

Hard-coding the AES key and service endpoint in the browser makes your “encryption” trivial to bypass. Update examples/sites/src/views/comprehensive/index.vue (lines 92–103) to:

• Move session URL/encryption logic to a secure backend endpoint.
• Or at minimum extract both the AES key and service URL into Vite env vars (e.g. import.meta.env.VITE_SESSION_KEY, VITE_SESSION_SERVICE_URL).
• If client-side encryption must remain, switch to a public-key/JWT approach instead of symmetric AES with a shared secret.

Example diff:

 watch(
   () => globalConversation.sessionId,
   async (newVal) => {
     if (newVal) {
-      const encryptedId = CryptoJS.AES.encrypt(newVal, 'secret-session-id').toString()
-      const secretId = encodeURIComponent(encryptedId)
-      sessionUrl.value = 'http://39.108.160.245?id=' + secretId
+      // Delegate URL signing to backend API or use env vars for key/URL
+      const key   = import.meta.env.VITE_SESSION_KEY
+      const base  = import.meta.env.VITE_SESSION_SERVICE_URL
+      const data  = CryptoJS.AES.encrypt(newVal, key).toString()
+      sessionUrl.value = `${base}/session?id=${encodeURIComponent(data)}`
   }
 },
 { immediate: true }
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
watch(
() => globalConversation.sessionId,
(newVal) => {
if (newVal) {
const encryptedId = CryptoJS.AES.encrypt(newVal, 'secret-session-id').toString()
const secretId = encodeURIComponent(encryptedId)
sessionUrl.value = 'http://39.108.160.245?id=' + secretId
}
},
{ immediate: true }
)
watch(
() => globalConversation.sessionId,
async (newVal) => {
if (newVal) {
// Delegate URL signing to backend API or use env vars for key/URL
const key = import.meta.env.VITE_SESSION_KEY
const base = import.meta.env.VITE_SESSION_SERVICE_URL
const data = CryptoJS.AES.encrypt(newVal, key).toString()
sessionUrl.value = `${base}/session?id=${encodeURIComponent(data)}`
}
},
{ immediate: true }
)
🤖 Prompt for AI Agents
In examples/sites/src/views/comprehensive/index.vue around lines 92 to 103,
remove the hardcoded AES encryption key and the fixed URL from the client-side
code. Instead, extract the AES key and the service URL into environment
variables accessed via import.meta.env (e.g., VITE_SESSION_KEY and
VITE_SESSION_SERVICE_URL). Ideally, move the encryption and session URL
generation to a secure backend endpoint. If client-side encryption is necessary,
replace the symmetric AES approach with a public-key or JWT-based method to
avoid exposing secrets in the client.

@zzcr zzcr merged commit fa86275 into release-3.24.0 Jun 11, 2025
12 checks passed
@zzcr zzcr deleted the zzc/add-mcp-demo-to-site branch June 11, 2025 09:57
zzcr added a commit that referenced this pull request Jun 14, 2025
* feat: New MCP comprehensive example added to the official website (#3497)

* feat: 官网新增mcp综合示例

* fix: 修复 tiny-robot-chat 组件中的提示项和建议项的传递逻辑,优化 Demo 组件的 QR 码展示样式

* fix: 修复引入路径问题

* chore: Update changelog and add TinyVue intelligent component access documentation (#3498)

* chore: 更新changelog,并添加TinyVue智能化组件接入文档

* chore: 添加菜单优化文档

* chore: 更新mcp.md文档中的sessionId说明,简化调试提示

* chore: 更新mcp-en.md文档中的标题和内容,统一语言为英文

* feat: add remote control page, update next dependency (#3501)

* feat: 添加遥控器页面,更新next依赖

* chore: 修改代理为https url

* feat: 更新会话ID的处理逻辑

* fix: 更新依赖版本约束以支持更高版本

* fix: 更新版本号至3.24.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request (功能增强)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant