cascader —— المحدد المتسلسل
يتعامل المحدد المتسلسل مع بيانات الاختيار طبقة طبقة من نوع «المحافظة > المدينة > المنطقة»: تظهر الطبقة التالية فقط بعد اختيار السابقة. ويعتبر core/cascader من أثقل وحدات core في share—فالبحث في البيانات الشجرية، واستعادة المسار، وتوليد اللوحات، والبحث، والتوحيد، كلها دوال نقية، وطبقة UI لا تتجاوز عرض عناصر اللوحة على هيئة أعمدة.
متى يُستخدم
Section titled “متى يُستخدم”- تنفّذ الأطراف المختلفة
LhCascader: التصنيفات متعددة المستويات، اختيار المناطق، الهيكل التنظيمي. - البيانات شجرية (أو قابلة للتوحيد إلى شجرة)، مع اختيار متباين في كل طبقة.
- لا ينبغي استخدامه: للاختيار من طبقة واحدة استخدم
select؛ وللتحديد المتسلسل (تحديد الأب يعني تحديد الأبناء) استخدمtree(لدىcheckStrictlyالدلالة المناسبة).
طريقة الاستيراد
Section titled “طريقة الاستيراد”import { findNode, findPathByValues, getField, getChildren, getPanelItems, searchNodes, getSelectedLabelPath, getCascaderClassNames, getPanelClassNames, expandLastValue, isLeafNode, normalizeCascaderNodes, flattenNodes, getCascaderTriggerAria, getCascaderColumnAria, getCascaderItemAria,} from '@lh-ui/share/core/cascader'import type { LhCascaderNode, LhCascaderPath, LhCascaderPanelItem, LhCascaderState, LhCascaderSearchResult, LhCascaderItemConfig,} from '@lh-ui/share/core/cascader'القصد التصميمي
Section titled “القصد التصميمي”عدة قرارات تصميمية رئيسية:
- استبعاد التكرار بسلسلة الأسلاف لا بـ visited عام: تحتاج عمليات الشجرة عموماً إلى الحماية من المراجع الدائرية. وهنا يُستخدم «سلسلة الأسلاف» للاستبعاد (تُمرَّر المعلمة
ancestorsطبقة طبقة)، لا Set على مستوى الوحدة أو عاماً—والثمن أن الحماية فعّالة ضمن استدعاء واحد فقط، والفائدة أن الأشجار الفرعية المشتركة بشكل مشروع في الـ DAG لن تُخطَأ تجاوزها، ولا توجد تسريبات حالة بين النسخ. getFieldيجعل الأسماء البديلة للحقول فعّالة فعلاً: لا تُسمّى بيانات الخلفية بالضرورةvalue/label/children(قد تكونid/name/subList). يُقرِّرLhCascaderItemConfigتعيين الأسماء البديلة، وتمرّ جميع عمليات الشجرة عبرgetFieldللقراءة.- التوحيد مرة واحدة، والاستخدام في كل مكان بالبنية المعيارية: يحوّل
normalizeCascaderNodesالشجرة الأصلية بأي بنية حقول إلىLhCascaderNode[]، وبعدها تواجه جميع الدوال البنية المعيارية. ويمكن أيضاً عدم التوحيد وتمرير itemConfig مباشرة، وكلا المسارين متكافئ (لكن الأداء أفضل بعد التوحيد، إذ لا حاجة لفحص الأسماء البديلة في كل طبقة). - تحمل نتائج البحث لسلسلة أسلاف كاملة: يحتوي كل سجل في نتائج
searchNodesعلىpath(مصفوفة قيم)، وnodes(مصفوفة عُقَد)، وpathLabels(مصفوفة وسوم)، فيعرضها UI مباشرة كسطر «تشجيانغ / هانغتشو / بحيرة شيهو» دون الحاجة لاستعلام عكسي. - الاستعادة البرمجية لا تُصفِّي المعطّلة: يتعمّد
findPathByValuesعدم تصفية العقد الفرعية المعطّلة—فالتعطيل قيد على النقر في طبقة UI، ومن المفترض أن تتمكن الاستعادة البرمجية (مثل القيمة الابتدائية لـv-model) من استعادة أي مسار، وإلا لضاعت القيم في سيناريو «تعطيل أولاً ثم استعادة».
جدول الدوال
Section titled “جدول الدوال”| الدالة | التوقيع | الإرجاع | الوظيفة |
|---|---|---|---|
findNode |
(nodes, value, strict?, ancestors?, itemConfig?) => { node, path } | null |
عقدة + سلسلة أسلاف | بحث تكراري |
getField |
<T>(node, standard: string, configKey?: string) => T |
قيمة الحقل | قراءة خصائص العقدة وفق الاسم البديل |
findPathByValues |
(nodes, values, itemConfig?) => LhCascaderPath | null |
المسار | استعادة طبقة طبقة وفق مصفوفة القيم |
getChildren |
(node, itemConfig?) => LhCascaderNode[] |
مصفوفة الأبناء | احتياطي للبيانات القذرة يُعيد [] |
getPanelItems |
(nodes, expandedValues?, itemConfig?) => LhCascaderPanelItem[] |
عناصر اللوحة | تحويل الشجرة → عناصر اللوحة |
searchNodes |
(nodes, keyword, results?, itemConfig?, maxResults?) => LhCascaderSearchResult[] |
نتائج البحث | غير حساس لحالة الأحرف، والحد الافتراضي 50 |
getSelectedLabelPath |
(nodes, values, itemConfig?) => string |
"تشجيانغ / هانغتشو" |
سلسلة الوسوم المختارة |
expandLastValue |
(nodes, value, itemConfig?) => (string|number)[] | null |
مصفوفة المسار | استعادة المسار من قيمة واحدة عند emitPath: false |
isLeafNode |
(node, itemConfig?) => boolean |
قيمة منطقية | leaf الصريح مقدَّم، وإلا يُحكم من children |
normalizeCascaderNodes |
(nodes: readonly unknown[], itemConfig?) => LhCascaderNode[] |
شجرة معيارية | توحيد دفعة واحدة |
flattenNodes |
(nodes, itemConfig?) => {node, path, pathValues}[] |
قائمة مسطّحة | يحمل كل عنصر مساره |
getCascaderClassNames |
(state) => string[] |
أسماء الأصناف | الحاوية |
getPanelClassNames |
(state) => string[] |
أسماء الأصناف | اللوحة (--hover-mode) |
تعريفات الأنواع
Section titled “تعريفات الأنواع”export interface LhCascaderNode { value: string | number label: string disabled?: boolean leaf?: boolean children?: LhCascaderNode[]}
export interface LhCascaderPath { path: (string | number)[] // مصفوفة القيم المختارة nodes: LhCascaderNode[] // مصفوفة العقد المطابقة}
export interface LhCascaderPanelItem { value: string | number label: string isDisabled: boolean isExpanded: boolean hasChildren: boolean}
export interface LhCascaderSearchResult { path: (string | number)[] nodes: LhCascaderNode[] pathLabels: string[]}
export interface LhCascaderItemConfig { value?: string // الاسم البديل لحقل value label?: string children?: string disabled?: string leaf?: string}
export interface LhCascaderState { options: LhCascaderNode[] size: LhComponentSize disabled: boolean clearable: boolean placeholder: string visible: boolean checkStrictly: boolean // عدم ربط الأب بالأبناء (اختيار أي طبقة) emitPath: boolean // true القيمة هي مصفوفة المسار، false تُعطى قيمة الطبقة الأخيرة فقط multiple: boolean cascading: boolean value: (string | number)[] selectedValue: (string | number)[] | null}إمكانية الوصول
Section titled “إمكانية الوصول”ثلاث طبقات من حزم السمات:
// العنصر المُطلِق: ينبثق بدلالة listboxgetCascaderTriggerAria(true, 'my-panel')// { 'aria-expanded': true, 'aria-controls': 'my-panel', 'aria-haspopup': 'listbox' }
// كل عمود: role="listbox" + aria-level يحدد العمق (ابتداءً من 1)getCascaderColumnAria('col-2', 2) // { role: 'listbox', id: 'col-2', 'aria-level': 2 }
// العنصر: role="option" + aria-expanded/selected/disabled حسب الحاجةgetCascaderItemAria({ selected: true, expanded: true, hasChildren: true })// { role: 'option', 'aria-expanded': true, 'aria-selected': true }لاحظ أن getCascaderItemAria لا يُخرِج aria-expanded عند عدم وجود أبناء—فالإقران بـ aria-expanded="false" لعنصر طرفي مسموح به في APG ولكنه زائد، ويوحي لقارئ الشاشة بأنه «قابل للتوسيع لكنه غير موسّع الآن».
أمثلة الاستخدام
Section titled “أمثلة الاستخدام”import { findPathByValues, getSelectedLabelPath, getPanelItems } from '@lh-ui/share/core/cascader'
const tree = [ { value: 'zj', label: 'تشجيانغ', children: [ { value: 'hz', label: 'هانغتشو', children: [{ value: 'xh', label: 'بحيرة شيهو' }] }, ], },]
findPathByValues(tree, ['zj', 'hz', 'xh'])// { path: ['zj','hz','xh'], nodes: [تشجيانغ, هانغتشو, بحيرة شيهو] }
getSelectedLabelPath(tree, ['zj', 'hz', 'xh']) // 'تشجيانغ / هانغتشو / بحيرة شيهو'getSelectedLabelPath(tree, ['nope']) // '' (عدم العثور يُعيد سلسلة فارغة)
getPanelItems(tree, new Set(['zj']))// [{ value:'zj', label:'تشجيانغ', isExpanded:true, hasChildren:true },// { value:'hz', label:'هانغتشو', isExpanded:false, hasChildren:true }]الأسماء البديلة للحقول:
const raw = [{ id: 'zj', name: 'تشجيانغ', subList: [{ id: 'hz', name: 'هانغتشو' }] }]const cfg = { value: 'id', label: 'name', children: 'subList' }const standard = normalizeCascaderNodes(raw, cfg)// [{ value:'zj', label:'تشجيانغ', children:[{ value:'hz', label:'هانغتشو' }] }]findPathByValues(standard, ['zj', 'hz']) // يعمل بشكل طبيعي<!-- Vue3: كل عمود يعرض طبقة من panelItems --><div :class="getCascaderClassNames({ size, disabled, visible, readonly })"> <button class="lh-cascader__input" v-bind="getCascaderTriggerAria(visible, panelId)"> {{ getSelectedLabelPath(options, value) || placeholder }} </button> <div v-if="visible" class="lh-cascader-panel" :class="getPanelClassNames({ hoverMode })"> <div v-for="(col, ci) in columns" :key="ci" class="lh-cascader-panel__column" v-bind="getCascaderColumnAria(`col-${ci}`, ci + 1)" > <div v-for="item in col" class="lh-cascader-panel__item" v-bind="getCascaderItemAria({ selected: ..., expanded: item.isExpanded, disabled: item.isDisabled, hasChildren: item.hasChildren })" @click="select(item, ci)" >{{ item.label }}</div> </div> </div></div>الأنماط والرموز
Section titled “الأنماط والرموز”| اسم الصنف | الدلالة |
|---|---|
.lh-cascader / --disabled / --visible |
الحاوية |
.lh-cascader__input / __input-inner |
منطقة إدخال العنصر المُطلِق |
.lh-cascader__tags / __tag / __tag-more |
وسوم التحديد المتعدد (طيّ التجاوز) |
.lh-cascader__clear |
زر المسح |
.lh-cascader-panel / --hover-mode |
اللوحة (وضع hover: المرور فوقها يوسّع الطبقة التالية) |
.lh-cascader-panel__column / __column-header |
العمود ورأسه |
.lh-cascader-panel__item / --active / --disabled |
عناصر العمود |
.lh-cascader-panel__search / __search-item |
منطقة نتائج البحث |
.lh-cascader-panel__empty |
عنصر نائب عند انعدام النتائج |
أصناف الانتقال lh-cascader-panel-* |
حركة دخول وخروج اللوحة |
ملاحظات
Section titled “ملاحظات”- الحد الافتراضي لـ
searchNodesهو 50: في الأشجار الكبيرة مع الإدخال عالي التردد سيؤدي استهلاك O(جميع العقد) مع كل ضغطة إلى تأخّر، والحد هو ضمان أداء؛ وعند الحاجة لنتائج أكثر تُنبه طبقة UI «النتائج كثيرة، رجاءً دقّق الكلمة المفتاحية». - نطاق عمل الحماية من المراجع الدائرية: استبعاد سلسلة الأسلاف فعّال within استدعاء واحد فقط، ولا يوجد تخزين مؤقت عبر الاستدعاءات. لا تحاول تخزين نتيجة
findNodeوإعادة استخدامها—إذا تغيّرت الشجرة يجب إعادة الحساب. expandLastValueيُستخدم فقط معemitPath: false: في هذا النمط يمنحv-modelقيمة الطبقة الأخيرة فقط، وعند الاستعادة يلزم إعادة بناء مسار التوسيع الكامل من القيمة الواحدة.- احتياط البيانات القذرة في
getChildren: العقدة null، والعقدة غير الكائن، وchildren غير المصفوفة جميعها تُعيد[]دون TypeError. وهذا يمكّن المحدد المتسلسل من استيعاب بيانات الخلفية القذرة (children فارغة مكتوبة null هي حالة شائعة). - تركيبة
checkStrictlyمعemitPath: يسمحcheckStrictlyباختيار أي طبقة، ويُقرَن عادةً معemitPath: false(أي قيمة الطبقة الأخيرة فقط)، وكلاهما مستقل عن الآخر.
@lh-ui/share/core/cascader → getSelectedLabelPath( )返回值
(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue