Skip to content
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

feat(grid-select): [grid-select] add remote feature #2530

Merged
merged 1 commit into from
Nov 19, 2024

Conversation

kagol
Copy link
Member

@kagol kagol commented Nov 18, 2024

PR

主要更新:

  1. 增加 remote 远程搜索功能和对应的 demo/api 文档

效果如下:

image

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 new grid selection component supporting both single and multiple selections with filtering and remote data fetching capabilities.
    • Added dynamic filtering functionality for the grid select component based on user input.
    • New demo for remote search functionality added to documentation.
  • Bug Fixes

    • Updated variable names for clarity in multiple components, enhancing code readability.
  • Documentation

    • Refined existing demo descriptions and added a new demo for remote searching functionality.
  • Chores

    • Removed the "GridSelect" component from the menu structure and added a new "Business Components" entry.

@kagol kagol added the enhancement New feature or request (功能增强) label Nov 18, 2024
Copy link

coderabbitai bot commented Nov 18, 2024

Walkthrough

The pull request introduces several changes across multiple Vue component files related to a grid selection interface. Key modifications include the renaming of variables for clarity, the addition of filtering methods, and the introduction of new components that enhance selection capabilities. The changes also encompass updates to the data structure and the inclusion of remote data fetching functionalities. Additionally, demo entries in the documentation have been updated to reflect these changes.

Changes

File Path Change Summary
examples/sites/demos/pc/app/grid-select/config-composition-api.vue Renamed valueMulti to valueMultiple and gridOpMulti to gridOpMultiple in template and script.
examples/sites/demos/pc/app/grid-select/filter.vue Changed valueSingle from an array to a string and added a filter method for dynamic filtering.
examples/sites/demos/pc/app/grid-select/multiple-composition-api.vue Renamed gridOpMulti to gridOpMultiple in script and template, structure unchanged.
examples/sites/demos/pc/app/grid-select/multiple.vue Renamed treeOp to gridOpMultiple, updated template binding to reflect the new variable name.
examples/sites/demos/pc/app/grid-select/remote-composition-api.vue Introduced a new component with single and multiple selection, added filtering and remote data fetching methods.
examples/sites/demos/pc/app/grid-select/remote.vue Added a new component for grid selection with remote data fetching and filtering capabilities.
examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js Modified demo descriptions and added a new demo for remote searching functionality.
examples/sites/demos/pc/menus.js Removed "GridSelect" entry and added "Business Components" entry conditionally in menu structure.
packages/renderless/src/grid-select/index.ts Expanded filter function to handle additional properties for remote data fetching.
packages/renderless/src/grid-select/vue.ts Added remoteData and selected properties to the state object in the renderless function.

Possibly related PRs

Suggested reviewers

  • zzcr

🐇 In the meadow, changes bloom bright,
With grids that select, oh what a sight!
Renamed and refined, our code takes flight,
Filtering data, making it right.
New demos to show, with features so grand,
Hopping along, we code hand in hand! 🌼✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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.

Copy link

[e2e-test-warn]
The component to be tested is missing.

The title of the Pull request should look like "fix(vue-renderless): [action-menu, alert] fix xxx bug".

Please make sure you've read our contributing guide

@kagol kagol changed the title feat(grid-select): add remote feature feat(grid-select): [grid-select] add remote feature Nov 18, 2024
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: 9

🧹 Outside diff range and nitpick comments (14)
packages/renderless/src/grid-select/vue.ts (1)

Line range hint 24-28: Consider watching remoteData updates

The current watch only handles updates to props.gridOp.data. If remote data is being used, consider watching and handling remote data updates as well.

   watch(
     () => props.gridOp.data,
     (data) => data && (state.gridData = data),
     { immediate: true, deep: true }
   )
+  // Add watch for remote data updates if needed
+  watch(
+    () => state.remoteData,
+    (data) => {
+      if (data && data.length) {
+        state.gridData = data
+      }
+    }
+  )
examples/sites/demos/pc/app/grid-select/multiple-composition-api.vue (1)

Line range hint 17-29: LGTM: Variable renamed for better clarity

The renaming from gridOpMulti to gridOpMultiple improves code readability. The data structure and column configuration are well-organized and properly support multiple selection functionality.

Consider adding a brief JSDoc comment to document the purpose and structure of the gridOpMultiple configuration object:

+/**
+ * Grid configuration for multiple selection demo
+ * @property {Array} data - Sample city data with area, province, and city information
+ * @property {Array} columns - Column definitions including selection column
+ */
const gridOpMultiple = reactive({
examples/sites/demos/pc/app/grid-select/filter.vue (1)

Line range hint 1-38: Consider adding loading and error states

As a demo component, it would be beneficial to showcase proper handling of different states:

  1. Add loading state handling for better UX during filtering
  2. Include error state handling for failed operations
  3. Add props validation for component inputs

Consider enhancing the component with:

 export default {
   components: {
     TinyGridSelect
   },
+  props: {
+    initialData: {
+      type: Array,
+      default: () => []
+    }
+  },
   data() {
     return {
       valueSingle: '',
+      loading: false,
+      error: null,
       gridOpSingle: {
         data: [
           // ... existing data
         ],
         columns: [
           // ... existing columns
         ]
       }
     }
   }
 }
examples/sites/demos/pc/app/grid-select/remote-composition-api.vue (4)

3-3: Consider using i18n for text labels

The hardcoded Chinese text should be internationalized to support multiple languages.

-    <p>场景1:单选</p>
+    <p>{{ t('gridSelect.singleSelection') }}</p>
-    <p>场景2:多选</p>
+    <p>{{ t('gridSelect.multipleSelection') }}</p>

Also applies to: 13-13


30-31: Consider adding TypeScript type annotations

Adding type annotations would improve type safety and developer experience.

-const valueSingle = ref('')
-const valueMultiple = ref([])
+const valueSingle = ref<string>('')
+const valueMultiple = ref<string[]>([])

80-84: Consider making the component responsive

The fixed width might not work well across different screen sizes.

 .tiny-grid-select {
-  width: 280px;
+  width: 100%;
+  max-width: 280px;
+  min-width: 200px;
 }

26-78: Add unit tests for the remote search functionality

As noted in the PR description, tests are missing. Please add unit tests to cover:

  1. Remote method behavior
  2. Filter function cases (empty input, case sensitivity)
  3. Loading state management
  4. Error handling scenarios

Would you like me to help generate the test cases for this component?

examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js (1)

46-58: Add missing English description for remote search demo.

While the Chinese documentation is comprehensive, the English description is empty. This could impact the experience for international developers.

Consider adding an English translation similar to:

desc: {
  'zh-CN':
    '<p>同时使用 <code>remote</code> 和 <code>remote-method</code> 和 <code>filterable</code> 3个属性开启远程搜索。通过 <code>remote-config</code> 设置自动搜索和显示展开按钮。<br>在多选模式下,可通过 <code>reserve-keyword</code> 设置选中一个选项后依然保留搜索关键字。</p>',
-  'en-US': ''
+  'en-US': '<p>Enable remote search by using the <code>remote</code>, <code>remote-method</code>, and <code>filterable</code> properties together. Use <code>remote-config</code> to configure auto-search and display expand button.<br>In multiple selection mode, use <code>reserve-keyword</code> to retain the search keyword after selecting an option.</p>'
}
examples/sites/demos/pc/app/grid-select/remote.vue (3)

5-11: Add loading and error states for better UX

The grid-select components should handle loading and error states during remote operations:

  1. Add loading prop to show loading state during fetch
  2. Add error handling for failed remote operations
  3. Add placeholder text for the search input
 <tiny-grid-select
   v-model="valueSingle"
   :grid-op="gridOpSingle"
   filterable
   remote
   :remote-method="remoteMethod"
+  :loading="loading"
+  placeholder="Search cities..."
+  @error="handleError"
 ></tiny-grid-select>

Also applies to: 15-22


3-3: Internationalize static text

Consider using i18n for the static Chinese text to support internationalization.

-    <p>场景1:单选</p>
+    <p>{{ t('gridSelect.singleSelection') }}</p>

Also applies to: 13-13


86-88: Consider responsive design

The fixed width of 280px might not be optimal for all screen sizes.

 .tiny-grid-select {
-  width: 280px;
+  width: 100%;
+  max-width: 280px;
+  min-width: 200px;
 }
examples/sites/demos/pc/menus.js (1)

139-146: Consider removing the code instead of commenting it out.

The GridSelect component configuration is commented out, which can lead to code clutter. Since this component is being replaced with a new implementation as per the PR objectives, it would be better to remove these lines entirely.

-      // {
-      //   'nameCn': '下拉表格选择器',
-      //   'name': 'GridSelect',
-      //   'key': 'grid-select',
-      //   'meta': {
-      //     'experimental': '3.20.0'
-      //   }
-      // },
packages/renderless/src/grid-select/index.ts (2)

44-44: Use includes instead of bitwise NOT for clarity

Using ~selectedIds.indexOf(row[valueField]) can be confusing and less readable. Consider using selectedIds.includes(row[valueField]) for better clarity and maintainability.

Apply this diff to improve readability:

- data.filter((row) => ~selectedIds.indexOf(row[valueField]))
+ data.filter((row) => selectedIds.includes(row[valueField]))

47-47: Simplify condition using !includes for better readability

The expression !~selectedIds.indexOf(row[valueField]) can be simplified to !selectedIds.includes(row[valueField]), which is more readable and understandable.

Apply this diff to simplify the condition:

- state.remoteData = data.filter((row) => !~selectedIds.indexOf(row[valueField])).concat(state.selected)
+ state.remoteData = data.filter((row) => !selectedIds.includes(row[valueField])).concat(state.selected)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 10c65c8 and b64f145.

📒 Files selected for processing (10)
  • examples/sites/demos/pc/app/grid-select/config-composition-api.vue (3 hunks)
  • examples/sites/demos/pc/app/grid-select/filter.vue (2 hunks)
  • examples/sites/demos/pc/app/grid-select/multiple-composition-api.vue (2 hunks)
  • examples/sites/demos/pc/app/grid-select/multiple.vue (2 hunks)
  • examples/sites/demos/pc/app/grid-select/remote-composition-api.vue (1 hunks)
  • examples/sites/demos/pc/app/grid-select/remote.vue (1 hunks)
  • examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js (2 hunks)
  • examples/sites/demos/pc/menus.js (1 hunks)
  • packages/renderless/src/grid-select/index.ts (1 hunks)
  • packages/renderless/src/grid-select/vue.ts (1 hunks)
🔇 Additional comments (14)
packages/renderless/src/grid-select/vue.ts (1)

11-11: Verify remote data handling implementation

The remoteData state is initialized but there's no visible handling of remote data updates. Ensure that remote data fetching and updates are properly implemented.

✅ Verification successful

Let me try a different search strategy to verify the remote data handling implementation.


Let me search for the remote data handling implementation in the grid-select component and its related files.


Based on the code analysis, I can now provide the final response:

Remote data handling is properly implemented

The remoteData state is correctly handled within the filter function when remote prop is true:

  • Remote data fetching is triggered through remoteMethod prop
  • Results are properly processed for both single and multiple selection modes
  • Remote data is stored in state and loaded into the grid
  • Selected items are preserved and merged with remote data in multiple selection mode
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for remote data handling implementation
echo "Searching for remote data handling..."
rg -l "remoteData" --type vue
ast-grep --pattern 'watch($$$remoteData$$$)' 

Length of output: 189


Script:

#!/bin/bash
# Search for remote data handling in TypeScript files
echo "Searching for remote data handling in the grid-select component..."
rg "remoteData" -A 5 --type ts

# Look for watch implementations
echo -e "\nSearching for watch implementations..."
ast-grep --pattern 'watch($$$)'

# Look for the complete grid-select component implementation
echo -e "\nSearching for the grid-select component file content..."
cat packages/renderless/src/grid-select/vue.ts

Length of output: 156674


Script:

#!/bin/bash
# Search for remote data handling in grid-select implementation
echo "Searching for remote data handling in grid-select implementation..."
rg -A 10 "remoteData" packages/renderless/src/grid-select/

# Look for remote-related functionality in index.ts
echo -e "\nChecking grid-select index.ts..."
cat packages/renderless/src/grid-select/index.ts

Length of output: 5236

examples/sites/demos/pc/app/grid-select/multiple-composition-api.vue (2)

5-5: LGTM: Template binding updated correctly

The grid-op binding is properly synchronized with the renamed reactive variable.


Line range hint 1-35: Verify remote search feature implementation

The PR objectives mention adding a remote search feature, but this demo file only implements basic multiple selection functionality. Let's verify if the remote search feature is implemented in other demo files.

examples/sites/demos/pc/app/grid-select/multiple.vue (3)

5-5: LGTM! Property binding updated consistently

The grid-op property binding has been updated to use the renamed variable, maintaining consistency with similar changes across other files.


21-21: LGTM! Improved variable naming

The rename from treeOp to gridOpMultiple better reflects the purpose of this configuration object, making the code more maintainable and self-documenting.


Line range hint 21-35: Verify remote feature implementation

Since this PR introduces a remote search feature, let's verify how it integrates with this multiple selection demo.

examples/sites/demos/pc/app/grid-select/filter.vue (1)

21-21: Verify the impact of changing valueSingle type from array to string

The change from array to string type aligns with single selection mode, but could be a breaking change if other code depends on the array type.

✅ Verification successful

Let me gather more information by searching the content of the documentation files and checking for any other Vue files in the grid-select directory.


No breaking changes found - valueSingle consistently uses string type

The search results show that valueSingle is consistently initialized as an empty string ('') across all grid-select examples, including both Options API (remote.vue, config.vue) and Composition API (remote-composition-api.vue, config-composition-api.vue, filter-composition-api.vue) implementations. This confirms that the string type for valueSingle is the standard pattern, not a breaking change.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of valueSingle to verify compatibility
rg -l "valueSingle" --type vue

# Search for any documentation or tests related to this component
fd -e md -e spec.js -e test.js grid-select

Length of output: 233


Script:

#!/bin/bash
# Check content of documentation files
cat examples/sites/demos/pc/app/grid-select/webdoc/grid-select.cn.md examples/sites/demos/pc/app/grid-select/webdoc/grid-select.en.md

# Find all Vue files in grid-select directory
fd -e vue grid-select

# Use grep without file type to find valueSingle references
rg "valueSingle"

Length of output: 1538

examples/sites/demos/pc/app/grid-select/remote-composition-api.vue (1)

26-29: LGTM!

Clean and minimal imports using the Composition API.

examples/sites/demos/pc/app/grid-select/config-composition-api.vue (2)

31-31: Review the selection logic restrictions.

The current implementation has potentially confusing row selection restrictions:

  • Single selection (radio) only allows odd-indexed rows (rowIndex % 2 === 1)
  • Multiple selection only allows even-indexed rows (rowIndex % 2 === 0)

This might lead to a confusing user experience where some rows are inexplicably disabled in different selection modes.

Let's check if this is consistent with other grid-select examples:

#!/bin/bash
# Search for other checkMethod implementations in grid-select components
rg -A 5 "checkMethod.*rowIndex" --type vue examples/sites/demos/pc/app/grid-select/

Also applies to: 49-49, 65-77


16-16: Variable renaming improves clarity but remote feature is missing.

The renaming from valueMulti/gridOpMulti to valueMultiple/gridOpMultiple improves code readability. However, the PR objectives mention adding a remote search feature, which is not implemented in this component.

Let's verify if the remote search feature is implemented elsewhere:

Also applies to: 20-20

examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js (2)

41-41: LGTM! Clear and well-formatted documentation.

The description clearly explains the filterable functionality and its configuration options.


67-67: LGTM! Proper HTML formatting.

The description is well-structured with proper HTML formatting using <br> tags.

examples/sites/demos/pc/app/grid-select/remote.vue (1)

27-32: Verify component registration

The component import and registration look correct. Let's verify the component's availability in the package.

✅ Verification successful

The previous command failed because vue file type wasn't recognized. Let's search across all files and also look for any mentions of the component to verify its existence and proper export.


The component is being used consistently across multiple demo files, but let's verify its actual export from the package to ensure it's properly exported and available.


Component import and registration are correct

The verification confirms that:

  • TinyGridSelect is properly exported from packages/vue/src/grid-select/index.ts
  • The component is consistently used across multiple demo files in the same way
  • The import from '@opentiny/vue' and component registration in the file are correct
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify TinyGridSelect component export
rg -l "export.*TinyGridSelect" --type vue

Length of output: 71


Script:

#!/bin/bash
# Search for TinyGridSelect export in all files
rg -l "export.*TinyGridSelect"

# Also search for any mentions of TinyGridSelect
rg "TinyGridSelect"

Length of output: 1497


Script:

#!/bin/bash
# Let's verify the actual export from @opentiny/vue package
rg -A 5 "export.*GridSelect" 

# Also check if there are any index files that might be re-exporting the component
fd -e js -e ts "index" -x rg -l "GridSelect" {}

Length of output: 4029

examples/sites/demos/pc/menus.js (1)

Line range hint 391-419: Review the security implications of business components exposure.

The business components section contains sensitive components like User, Company, Currency, etc. While there is a check for envTarget === 'inner', the showBusiness condition based on URL path could potentially expose these components in unintended environments.

✅ Verification successful

Business components access control is properly implemented

The verification shows that the business components exposure is properly controlled through multiple layers:

  1. The business components are only exposed when either:

    • envTarget === 'inner' (internal environment) OR
    • showBusiness is true, which is only set when the path explicitly includes '/all'
  2. The implementation shows this is a documentation/demo site feature rather than production code, as evidenced by:

    • The code exists in examples/sites/demos/pc/menus.js
    • The components are part of a documentation menu structure
    • No actual sensitive business logic implementation was found in the codebase

The current implementation provides appropriate access control for business component documentation, and no security concerns were identified.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the security implications of business components exposure

# Check if there are any direct routes to business components without proper guards
rg -l "path.*(/all|/business)" -g "!menus.js"

# Check for any hardcoded environment checks related to business components
rg "envTarget.*inner.*business" -g "!menus.js"

Length of output: 146


Script:

#!/bin/bash
# Let's check the content of the build-runtime.ts to understand the context
cat internals/cli/src/commands/build/build-runtime.ts

# Let's also check for any environment-based routing or access control patterns
rg -g "*.{js,ts}" "envTarget|environment.*inner" -A 2 -B 2

# Check for business component imports and their usage
rg -g "*.{js,ts}" "import.*Business|business.*component" -A 2 -B 2

# Look for any router configuration or route guards
rg -g "*.{js,ts}" "router.*(guard|beforeEach|middleware)" -A 2 -B 2

Length of output: 8396


Script:

#!/bin/bash
# Let's check for any route definitions or access controls related to business components
rg -g "*.{js,ts,vue}" "showBusiness|cmp-business-components" -A 5 -B 5

# Check for any authentication or authorization mechanisms
rg -g "*.{js,ts,vue}" "auth|isAuthenticated|checkAccess" -A 3 -B 3

# Look for environment configuration files
fd -e env -e env.development -e env.production

# Check the actual business components implementation
rg -g "*.{js,ts,vue}" "export.*class.*(User|Company|Currency|Amount)" -A 3 -B 3

Length of output: 7623

gridData: props.gridOp.data
gridData: props.gridOp.data,
remoteData: [],
selected: props.multiple ? [] : {}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider type safety for selected property

The selected property switches between array and object types based on props.multiple. This could lead to type-related issues in consuming components.

Consider using TypeScript or adding runtime type checking:

-    selected: props.multiple ? [] : {}
+    selected: props.multiple ? [] : {},
+    type: props.multiple ? 'multiple' : 'single'

Also, consider adding prop validation:

props: {
  multiple: {
    type: Boolean,
    default: false
  }
}

Comment on lines +40 to +48
filter(value) {
if (!value) {
return this.gridOpSingle.data
}

return this.gridOpSingle.data.filter((item) => {
return item.city.includes(value)
})
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance filter method robustness and performance

The current implementation could benefit from several improvements:

  1. Add error handling for malformed data
  2. Make the search case-insensitive for better user experience
  3. Consider filtering across multiple fields (area, province, city)
  4. Consider debouncing for large datasets

Consider this improved implementation:

 filter(value) {
   if (!value) {
     return this.gridOpSingle.data
   }
 
+  const searchTerm = value.toLowerCase()
   return this.gridOpSingle.data.filter((item) => {
-    return item.city.includes(value)
+    try {
+      return (
+        item.city?.toLowerCase().includes(searchTerm) ||
+        item.area?.toLowerCase().includes(searchTerm) ||
+        item.province?.toLowerCase().includes(searchTerm)
+      )
+    } catch (error) {
+      console.warn('Malformed data in grid item:', item)
+      return false
+    }
   })
 }
📝 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
filter(value) {
if (!value) {
return this.gridOpSingle.data
}
return this.gridOpSingle.data.filter((item) => {
return item.city.includes(value)
})
}
filter(value) {
if (!value) {
return this.gridOpSingle.data
}
const searchTerm = value.toLowerCase()
return this.gridOpSingle.data.filter((item) => {
try {
return (
item.city?.toLowerCase().includes(searchTerm) ||
item.area?.toLowerCase().includes(searchTerm) ||
item.province?.toLowerCase().includes(searchTerm)
)
} catch (error) {
console.warn('Malformed data in grid item:', item)
return false
}
})
}

Comment on lines +33 to +53
const gridOpSingle = reactive({
data: [],
height: 300,
columns: [
{ type: 'radio', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
})

const gridOpMultiple = reactive({
data: [],
height: 300,
columns: [
{ type: 'selection', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider extracting common configuration and improving flexibility

The grid configurations have duplicated structure that could be refactored.

+const baseColumns = [
+  { field: 'area', title: '区域', width: 90 },
+  { field: 'province', title: '省份', width: 60 },
+  { field: 'city', title: '城市', width: 60 }
+]

+const createGridOp = (selectionType) => reactive({
+  data: [],
+  height: 300,
+  columns: [
+    { type: selectionType, title: '' },
+    ...baseColumns
+  ]
+})

-const gridOpSingle = reactive({...})
-const gridOpMultiple = reactive({...})
+const gridOpSingle = createGridOp('radio')
+const gridOpMultiple = createGridOp('selection')

Also consider:

  1. Making the height responsive or configurable
  2. Adding column resize and sort capabilities
  3. Adding column min/max width constraints
📝 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 gridOpSingle = reactive({
data: [],
height: 300,
columns: [
{ type: 'radio', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
})
const gridOpMultiple = reactive({
data: [],
height: 300,
columns: [
{ type: 'selection', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
})
const baseColumns = [
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
const createGridOp = (selectionType) => reactive({
data: [],
height: 300,
columns: [
{ type: selectionType, title: '' },
...baseColumns
]
})
const gridOpSingle = createGridOp('radio')
const gridOpMultiple = createGridOp('selection')

Comment on lines +65 to +77
const filter = (value) => {
return allData.filter((item) => item.city.includes(value))
}

const remoteMethod = (value) => {
const filterData = filter(value)

return new Promise((resolve) => {
setTimeout(() => {
resolve(filterData)
}, 500)
})
}
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 robustness of remote search implementation

The current implementation has several areas that need improvement:

  1. Case-sensitive exact matching might not be user-friendly
  2. No error handling in the remote method
  3. No loading state management
  4. No debouncing of remote calls
+const isLoading = ref(false)
+
+const debounce = (fn, delay) => {
+  let timeoutId
+  return (...args) => {
+    clearTimeout(timeoutId)
+    return new Promise((resolve) => {
+      timeoutId = setTimeout(() => resolve(fn(...args)), delay)
+    })
+  }
+}
+
 const filter = (value) => {
-  return allData.filter((item) => item.city.includes(value))
+  const searchTerm = value.toLowerCase()
+  return allData.filter((item) => 
+    item.city.toLowerCase().includes(searchTerm) ||
+    item.province.toLowerCase().includes(searchTerm) ||
+    item.area.toLowerCase().includes(searchTerm)
+  )
 }

-const remoteMethod = (value) => {
+const remoteMethod = debounce(async (value) => {
+  if (!value) return []
+  
+  isLoading.value = true
   const filterData = filter(value)
-
-  return new Promise((resolve) => {
+  try {
+    await new Promise((resolve) => {
     setTimeout(() => {
       resolve(filterData)
     }, 500)
   })
-}
+    return filterData
+  } catch (error) {
+    console.error('Failed to fetch data:', error)
+    return []
+  } finally {
+    isLoading.value = false
+  }
+}, 300)
📝 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 filter = (value) => {
return allData.filter((item) => item.city.includes(value))
}
const remoteMethod = (value) => {
const filterData = filter(value)
return new Promise((resolve) => {
setTimeout(() => {
resolve(filterData)
}, 500)
})
}
const isLoading = ref(false)
const debounce = (fn, delay) => {
let timeoutId
return (...args) => {
clearTimeout(timeoutId)
return new Promise((resolve) => {
timeoutId = setTimeout(() => resolve(fn(...args)), delay)
})
}
}
const filter = (value) => {
const searchTerm = value.toLowerCase()
return allData.filter((item) =>
item.city.toLowerCase().includes(searchTerm) ||
item.province.toLowerCase().includes(searchTerm) ||
item.area.toLowerCase().includes(searchTerm)
)
}
const remoteMethod = debounce(async (value) => {
if (!value) return []
isLoading.value = true
const filterData = filter(value)
try {
await new Promise((resolve) => {
setTimeout(() => {
resolve(filterData)
}, 500)
})
return filterData
} catch (error) {
console.error('Failed to fetch data:', error)
return []
} finally {
isLoading.value = false
}
}, 300)

Comment on lines +69 to +71
filter(value) {
return this.allData.filter((item) => item.city.includes(value))
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance filter method robustness

The current filter implementation has several limitations:

  1. No input validation
  2. Case-sensitive search
  3. Only searches the city field
     filter(value) {
-      return this.allData.filter((item) => item.city.includes(value))
+      if (!value?.trim()) return this.allData
+      const searchValue = value.toLowerCase().trim()
+      return this.allData.filter((item) => 
+        item.city.toLowerCase().includes(searchValue) ||
+        item.province.toLowerCase().includes(searchValue) ||
+        item.area.toLowerCase().includes(searchValue)
+      )
     }
📝 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
filter(value) {
return this.allData.filter((item) => item.city.includes(value))
},
filter(value) {
if (!value?.trim()) return this.allData
const searchValue = value.toLowerCase().trim()
return this.allData.filter((item) =>
item.city.toLowerCase().includes(searchValue) ||
item.province.toLowerCase().includes(searchValue) ||
item.area.toLowerCase().includes(searchValue)
)
},

Comment on lines +57 to +65
allData: Array.from({ length: 1000 }, (a, i) => {
return {
value: '00' + i,
province: '省份' + i,
city: '城市' + i,
area: '区域' + i,
label: `省${i}-市${i}`
}
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider lazy loading mock data

Creating a large array of 1000 items during component initialization could impact initial load performance.

-      allData: Array.from({ length: 1000 }, (a, i) => {
-        return {
-          value: '00' + i,
-          province: '省份' + i,
-          city: '城市' + i,
-          area: '区域' + i,
-          label: `省${i}-市${i}`
-        }
-      })
+      allData: [],
+      // Move data generation to created/mounted hook or on-demand
+      generateMockData(count = 1000) {
+        this.allData = Array.from({ length: count }, (a, i) => ({
+          value: '00' + i,
+          province: '省份' + i,
+          city: '城市' + i,
+          area: '区域' + i,
+          label: `省${i}-市${i}`
+        }))
+      }

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

Comment on lines +72 to +80
remoteMethod(value) {
const filterData = this.filter(value)

return new Promise((resolve) => {
setTimeout(() => {
resolve(filterData)
}, 500)
})
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve remote method implementation

The remoteMethod implementation needs enhancement:

  1. Add error handling
  2. Make timeout configurable
  3. Add request debouncing
+    data() {
+      return {
+        // ... existing data
+        searchTimeout: 500,
+        loading: false
+      }
+    },
     methods: {
+      debounce(fn, delay) {
+        let timeoutId
+        return (...args) => {
+          clearTimeout(timeoutId)
+          return new Promise((resolve) => {
+            timeoutId = setTimeout(() => resolve(fn.apply(this, args)), delay)
+          })
+        }
+      },
       remoteMethod(value) {
+        this.loading = true
         const filterData = this.filter(value)
 
         return new Promise((resolve) => {
           setTimeout(() => {
+            this.loading = false
             resolve(filterData)
-          }, 500)
+          }, this.searchTimeout)
+        }).catch(error => {
+          this.loading = false
+          console.error('Remote search failed:', error)
+          throw error
         })
       }
     }

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

Comment on lines +38 to +57
remoteMethod(value, props.extraQueryParams).then((data) => {
// 多选时取远端数据与当前已选数据的并集
if (multiple) {
const selectedIds = state.selected.map((sel) => sel[valueField])
vm.$refs.gridRef.clearSelection()
vm.$refs.gridRef.setSelection(
data.filter((row) => ~selectedIds.indexOf(row[valueField])),
true
)
state.remoteData = data.filter((row) => !~selectedIds.indexOf(row[valueField])).concat(state.selected)
} else {
vm.$refs.gridRef.clearRadioRow()
vm.$refs.gridRef.setRadioRow(find(data, (item) => props.modelValue === item[props.valueField]))
state.remoteData = data
}

vm.$refs.gridRef.$refs.tinyTable.lastScrollTop = 0
vm.$refs.gridRef.loadData(data)
vm.$refs.gridRef.handleTableData(!value)
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for the remote data fetch

Currently, the remoteMethod call does not handle errors if the Promise is rejected. It's crucial to add error handling to prevent unhandled promise rejections and to provide feedback to the user in case of failure.

Consider adding a .catch block to handle possible errors:

remoteMethod(value, props.extraQueryParams)
  .then((data) => {
    // existing code
  })
  .catch((error) => {
    // handle the error, e.g., show an error message or update the state
  })

state.remoteData = data.filter((row) => !~selectedIds.indexOf(row[valueField])).concat(state.selected)
} else {
vm.$refs.gridRef.clearRadioRow()
vm.$refs.gridRef.setRadioRow(find(data, (item) => props.modelValue === item[props.valueField]))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure find is correctly used or imported

The find function used here may not be defined in this context. If you're intending to use the native Array find method, adjust the syntax accordingly.

Modify the code to use the Array find method:

- vm.$refs.gridRef.setRadioRow(find(data, (item) => props.modelValue === item[props.valueField]))
+ vm.$refs.gridRef.setRadioRow(data.find((item) => props.modelValue === item[props.valueField]))

If find is a utility function from a library like Lodash, ensure it is properly imported.

📝 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
vm.$refs.gridRef.setRadioRow(find(data, (item) => props.modelValue === item[props.valueField]))
vm.$refs.gridRef.setRadioRow(data.find((item) => props.modelValue === item[props.valueField]))

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.

2 participants