liyu-pc-base 1.0.721 → 1.0.723
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{FieldRender-BvOABMiR.js → FieldRender-CwF_kXRF.js} +1 -1
- package/dist/{FieldRender-BvOABMiR.js.map → FieldRender-CwF_kXRF.js.map} +1 -1
- package/dist/{FieldRender2-vtiebhY7.js → FieldRender2-pYxWeF1l.js} +2 -2
- package/dist/{FieldRender2-vtiebhY7.js.map → FieldRender2-pYxWeF1l.js.map} +1 -1
- package/dist/{FormRender3-CAmEsbHQ.js → FormRender3-qvj_4_TI.js} +1 -1
- package/dist/{FormRender3-CAmEsbHQ.js.map → FormRender3-qvj_4_TI.js.map} +1 -1
- package/dist/{RemoteSelectPro-6Q-9of9p.js → RemoteSelectPro-BSuDAsj9.js} +1 -1
- package/dist/{RemoteSelectPro-6Q-9of9p.js.map → RemoteSelectPro-BSuDAsj9.js.map} +1 -1
- package/dist/{SearchRender-B7RzqhTX.js → SearchRender-BX256Tnt.js} +1 -1
- package/dist/{SearchRender-B7RzqhTX.js.map → SearchRender-BX256Tnt.js.map} +1 -1
- package/dist/components/index.js +6 -6
- package/dist/index.js +1 -1
- package/dist/liyu-pc-base.css +1 -1
- package/dist/{tableModal-HdMKJWWl.js → tableModal-Cu6gU61G.js} +1 -1
- package/dist/{tableModal-HdMKJWWl.js.map → tableModal-Cu6gU61G.js.map} +1 -1
- package/dist/{tablePro-CrjC08DS.js → tablePro-nlorKH23.js} +2356 -2345
- package/dist/tablePro-nlorKH23.js.map +1 -0
- package/package.json +1 -1
- package/dist/tablePro-CrjC08DS.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tablePro-nlorKH23.js","names":["codeToLocale","isNumOp","fmtValue","handleDates"],"sources":["../src/components/FormRender/SmallTableSerarchField.vue","../src/components/FormRender/SmallTableSerarchField.vue","../src/api/view-model.ts","../src/utils/dom.ts","../src/utils/hooks/useFullscreen.ts","../src/components/tablePro/utils.ts","../src/components/tablePro/searchSummary.vue","../src/components/tablePro/searchSummary.vue","../src/components/tablePro/tableConfig/searchConfig.vue","../src/components/tablePro/tableConfig/searchConfig.vue","../src/components/tablePro/SearchForm.vue","../src/components/tablePro/SearchForm.vue","../src/components/tablePro/search.vue","../src/components/tablePro/search.vue","../src/components/tablePro/SearchFormGroup.vue","../src/components/tablePro/SearchFormGroup.vue","../src/components/tablePro/TableFilterDropdown.vue","../src/components/tablePro/TableFilterDropdown.vue","../src/components/tablePro/tableConfig/columnsConfig.vue","../src/components/tablePro/tableConfig/columnsConfig.vue","../node_modules/numfmt/lib/constants.js","../node_modules/numfmt/lib/codeToLocale.js","../node_modules/numfmt/lib/locale.js","../node_modules/numfmt/lib/options.js","../node_modules/numfmt/lib/round.js","../node_modules/numfmt/lib/dec2frac.js","../node_modules/numfmt/lib/toYMD.js","../node_modules/numfmt/lib/serialDate.js","../node_modules/numfmt/lib/parseValue.js","../node_modules/numfmt/lib/parseFormatSection.js","../node_modules/numfmt/lib/clamp.js","../node_modules/numfmt/lib/numberProps.js","../node_modules/numfmt/lib/numdec.js","../node_modules/numfmt/lib/general.js","../node_modules/numfmt/lib/pad.js","../node_modules/numfmt/lib/runPart.js","../node_modules/numfmt/lib/formatNumber.js","../node_modules/numfmt/lib/tokenize.js","../node_modules/numfmt/lib/parsePattern.js","../node_modules/numfmt/lib/index.js","../src/components/tablePro/func.ts","../src/components/tablePro/tableConfig/valueFieldDisplay.vue","../src/components/tablePro/tableConfig/valueFieldDisplay.vue","../src/components/tablePro/tableConfig/DebouncedInput.vue","../src/components/tablePro/tableConfig/DebouncedInput.vue","../src/components/tablePro/tableConfig/valueExprEditor.vue","../src/components/tablePro/tableConfig/valueExprEditor.vue","../src/components/tablePro/tableConfig/subExpressionEditor.vue","../src/components/tablePro/tableConfig/subExpressionEditor.vue","../src/components/tablePro/tableConfig/pivotConfig.vue","../src/components/tablePro/tableConfig/pivotConfig.vue","../src/components/tablePro/tableConfig/pivotChartsConfig.vue","../src/components/tablePro/tableConfig/pivotChartsConfig.vue","../src/components/tablePro/hooks/view.ts","../src/components/tablePro/tableConfig/viewsConfig.vue","../src/components/tablePro/tableConfig/viewsConfig.vue","../src/components/tablePro/tableConfig/valueFieldConfig.vue","../src/components/tablePro/tableConfig/valueFieldConfig.vue","../src/components/tablePro/tableConfig/index.vue","../src/components/tablePro/tableConfig/index.vue","../src/components/tablePro/head_btn_confirm.ts","../src/components/tablePro/head_btn_custom_component.ts","../src/components/tablePro/head_btn_form.ts","../src/components/tablePro/headBtnChildren.vue","../src/components/tablePro/headBtnChildren.vue","../src/components/tablePro/headBtn.vue","../src/components/tablePro/headBtn.vue","../src/components/tablePro/refresh-type-modal/index.vue","../src/components/tablePro/refresh-type-modal/index.vue","../src/components/tablePro/ConditionGroupSelecter.vue","../src/components/tablePro/ConditionGroupSelecter.vue","../src/components/tablePro/ConditionGroupView.vue","../src/components/tablePro/ConditionGroupView.vue","../src/components/tablePro/tableHeader.vue","../src/components/tablePro/tableHeader.vue","../src/components/tablePro/TableSummary.vue","../src/components/tablePro/TableSummary.vue","../src/components/tablePro/TableContextMenu.vue","../src/components/tablePro/TableContextMenu.vue","../src/components/tablePro/tablePro.vue","../src/components/tablePro/tablePro.vue"],"sourcesContent":["<template>\n <template\n v-if=\"field.type !== 'divider' && (field?.is_search_show !== false || field_render_show)\"\n >\n <slot />\n <div\n v-if=\"field.type === 'kong'\"\n style=\"height: 62px; width: 200px\"\n ></div>\n <!-- 展示内容-->\n <div v-if=\"field.type === 'tag'\">\n <a-tag\n :color=\"field.color ? field.color(field, model) : 'green'\"\n :style=\"field.style\"\n v-if=\"(field.content && field.content(field, model)) || model\"\n @click=\"field.cb ? field.cb(field, model) : ''\"\n >\n <span v-if=\"field.content\">\n {{ field.content(field, model) }}\n </span>\n <span v-else>{{ model }}</span>\n </a-tag>\n </div>\n <!-- 表单占位组件-->\n <!-- 默认 【表单空位 高62px; 名称空位 高30px;】 如遇个别特殊表单,根据实际情况自行调节 高度-->\n <!-- height 属性必填-->\n <div\n v-if=\"field.type === 'kong_title'\"\n :style=\"'height: ' + field.height + 'px'\"\n ></div>\n <div\n v-if=\"field.type === 'title'\"\n style=\"height: 30px; position: relative\"\n >\n <span\n style=\"\n position: absolute;\n bottom: 0;\n padding: 0;\n margin: 0;\n font-weight: bolder;\n font-size: 17px;\n \"\n >\n {{ field.name }}\n </span>\n </div>\n <a-rate\n v-if=\"field.type === 'rate'\"\n v-model:value=\"model\"\n :disabled=\"field.disabled\"\n allow-half\n :style=\"`width: ${field_width}px`\"\n />\n <a-input\n v-if=\"field.type === 'input'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear == undefined ? true : field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :maxlength=\"field.maxlength\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请输入') + field.label\"\n :type=\"field.inputType ? field.inputType : 'text'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <template\n v-if=\"field?.prefix || field?.prefixIcon\"\n #prefix\n >\n <span v-if=\"field?.prefix\">{{ field.prefix }}</span>\n <component\n v-if=\"field?.prefixIcon\"\n :is=\"field?.prefixIcon\"\n />\n </template>\n <template\n v-if=\"field?.suffix || field?.suffixIcon\"\n #suffix\n >\n <span v-if=\"field?.suffix\">{{ field.suffix }}</span>\n <component\n v-if=\"field?.suffixIcon\"\n :is=\"field?.suffixIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonBefore || field?.addonBeforeIcon\"\n #addonBefore\n >\n <span v-if=\"field?.addonBefore\">{{ field.addonBefore }}</span>\n <component\n v-if=\"field?.addonBeforeIcon\"\n :is=\"field?.addonBeforeIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonAfter || field?.addonAfterIcon\"\n #addonAfter\n >\n <span v-if=\"field?.addonAfter\">{{ field.addonAfter }}</span>\n <component\n v-if=\"field?.addonAfterIcon\"\n :is=\"field?.addonAfterIcon\"\n />\n </template>\n </a-input>\n\n <a-input-number\n v-if=\"field.type === 'money'\"\n size=\"small\"\n v-model:value=\"model\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :disabled=\"field.disabled\"\n :default-value=\"field.defaultValue\"\n :min=\"field.min\"\n :max=\"field.max\"\n :formatter=\"value => `${value}`.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\"\n :precision=\"field.precision\"\n :step=\"field.step\"\n :placeholder=\"\n field.placeholder_i18n == undefined ? field.placeholder : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <a-input-number\n v-if=\"field.type === 'number'\"\n v-model:value=\"model\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :formatter=\"field.formatter\"\n :max=\"field.max\"\n :min=\"field.min\"\n :placeholder=\"field.placeholder ? field.placeholder : '请输入' + field.label\"\n :precision=\"field.precision\"\n :step=\"field.step\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <template\n v-if=\"field?.prefix || field?.prefixIcon\"\n #prefix\n >\n <span v-if=\"field?.prefix\">{{ field.prefix }}</span>\n <component\n v-if=\"field?.prefixIcon\"\n :is=\"field?.prefixIcon\"\n />\n </template>\n <template\n v-if=\"field?.suffix || field?.suffixIcon\"\n #suffix\n >\n <span v-if=\"field?.suffix\">{{ field.suffix }}</span>\n <component\n v-if=\"field?.suffixIcon\"\n :is=\"field?.suffixIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonBefore || field?.addonBeforeIcon\"\n #addonBefore\n >\n <span v-if=\"field?.addonBefore\">{{ field.addonBefore }}</span>\n <component\n v-if=\"field?.addonBeforeIcon\"\n :is=\"field?.addonBeforeIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonAfter || field?.addonAfterIcon\"\n #addonAfter\n >\n <span v-if=\"field?.addonAfter\">{{ field.addonAfter }}</span>\n <component\n v-if=\"field?.addonAfterIcon\"\n :is=\"field?.addonAfterIcon\"\n />\n </template>\n </a-input-number>\n\n <template v-if=\"field.type === 'select'\">\n <a-select\n v-if=\"field.mode === 'default'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-select-option\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-select-option>\n </a-select>\n <a-select\n v-else\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :mode=\"field.mode\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-select-option\n v-for=\"(option, index) in options\"\n :key=\"index\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-select-option>\n </a-select>\n </template>\n <template v-if=\"field.type === 'groupselect'\">\n <a-select\n v-if=\"field.mode === 'default'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :getPopupContainer=\"field.getPopupContainer == undefined ? null : field.getPopupContainer\"\n :labelInValue=\"field.labelInValue === true\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :options=\"options\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n ></a-select>\n <a-select\n v-else\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :getPopupContainer=\"field.getPopupContainer == undefined ? null : field.getPopupContainer\"\n :labelInValue=\"field.labelInValue === true\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :mode=\"field.mode\"\n :options=\"options\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n ></a-select>\n </template>\n\n <a-cascader\n v-if=\"field.type === 'cascader'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :multiple=\"field.multiple\"\n :show-search=\"field.show_search\"\n :changeOnSelect=\"field.changeOnSelect ? field.changeOnSelect : false\"\n :options=\"field.getOptionsData ? field.getOptionsData(options, model) : options\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <remote-cascader\n v-if=\"field.type === 'remotecascader'\"\n v-model:value=\"model\"\n v-model:label=\"labelModel\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :label-key=\"field.labelKey\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-cascader-pro\n v-if=\"field.type === 'remotecascaderpro'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :multiple=\"field.multiple\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :show_search=\"field.show_search\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :label-key=\"field.labelKey\"\n :value-key=\"field.valueKey\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n\n <month-picker\n v-if=\"field.type === 'monthpicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n size=\"small\"\n :is_range=\"field.is_range\"\n :value_has_time=\"field.value_has_time\"\n :start_offset_day=\"field.start_offset_day\"\n :end_offset_day=\"field.end_offset_day\"\n :fix_day=\"field.fix_day\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-date-picker\n v-if=\"field.type === 'datepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :format=\"field.format\"\n :picker=\"field.picker\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-time=\"field.showTime\"\n :value-format=\"field.valueFormat\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <a-range-picker\n v-if=\"field.type === 'rangepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue || []\"\n :disabled=\"field.disabled\"\n :format=\"field.format\"\n :placeholder=\"\n field.placeholder && isArray(field.placeholder) && field.placeholder.length > 0\n ? field.placeholder\n : [$t('开始时间'), $t('结束时间')]\n \"\n :show-time=\"field.showTime\"\n :value-format=\"field.valueFormat\"\n :picker=\"field.picker ? field.picker : 'date'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <month-range-picker\n v-if=\"field.type === 'monthrangepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue || []\"\n :disabled=\"field.disabled\"\n :placeholder=\"field.placeholder\"\n size=\"small\"\n :is_range=\"field.is_range\"\n :value_has_time=\"field.value_has_time\"\n :start_offset_day=\"field.start_offset_day\"\n :end_offset_day=\"field.end_offset_day\"\n :fix_day=\"field.fix_day\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <a-time-picker\n v-if=\"field.type === 'timepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :format=\"field.format ? field.format : 'HH:mm'\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :value-format=\"field.valueFormat ? field.valueFormat : 'HH:mm'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-time-range-picker\n v-if=\"field.type === 'timerangepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :format=\"field.format ? field.format : 'HH:mm'\"\n :placeholder=\"\n field.placeholder && isArray(field.placeholder) && field.placeholder.length > 0\n ? field.placeholder\n : [$t('开始时间'), $t('结束时间')]\n \"\n :value-format=\"field.valueFormat ? field.valueFormat : 'HH:mm'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-radio-group\n v-if=\"field.type === 'radio'\"\n v-model:value=\"model\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-radio\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :disabled=\"option.disabled\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-radio>\n </a-radio-group>\n\n <a-checkbox-group\n v-if=\"field.type === 'checkbox'\"\n v-model:value=\"model\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-checkbox\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :disabled=\"option.disabled\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-checkbox>\n </a-checkbox-group>\n\n <a-tree-select\n v-if=\"field.type === 'treeselect'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :multiple=\"field.multiple\"\n :tree-checkable=\"field.treeCheckable\"\n :tree-data=\"options\"\n :tree-label-prop=\"field.labelKey\"\n :tree-node-filter-prop=\"field.valueKey\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-filter-select\n v-if=\"field.type === 'remotefilterselect'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :label-key=\"field.labelKey\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-select\n v-if=\"field.type === 'remoteselect'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :label-key=\"field.labelKey\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-select-pro\n v-if=\"field.type === 'remoteselectpro'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :label-key=\"field.labelKey\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :is_input=\"field.is_input\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : $t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-complete\n v-if=\"field.type === 'remotecomplete'\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :label-key=\"field.labelKey\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :maxlength=\"field.maxlength\"\n :is_input=\"field.is_input\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : $t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-tree-select\n v-if=\"field.type === 'remotetreeselect'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :label-key=\"field.labelKey\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-tree-select-pro\n v-if=\"field.type === 'remotetreeselectpro'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :multiple=\"field.multiple\"\n :search_config=\"field.search_config\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :label-key=\"field.labelKey\"\n :value-key=\"field.valueKey\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : $t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-modal\n v-if=\"field.type === 'remotemodal'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :columns=\"field.columns\"\n :field=\"field\"\n :field_status=\"field.field_status\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :modalType=\"field.modalType\"\n :search_config=\"field.search_config\"\n :watch=\"field.watch\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-table\n v-if=\"field.type === 'remotetable'\"\n v-model:value=\"model\"\n :columns=\"field.columns\"\n :formModel=\"formModel\"\n :field=\"field\"\n :field_status=\"field.field_status\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :modalType=\"field.modalType\"\n :search_config=\"field.search_config\"\n :watch=\"field.watch\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <remote-text\n v-if=\"field.type === 'remotetext'\"\n v-model:value=\"model\"\n :columns=\"field.columns\"\n :formModel=\"formModel\"\n :field=\"field\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :modalType=\"field.modalType\"\n :search_config=\"field.search_config\"\n :watch=\"field.watch\"\n />\n\n <a-rate\n v-if=\"field.type === 'rate'\"\n v-model:value=\"model\"\n :count=\"field.count ? field.count : 5\"\n :allow-half=\"field.allowHalf\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-radio-group\n v-if=\"field.type === 'radio-color'\"\n v-model:value=\"model\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-radio\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :disabled=\"option.disabled\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n <div\n :style=\"{\n width: '20px',\n height: '20px',\n backgroundColor: option[field.valueKey ? field.valueKey : 'value'],\n }\"\n ></div>\n </a-radio>\n </a-radio-group>\n </template>\n\n <a-divider\n v-if=\"field.type === 'divider'\"\n :orientation=\"field.orientation\"\n >\n {{ field.label }}\n </a-divider>\n</template>\n<script lang=\"ts\">\nimport RemoteSelect from 'liyu-pc-base/components/FormRender/RemoteSelect/RemoteSelect.vue';\nimport RemoteCascader from 'liyu-pc-base/components/FormRender/RemoteCascader/RemoteCascader.vue';\nimport RemoteModal from 'liyu-pc-base/components/FormRender/RemoteModal/RemoteModal.vue';\nimport RemoteTreeSelect from 'liyu-pc-base/components/FormRender/RemoteTreeSelect/RemoteTreeSelect.vue';\nimport RemoteTable from 'liyu-pc-base/components/FormRender/RemoteTable/RemoteTable.vue';\nimport { defineComponent, watch, onMounted, ref } from 'vue';\nimport RemoteFilterSelect from 'liyu-pc-base/components/FormRender/RemoteFilterSelect/RemoteFilterSelect.vue';\nimport AttributeNomTable from 'liyu-pc-base/components/FormRender/AttributeNomTable/index.vue';\nimport AttributeTable from 'liyu-pc-base/components/FormRender/AttributeTable/index.vue';\nimport JsonStr from 'liyu-pc-base/components/FormRender/JsonStr/index.vue';\nimport JsonObj from 'liyu-pc-base/components/FormRender/JsonObj/index.vue';\nimport RemoteText from 'liyu-pc-base/components/FormRender/RemoteText/RemoteText.vue';\nimport RemoteTablePro from 'liyu-pc-base/components/FormRender/RemoteTablePro/RemoteTablePro.vue';\nimport { debounce, isArray, isEqual } from 'lodash-es';\nimport moment from 'moment';\nimport { isEmpty, isNotEmpty } from 'liyu-pc-base/utils/function';\nimport { PlusOutlined } from '@ant-design/icons-vue';\nimport RemoteSelectPro from 'liyu-pc-base/components/FormRender/RemoteSelectPro/RemoteSelectPro.vue';\nimport RemoteComplete from 'liyu-pc-base/components/FormRender/RemoteComplete/RemoteComplete.vue';\nimport RemoteTreeSelectPro from 'liyu-pc-base/components/FormRender/RemoteTreeSelectPro/RemoteTreeSelectPro.vue';\nimport RemoteCascaderPro from 'liyu-pc-base/components/FormRender/RemoteCascaderPro/RemoteCascaderPro.vue';\nimport Upload from 'liyu-pc-base/components/FormRender/Upload/upload.vue';\nimport RichTextEditor from 'liyu-pc-base/components/FormRender/RichTextEditor/index.vue';\nimport { useI18n } from 'vue-i18n';\nimport IconSelect from 'liyu-pc-base/components/FormRender/iconSelect/index.vue';\nimport MonthPicker from 'liyu-pc-base/components/FormRender/MonthPicker/index.vue';\nimport MonthRangePicker from 'liyu-pc-base/components/FormRender/MonthRangePicker/index.vue';\nimport CodeEditor from 'liyu-pc-base/components/code/CodeEditor.vue';\nimport TinymceEditor from 'liyu-pc-base/components/tinymce/TinymceEditor.vue';\n\nconst isValidDate = (dateString, format = 'YYYY-MM-DD') => {\n const date = moment(dateString, format, true);\n return date.isValid();\n};\nconst isValidDateTime = (dateString, format = 'YYYY-MM-DD HH:mm:ss') => {\n const date = moment(dateString, format, true);\n return date.isValid();\n};\nconst isValidTime = (dateString, format = 'HH:mm:ss') => {\n const date = moment(dateString, format, true);\n return date.isValid();\n};\nexport default defineComponent({\n name: 'FieldRender2',\n methods: { isArray },\n props: {\n field: {\n type: Object,\n required: true,\n },\n value: {\n required: true,\n },\n label: {\n required: false,\n },\n options: {\n type: Array,\n default: () => [],\n },\n validateInfos: {\n type: Object,\n default: () => {},\n },\n formModel: {\n type: Object,\n default: () => {},\n },\n outModel: {\n type: Object,\n default: () => {},\n },\n field_render_show: {\n type: Boolean,\n default: true,\n },\n is_show_label: {\n type: Boolean,\n default: true,\n },\n isUseConditions: {\n type: Boolean,\n default: true,\n },\n is_show_field: {\n type: Boolean,\n default: true,\n },\n is_edit: {\n type: Boolean,\n default: true,\n },\n conditionsVisibleNum: {\n type: Number,\n default: 0,\n },\n is_form_design: {\n type: Boolean,\n default: false,\n },\n field_width: {\n type: Number,\n default: 150,\n },\n },\n emits: [\n 'add',\n 'update:value',\n 'update:label',\n 'update_model',\n 'update:is_show_field',\n 'update:is_edit',\n 'update_name2',\n ],\n setup(props, { emit }) {\n const newModel = ref({ ...props.formModel });\n const rangeDate = (v: any) => {\n if (typeof v == 'string' && v.indexOf(',') >= 0) {\n return v.split(',');\n }\n return v;\n };\n const convertValueByClassName = (value, class_name) => {\n //class_name 'int','float',str','bool','date','datetime','time'\n let new_value;\n if (class_name == 'int') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'string') {\n new_value.push(Number(item));\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'string') {\n new_value = Number(value);\n } else {\n new_value = value;\n }\n } else if (class_name == 'str') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'number') {\n new_value.push(item.toString());\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'number') {\n new_value = value.toString();\n } else {\n new_value = value;\n }\n } else if (class_name == 'date') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isValidDate(item) && typeof item === 'string') {\n new_value.push(moment(item, 'YYYY-MM-DD'));\n } else {\n new_value.push(item);\n }\n });\n } else if (isValidDate(value) && typeof value === 'string') {\n new_value = value.toString();\n } else {\n new_value = value;\n }\n } else if (class_name == 'datetime') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isValidDateTime(item) && typeof item === 'string') {\n new_value.push(moment(item, 'YYYY-MM-DD HH:mm:ss'));\n } else {\n new_value.push(item);\n }\n });\n } else if (isValidDateTime(value) && typeof value === 'string') {\n new_value = moment(value, 'YYYY-MM-DD HH:mm:ss');\n } else {\n new_value = value;\n }\n } else if (class_name == 'time') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isValidTime(item) && typeof item === 'string') {\n new_value.push(moment(item, 'HH:mm:ss'));\n } else {\n new_value.push(item);\n }\n });\n } else if (isValidTime(value) && typeof value === 'string') {\n new_value = moment(value, 'HH:mm:ss');\n } else {\n new_value = value;\n }\n } else if (class_name == 'float') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'string') {\n new_value.push(Number(item));\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'string') {\n new_value = Number(value);\n } else {\n new_value = value;\n }\n } else if (class_name == 'bool') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'string') {\n new_value.push(Boolean(item));\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'string') {\n new_value = Boolean(value);\n } else {\n new_value = value;\n }\n }\n return new_value;\n };\n const is_show_field = ref(props.is_show_field);\n const is_edit = ref(false);\n const handleToggleEdit = () => {\n emit('update:is_edit', is_edit.value);\n };\n const handleAdd = () => {\n emit('add');\n };\n const check_visible_field_fun = debounce(() => {\n const old_is_show_field = is_show_field.value;\n const condition_result_list: any = [];\n for (const i in props.field?.conditions) {\n const group_list = props.field?.conditions[i];\n const group_results = [];\n for (const j in group_list) {\n const item = group_list[j];\n const value = props.formModel[item.watchKey];\n let new_value;\n if (item.class_name) {\n new_value = convertValueByClassName(item.value, item.class_name);\n } else {\n new_value = item.value;\n }\n let first, second;\n if (new_value instanceof Array) {\n [first, second] = new_value;\n }\n if (item.operator == 'lt') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value < first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (moment(value).isBefore(moment(first))) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'lte') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value <= first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n moment(value).isBefore(moment(first)) ||\n moment(value).isSame(moment(first))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'gt') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value > first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (moment(value).isAfter(moment(first))) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'gte') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value >= first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n moment(value).isAfter(moment(first)) ||\n moment(value).isSame(moment(first))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'between') {\n if (typeof first === 'number' && typeof second === 'number' && first && second) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value >= first && value <= second) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n (moment(value).isAfter(moment(first)) || moment(value).isSame(moment(first))) &&\n (moment(value).isBefore(moment(second)) || moment(value).isSame(moment(second)))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'notbetween') {\n if (typeof first === 'number' && typeof second === 'number' && first && second) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value < first || value > second) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n moment(value).isBefore(moment(first)) ||\n moment(value).isAfter(moment(second))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'in') {\n if (value instanceof Array) {\n const f_list = new_value.filter(x => {\n return value.includes(x);\n });\n if (f_list.length > 0) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (typeof value == 'string' || typeof value == 'number' || isEmpty(value)) {\n // 数字 字符串 空\n const f = new_value.find(x => {\n return value == x;\n });\n if (f) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'eq') {\n if (value === first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'contains') {\n if (value.toString().indexOf(first) > -1) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'startswith') {\n if (value.toString().startsWith(first)) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'notin') {\n if (value instanceof Array) {\n const f_list = new_value.filter(x => {\n return value.includes(x);\n });\n if (f_list.length > 0) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (typeof value == 'string' || typeof value == 'number' || isEmpty(value)) {\n // 数字 字符串 空\n const f = new_value.find(x => {\n return value == x;\n });\n if (!f) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'notcontains') {\n if (value.toString().indexOf(first) == -1) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == '_eq') {\n //todo\n } else if (item.operator == 'json_list_not_blank_not_null') {\n // 非空\n if (value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'json_list_is_blank_is_null') {\n //为空\n if (!value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'not_eq') {\n //为空\n if (!value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'not_blank_not_null') {\n // 非空\n if (value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n }\n condition_result_list.push(group_results);\n }\n const con_list = [];\n condition_result_list.forEach(item => {\n const find_false_list = item.filter(x => x === false);\n if (find_false_list && find_false_list.length > 0) {\n con_list.push(false);\n } else {\n con_list.push(true);\n }\n });\n\n const find_true_list = con_list.filter(x => x === true);\n if (find_true_list && find_true_list.length > 0) {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n } else {\n is_show_field.value = false;\n emit('update:is_show_field', is_show_field.value);\n }\n if (is_show_field.value !== old_is_show_field) {\n if (is_show_field.value) {\n newModel.value[props.field.name] = null;\n emit('update_model', {\n add_key: props.field.name,\n value: null,\n });\n } else {\n delete newModel.value[props.field.name];\n emit('update_model', {\n remove_key: props.field.name,\n });\n }\n }\n }, 300);\n const model = ref<any>('');\n const labelModel = ref(props.label);\n watch(\n () => model.value,\n (newValue, oldValue) => {\n if (isEqual(newValue, oldValue)) {\n return;\n }\n emit('update:value', newValue);\n if (props.field.name2) {\n let label: any;\n if (props.field.type === 'radio') {\n const obj = props.options.find(\n (item: any) =>\n item[props.field.valueKey ? props.field.valueKey : 'value'] == newValue,\n );\n label = obj?.[props.field.labelKey ? props.field.labelKey : 'label'];\n } else if (props.field.type === 'checkbox' && newValue && newValue.length >= 0) {\n const list = [];\n props.options.forEach((item: any) => {\n newValue.forEach((item1: any) => {\n if (item1 == item?.[props.field.valueKey ? props.field.valueKey : 'value']) {\n list.push(item?.[props.field.labelKey ? props.field.labelKey : 'label']);\n }\n });\n });\n label = list.toString();\n } else if (props.field.type === 'select') {\n if (isArray(newValue)) {\n const list = [];\n props.options.forEach((item: any) => {\n newValue.forEach((item1: any) => {\n if (item1 == item?.[props.field.valueKey ? props.field.valueKey : 'value']) {\n list.push(item?.[props.field.labelKey ? props.field.labelKey : 'label']);\n }\n });\n });\n label = list.toString();\n } else {\n const obj = props.options.find(\n (item: any) =>\n item[props.field.valueKey ? props.field.valueKey : 'value'] == newValue,\n );\n label = obj?.[props.field.labelKey ? props.field.labelKey : 'label'];\n }\n }\n emit('update_name2', {\n key: props.field.name2,\n value: label,\n });\n }\n },\n { deep: true },\n );\n watch(\n () => props.value,\n newValue => {\n let value = newValue;\n if (props.field.type === 'rangepicker' && value) {\n value = rangeDate(value);\n } else if (props.field.type === 'input' && value) {\n value = value.toString().replace(/(^\\s*)|(\\s*$)/g, '');\n }\n if (is_show_field.value) {\n //当该表单项显示时,才做向上的数据绑定,避免把undefined向上传递,影响rules字段的有效过滤\n model.value = value;\n }\n },\n { immediate: true },\n );\n watch(\n () => labelModel.value,\n newValue => {\n emit('update:label', newValue);\n if ('name2' in props.field) {\n emit('update_name2', { key: props.field.name2, value: newValue.toString() });\n }\n },\n { deep: true },\n );\n watch(\n () => props.label,\n newValue => {\n labelModel.value = newValue;\n },\n );\n watch(\n () => props.formModel,\n newValue => {\n newModel.value = { ...newValue };\n },\n { deep: true },\n );\n watch(\n () => newModel.value,\n (newValue, oldValue) => {\n if (props.field?.conditions) {\n props.field?.conditions.forEach(condition_list => {\n condition_list.forEach(item => {\n if (oldValue[item.watchKey] != newValue[item.watchKey]) {\n check_visible_field_fun();\n }\n });\n });\n }\n },\n { deep: true },\n );\n watch(\n () => [props.conditionsVisibleNum, props.isUseConditions],\n () => {\n if (props.field?.conditions) {\n if (props.isUseConditions) {\n check_visible_field_fun();\n } else {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n }\n } else {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n }\n },\n );\n onMounted(() => {\n if (props.field?.conditions instanceof Array && props.field?.conditions.length > 0) {\n check_visible_field_fun();\n } else {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n }\n if (!is_show_field.value) {\n // 表单项所见即所得,如果有隐藏的字段,那么model的key也要清除\n delete newModel.value[props.field.name];\n emit('update_model', {\n remove_key: props.field.name,\n });\n }\n });\n const { t } = useI18n();\n return {\n t,\n is_show_field,\n is_edit,\n handleToggleEdit,\n rangeDate,\n handleAdd,\n model,\n labelModel,\n };\n },\n components: {\n IconSelect,\n RichTextEditor,\n Upload,\n RemoteCascaderPro,\n RemoteTreeSelectPro,\n RemoteSelectPro,\n RemoteComplete,\n AttributeTable,\n AttributeNomTable,\n RemoteFilterSelect,\n RemoteModal,\n RemoteSelect,\n RemoteCascader,\n RemoteTreeSelect,\n RemoteTable,\n JsonStr,\n JsonObj,\n RemoteText,\n RemoteTablePro,\n PlusOutlined,\n MonthPicker,\n MonthRangePicker,\n CodeEditor,\n TinymceEditor,\n },\n});\n</script>\n\n<style scoped>\n.ant-calendar-picker {\n width: 100% !important;\n}\n</style>\n","<template>\n <template\n v-if=\"field.type !== 'divider' && (field?.is_search_show !== false || field_render_show)\"\n >\n <slot />\n <div\n v-if=\"field.type === 'kong'\"\n style=\"height: 62px; width: 200px\"\n ></div>\n <!-- 展示内容-->\n <div v-if=\"field.type === 'tag'\">\n <a-tag\n :color=\"field.color ? field.color(field, model) : 'green'\"\n :style=\"field.style\"\n v-if=\"(field.content && field.content(field, model)) || model\"\n @click=\"field.cb ? field.cb(field, model) : ''\"\n >\n <span v-if=\"field.content\">\n {{ field.content(field, model) }}\n </span>\n <span v-else>{{ model }}</span>\n </a-tag>\n </div>\n <!-- 表单占位组件-->\n <!-- 默认 【表单空位 高62px; 名称空位 高30px;】 如遇个别特殊表单,根据实际情况自行调节 高度-->\n <!-- height 属性必填-->\n <div\n v-if=\"field.type === 'kong_title'\"\n :style=\"'height: ' + field.height + 'px'\"\n ></div>\n <div\n v-if=\"field.type === 'title'\"\n style=\"height: 30px; position: relative\"\n >\n <span\n style=\"\n position: absolute;\n bottom: 0;\n padding: 0;\n margin: 0;\n font-weight: bolder;\n font-size: 17px;\n \"\n >\n {{ field.name }}\n </span>\n </div>\n <a-rate\n v-if=\"field.type === 'rate'\"\n v-model:value=\"model\"\n :disabled=\"field.disabled\"\n allow-half\n :style=\"`width: ${field_width}px`\"\n />\n <a-input\n v-if=\"field.type === 'input'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear == undefined ? true : field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :maxlength=\"field.maxlength\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请输入') + field.label\"\n :type=\"field.inputType ? field.inputType : 'text'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <template\n v-if=\"field?.prefix || field?.prefixIcon\"\n #prefix\n >\n <span v-if=\"field?.prefix\">{{ field.prefix }}</span>\n <component\n v-if=\"field?.prefixIcon\"\n :is=\"field?.prefixIcon\"\n />\n </template>\n <template\n v-if=\"field?.suffix || field?.suffixIcon\"\n #suffix\n >\n <span v-if=\"field?.suffix\">{{ field.suffix }}</span>\n <component\n v-if=\"field?.suffixIcon\"\n :is=\"field?.suffixIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonBefore || field?.addonBeforeIcon\"\n #addonBefore\n >\n <span v-if=\"field?.addonBefore\">{{ field.addonBefore }}</span>\n <component\n v-if=\"field?.addonBeforeIcon\"\n :is=\"field?.addonBeforeIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonAfter || field?.addonAfterIcon\"\n #addonAfter\n >\n <span v-if=\"field?.addonAfter\">{{ field.addonAfter }}</span>\n <component\n v-if=\"field?.addonAfterIcon\"\n :is=\"field?.addonAfterIcon\"\n />\n </template>\n </a-input>\n\n <a-input-number\n v-if=\"field.type === 'money'\"\n size=\"small\"\n v-model:value=\"model\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :disabled=\"field.disabled\"\n :default-value=\"field.defaultValue\"\n :min=\"field.min\"\n :max=\"field.max\"\n :formatter=\"value => `${value}`.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\"\n :precision=\"field.precision\"\n :step=\"field.step\"\n :placeholder=\"\n field.placeholder_i18n == undefined ? field.placeholder : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <a-input-number\n v-if=\"field.type === 'number'\"\n v-model:value=\"model\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :formatter=\"field.formatter\"\n :max=\"field.max\"\n :min=\"field.min\"\n :placeholder=\"field.placeholder ? field.placeholder : '请输入' + field.label\"\n :precision=\"field.precision\"\n :step=\"field.step\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <template\n v-if=\"field?.prefix || field?.prefixIcon\"\n #prefix\n >\n <span v-if=\"field?.prefix\">{{ field.prefix }}</span>\n <component\n v-if=\"field?.prefixIcon\"\n :is=\"field?.prefixIcon\"\n />\n </template>\n <template\n v-if=\"field?.suffix || field?.suffixIcon\"\n #suffix\n >\n <span v-if=\"field?.suffix\">{{ field.suffix }}</span>\n <component\n v-if=\"field?.suffixIcon\"\n :is=\"field?.suffixIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonBefore || field?.addonBeforeIcon\"\n #addonBefore\n >\n <span v-if=\"field?.addonBefore\">{{ field.addonBefore }}</span>\n <component\n v-if=\"field?.addonBeforeIcon\"\n :is=\"field?.addonBeforeIcon\"\n />\n </template>\n <template\n v-if=\"field?.addonAfter || field?.addonAfterIcon\"\n #addonAfter\n >\n <span v-if=\"field?.addonAfter\">{{ field.addonAfter }}</span>\n <component\n v-if=\"field?.addonAfterIcon\"\n :is=\"field?.addonAfterIcon\"\n />\n </template>\n </a-input-number>\n\n <template v-if=\"field.type === 'select'\">\n <a-select\n v-if=\"field.mode === 'default'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-select-option\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-select-option>\n </a-select>\n <a-select\n v-else\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :mode=\"field.mode\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-select-option\n v-for=\"(option, index) in options\"\n :key=\"index\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-select-option>\n </a-select>\n </template>\n <template v-if=\"field.type === 'groupselect'\">\n <a-select\n v-if=\"field.mode === 'default'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :getPopupContainer=\"field.getPopupContainer == undefined ? null : field.getPopupContainer\"\n :labelInValue=\"field.labelInValue === true\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :options=\"options\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n ></a-select>\n <a-select\n v-else\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :getPopupContainer=\"field.getPopupContainer == undefined ? null : field.getPopupContainer\"\n :labelInValue=\"field.labelInValue === true\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :mode=\"field.mode\"\n :options=\"options\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-search=\"!!field.show_search\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n ></a-select>\n </template>\n\n <a-cascader\n v-if=\"field.type === 'cascader'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :multiple=\"field.multiple\"\n :show-search=\"field.show_search\"\n :changeOnSelect=\"field.changeOnSelect ? field.changeOnSelect : false\"\n :options=\"field.getOptionsData ? field.getOptionsData(options, model) : options\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <remote-cascader\n v-if=\"field.type === 'remotecascader'\"\n v-model:value=\"model\"\n v-model:label=\"labelModel\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :label-key=\"field.labelKey\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-cascader-pro\n v-if=\"field.type === 'remotecascaderpro'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :multiple=\"field.multiple\"\n :maxTagCount=\"field.maxTagCount ? field.maxTagCount : 'responsive'\"\n :show_search=\"field.show_search\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :label-key=\"field.labelKey\"\n :value-key=\"field.valueKey\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n\n <month-picker\n v-if=\"field.type === 'monthpicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n size=\"small\"\n :is_range=\"field.is_range\"\n :value_has_time=\"field.value_has_time\"\n :start_offset_day=\"field.start_offset_day\"\n :end_offset_day=\"field.end_offset_day\"\n :fix_day=\"field.fix_day\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-date-picker\n v-if=\"field.type === 'datepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :autocomplete=\"field.autocomplete == undefined ? 'off' : field.autocomplete\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :format=\"field.format\"\n :picker=\"field.picker\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :show-time=\"field.showTime\"\n :value-format=\"field.valueFormat\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <a-range-picker\n v-if=\"field.type === 'rangepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue || []\"\n :disabled=\"field.disabled\"\n :format=\"field.format\"\n :placeholder=\"\n field.placeholder && isArray(field.placeholder) && field.placeholder.length > 0\n ? field.placeholder\n : [$t('开始时间'), $t('结束时间')]\n \"\n :show-time=\"field.showTime\"\n :value-format=\"field.valueFormat\"\n :picker=\"field.picker ? field.picker : 'date'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <month-range-picker\n v-if=\"field.type === 'monthrangepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue || []\"\n :disabled=\"field.disabled\"\n :placeholder=\"field.placeholder\"\n size=\"small\"\n :is_range=\"field.is_range\"\n :value_has_time=\"field.value_has_time\"\n :start_offset_day=\"field.start_offset_day\"\n :end_offset_day=\"field.end_offset_day\"\n :fix_day=\"field.fix_day\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <a-time-picker\n v-if=\"field.type === 'timepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :format=\"field.format ? field.format : 'HH:mm'\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :value-format=\"field.valueFormat ? field.valueFormat : 'HH:mm'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-time-range-picker\n v-if=\"field.type === 'timerangepicker'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :format=\"field.format ? field.format : 'HH:mm'\"\n :placeholder=\"\n field.placeholder && isArray(field.placeholder) && field.placeholder.length > 0\n ? field.placeholder\n : [$t('开始时间'), $t('结束时间')]\n \"\n :value-format=\"field.valueFormat ? field.valueFormat : 'HH:mm'\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-radio-group\n v-if=\"field.type === 'radio'\"\n v-model:value=\"model\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-radio\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :disabled=\"option.disabled\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-radio>\n </a-radio-group>\n\n <a-checkbox-group\n v-if=\"field.type === 'checkbox'\"\n v-model:value=\"model\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-checkbox\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :disabled=\"option.disabled\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n {{ option[field.labelKey ? field.labelKey : 'label'] }}\n </a-checkbox>\n </a-checkbox-group>\n\n <a-tree-select\n v-if=\"field.type === 'treeselect'\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :dropdownMatchSelectWidth=\"false\"\n :placeholder=\"field.placeholder ? field.placeholder : $t('请选择') + field.label\"\n :multiple=\"field.multiple\"\n :tree-checkable=\"field.treeCheckable\"\n :tree-data=\"options\"\n :tree-label-prop=\"field.labelKey\"\n :tree-node-filter-prop=\"field.valueKey\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-filter-select\n v-if=\"field.type === 'remotefilterselect'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :label-key=\"field.labelKey\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-select\n v-if=\"field.type === 'remoteselect'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :label-key=\"field.labelKey\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-select-pro\n v-if=\"field.type === 'remoteselectpro'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :label-key=\"field.labelKey\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :is_input=\"field.is_input\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : $t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-complete\n v-if=\"field.type === 'remotecomplete'\"\n v-model:value=\"model\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :label-key=\"field.labelKey\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :maxlength=\"field.maxlength\"\n :is_input=\"field.is_input\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : $t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-tree-select\n v-if=\"field.type === 'remotetreeselect'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :field=\"field\"\n :formModel=\"formModel\"\n :get-data=\"field.getData\"\n :label-key=\"field.labelKey\"\n :search_config=\"field.search_config\"\n :value-key=\"field.valueKey\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-tree-select-pro\n v-if=\"field.type === 'remotetreeselectpro'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :allow-clear=\"field.allowClear\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n :field=\"field\"\n :formModel=\"formModel\"\n :outModel=\"outModel\"\n :get-data=\"field.getData\"\n :multiple=\"field.multiple\"\n :search_config=\"field.search_config\"\n :label-key-type=\"field.labelKeyType\"\n :label-key-list=\"field.labelKeyList\"\n :label-space=\"field.labelSpace\"\n :label-key=\"field.labelKey\"\n :value-key=\"field.valueKey\"\n :init_model=\"field.init_model\"\n :init_watch=\"field.init_watch\"\n :is_form_design=\"is_form_design\"\n :placeholder=\"\n field.placeholder_i18n == undefined\n ? field.placeholder\n ? field.placeholder\n : $t('请选择') + field.label\n : t(field.placeholder_i18n)\n \"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-modal\n v-if=\"field.type === 'remotemodal'\"\n v-model:label=\"labelModel\"\n v-model:value=\"model\"\n :columns=\"field.columns\"\n :field=\"field\"\n :field_status=\"field.field_status\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :modalType=\"field.modalType\"\n :search_config=\"field.search_config\"\n :watch=\"field.watch\"\n :style=\"`width: ${field_width}px`\"\n />\n <remote-table\n v-if=\"field.type === 'remotetable'\"\n v-model:value=\"model\"\n :columns=\"field.columns\"\n :formModel=\"formModel\"\n :field=\"field\"\n :field_status=\"field.field_status\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :modalType=\"field.modalType\"\n :search_config=\"field.search_config\"\n :watch=\"field.watch\"\n :style=\"`width: ${field_width}px`\"\n />\n\n <remote-text\n v-if=\"field.type === 'remotetext'\"\n v-model:value=\"model\"\n :columns=\"field.columns\"\n :formModel=\"formModel\"\n :field=\"field\"\n :get-data=\"field.getData\"\n :init_model=\"field.init_model\"\n :modalType=\"field.modalType\"\n :search_config=\"field.search_config\"\n :watch=\"field.watch\"\n />\n\n <a-rate\n v-if=\"field.type === 'rate'\"\n v-model:value=\"model\"\n :count=\"field.count ? field.count : 5\"\n :allow-half=\"field.allowHalf\"\n :style=\"`width: ${field_width}px`\"\n />\n <a-radio-group\n v-if=\"field.type === 'radio-color'\"\n v-model:value=\"model\"\n :default-value=\"field.defaultValue\"\n :disabled=\"field.disabled\"\n size=\"small\"\n :style=\"`width: ${field_width}px`\"\n >\n <a-radio\n v-for=\"option in options\"\n :key=\"option[field.valueKey ? field.valueKey : 'value']\"\n :disabled=\"option.disabled\"\n :value=\"option[field.valueKey ? field.valueKey : 'value']\"\n >\n <div\n :style=\"{\n width: '20px',\n height: '20px',\n backgroundColor: option[field.valueKey ? field.valueKey : 'value'],\n }\"\n ></div>\n </a-radio>\n </a-radio-group>\n </template>\n\n <a-divider\n v-if=\"field.type === 'divider'\"\n :orientation=\"field.orientation\"\n >\n {{ field.label }}\n </a-divider>\n</template>\n<script lang=\"ts\">\nimport RemoteSelect from 'liyu-pc-base/components/FormRender/RemoteSelect/RemoteSelect.vue';\nimport RemoteCascader from 'liyu-pc-base/components/FormRender/RemoteCascader/RemoteCascader.vue';\nimport RemoteModal from 'liyu-pc-base/components/FormRender/RemoteModal/RemoteModal.vue';\nimport RemoteTreeSelect from 'liyu-pc-base/components/FormRender/RemoteTreeSelect/RemoteTreeSelect.vue';\nimport RemoteTable from 'liyu-pc-base/components/FormRender/RemoteTable/RemoteTable.vue';\nimport { defineComponent, watch, onMounted, ref } from 'vue';\nimport RemoteFilterSelect from 'liyu-pc-base/components/FormRender/RemoteFilterSelect/RemoteFilterSelect.vue';\nimport AttributeNomTable from 'liyu-pc-base/components/FormRender/AttributeNomTable/index.vue';\nimport AttributeTable from 'liyu-pc-base/components/FormRender/AttributeTable/index.vue';\nimport JsonStr from 'liyu-pc-base/components/FormRender/JsonStr/index.vue';\nimport JsonObj from 'liyu-pc-base/components/FormRender/JsonObj/index.vue';\nimport RemoteText from 'liyu-pc-base/components/FormRender/RemoteText/RemoteText.vue';\nimport RemoteTablePro from 'liyu-pc-base/components/FormRender/RemoteTablePro/RemoteTablePro.vue';\nimport { debounce, isArray, isEqual } from 'lodash-es';\nimport moment from 'moment';\nimport { isEmpty, isNotEmpty } from 'liyu-pc-base/utils/function';\nimport { PlusOutlined } from '@ant-design/icons-vue';\nimport RemoteSelectPro from 'liyu-pc-base/components/FormRender/RemoteSelectPro/RemoteSelectPro.vue';\nimport RemoteComplete from 'liyu-pc-base/components/FormRender/RemoteComplete/RemoteComplete.vue';\nimport RemoteTreeSelectPro from 'liyu-pc-base/components/FormRender/RemoteTreeSelectPro/RemoteTreeSelectPro.vue';\nimport RemoteCascaderPro from 'liyu-pc-base/components/FormRender/RemoteCascaderPro/RemoteCascaderPro.vue';\nimport Upload from 'liyu-pc-base/components/FormRender/Upload/upload.vue';\nimport RichTextEditor from 'liyu-pc-base/components/FormRender/RichTextEditor/index.vue';\nimport { useI18n } from 'vue-i18n';\nimport IconSelect from 'liyu-pc-base/components/FormRender/iconSelect/index.vue';\nimport MonthPicker from 'liyu-pc-base/components/FormRender/MonthPicker/index.vue';\nimport MonthRangePicker from 'liyu-pc-base/components/FormRender/MonthRangePicker/index.vue';\nimport CodeEditor from 'liyu-pc-base/components/code/CodeEditor.vue';\nimport TinymceEditor from 'liyu-pc-base/components/tinymce/TinymceEditor.vue';\n\nconst isValidDate = (dateString, format = 'YYYY-MM-DD') => {\n const date = moment(dateString, format, true);\n return date.isValid();\n};\nconst isValidDateTime = (dateString, format = 'YYYY-MM-DD HH:mm:ss') => {\n const date = moment(dateString, format, true);\n return date.isValid();\n};\nconst isValidTime = (dateString, format = 'HH:mm:ss') => {\n const date = moment(dateString, format, true);\n return date.isValid();\n};\nexport default defineComponent({\n name: 'FieldRender2',\n methods: { isArray },\n props: {\n field: {\n type: Object,\n required: true,\n },\n value: {\n required: true,\n },\n label: {\n required: false,\n },\n options: {\n type: Array,\n default: () => [],\n },\n validateInfos: {\n type: Object,\n default: () => {},\n },\n formModel: {\n type: Object,\n default: () => {},\n },\n outModel: {\n type: Object,\n default: () => {},\n },\n field_render_show: {\n type: Boolean,\n default: true,\n },\n is_show_label: {\n type: Boolean,\n default: true,\n },\n isUseConditions: {\n type: Boolean,\n default: true,\n },\n is_show_field: {\n type: Boolean,\n default: true,\n },\n is_edit: {\n type: Boolean,\n default: true,\n },\n conditionsVisibleNum: {\n type: Number,\n default: 0,\n },\n is_form_design: {\n type: Boolean,\n default: false,\n },\n field_width: {\n type: Number,\n default: 150,\n },\n },\n emits: [\n 'add',\n 'update:value',\n 'update:label',\n 'update_model',\n 'update:is_show_field',\n 'update:is_edit',\n 'update_name2',\n ],\n setup(props, { emit }) {\n const newModel = ref({ ...props.formModel });\n const rangeDate = (v: any) => {\n if (typeof v == 'string' && v.indexOf(',') >= 0) {\n return v.split(',');\n }\n return v;\n };\n const convertValueByClassName = (value, class_name) => {\n //class_name 'int','float',str','bool','date','datetime','time'\n let new_value;\n if (class_name == 'int') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'string') {\n new_value.push(Number(item));\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'string') {\n new_value = Number(value);\n } else {\n new_value = value;\n }\n } else if (class_name == 'str') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'number') {\n new_value.push(item.toString());\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'number') {\n new_value = value.toString();\n } else {\n new_value = value;\n }\n } else if (class_name == 'date') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isValidDate(item) && typeof item === 'string') {\n new_value.push(moment(item, 'YYYY-MM-DD'));\n } else {\n new_value.push(item);\n }\n });\n } else if (isValidDate(value) && typeof value === 'string') {\n new_value = value.toString();\n } else {\n new_value = value;\n }\n } else if (class_name == 'datetime') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isValidDateTime(item) && typeof item === 'string') {\n new_value.push(moment(item, 'YYYY-MM-DD HH:mm:ss'));\n } else {\n new_value.push(item);\n }\n });\n } else if (isValidDateTime(value) && typeof value === 'string') {\n new_value = moment(value, 'YYYY-MM-DD HH:mm:ss');\n } else {\n new_value = value;\n }\n } else if (class_name == 'time') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isValidTime(item) && typeof item === 'string') {\n new_value.push(moment(item, 'HH:mm:ss'));\n } else {\n new_value.push(item);\n }\n });\n } else if (isValidTime(value) && typeof value === 'string') {\n new_value = moment(value, 'HH:mm:ss');\n } else {\n new_value = value;\n }\n } else if (class_name == 'float') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'string') {\n new_value.push(Number(item));\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'string') {\n new_value = Number(value);\n } else {\n new_value = value;\n }\n } else if (class_name == 'bool') {\n if (isNotEmpty(value) && value instanceof Array) {\n new_value = [];\n value.forEach(item => {\n if (isNotEmpty(item) && typeof item === 'string') {\n new_value.push(Boolean(item));\n } else {\n new_value.push(item);\n }\n });\n } else if (isNotEmpty(value) && typeof value === 'string') {\n new_value = Boolean(value);\n } else {\n new_value = value;\n }\n }\n return new_value;\n };\n const is_show_field = ref(props.is_show_field);\n const is_edit = ref(false);\n const handleToggleEdit = () => {\n emit('update:is_edit', is_edit.value);\n };\n const handleAdd = () => {\n emit('add');\n };\n const check_visible_field_fun = debounce(() => {\n const old_is_show_field = is_show_field.value;\n const condition_result_list: any = [];\n for (const i in props.field?.conditions) {\n const group_list = props.field?.conditions[i];\n const group_results = [];\n for (const j in group_list) {\n const item = group_list[j];\n const value = props.formModel[item.watchKey];\n let new_value;\n if (item.class_name) {\n new_value = convertValueByClassName(item.value, item.class_name);\n } else {\n new_value = item.value;\n }\n let first, second;\n if (new_value instanceof Array) {\n [first, second] = new_value;\n }\n if (item.operator == 'lt') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value < first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (moment(value).isBefore(moment(first))) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'lte') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value <= first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n moment(value).isBefore(moment(first)) ||\n moment(value).isSame(moment(first))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'gt') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value > first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (moment(value).isAfter(moment(first))) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'gte') {\n if (typeof first === 'number') {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value >= first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n moment(value).isAfter(moment(first)) ||\n moment(value).isSame(moment(first))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'between') {\n if (typeof first === 'number' && typeof second === 'number' && first && second) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value >= first && value <= second) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n (moment(value).isAfter(moment(first)) || moment(value).isSame(moment(first))) &&\n (moment(value).isBefore(moment(second)) || moment(value).isSame(moment(second)))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'notbetween') {\n if (typeof first === 'number' && typeof second === 'number' && first && second) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (value < first || value > second) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (isValidDate(first)) {\n if (isEmpty(value)) {\n group_results.push(false);\n } else if (\n moment(value).isBefore(moment(first)) ||\n moment(value).isAfter(moment(second))\n ) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'in') {\n if (value instanceof Array) {\n const f_list = new_value.filter(x => {\n return value.includes(x);\n });\n if (f_list.length > 0) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (typeof value == 'string' || typeof value == 'number' || isEmpty(value)) {\n // 数字 字符串 空\n const f = new_value.find(x => {\n return value == x;\n });\n if (f) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'eq') {\n if (value === first) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'contains') {\n if (value.toString().indexOf(first) > -1) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'startswith') {\n if (value.toString().startsWith(first)) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'notin') {\n if (value instanceof Array) {\n const f_list = new_value.filter(x => {\n return value.includes(x);\n });\n if (f_list.length > 0) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (typeof value == 'string' || typeof value == 'number' || isEmpty(value)) {\n // 数字 字符串 空\n const f = new_value.find(x => {\n return value == x;\n });\n if (!f) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n } else if (item.operator == 'notcontains') {\n if (value.toString().indexOf(first) == -1) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == '_eq') {\n //todo\n } else if (item.operator == 'json_list_not_blank_not_null') {\n // 非空\n if (value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'json_list_is_blank_is_null') {\n //为空\n if (!value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'not_eq') {\n //为空\n if (!value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n } else if (item.operator == 'not_blank_not_null') {\n // 非空\n if (value) {\n group_results.push(true);\n } else {\n group_results.push(false);\n }\n }\n }\n condition_result_list.push(group_results);\n }\n const con_list = [];\n condition_result_list.forEach(item => {\n const find_false_list = item.filter(x => x === false);\n if (find_false_list && find_false_list.length > 0) {\n con_list.push(false);\n } else {\n con_list.push(true);\n }\n });\n\n const find_true_list = con_list.filter(x => x === true);\n if (find_true_list && find_true_list.length > 0) {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n } else {\n is_show_field.value = false;\n emit('update:is_show_field', is_show_field.value);\n }\n if (is_show_field.value !== old_is_show_field) {\n if (is_show_field.value) {\n newModel.value[props.field.name] = null;\n emit('update_model', {\n add_key: props.field.name,\n value: null,\n });\n } else {\n delete newModel.value[props.field.name];\n emit('update_model', {\n remove_key: props.field.name,\n });\n }\n }\n }, 300);\n const model = ref<any>('');\n const labelModel = ref(props.label);\n watch(\n () => model.value,\n (newValue, oldValue) => {\n if (isEqual(newValue, oldValue)) {\n return;\n }\n emit('update:value', newValue);\n if (props.field.name2) {\n let label: any;\n if (props.field.type === 'radio') {\n const obj = props.options.find(\n (item: any) =>\n item[props.field.valueKey ? props.field.valueKey : 'value'] == newValue,\n );\n label = obj?.[props.field.labelKey ? props.field.labelKey : 'label'];\n } else if (props.field.type === 'checkbox' && newValue && newValue.length >= 0) {\n const list = [];\n props.options.forEach((item: any) => {\n newValue.forEach((item1: any) => {\n if (item1 == item?.[props.field.valueKey ? props.field.valueKey : 'value']) {\n list.push(item?.[props.field.labelKey ? props.field.labelKey : 'label']);\n }\n });\n });\n label = list.toString();\n } else if (props.field.type === 'select') {\n if (isArray(newValue)) {\n const list = [];\n props.options.forEach((item: any) => {\n newValue.forEach((item1: any) => {\n if (item1 == item?.[props.field.valueKey ? props.field.valueKey : 'value']) {\n list.push(item?.[props.field.labelKey ? props.field.labelKey : 'label']);\n }\n });\n });\n label = list.toString();\n } else {\n const obj = props.options.find(\n (item: any) =>\n item[props.field.valueKey ? props.field.valueKey : 'value'] == newValue,\n );\n label = obj?.[props.field.labelKey ? props.field.labelKey : 'label'];\n }\n }\n emit('update_name2', {\n key: props.field.name2,\n value: label,\n });\n }\n },\n { deep: true },\n );\n watch(\n () => props.value,\n newValue => {\n let value = newValue;\n if (props.field.type === 'rangepicker' && value) {\n value = rangeDate(value);\n } else if (props.field.type === 'input' && value) {\n value = value.toString().replace(/(^\\s*)|(\\s*$)/g, '');\n }\n if (is_show_field.value) {\n //当该表单项显示时,才做向上的数据绑定,避免把undefined向上传递,影响rules字段的有效过滤\n model.value = value;\n }\n },\n { immediate: true },\n );\n watch(\n () => labelModel.value,\n newValue => {\n emit('update:label', newValue);\n if ('name2' in props.field) {\n emit('update_name2', { key: props.field.name2, value: newValue.toString() });\n }\n },\n { deep: true },\n );\n watch(\n () => props.label,\n newValue => {\n labelModel.value = newValue;\n },\n );\n watch(\n () => props.formModel,\n newValue => {\n newModel.value = { ...newValue };\n },\n { deep: true },\n );\n watch(\n () => newModel.value,\n (newValue, oldValue) => {\n if (props.field?.conditions) {\n props.field?.conditions.forEach(condition_list => {\n condition_list.forEach(item => {\n if (oldValue[item.watchKey] != newValue[item.watchKey]) {\n check_visible_field_fun();\n }\n });\n });\n }\n },\n { deep: true },\n );\n watch(\n () => [props.conditionsVisibleNum, props.isUseConditions],\n () => {\n if (props.field?.conditions) {\n if (props.isUseConditions) {\n check_visible_field_fun();\n } else {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n }\n } else {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n }\n },\n );\n onMounted(() => {\n if (props.field?.conditions instanceof Array && props.field?.conditions.length > 0) {\n check_visible_field_fun();\n } else {\n is_show_field.value = true;\n emit('update:is_show_field', is_show_field.value);\n }\n if (!is_show_field.value) {\n // 表单项所见即所得,如果有隐藏的字段,那么model的key也要清除\n delete newModel.value[props.field.name];\n emit('update_model', {\n remove_key: props.field.name,\n });\n }\n });\n const { t } = useI18n();\n return {\n t,\n is_show_field,\n is_edit,\n handleToggleEdit,\n rangeDate,\n handleAdd,\n model,\n labelModel,\n };\n },\n components: {\n IconSelect,\n RichTextEditor,\n Upload,\n RemoteCascaderPro,\n RemoteTreeSelectPro,\n RemoteSelectPro,\n RemoteComplete,\n AttributeTable,\n AttributeNomTable,\n RemoteFilterSelect,\n RemoteModal,\n RemoteSelect,\n RemoteCascader,\n RemoteTreeSelect,\n RemoteTable,\n JsonStr,\n JsonObj,\n RemoteText,\n RemoteTablePro,\n PlusOutlined,\n MonthPicker,\n MonthRangePicker,\n CodeEditor,\n TinymceEditor,\n },\n});\n</script>\n\n<style scoped>\n.ant-calendar-picker {\n width: 100% !important;\n}\n</style>\n","import { useRequest } from 'liyu-pc-base/init-plugin';\nimport type { PageResult } from 'liyu-pc-base/api/typing';\n\nexport async function create_view_settings(params: Record<string, any>) {\n return useRequest().post('/view/create_view_settings', params);\n}\n\nexport async function update_view_settings(params: Record<string, any>) {\n return useRequest().post('/view/update_view_settings', params);\n}\n\nexport async function refresh_view_settings_cache(params: Record<string, any>) {\n return useRequest().post('/view/refresh_view_settings_cache', params);\n}\n\nexport async function set_view_settings_default(params: Record<string, any>) {\n return useRequest().post('/view/set_view_settings_default', params);\n}\n\nexport async function delete_view_settings(params: Record<string, any>) {\n return useRequest().post('/view/delete_view_settings', params);\n}\n\nexport async function recover_view_settings(params: Record<string, any>) {\n return useRequest().post('/view/recover_view_settings', params);\n}\n\nexport async function get_view_settings(params: Record<string, any>) {\n return useRequest().post('/view/get_view_settings', params);\n}\n\nexport async function create_user_view(params: Record<string, any>) {\n return useRequest().post('/view/create_user_view', params);\n}\n\nexport async function update_user_view(params: Record<string, any>) {\n return useRequest().post('/view/update_user_view', params);\n}\n\nexport async function delete_user_view(params: Record<string, any>) {\n return useRequest().post('/view/delete_user_view', params);\n}\n\nexport async function recover_user_view(params: Record<string, any>) {\n return useRequest().post('/view/recover_user_view', params);\n}\n\nexport async function get_user_view(params: Record<string, any>) {\n return useRequest().post('/view/get_user_view', params);\n}\n\nexport async function get_view_settings_for_user(params: Record<string, any>) {\n return useRequest().post('/view/get_view_settings_for_user', params);\n}\n\nexport async function change_view_settings_sort(params: Record<string, any>) {\n return useRequest().post('/view/change_view_settings_sort', params);\n}\n\nexport async function query_view_settings_list(params?: {\n [key: string]: any;\n}): Promise<PageResult> {\n return useRequest().post('/view/query_view_settings_list', params);\n}\n\nexport async function query_user_view_list(params?: { [key: string]: any }): Promise<PageResult> {\n return useRequest().post('/view/query_user_view_list', params);\n}\n","import type { Ref, ComponentPublicInstance } from 'vue';\n\nexport type BasicTarget<T = HTMLElement> = (() => T | null) | T | null | Ref<T | null | undefined>;\n\nexport type TargetElement = ComponentPublicInstance | HTMLElement | Element | Document | Window;\n\nexport function getTargetElement(\n target?: BasicTarget<TargetElement>,\n defaultElement?: TargetElement,\n): TargetElement | undefined | null {\n if (!target) {\n return defaultElement;\n }\n\n let targetElement: TargetElement | undefined | null;\n\n if (typeof target === 'function') {\n targetElement = target();\n } else if ('value' in target) {\n targetElement = (target.value as ComponentPublicInstance)?.$el;\n } else {\n targetElement = target;\n }\n\n return targetElement;\n}\n","import type { Ref, ComponentPublicInstance } from 'vue';\nimport { ref, onUnmounted } from 'vue';\nimport { getTargetElement } from 'liyu-pc-base/utils/dom';\nimport { default as screenfull } from 'screenfull';\n\nexport interface Options {\n onExitFull?: () => void;\n onFull?: () => void;\n}\n\nexport type UseFullscreen = readonly [\n Ref<ComponentPublicInstance | null>,\n Ref<boolean>,\n {\n setFull: () => void;\n exitFull: () => void;\n toggleFull: () => void;\n },\n];\n\nexport const useFullscreen = (options?: Options): UseFullscreen => {\n const { onExitFull, onFull } = options || {};\n const elRef = ref<ComponentPublicInstance | null>(null);\n const onExitFullRef = ref(onExitFull);\n const onFullRef = ref(onFull);\n\n const state = ref(false);\n\n const onChange = () => {\n if (screenfull.isEnabled) {\n const { isFullscreen } = screenfull;\n if (isFullscreen) {\n onFullRef.value && onFullRef.value();\n } else {\n screenfull.off('change', onChange);\n onExitFullRef.value && onExitFullRef.value();\n }\n state.value = isFullscreen;\n }\n };\n\n const setFull = () => {\n const el = getTargetElement(elRef);\n if (!el) {\n return;\n }\n if (screenfull.isEnabled) {\n screenfull.request(el as HTMLElement);\n screenfull.on('change', onChange);\n }\n };\n\n const exitFull = () => {\n if (!state.value) {\n return;\n }\n if (screenfull.isEnabled) {\n screenfull.exit();\n }\n };\n\n const toggleFull = () => {\n if (state.value) {\n exitFull();\n } else {\n setFull();\n }\n };\n\n onUnmounted(() => {\n if (screenfull.isEnabled) {\n screenfull.off('change', onChange);\n }\n });\n\n return [\n elRef,\n state,\n {\n setFull,\n exitFull,\n toggleFull,\n },\n ] as const;\n};\n\n// examples\n/*\nconst [elRef, screenState, { setFull, exitFull, toggleFull } ] = useFullscreen({\n onExitFull: () => {\n console.log('exitFull callback');\n },\n onFull: () => {\n console.log('inFull callback');\n }\n});\n\n<template>\n <div ref=\"elRef\">\n <p>123</p>\n <p>current fullscreen state: {{ screenState }}</p>\n <a-button @click=\"toggleFull\">Toggle</a-button>\n <a-button v-if=\"!screenState\" @click=\"setFull\">fullScreen</a-button>\n <a-button v-else @click=\"exitFull\">exitFullScreen</a-button>\n </div>\n</template>\n*/\n","import { isEmpty, isNotEmpty } from 'liyu-pc-base/utils/function';\nimport { getApiData } from 'liyu-pc-base/api/api-model';\nimport { ConditionOperatorMap } from 'liyu-pc-base/api/typing';\nimport { inject } from 'vue';\nimport type { TableProState, PivotValueExprToken } from './typing';\nimport { get_view_settings_for_user } from 'liyu-pc-base/api/view-model';\nimport { useUserStore, useEnv } from 'liyu-pc-base/init-plugin';\nexport function removeNestedDuplicates(nodes, seenIds = new Set(), parentPath = []) {\n const result = [];\n\n nodes.forEach((node, index) => {\n const currentPath = [...parentPath, index]; // 记录当前节点的路径\n const nodeId = node.name;\n\n if (!seenIds.has(nodeId)) {\n seenIds.add(nodeId);\n const newNode = { ...node };\n\n if (newNode.children && Array.isArray(newNode.children)) {\n newNode.children = removeNestedDuplicates(newNode.children, seenIds, currentPath);\n }\n\n result.push(newNode);\n } else {\n // 如果需要处理子对象(即使父对象被忽略了),则递归调用\n if (node.children && Array.isArray(node.children)) {\n // 注意:这里我们不将子对象添加到结果数组中,只是处理它们以避免丢失新的 id\n removeNestedDuplicates(node.children, seenIds, currentPath);\n }\n }\n });\n\n return result;\n}\nexport const operator_type_map = ConditionOperatorMap;\n\nexport const options_for_date_eq = [\n { value: '上月', label: '上月' },\n { value: '本月', label: '本月' },\n { value: '今日', label: '今日' },\n { value: '昨日', label: '昨日' },\n { value: '期初', label: '期初' },\n];\n\nconst operator_params_count = {\n between: 2,\n gt: 1,\n lt: 1,\n gte: 1,\n lte: 1,\n in: 0,\n eq: 1,\n _eq: 1,\n contains: 1,\n icontains: 1,\n startswith: 1,\n notbetween: 2,\n notin: 0,\n notcontains: 1,\n not_eq: 1,\n not_blank_not_null: 1,\n json_list_not_blank_not_null: 1,\n json_list_is_blank_is_null: 1,\n is_or_not_blank_or_null: 1,\n is_or_not_null: 1,\n};\n\nexport const getParamsCount = (operator, configItem) => {\n return operator_params_count[operator];\n};\n\nexport const filterConditions = conditions => {\n if (!conditions) {\n return [];\n }\n const res = conditions.filter(item => {\n return (\n item.value instanceof Array &&\n item.value.length > 0 &&\n !item.value.some(item => isEmpty(item))\n );\n });\n return res;\n};\n\nconst processColumnDataSource = (res_data: any, name_map: object) => {\n res_data.forEach((item: any) => {\n name_map[item.name] = item.verbose_name;\n if (item.children) {\n processColumnDataSource(item.children, name_map);\n }\n });\n};\n\nexport const getColumnNameList = async (modelType: string): Promise<any[]> => {\n if (!modelType) {\n return [];\n }\n const url = modelType.split('-')[0] + '/obtain_' + modelType.split('-')[1] + '_column_data';\n const res = await getApiData({}, url, false, { skip_error: true });\n const name_map = {};\n let res_data = removeNestedDuplicates(res || []);\n processColumnDataSource(res_data, name_map);\n if (!('id' in name_map)) {\n name_map['id'] = 'ID';\n }\n return Object.keys(name_map).map((item: any) => ({\n value: item,\n label: name_map[item],\n }));\n};\n\nexport const tableProStateSymbol = Symbol('tableProState');\n\nexport const useTableProState = (): TableProState => {\n const state = inject<TableProState>(tableProStateSymbol);\n return state;\n};\nexport const fetchViewsData = async (view_key, getQueryParamsFunc, count_url, config) => {\n const userStore = useUserStore();\n const userid = userStore.userid;\n const env = useEnv();\n const res = await get_view_settings_for_user({\n user_id: userid,\n view_project__flag: env.VITE_APP_VIEW_PROJECT_FLAG,\n view_menu__flag: view_key,\n sort_fields: ['sort', 'create_time'],\n is_cache: false,\n });\n\n const getGroupDataList = async viewData => {\n let selectedConditionGroup = null;\n if (viewData.groupConditionFieldName && config.condition_group_config_list?.length > 0) {\n selectedConditionGroup = config.condition_group_config_list.find(\n item => item.field_name === viewData.groupConditionFieldName,\n );\n } else {\n selectedConditionGroup = null;\n }\n if (!selectedConditionGroup) {\n return [];\n }\n if (!config.query_group_url) {\n return [];\n }\n\n const params = getQueryParamsFunc({ type: 'group', viewData });\n params['group_condition'] = {\n field_name: selectedConditionGroup.field_name,\n display_name: selectedConditionGroup.display_name,\n order_name: selectedConditionGroup.order_name || [],\n };\n const res = await getApiData(params, config.query_group_url || '', false, {\n api_log: `tablePro ${config.key} ${config.title}`,\n });\n return res || [];\n };\n\n let list: any = [];\n if (res.system_user_datas) list = list.concat(res.system_user_datas);\n if (res.system_public_datas) list = list.concat(res.system_public_datas);\n if (res.public_datas) list = list.concat(res.public_datas);\n if (res.user_datas) list = list.concat(res.user_datas);\n\n return Promise.all(\n list.map(async (item: any) => {\n let data = item.data;\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n } catch (e) {}\n }\n if (count_url) {\n const groupDataList = await getGroupDataList(data);\n const params = getQueryParamsFunc({ type: 'count', viewData: data, groupDataList });\n const num = await getApiData(params, count_url || '', false, {\n api_log: `tablePro ${config.key} ${config.title}`,\n });\n item.num = num;\n }\n return { ...item, data };\n }),\n );\n};\n\nexport const getCurrentGroupConditionValue = (conditionGroupDataList, groupConditionValue) => {\n if (!conditionGroupDataList) {\n return null;\n }\n const findItem = conditionGroupDataList.find(\n item => item.group_info.value === groupConditionValue,\n );\n return findItem?.group_info || conditionGroupDataList[0]?.group_info;\n};\n\n/**\n * 将 RemoteSelectPro 字段的 value 数组转换为可读 label 字符串\n * 参考 src/components/FormRender/RemoteSelectPro/RemoteSelectPro.vue 的查询逻辑\n * 内置全局缓存:按\"单条记录 + valueKey\"独立缓存\n * - 缓存 key 形如 `${postUrl}::${valueKey}::${id}`,valueKey 不同 → 不同缓存\n * - 命中后不再请求;支持跨调用复用\n * - 即便某次请求只查 ids=[1,2,3],下次请求 ids=[2,3,4] 也只会再请求 4\n * - 带 TTL 过期;其他参数(labelKey/labelKeyList 等)不在 key 中,\n * 若需要按字段组合切换建议主动调用 clearRemoteSelectProCache(postUrl)\n *\n * 函数只接收解构后的原始参数,不再依赖某个特定的 configItem 对象,便于在其它场景下复用:\n * - 入参全部独立命名,未提供的字段走默认值\n * - value 仅作为入参传入,内部统一处理 Array / 单值 / 空值场景\n * - 接口请求参数与 RemoteSelectPro 保持完全一致\n *\n * @param value value 数组(RemoteSelectPro 选中项的 id 集合)\n * @param params.RemoteSelectPro 字段配置(任一字段可省略,使用默认值)\n * - postUrl: 查询接口,必填\n * - valueKey: id 字段,默认 'id'\n * - labelKey: 单 label 字段,默认 'name'\n * - labelKeyList: 多 label 字段数组,默认 []\n * - labelKeyType: labelKeyList 类型,默认 0\n * - labelSpace: 多 label 拼接分隔符,默认 '-'\n * - queryType: 'simple' 走简单查询,其它走高级 conditions\n * @param params.cacheTTL 缓存有效期(毫秒),默认 5 分钟;传 0 表示不缓存\n * @returns label 用逗号拼接的字符串\n */\n\n// 单条记录级缓存:key = `${postUrl}::${valueKey}::${id}` -> { record, timestamp }\n// 使用 (globalThis as any) 挂载,保证整个应用单例\nconst _GLOBAL_CACHE_KEY = '__liyu_pc_base_remote_select_pro_cache__';\ntype RemoteSelectProCacheEntry = { record: any; timestamp: number };\ntype RemoteSelectProCacheStore = Record<string, RemoteSelectProCacheEntry>;\nconst remoteSelectProCache: RemoteSelectProCacheStore = ((globalThis as any)[_GLOBAL_CACHE_KEY] =\n (globalThis as any)[_GLOBAL_CACHE_KEY] || {}) as RemoteSelectProCacheStore;\n\n/**\n * 构造单条记录缓存 key:postUrl + valueKey + id\n */\nconst buildRecordCacheKey = (postUrl: string, valueKey: string, id: any): string => {\n const idStr = id === null || id === undefined ? '' : String(id);\n return `${postUrl}::${valueKey}::${idStr}`;\n};\n\n/**\n * 读取单条缓存;过期则删除并返回 null\n */\nconst getRecordCache = (\n postUrl: string,\n valueKey: string,\n id: any,\n cacheTTL: number,\n): any | null => {\n const key = buildRecordCacheKey(postUrl, valueKey, id);\n const entry = remoteSelectProCache[key];\n if (!entry) return null;\n if (Date.now() - entry.timestamp > cacheTTL) {\n delete remoteSelectProCache[key];\n return null;\n }\n return entry.record;\n};\n\n/**\n * 写入单条缓存\n */\nconst setRecordCache = (postUrl: string, valueKey: string, id: any, record: any): void => {\n const key = buildRecordCacheKey(postUrl, valueKey, id);\n remoteSelectProCache[key] = { record, timestamp: Date.now() };\n};\n\n/**\n * 主动清空缓存\n * @param postUrl 指定 postUrl 则只清空该接口的缓存,传空则清空全部\n */\nexport const clearRemoteSelectProCache = (postUrl?: string): void => {\n if (!postUrl) {\n for (const k of Object.keys(remoteSelectProCache)) {\n delete remoteSelectProCache[k];\n }\n return;\n }\n const prefix = `${postUrl}::`;\n for (const k of Object.keys(remoteSelectProCache)) {\n if (k.startsWith(prefix)) {\n delete remoteSelectProCache[k];\n }\n }\n};\n\nexport const transferRemoteSelectProValue = async (\n value: any,\n params: {\n postUrl?: string;\n valueKey?: string;\n labelKey?: string;\n labelKeyList?: string[];\n labelKeyType?: number;\n labelSpace?: string;\n queryType?: string;\n cacheTTL?: number;\n } = {},\n): Promise<string> => {\n const cacheTTL = params.cacheTTL ?? 5 * 60 * 1000; // 默认 5 分钟\n const postUrl = params.postUrl;\n // 提取 value 数组中的 id\n let ids: any[] = [];\n if (value instanceof Array) {\n ids = value.filter((item: any) => isNotEmpty(item));\n } else if (isNotEmpty(value)) {\n ids = [value];\n }\n if (ids.length === 0) {\n return '';\n }\n if (!postUrl) {\n return ids.join(',');\n }\n const valueKey = params.valueKey || 'id';\n const labelKey = params.labelKey || 'name';\n const labelKeyList: string[] = params.labelKeyList || [];\n const labelKeyType = params.labelKeyType ?? 0;\n const labelSpace = params.labelSpace || '-';\n const queryType = params.queryType;\n // 组装 column_fields 和 sort_fields,参考 RemoteSelectPro 中的 getColumnFields / get_sort_fields\n const columnFields: string[] = [valueKey];\n if (labelKeyList.length > 0 && labelKeyType !== 1) {\n columnFields.push(...labelKeyList);\n } else {\n columnFields.push(labelKey);\n }\n const uniqueFields = [...new Set(columnFields)];\n const sortFields = labelKeyList.length > 0 && labelKeyType !== 1 ? labelKeyList : [labelKey];\n\n /**\n * 查询一组 ids,返回 id -> record 映射\n * 内部会先从全局缓存批量取,未命中的再发起一次接口请求\n */\n const fetchRecordsByIds = async (targetIds: any[]): Promise<Record<string, any>> => {\n const idMap: Record<string, any> = {};\n if (targetIds.length === 0) return idMap;\n let missingIds: any[] = targetIds;\n if (cacheTTL > 0) {\n const cachedList: any[] = [];\n const missed: any[] = [];\n targetIds.forEach((id: any) => {\n const cached = getRecordCache(postUrl, valueKey, id, cacheTTL);\n if (cached !== null) {\n cachedList.push(cached);\n } else {\n missed.push(id);\n }\n });\n missingIds = missed;\n cachedList.forEach((rec: any) => {\n idMap[String(rec[valueKey])] = rec;\n });\n if (missingIds.length === 0) return idMap;\n }\n let res: any;\n if (queryType === 'simple') {\n res = await getApiData(\n {\n current: 1,\n pageSize: missingIds.length,\n is_pagination: true,\n request_source: 'strict',\n is_active: true,\n column_fields: uniqueFields,\n sort_fields: sortFields,\n [valueKey]: missingIds.join(','),\n },\n postUrl,\n false,\n { skip_error: true },\n );\n } else {\n res = await getApiData(\n {\n current: 1,\n pageSize: missingIds.length,\n is_pagination: true,\n request_source: 'strict',\n is_active: true,\n column_fields: uniqueFields,\n sort_fields: sortFields,\n conditions: [\n [\n {\n class_name: 'int',\n operator: 'in',\n value: missingIds,\n format: 'remote',\n field_name: valueKey,\n },\n ],\n ],\n },\n postUrl,\n false,\n { skip_error: true },\n );\n }\n const dataList: any[] = res?.data || [];\n dataList.forEach((rec: any) => {\n const recId = rec?.[valueKey];\n if (recId === null || recId === undefined) return;\n idMap[String(recId)] = rec;\n if (cacheTTL > 0) {\n setRecordCache(postUrl, valueKey, recId, rec);\n }\n });\n return idMap;\n };\n\n let idMap: Record<string, any> = {};\n try {\n idMap = await fetchRecordsByIds(ids);\n } catch (e) {\n // 查询失败时回退为原始 value\n return ids.join(',');\n }\n // 按原 ids 顺序匹配,组装 label(与 RemoteSelectPro 中 datasource 的逻辑保持一致)\n const labelList: any[] = [];\n ids.forEach((id: any) => {\n const foundItem: any = idMap[String(id)];\n if (foundItem) {\n if (labelKeyList.length > 0 && labelKeyType !== 1) {\n const labels: any[] = [];\n for (const key of labelKeyList) {\n labels.push(foundItem[key]);\n }\n labelList.push(labels.join(labelSpace));\n } else {\n labelList.push(foundItem[labelKey]);\n }\n } else {\n labelList.push(id);\n }\n });\n return labelList.join(',');\n};\n\nexport const combineLabel = (labelList: any[], linkType: string) => {\n if (!labelList || labelList.length === 0) return '';\n if (labelList.length === 1) return String(labelList[0]);\n\n switch (linkType) {\n case '()': {\n const [first, ...rest] = labelList;\n return [first, ...rest.map(item => `(${item})`)].join('');\n }\n case '-':\n return labelList.join('-');\n default:\n return labelList.join('_');\n }\n};\n\n/**\n * 值字段「高级表达式 / 过滤条件」合法性校验(统一入口,供编辑器与展示组件复用)。\n *\n * 采用与 Rust 后端一致的递归下降文法校验:\n * expr = or ( '?' expr ':' expr )?\n * or = and ( '||' and )*\n * and = unary ( '&&' unary )*\n * unary = '!' unary | comparison\n * comparison = additive ( REL additive )? // REL: > < >= <= == !=\n * additive = multiplicative ( ('+'|'-') multiplicative )*\n * multiplicative = power ( ('*'|'/') power )*\n * power = primary ( '^' power )* // 幂,右结合\n * primary = 'abs' primary | field | subexpr | number | string | bool | '(' expr ')'\n * // 'abs' 为一元绝对值前缀:绑定比所有二元运算符更紧,\n * // 故 abs(x-3) > 1 解析为 (abs(x-3)) > 1,而非 abs((x-3) > 1)\n * // 'subexpr' 为子表达式引用(value 为子表达式 id),语义上等价于一个操作数,\n * // 递归展开由后端(pivot_utils._tokens_to_rust)完成,前端校验仅视其为操作数。\n *\n * 支持字段引用、子表达式引用、数字、字符串、布尔字面量、以及 + - * / ^ > < >= <= == != && || ! abs ? : 运算符。\n * 返回 { valid, message },message 为非法时的中文说明(无需依赖 i18n 词条)。\n */\nexport interface ValueExprValidation {\n valid: boolean;\n message?: string;\n}\n\nexport function validateValueExpression(\n tokens?: PivotValueExprToken[] | null,\n): ValueExprValidation {\n const toks = tokens || [];\n if (toks.length === 0) return { valid: false, message: '表达式为空' };\n\n const OPERANDS = new Set(['field', 'number', 'string', 'bool', 'subexpr']);\n const REL = new Set(['>', '<', '>=', '<=', '==', '!=']);\n const isOperand = (t: PivotValueExprToken | null) => !!t && OPERANDS.has(t.type);\n const isOp = (t: PivotValueExprToken | null, v?: string) =>\n !!t && t.type === 'operator' && (v === undefined || t.value === v);\n\n let pos = 0;\n let error: string | null = null;\n\n const peek = () => (pos < toks.length ? toks[pos] : null);\n const err = (msg: string) => {\n if (!error) error = msg;\n throw 0;\n };\n\n const parsePrimary = () => {\n const t = peek();\n if (!t) return err('表达式不完整,缺少操作数');\n if (isOp(t, 'abs')) {\n // 一元绝对值前缀:作用于其后的原子(可嵌套 abs abs x)\n pos++;\n parsePrimary();\n return;\n }\n if (isOperand(t)) {\n pos++;\n return;\n }\n if (t.type === 'lparen') {\n pos++;\n parseExpr();\n const close = peek();\n if (!close || close.type !== 'rparen') return err('括号不匹配,缺少右括号');\n pos++;\n return;\n }\n return err('期望字段、数字、字符串、布尔或左括号');\n };\n\n const parsePower = () => {\n parsePrimary();\n while (isOp(peek(), '^')) {\n pos++;\n parsePower(); // 右结合\n }\n };\n const parseMultiplicative = () => {\n parsePower();\n while (isOp(peek(), '*') || isOp(peek(), '/')) {\n pos++;\n parsePower();\n }\n };\n const parseAdditive = () => {\n parseMultiplicative();\n while (isOp(peek(), '+') || isOp(peek(), '-')) {\n pos++;\n parseMultiplicative();\n }\n };\n const parseComparison = () => {\n parseAdditive();\n if (isOp(peek()) && REL.has(peek()!.value)) {\n pos++;\n parseAdditive();\n }\n };\n const parseUnary = () => {\n if (isOp(peek(), '!')) {\n pos++;\n parseUnary();\n return;\n }\n parseComparison();\n };\n const parseAnd = () => {\n parseUnary();\n while (isOp(peek(), '&&')) {\n pos++;\n parseUnary();\n }\n };\n const parseOr = () => {\n parseAnd();\n while (isOp(peek(), '||')) {\n pos++;\n parseAnd();\n }\n };\n const parseExpr = () => {\n parseOr();\n if (isOp(peek(), '?')) {\n pos++;\n parseExpr();\n if (!isOp(peek(), ':')) return err('三目运算符缺少 \":\"');\n pos++;\n parseExpr();\n }\n };\n\n try {\n parseExpr();\n } catch {\n return { valid: false, message: error || '表达式非法' };\n }\n if (pos !== toks.length) {\n return { valid: false, message: '表达式存在多余内容或缺少运算符' };\n }\n if (!toks.some(t => OPERANDS.has(t.type))) {\n return { valid: false, message: '表达式至少需要一个操作数' };\n }\n return { valid: true };\n}\n\n/** 便捷判断:表达式是否合法(useExpression=true 时调用)。 */\nexport function isValueExpressionValid(tokens?: PivotValueExprToken[] | null): boolean {\n return validateValueExpression(tokens).valid;\n}\n","<template>\n <span class=\"search-summary\">\n <a-tag\n v-for=\"item in dataSource\"\n :key=\"item.field_name\"\n color=\"green\"\n closable\n @close=\"handleClose(item.field_name)\"\n >\n {{ item.summary }}\n </a-tag>\n <a-button\n size=\"small\"\n type=\"text\"\n @click=\"handleRefresh\"\n v-if=\"showRefresh\"\n >\n <RedoOutlined />\n </a-button>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { isEmpty } from 'liyu-pc-base/utils/function';\nimport moment from 'moment';\nimport { onMounted, ref, watch } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport { SearchConditionItem, SearchConfig, SearchType } from './typing';\nimport { operator_type_map, transferRemoteSelectProValue } from './utils';\ninterface Props {\n searchConfig: SearchConfig[];\n searchConditions: SearchConditionItem[];\n eventBus: any;\n searchType: SearchType;\n showRefresh: boolean;\n}\nconst props = withDefaults(defineProps<Props>(), {\n searchType: 'advanced',\n showRefresh: true,\n});\nconst emit = defineEmits(['update:searchConditions']);\n\nconst { t } = useI18n();\nconst dataSource = ref<any[]>([]);\nconst transferValue = async (configItem, operator, value) => {\n const str_list: any = [];\n let transfer_value;\n if (operator === 'is_or_not_blank_or_null' || operator === 'is_or_not_null') {\n if (value === 'yes') {\n return t('is_or_not_blank_or_null.yes');\n } else {\n return t('is_or_not_blank_or_null.no');\n }\n } else if (operator === '_eq') {\n if (['上月', '本月', '今日', '昨日', '期初'].includes(value[0])) {\n return t(value[0]);\n }\n } else if (configItem.options instanceof Array) {\n if (value instanceof Array) {\n value.forEach(item => {\n configItem.options.forEach(i => {\n if (item == i.value) {\n str_list.push(i.label || i.name);\n }\n });\n });\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${str_list.join(',')}`;\n } else {\n return str_list.join(',');\n }\n } else {\n configItem.options.forEach(i => {\n if (value == i.value) {\n transfer_value = i.label;\n }\n });\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${transfer_value}`;\n } else {\n return transfer_value;\n }\n }\n } else if (configItem.type == 'monthpicker' && configItem.dateFormat) {\n if (value instanceof Array) {\n const str_list = [];\n value.forEach(i => {\n if (i) {\n str_list.push(moment(i).format(configItem.dateFormat));\n } else {\n str_list.push(i);\n }\n });\n transfer_value = str_list.join(',');\n } else {\n transfer_value = moment(value).format(configItem.dateFormat);\n }\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${transfer_value}`;\n } else {\n return transfer_value;\n }\n } else if (configItem.type === 'remoteselectpro') {\n const transfer_value = await transferRemoteSelectProValue(value, {\n postUrl: configItem.postUrl,\n valueKey: configItem.valueKey,\n labelKey: configItem.labelKey,\n labelKeyList: configItem.labelKeyList,\n labelKeyType: configItem.labelKeyType,\n labelSpace: configItem.labelSpace,\n queryType: configItem.queryType,\n });\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${transfer_value}`;\n } else {\n return transfer_value;\n }\n } else {\n if (value instanceof Array) {\n value = value.map(item => {\n if (item instanceof Boolean) {\n return item ? '是' : '否';\n } else {\n return item;\n }\n });\n value = value.join(',');\n } else {\n if (value instanceof Boolean) {\n value = value ? '是' : '否';\n }\n }\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${value}`;\n } else {\n return value;\n }\n }\n};\nconst getDataSource = async () => {\n const searchConditionsMap = {};\n for (const condition of props.searchConditions) {\n searchConditionsMap[condition.field_name] = {\n value: condition.value,\n operator: condition.operator,\n };\n }\n let tmpValue: any = props.searchConfig?.map(async configItem => {\n const data = searchConditionsMap[configItem.field_name] || {\n operator: configItem.default_operator || 'eq',\n value: null,\n };\n let summary = null;\n if (\n data.value instanceof Array &&\n data.value.length > 0 &&\n !data.value.some(item => isEmpty(item))\n ) {\n const transfer_value = await transferValue(configItem, data.operator, data.value);\n summary = `${configItem.label}: ${transfer_value}`;\n }\n return {\n field_name: configItem.field_name,\n summary,\n };\n });\n tmpValue = await Promise.all(tmpValue);\n dataSource.value = tmpValue.filter(item => item.summary);\n};\nconst handleClose = (fieldName: string) => {\n emit(\n 'update:searchConditions',\n props.searchConditions.filter(item => item.field_name !== fieldName),\n );\n props.eventBus.emit('refresh');\n};\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\nonMounted(() => {\n getDataSource();\n});\nwatch(() => props.searchConditions, getDataSource);\nwatch(() => props.searchConfig, getDataSource);\nwatch(() => props.searchType, getDataSource);\n</script>\n<style lang=\"less\" scoped>\n.search-summary {\n display: inline-block;\n padding: 4px 2px;\n}\n</style>\n","<template>\n <span class=\"search-summary\">\n <a-tag\n v-for=\"item in dataSource\"\n :key=\"item.field_name\"\n color=\"green\"\n closable\n @close=\"handleClose(item.field_name)\"\n >\n {{ item.summary }}\n </a-tag>\n <a-button\n size=\"small\"\n type=\"text\"\n @click=\"handleRefresh\"\n v-if=\"showRefresh\"\n >\n <RedoOutlined />\n </a-button>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { isEmpty } from 'liyu-pc-base/utils/function';\nimport moment from 'moment';\nimport { onMounted, ref, watch } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport { SearchConditionItem, SearchConfig, SearchType } from './typing';\nimport { operator_type_map, transferRemoteSelectProValue } from './utils';\ninterface Props {\n searchConfig: SearchConfig[];\n searchConditions: SearchConditionItem[];\n eventBus: any;\n searchType: SearchType;\n showRefresh: boolean;\n}\nconst props = withDefaults(defineProps<Props>(), {\n searchType: 'advanced',\n showRefresh: true,\n});\nconst emit = defineEmits(['update:searchConditions']);\n\nconst { t } = useI18n();\nconst dataSource = ref<any[]>([]);\nconst transferValue = async (configItem, operator, value) => {\n const str_list: any = [];\n let transfer_value;\n if (operator === 'is_or_not_blank_or_null' || operator === 'is_or_not_null') {\n if (value === 'yes') {\n return t('is_or_not_blank_or_null.yes');\n } else {\n return t('is_or_not_blank_or_null.no');\n }\n } else if (operator === '_eq') {\n if (['上月', '本月', '今日', '昨日', '期初'].includes(value[0])) {\n return t(value[0]);\n }\n } else if (configItem.options instanceof Array) {\n if (value instanceof Array) {\n value.forEach(item => {\n configItem.options.forEach(i => {\n if (item == i.value) {\n str_list.push(i.label || i.name);\n }\n });\n });\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${str_list.join(',')}`;\n } else {\n return str_list.join(',');\n }\n } else {\n configItem.options.forEach(i => {\n if (value == i.value) {\n transfer_value = i.label;\n }\n });\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${transfer_value}`;\n } else {\n return transfer_value;\n }\n }\n } else if (configItem.type == 'monthpicker' && configItem.dateFormat) {\n if (value instanceof Array) {\n const str_list = [];\n value.forEach(i => {\n if (i) {\n str_list.push(moment(i).format(configItem.dateFormat));\n } else {\n str_list.push(i);\n }\n });\n transfer_value = str_list.join(',');\n } else {\n transfer_value = moment(value).format(configItem.dateFormat);\n }\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${transfer_value}`;\n } else {\n return transfer_value;\n }\n } else if (configItem.type === 'remoteselectpro') {\n const transfer_value = await transferRemoteSelectProValue(value, {\n postUrl: configItem.postUrl,\n valueKey: configItem.valueKey,\n labelKey: configItem.labelKey,\n labelKeyList: configItem.labelKeyList,\n labelKeyType: configItem.labelKeyType,\n labelSpace: configItem.labelSpace,\n queryType: configItem.queryType,\n });\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${transfer_value}`;\n } else {\n return transfer_value;\n }\n } else {\n if (value instanceof Array) {\n value = value.map(item => {\n if (item instanceof Boolean) {\n return item ? '是' : '否';\n } else {\n return item;\n }\n });\n value = value.join(',');\n } else {\n if (value instanceof Boolean) {\n value = value ? '是' : '否';\n }\n }\n if (props.searchType === 'advanced') {\n return `${operator_type_map[operator]} ${value}`;\n } else {\n return value;\n }\n }\n};\nconst getDataSource = async () => {\n const searchConditionsMap = {};\n for (const condition of props.searchConditions) {\n searchConditionsMap[condition.field_name] = {\n value: condition.value,\n operator: condition.operator,\n };\n }\n let tmpValue: any = props.searchConfig?.map(async configItem => {\n const data = searchConditionsMap[configItem.field_name] || {\n operator: configItem.default_operator || 'eq',\n value: null,\n };\n let summary = null;\n if (\n data.value instanceof Array &&\n data.value.length > 0 &&\n !data.value.some(item => isEmpty(item))\n ) {\n const transfer_value = await transferValue(configItem, data.operator, data.value);\n summary = `${configItem.label}: ${transfer_value}`;\n }\n return {\n field_name: configItem.field_name,\n summary,\n };\n });\n tmpValue = await Promise.all(tmpValue);\n dataSource.value = tmpValue.filter(item => item.summary);\n};\nconst handleClose = (fieldName: string) => {\n emit(\n 'update:searchConditions',\n props.searchConditions.filter(item => item.field_name !== fieldName),\n );\n props.eventBus.emit('refresh');\n};\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\nonMounted(() => {\n getDataSource();\n});\nwatch(() => props.searchConditions, getDataSource);\nwatch(() => props.searchConfig, getDataSource);\nwatch(() => props.searchType, getDataSource);\n</script>\n<style lang=\"less\" scoped>\n.search-summary {\n display: inline-block;\n padding: 4px 2px;\n}\n</style>\n","<template>\n <a-modal\n :title=\"$t('设置搜索字段')\"\n v-model:open=\"visible\"\n width=\"1000px\"\n @ok=\"handleOk\"\n @cancel=\"handleCancel\"\n >\n <div class=\"search-config-container\">\n <div class=\"table-wrapper left-table\">\n <div class=\"table-title\">\n {{ $t('所有字段') }}\n <a-input\n v-model:value=\"leftSearch\"\n size=\"small\"\n :placeholder=\"$t('搜索标签/字段名')\"\n style=\"margin-top: 8px\"\n allow-clear\n />\n </div>\n <s-table\n :columns=\"leftColumns\"\n :data-source=\"filteredLeftData\"\n :pagination=\"false\"\n :row-selection=\"leftRowSelection\"\n v-model:selectedRowKeys=\"leftSelectedRowKeys\"\n row-key=\"field_name\"\n size=\"small\"\n :row-class-name=\"record => (record.is_selected ? 'row-selected' : '')\"\n expand-row-by-click\n >\n <template #bodyCell=\"{ column, record }\">\n <template v-if=\"column.dataIndex === 'label'\">\n <span\n :style=\"{\n color: record.is_selected ? '#ccc' : 'inherit',\n }\"\n >\n {{ record.label }}\n </span>\n </template>\n </template>\n </s-table>\n </div>\n\n <div class=\"middle-btns\">\n <a-button\n type=\"primary\"\n shape=\"circle\"\n :disabled=\"leftSelectedRowKeys.length === 0\"\n @click=\"moveToRight\"\n >\n <template #icon><RightOutlined /></template>\n </a-button>\n <a-button\n type=\"primary\"\n shape=\"circle\"\n :disabled=\"rightSelectedRowKeys.length === 0\"\n @click=\"moveToLeft\"\n >\n <template #icon><LeftOutlined /></template>\n </a-button>\n </div>\n\n <div class=\"table-wrapper right-table\">\n <div class=\"table-title\">\n {{ $t('已选字段') }}\n <a-input\n v-model:value=\"rightSearch\"\n size=\"small\"\n :placeholder=\"$t('搜索标签/字段名')\"\n style=\"margin-top: 8px\"\n allow-clear\n />\n </div>\n <s-table\n :columns=\"rightColumns\"\n :data-source=\"filteredRightData\"\n :pagination=\"false\"\n :row-selection=\"rightRowSelection\"\n v-model:selectedRowKeys=\"rightSelectedRowKeys\"\n row-key=\"field_name\"\n size=\"small\"\n @row-drag-end=\"onRowDragEnd\"\n />\n </div>\n </div>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch, PropType } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport { RightOutlined, LeftOutlined } from '@ant-design/icons-vue';\nimport { SearchConfig, ViewSearchItem } from '../typing';\nimport { cloneDeep } from 'lodash';\n\nconst props = defineProps({\n open: {\n type: Boolean,\n default: false,\n },\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n default: () => [],\n },\n viewSearchConfig: {\n type: Object as PropType<Record<string, ViewSearchItem>>,\n default: () => ({}),\n },\n});\n\nconst emit = defineEmits(['update:open', 'change']);\n\nconst { t } = useI18n();\n\nconst visible = ref(false);\nconst leftSearch = ref('');\nconst rightSearch = ref('');\nconst leftSelectedRowKeys = ref<string[]>([]);\nconst rightSelectedRowKeys = ref<string[]>([]);\nconst rightData = ref<SearchConfig[]>([]);\n\nconst leftColumns = [\n {\n title: t('字段名称'),\n dataIndex: 'label',\n key: 'label',\n width: 250,\n ellipsis: true,\n },\n];\n\nconst rightColumns = computed(() => {\n const columns: any = [\n {\n title: t('字段名称'),\n dataIndex: 'label',\n key: 'label',\n rowDrag: true,\n width: 150,\n ellipsis: true,\n },\n ];\n if (filteredRightData.value.length > 0) {\n if (filteredRightData.value.some(item => item.group_name)) {\n columns.push({\n title: t('分组'),\n dataIndex: 'group_name',\n key: 'group_name',\n width: 150,\n ellipsis: true,\n });\n }\n }\n return columns;\n});\n\nwatch(\n () => props.open,\n val => {\n visible.value = val;\n if (val) {\n leftSearch.value = '';\n rightSearch.value = '';\n initData();\n }\n },\n);\n\nwatch(visible, val => {\n emit('update:open', val);\n});\n\nconst initData = () => {\n // 根据 viewSearchConfig 和 searchConfig 初始化右侧数据\n const configMap = new Map();\n props.searchConfig.forEach(item => {\n configMap.set(item.field_name, item);\n });\n\n const selectedFields: SearchConfig[] = [];\n\n if (props.viewSearchConfig && Object.keys(props.viewSearchConfig).length > 0) {\n // 先处理 viewSearchConfig 中已有的配置\n Object.keys(props.viewSearchConfig).forEach(fieldName => {\n const viewItem = props.viewSearchConfig[fieldName];\n if (viewItem.checked && configMap.has(fieldName)) {\n selectedFields.push({\n ...configMap.get(fieldName),\n order: viewItem.order ?? 0,\n });\n }\n });\n // 排序\n selectedFields.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n } else {\n // 当 viewSearchConfig 为空时,所有 item.checked !== false 的字段都是选中字段\n props.searchConfig.forEach((item, index) => {\n if (item.checked !== false) {\n selectedFields.push({\n ...item,\n order: index + 1,\n });\n }\n });\n }\n\n rightData.value = cloneDeep(selectedFields);\n\n leftSelectedRowKeys.value = [];\n rightSelectedRowKeys.value = [];\n};\n\nconst leftData = computed(() => {\n const selectedKeys = new Set(rightData.value.map(item => item.field_name));\n const groups: Record<string, any[]> = {};\n const noGroup: any[] = [];\n\n props.searchConfig.forEach(item => {\n const itemData = {\n ...item,\n is_selected: selectedKeys.has(item.field_name),\n };\n if (item.group_name) {\n if (!groups[item.group_name]) {\n groups[item.group_name] = [];\n }\n groups[item.group_name].push(itemData);\n } else {\n noGroup.push(itemData);\n }\n });\n\n const result: any[] = [];\n Object.keys(groups).forEach(groupName => {\n result.push({\n field_name: `group_${groupName}`,\n label: groupName,\n is_group: true,\n children: groups[groupName],\n });\n });\n\n return [...result, ...noGroup];\n});\n\nconst filteredLeftData = computed(() => {\n if (!leftSearch.value) return leftData.value;\n const search = leftSearch.value.toLowerCase();\n\n const filterNode = (nodes: any[]) => {\n return nodes.reduce((acc: any[], node: any) => {\n const matchSelf =\n node.label?.toLowerCase().includes(search) ||\n node.field_name?.toLowerCase().includes(search);\n\n if (node.children) {\n const filteredChildren = filterNode(node.children);\n if (filteredChildren.length > 0) {\n acc.push({ ...node, children: filteredChildren });\n } else if (matchSelf) {\n acc.push({ ...node, children: [] });\n }\n } else if (matchSelf) {\n acc.push(node);\n }\n return acc;\n }, []);\n };\n\n return filterNode(leftData.value);\n});\n\nconst filteredRightData = computed(() => {\n if (!rightSearch.value) return rightData.value;\n const search = rightSearch.value.toLowerCase();\n return rightData.value.filter(\n item =>\n item.label?.toLowerCase().includes(search) || item.field_name?.toLowerCase().includes(search),\n );\n});\n\nconst leftRowSelection = {\n onChange: (selectedRowKeys: string[]) => {\n leftSelectedRowKeys.value = selectedRowKeys;\n },\n getCheckboxProps: (record: any) => ({\n disabled: record.is_group || record.is_selected,\n }),\n};\n\nconst rightRowSelection = {\n onChange: (selectedRowKeys: string[]) => {\n rightSelectedRowKeys.value = selectedRowKeys;\n },\n};\n\nconst moveToRight = () => {\n const selectedKeys = new Set(leftSelectedRowKeys.value);\n const toMove: SearchConfig[] = [];\n\n // 遍历所有 searchConfig,按原始顺序找到匹配项\n props.searchConfig.forEach(item => {\n if (selectedKeys.has(item.field_name)) {\n toMove.push(item);\n }\n });\n\n if (toMove.length > 0) {\n rightData.value = [...rightData.value, ...cloneDeep(toMove)];\n }\n leftSelectedRowKeys.value = [];\n};\n\nconst moveToLeft = () => {\n rightData.value = rightData.value.filter(\n item => !rightSelectedRowKeys.value.includes(item.field_name),\n );\n rightSelectedRowKeys.value = [];\n};\n\nconst onRowDragEnd = ({ oldIndex, newIndex }) => {\n const target = rightData.value.splice(oldIndex, 1)[0];\n rightData.value.splice(newIndex, 0, target);\n};\n\nconst handleOk = () => {\n const result: Record<string, ViewSearchItem> = {};\n\n // 处理所有字段,默认为未选中\n props.searchConfig.forEach(item => {\n result[item.field_name] = {\n checked: false,\n order: 0,\n };\n });\n\n // 更新已选中的字段\n rightData.value.forEach((item, index) => {\n result[item.field_name] = {\n checked: true,\n order: index + 1,\n };\n });\n\n emit('change', result);\n visible.value = false;\n};\n\nconst handleCancel = () => {\n visible.value = false;\n};\n</script>\n\n<style lang=\"less\" scoped>\n.search-config-container {\n display: flex;\n align-items: center;\n gap: 16px;\n height: 70vh;\n .table-wrapper {\n width: 450px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow: auto;\n\n .table-title {\n padding: 8px 12px;\n background: #fafafa;\n border-bottom: 1px solid #f0f0f0;\n font-weight: bold;\n }\n }\n\n .middle-btns {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n}\n</style>\n","<template>\n <a-modal\n :title=\"$t('设置搜索字段')\"\n v-model:open=\"visible\"\n width=\"1000px\"\n @ok=\"handleOk\"\n @cancel=\"handleCancel\"\n >\n <div class=\"search-config-container\">\n <div class=\"table-wrapper left-table\">\n <div class=\"table-title\">\n {{ $t('所有字段') }}\n <a-input\n v-model:value=\"leftSearch\"\n size=\"small\"\n :placeholder=\"$t('搜索标签/字段名')\"\n style=\"margin-top: 8px\"\n allow-clear\n />\n </div>\n <s-table\n :columns=\"leftColumns\"\n :data-source=\"filteredLeftData\"\n :pagination=\"false\"\n :row-selection=\"leftRowSelection\"\n v-model:selectedRowKeys=\"leftSelectedRowKeys\"\n row-key=\"field_name\"\n size=\"small\"\n :row-class-name=\"record => (record.is_selected ? 'row-selected' : '')\"\n expand-row-by-click\n >\n <template #bodyCell=\"{ column, record }\">\n <template v-if=\"column.dataIndex === 'label'\">\n <span\n :style=\"{\n color: record.is_selected ? '#ccc' : 'inherit',\n }\"\n >\n {{ record.label }}\n </span>\n </template>\n </template>\n </s-table>\n </div>\n\n <div class=\"middle-btns\">\n <a-button\n type=\"primary\"\n shape=\"circle\"\n :disabled=\"leftSelectedRowKeys.length === 0\"\n @click=\"moveToRight\"\n >\n <template #icon><RightOutlined /></template>\n </a-button>\n <a-button\n type=\"primary\"\n shape=\"circle\"\n :disabled=\"rightSelectedRowKeys.length === 0\"\n @click=\"moveToLeft\"\n >\n <template #icon><LeftOutlined /></template>\n </a-button>\n </div>\n\n <div class=\"table-wrapper right-table\">\n <div class=\"table-title\">\n {{ $t('已选字段') }}\n <a-input\n v-model:value=\"rightSearch\"\n size=\"small\"\n :placeholder=\"$t('搜索标签/字段名')\"\n style=\"margin-top: 8px\"\n allow-clear\n />\n </div>\n <s-table\n :columns=\"rightColumns\"\n :data-source=\"filteredRightData\"\n :pagination=\"false\"\n :row-selection=\"rightRowSelection\"\n v-model:selectedRowKeys=\"rightSelectedRowKeys\"\n row-key=\"field_name\"\n size=\"small\"\n @row-drag-end=\"onRowDragEnd\"\n />\n </div>\n </div>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch, PropType } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport { RightOutlined, LeftOutlined } from '@ant-design/icons-vue';\nimport { SearchConfig, ViewSearchItem } from '../typing';\nimport { cloneDeep } from 'lodash';\n\nconst props = defineProps({\n open: {\n type: Boolean,\n default: false,\n },\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n default: () => [],\n },\n viewSearchConfig: {\n type: Object as PropType<Record<string, ViewSearchItem>>,\n default: () => ({}),\n },\n});\n\nconst emit = defineEmits(['update:open', 'change']);\n\nconst { t } = useI18n();\n\nconst visible = ref(false);\nconst leftSearch = ref('');\nconst rightSearch = ref('');\nconst leftSelectedRowKeys = ref<string[]>([]);\nconst rightSelectedRowKeys = ref<string[]>([]);\nconst rightData = ref<SearchConfig[]>([]);\n\nconst leftColumns = [\n {\n title: t('字段名称'),\n dataIndex: 'label',\n key: 'label',\n width: 250,\n ellipsis: true,\n },\n];\n\nconst rightColumns = computed(() => {\n const columns: any = [\n {\n title: t('字段名称'),\n dataIndex: 'label',\n key: 'label',\n rowDrag: true,\n width: 150,\n ellipsis: true,\n },\n ];\n if (filteredRightData.value.length > 0) {\n if (filteredRightData.value.some(item => item.group_name)) {\n columns.push({\n title: t('分组'),\n dataIndex: 'group_name',\n key: 'group_name',\n width: 150,\n ellipsis: true,\n });\n }\n }\n return columns;\n});\n\nwatch(\n () => props.open,\n val => {\n visible.value = val;\n if (val) {\n leftSearch.value = '';\n rightSearch.value = '';\n initData();\n }\n },\n);\n\nwatch(visible, val => {\n emit('update:open', val);\n});\n\nconst initData = () => {\n // 根据 viewSearchConfig 和 searchConfig 初始化右侧数据\n const configMap = new Map();\n props.searchConfig.forEach(item => {\n configMap.set(item.field_name, item);\n });\n\n const selectedFields: SearchConfig[] = [];\n\n if (props.viewSearchConfig && Object.keys(props.viewSearchConfig).length > 0) {\n // 先处理 viewSearchConfig 中已有的配置\n Object.keys(props.viewSearchConfig).forEach(fieldName => {\n const viewItem = props.viewSearchConfig[fieldName];\n if (viewItem.checked && configMap.has(fieldName)) {\n selectedFields.push({\n ...configMap.get(fieldName),\n order: viewItem.order ?? 0,\n });\n }\n });\n // 排序\n selectedFields.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n } else {\n // 当 viewSearchConfig 为空时,所有 item.checked !== false 的字段都是选中字段\n props.searchConfig.forEach((item, index) => {\n if (item.checked !== false) {\n selectedFields.push({\n ...item,\n order: index + 1,\n });\n }\n });\n }\n\n rightData.value = cloneDeep(selectedFields);\n\n leftSelectedRowKeys.value = [];\n rightSelectedRowKeys.value = [];\n};\n\nconst leftData = computed(() => {\n const selectedKeys = new Set(rightData.value.map(item => item.field_name));\n const groups: Record<string, any[]> = {};\n const noGroup: any[] = [];\n\n props.searchConfig.forEach(item => {\n const itemData = {\n ...item,\n is_selected: selectedKeys.has(item.field_name),\n };\n if (item.group_name) {\n if (!groups[item.group_name]) {\n groups[item.group_name] = [];\n }\n groups[item.group_name].push(itemData);\n } else {\n noGroup.push(itemData);\n }\n });\n\n const result: any[] = [];\n Object.keys(groups).forEach(groupName => {\n result.push({\n field_name: `group_${groupName}`,\n label: groupName,\n is_group: true,\n children: groups[groupName],\n });\n });\n\n return [...result, ...noGroup];\n});\n\nconst filteredLeftData = computed(() => {\n if (!leftSearch.value) return leftData.value;\n const search = leftSearch.value.toLowerCase();\n\n const filterNode = (nodes: any[]) => {\n return nodes.reduce((acc: any[], node: any) => {\n const matchSelf =\n node.label?.toLowerCase().includes(search) ||\n node.field_name?.toLowerCase().includes(search);\n\n if (node.children) {\n const filteredChildren = filterNode(node.children);\n if (filteredChildren.length > 0) {\n acc.push({ ...node, children: filteredChildren });\n } else if (matchSelf) {\n acc.push({ ...node, children: [] });\n }\n } else if (matchSelf) {\n acc.push(node);\n }\n return acc;\n }, []);\n };\n\n return filterNode(leftData.value);\n});\n\nconst filteredRightData = computed(() => {\n if (!rightSearch.value) return rightData.value;\n const search = rightSearch.value.toLowerCase();\n return rightData.value.filter(\n item =>\n item.label?.toLowerCase().includes(search) || item.field_name?.toLowerCase().includes(search),\n );\n});\n\nconst leftRowSelection = {\n onChange: (selectedRowKeys: string[]) => {\n leftSelectedRowKeys.value = selectedRowKeys;\n },\n getCheckboxProps: (record: any) => ({\n disabled: record.is_group || record.is_selected,\n }),\n};\n\nconst rightRowSelection = {\n onChange: (selectedRowKeys: string[]) => {\n rightSelectedRowKeys.value = selectedRowKeys;\n },\n};\n\nconst moveToRight = () => {\n const selectedKeys = new Set(leftSelectedRowKeys.value);\n const toMove: SearchConfig[] = [];\n\n // 遍历所有 searchConfig,按原始顺序找到匹配项\n props.searchConfig.forEach(item => {\n if (selectedKeys.has(item.field_name)) {\n toMove.push(item);\n }\n });\n\n if (toMove.length > 0) {\n rightData.value = [...rightData.value, ...cloneDeep(toMove)];\n }\n leftSelectedRowKeys.value = [];\n};\n\nconst moveToLeft = () => {\n rightData.value = rightData.value.filter(\n item => !rightSelectedRowKeys.value.includes(item.field_name),\n );\n rightSelectedRowKeys.value = [];\n};\n\nconst onRowDragEnd = ({ oldIndex, newIndex }) => {\n const target = rightData.value.splice(oldIndex, 1)[0];\n rightData.value.splice(newIndex, 0, target);\n};\n\nconst handleOk = () => {\n const result: Record<string, ViewSearchItem> = {};\n\n // 处理所有字段,默认为未选中\n props.searchConfig.forEach(item => {\n result[item.field_name] = {\n checked: false,\n order: 0,\n };\n });\n\n // 更新已选中的字段\n rightData.value.forEach((item, index) => {\n result[item.field_name] = {\n checked: true,\n order: index + 1,\n };\n });\n\n emit('change', result);\n visible.value = false;\n};\n\nconst handleCancel = () => {\n visible.value = false;\n};\n</script>\n\n<style lang=\"less\" scoped>\n.search-config-container {\n display: flex;\n align-items: center;\n gap: 16px;\n height: 70vh;\n .table-wrapper {\n width: 450px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow: auto;\n\n .table-title {\n padding: 8px 12px;\n background: #fafafa;\n border-bottom: 1px solid #f0f0f0;\n font-weight: bold;\n }\n }\n\n .middle-btns {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n}\n</style>\n","<template>\n <a-form\n :layout=\"searchColumnCount > 0 ? 'horizontal' : 'inline'\"\n :class=\"{ 'search-form-grid-inner': searchColumnCount > 0 }\"\n >\n <template\n v-for=\"condition in dataSource\"\n :key=\"condition.field_name\"\n >\n <div\n v-if=\"condition.is_group\"\n class=\"search-field-group-title\"\n >\n {{ condition.label }}\n </div>\n <a-form-item v-else>\n <template #label>\n <span\n :title=\"condition.label\"\n class=\"search-item-label-text\"\n >\n {{ condition.label }}\n </span>\n </template>\n <a-input-group\n compact\n size=\"small\"\n >\n <a-select\n v-if=\"condition.operator_list.length > 0 && searchType === 'advanced'\"\n style=\"width: 100px\"\n :dropdownMatchSelectWidth=\"false\"\n :value=\"condition.operator\"\n @change=\"onChange(condition.field_name, 'operator', $event)\"\n size=\"small\"\n >\n <a-select-option\n v-for=\"item in condition.operator_list\"\n :value=\"item\"\n :key=\"item\"\n >\n <a-tooltip :title=\"operator_type_map[item]\">\n {{ operator_type_map[item] }}\n </a-tooltip>\n </a-select-option>\n </a-select>\n <template\n v-if=\"['is_or_not_blank_or_null', 'is_or_not_null'].includes(condition.operator)\"\n >\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: $t('请选择是否为空'),\n }\"\n :formModel=\"formModel\"\n :options=\"is_or_not_blank_or_null_options\"\n :validateInfos=\"validateInfos\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n :field_width=\"condition.field_width\"\n />\n </template>\n <template\n v-else-if=\"condition.operator === 'between' || condition.operator === 'notbetween'\"\n >\n <template v-if=\"condition.type === 'datepicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{ ...condition, type: 'rangepicker', format: condition.dateFormat }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width * 2\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{\n ...condition,\n type: 'monthrangepicker',\n }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width * 2\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"condition.value?.[0]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event, 0)\"\n />\n <span class=\"range\">~</span>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"condition.value?.[1]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event, 1)\"\n />\n </template>\n </template>\n <template v-else-if=\"condition.operator === '_eq'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: '',\n }\"\n :formModel=\"formModel\"\n :options=\"options_for_date_eq\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'datepicker' || condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{ ...condition, format: condition.dateFormat }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{ ...condition, name: condition?.field_name }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n </a-input-group>\n </a-form-item>\n </template>\n <div\n class=\"btn-content\"\n v-if=\"showQueryBtn && !autoRefreshOnSearchChange\"\n >\n <a-button\n size=\"small\"\n type=\"primary\"\n @click=\"handleRefresh\"\n >\n 查询\n </a-button>\n </div>\n </a-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { cloneDeep, debounce } from 'lodash';\nimport { computed, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport { SearchConditionItem, SearchConfig, SearchType, ViewSearchItem } from './typing';\nimport SmallTableSerarchField from 'liyu-pc-base/components/FormRender/SmallTableSerarchField.vue';\nimport { getParamsCount, operator_type_map, options_for_date_eq } from './utils';\nimport { isEmpty, isNotEmpty } from 'liyu-pc-base/utils/function';\n\ndefineOptions({\n name: 'SearchForm',\n});\n\nconst props = defineProps({\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchConditions: {\n type: Array as PropType<SearchConditionItem[]>,\n required: true,\n },\n viewSearchConfig: {\n type: Object as PropType<Record<string, ViewSearchItem>>,\n default: () => ({}),\n },\n searchType: {\n type: String as () => SearchType,\n default: 'advanced',\n },\n searchColumnCount: {\n type: Number,\n default: 0,\n },\n autoRefreshOnSearchChange: {\n type: Boolean,\n default: false,\n },\n eventBus: {\n type: Object as any,\n required: false,\n },\n showQueryBtn: {\n type: Boolean,\n default: false,\n },\n disabledShowSearchGroupName: {\n type: Boolean,\n default: false, // 是否显示搜索分组名称,默认不显示\n },\n});\n\nconst emit = defineEmits(['update:searchConditions', 'change']);\n\nconst { t } = useI18n();\nconst formModel = ref<Record<string, any>>({});\nconst validateInfos = ref({});\n\nconst is_or_not_blank_or_null_options = ref([\n { value: 'yes', label: t('is_or_not_blank_or_null.yes') },\n { value: 'no', label: t('is_or_not_blank_or_null.no') },\n]);\n\nconst dataSource = computed(() => {\n const searchConditionsMap: Record<string, any> = {};\n for (const condition of props.searchConditions) {\n searchConditionsMap[condition.field_name] = {\n value: condition.value,\n operator: condition.operator,\n };\n }\n\n const flatData = props.searchConfig\n ?.filter(item => {\n if (props.viewSearchConfig[item.field_name]) {\n return props.viewSearchConfig[item.field_name].checked !== false;\n } else {\n return item.checked !== false;\n }\n })\n ?.map(configItem => {\n const data = searchConditionsMap[configItem.field_name] || {\n operator: configItem.default_operator || 'eq',\n value: undefined,\n };\n let formValue = data.value;\n if (isNotEmpty(data.value)) {\n const paramsCount = getParamsCount(data.operator, configItem);\n if (paramsCount === 1 && formValue instanceof Array) {\n formValue = formValue[0];\n }\n }\n formModel.value[configItem.field_name] = formValue;\n return {\n ...configItem,\n value: data.value,\n operator: data.operator,\n model: 'default',\n _order: props.viewSearchConfig[configItem.field_name]?.order || 0,\n field_width: configItem.field_width || 165,\n };\n })\n .sort((a, b) => a._order - b._order);\n\n const groups: Record<string, any[]> = {};\n const orderList: any[] = [];\n\n flatData.forEach(item => {\n if (item.group_name) {\n if (!groups[item.group_name]) {\n groups[item.group_name] = [];\n orderList.push({\n field_name: `group_${item.group_name}`,\n label: item.group_name,\n is_group: true,\n _group_key: item.group_name,\n });\n }\n groups[item.group_name].push(item);\n } else {\n orderList.push(item);\n }\n });\n\n const finalResult: any[] = [];\n orderList.forEach(item => {\n if (item.is_group) {\n if (!props.disabledShowSearchGroupName) {\n finalResult.push(item);\n }\n finalResult.push(...groups[item._group_key]);\n } else {\n finalResult.push(item);\n }\n });\n\n return finalResult;\n});\n\nconst getValue = (configItemAndValue: any) => {\n const value_count = getParamsCount(configItemAndValue.operator, configItemAndValue);\n let value = configItemAndValue.value;\n if (value_count == 1) {\n if (value instanceof Array) {\n return value[0] || undefined;\n } else {\n return value || undefined;\n }\n } else if (value_count == 2) {\n if (value instanceof Array) {\n const [first, seconds] = value;\n return [first, seconds];\n } else if (value) {\n const first = value;\n return [first, null];\n } else {\n return [null, null];\n }\n } else {\n return value;\n }\n};\n\nconst delayRefresh = debounce(() => {\n props.eventBus.emit('refresh');\n}, 500);\n\nconst onChange = (\n field_name: string,\n type: 'value' | 'operator',\n newValue: any,\n array_index: number | undefined = undefined,\n) => {\n const conditions = cloneDeep(props.searchConditions);\n const configItem = props.searchConfig.find(item => item.field_name === field_name);\n let value: any;\n let operator: string;\n let condition: any = conditions.find(item => item.field_name === field_name);\n if (type === 'value') {\n if (condition) {\n operator = condition.operator;\n } else {\n operator = configItem?.default_operator || 'eq';\n }\n if (array_index !== undefined) {\n const value_count = getParamsCount(newValue, configItem);\n if (condition) {\n value = condition.value;\n if (isEmpty(value) || !(value instanceof Array)) {\n value = [];\n for (let i = 0; i < value_count; i++) {\n value.push(null);\n }\n }\n value[array_index] = newValue;\n } else {\n value = [];\n for (let i = 0; i < value_count; i++) {\n value.push(null);\n }\n value[array_index] = newValue;\n }\n } else {\n if (isNotEmpty(newValue) && !(newValue instanceof Array)) {\n newValue = [newValue];\n }\n value = newValue;\n }\n } else if (type === 'operator') {\n operator = newValue;\n const value_count = getParamsCount(newValue, configItem);\n if (value_count) {\n value = [];\n for (let i = 0; i < value_count; i++) {\n value.push(null);\n }\n } else {\n value = undefined;\n }\n }\n if (condition) {\n condition.operator = operator!;\n condition.value = value;\n } else {\n let format = 'input';\n let class_name = 'str';\n if (configItem) {\n format = configItem.format || 'input';\n class_name = configItem.class_name || 'str';\n }\n condition = {\n field_name: field_name,\n value,\n operator: operator!,\n format,\n class_name,\n };\n conditions.push(condition);\n }\n emit('update:searchConditions', conditions);\n emit('change', conditions);\n if (props.autoRefreshOnSearchChange) {\n delayRefresh();\n }\n};\n\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\n</script>\n\n<style lang=\"less\" scoped>\n.search-field-group-title {\n font-weight: bold;\n padding: 8px 4px 4px 4px;\n margin-bottom: 4px;\n border-bottom: 1px solid #f0f0f0;\n color: var(--primary-color, #1890ff);\n display: flex;\n align-items: center;\n width: 100%;\n flex-basis: 100%;\n &:not(:first-child) {\n margin-top: 8px;\n }\n}\n.search-form-grid-inner {\n display: grid;\n grid-template-columns: repeat(var(--column-count), max-content 1fr);\n gap: 2px 8px;\n align-items: center;\n .search-field-group-title {\n grid-column: span calc(var(--column-count) * 2);\n &:not(:first-child) {\n margin-top: 8px;\n }\n }\n :deep(.ant-form-item) {\n display: contents;\n margin-right: 0;\n margin-bottom: 2px;\n .ant-form-item-row,\n .ant-form-item-label,\n .ant-form-item-control,\n .ant-form-item-control-input,\n .ant-form-item-control-input-content {\n display: contents;\n }\n }\n :deep(.ant-form-item-label) {\n justify-self: end;\n display: flex !important;\n align-items: center;\n max-width: 150px;\n .search-item-label-text {\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n padding-right: 8px;\n text-align: right;\n }\n }\n :deep(.ant-input-group) {\n display: flex !important;\n width: 100%;\n }\n .btn-content {\n grid-column: span calc(var(--column-count) * 2);\n }\n}\n.btn-content {\n padding: 4px;\n margin-left: auto;\n}\n.range {\n padding: 0 4px;\n}\n:deep(.ant-select-selection-placeholder) {\n overflow: hidden;\n width: 100%;\n}\n</style>\n","<template>\n <a-form\n :layout=\"searchColumnCount > 0 ? 'horizontal' : 'inline'\"\n :class=\"{ 'search-form-grid-inner': searchColumnCount > 0 }\"\n >\n <template\n v-for=\"condition in dataSource\"\n :key=\"condition.field_name\"\n >\n <div\n v-if=\"condition.is_group\"\n class=\"search-field-group-title\"\n >\n {{ condition.label }}\n </div>\n <a-form-item v-else>\n <template #label>\n <span\n :title=\"condition.label\"\n class=\"search-item-label-text\"\n >\n {{ condition.label }}\n </span>\n </template>\n <a-input-group\n compact\n size=\"small\"\n >\n <a-select\n v-if=\"condition.operator_list.length > 0 && searchType === 'advanced'\"\n style=\"width: 100px\"\n :dropdownMatchSelectWidth=\"false\"\n :value=\"condition.operator\"\n @change=\"onChange(condition.field_name, 'operator', $event)\"\n size=\"small\"\n >\n <a-select-option\n v-for=\"item in condition.operator_list\"\n :value=\"item\"\n :key=\"item\"\n >\n <a-tooltip :title=\"operator_type_map[item]\">\n {{ operator_type_map[item] }}\n </a-tooltip>\n </a-select-option>\n </a-select>\n <template\n v-if=\"['is_or_not_blank_or_null', 'is_or_not_null'].includes(condition.operator)\"\n >\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: $t('请选择是否为空'),\n }\"\n :formModel=\"formModel\"\n :options=\"is_or_not_blank_or_null_options\"\n :validateInfos=\"validateInfos\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n :field_width=\"condition.field_width\"\n />\n </template>\n <template\n v-else-if=\"condition.operator === 'between' || condition.operator === 'notbetween'\"\n >\n <template v-if=\"condition.type === 'datepicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{ ...condition, type: 'rangepicker', format: condition.dateFormat }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width * 2\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{\n ...condition,\n type: 'monthrangepicker',\n }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width * 2\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"condition.value?.[0]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event, 0)\"\n />\n <span class=\"range\">~</span>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"condition.value?.[1]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event, 1)\"\n />\n </template>\n </template>\n <template v-else-if=\"condition.operator === '_eq'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: '',\n }\"\n :formModel=\"formModel\"\n :options=\"options_for_date_eq\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'datepicker' || condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{ ...condition, format: condition.dateFormat }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"condition.field_name\"\n :value=\"getValue(condition)\"\n :field=\"{ ...condition, name: condition?.field_name }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width\"\n @update:value=\"onChange(condition.field_name, 'value', $event)\"\n />\n </template>\n </a-input-group>\n </a-form-item>\n </template>\n <div\n class=\"btn-content\"\n v-if=\"showQueryBtn && !autoRefreshOnSearchChange\"\n >\n <a-button\n size=\"small\"\n type=\"primary\"\n @click=\"handleRefresh\"\n >\n 查询\n </a-button>\n </div>\n </a-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { cloneDeep, debounce } from 'lodash';\nimport { computed, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport { SearchConditionItem, SearchConfig, SearchType, ViewSearchItem } from './typing';\nimport SmallTableSerarchField from 'liyu-pc-base/components/FormRender/SmallTableSerarchField.vue';\nimport { getParamsCount, operator_type_map, options_for_date_eq } from './utils';\nimport { isEmpty, isNotEmpty } from 'liyu-pc-base/utils/function';\n\ndefineOptions({\n name: 'SearchForm',\n});\n\nconst props = defineProps({\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchConditions: {\n type: Array as PropType<SearchConditionItem[]>,\n required: true,\n },\n viewSearchConfig: {\n type: Object as PropType<Record<string, ViewSearchItem>>,\n default: () => ({}),\n },\n searchType: {\n type: String as () => SearchType,\n default: 'advanced',\n },\n searchColumnCount: {\n type: Number,\n default: 0,\n },\n autoRefreshOnSearchChange: {\n type: Boolean,\n default: false,\n },\n eventBus: {\n type: Object as any,\n required: false,\n },\n showQueryBtn: {\n type: Boolean,\n default: false,\n },\n disabledShowSearchGroupName: {\n type: Boolean,\n default: false, // 是否显示搜索分组名称,默认不显示\n },\n});\n\nconst emit = defineEmits(['update:searchConditions', 'change']);\n\nconst { t } = useI18n();\nconst formModel = ref<Record<string, any>>({});\nconst validateInfos = ref({});\n\nconst is_or_not_blank_or_null_options = ref([\n { value: 'yes', label: t('is_or_not_blank_or_null.yes') },\n { value: 'no', label: t('is_or_not_blank_or_null.no') },\n]);\n\nconst dataSource = computed(() => {\n const searchConditionsMap: Record<string, any> = {};\n for (const condition of props.searchConditions) {\n searchConditionsMap[condition.field_name] = {\n value: condition.value,\n operator: condition.operator,\n };\n }\n\n const flatData = props.searchConfig\n ?.filter(item => {\n if (props.viewSearchConfig[item.field_name]) {\n return props.viewSearchConfig[item.field_name].checked !== false;\n } else {\n return item.checked !== false;\n }\n })\n ?.map(configItem => {\n const data = searchConditionsMap[configItem.field_name] || {\n operator: configItem.default_operator || 'eq',\n value: undefined,\n };\n let formValue = data.value;\n if (isNotEmpty(data.value)) {\n const paramsCount = getParamsCount(data.operator, configItem);\n if (paramsCount === 1 && formValue instanceof Array) {\n formValue = formValue[0];\n }\n }\n formModel.value[configItem.field_name] = formValue;\n return {\n ...configItem,\n value: data.value,\n operator: data.operator,\n model: 'default',\n _order: props.viewSearchConfig[configItem.field_name]?.order || 0,\n field_width: configItem.field_width || 165,\n };\n })\n .sort((a, b) => a._order - b._order);\n\n const groups: Record<string, any[]> = {};\n const orderList: any[] = [];\n\n flatData.forEach(item => {\n if (item.group_name) {\n if (!groups[item.group_name]) {\n groups[item.group_name] = [];\n orderList.push({\n field_name: `group_${item.group_name}`,\n label: item.group_name,\n is_group: true,\n _group_key: item.group_name,\n });\n }\n groups[item.group_name].push(item);\n } else {\n orderList.push(item);\n }\n });\n\n const finalResult: any[] = [];\n orderList.forEach(item => {\n if (item.is_group) {\n if (!props.disabledShowSearchGroupName) {\n finalResult.push(item);\n }\n finalResult.push(...groups[item._group_key]);\n } else {\n finalResult.push(item);\n }\n });\n\n return finalResult;\n});\n\nconst getValue = (configItemAndValue: any) => {\n const value_count = getParamsCount(configItemAndValue.operator, configItemAndValue);\n let value = configItemAndValue.value;\n if (value_count == 1) {\n if (value instanceof Array) {\n return value[0] || undefined;\n } else {\n return value || undefined;\n }\n } else if (value_count == 2) {\n if (value instanceof Array) {\n const [first, seconds] = value;\n return [first, seconds];\n } else if (value) {\n const first = value;\n return [first, null];\n } else {\n return [null, null];\n }\n } else {\n return value;\n }\n};\n\nconst delayRefresh = debounce(() => {\n props.eventBus.emit('refresh');\n}, 500);\n\nconst onChange = (\n field_name: string,\n type: 'value' | 'operator',\n newValue: any,\n array_index: number | undefined = undefined,\n) => {\n const conditions = cloneDeep(props.searchConditions);\n const configItem = props.searchConfig.find(item => item.field_name === field_name);\n let value: any;\n let operator: string;\n let condition: any = conditions.find(item => item.field_name === field_name);\n if (type === 'value') {\n if (condition) {\n operator = condition.operator;\n } else {\n operator = configItem?.default_operator || 'eq';\n }\n if (array_index !== undefined) {\n const value_count = getParamsCount(newValue, configItem);\n if (condition) {\n value = condition.value;\n if (isEmpty(value) || !(value instanceof Array)) {\n value = [];\n for (let i = 0; i < value_count; i++) {\n value.push(null);\n }\n }\n value[array_index] = newValue;\n } else {\n value = [];\n for (let i = 0; i < value_count; i++) {\n value.push(null);\n }\n value[array_index] = newValue;\n }\n } else {\n if (isNotEmpty(newValue) && !(newValue instanceof Array)) {\n newValue = [newValue];\n }\n value = newValue;\n }\n } else if (type === 'operator') {\n operator = newValue;\n const value_count = getParamsCount(newValue, configItem);\n if (value_count) {\n value = [];\n for (let i = 0; i < value_count; i++) {\n value.push(null);\n }\n } else {\n value = undefined;\n }\n }\n if (condition) {\n condition.operator = operator!;\n condition.value = value;\n } else {\n let format = 'input';\n let class_name = 'str';\n if (configItem) {\n format = configItem.format || 'input';\n class_name = configItem.class_name || 'str';\n }\n condition = {\n field_name: field_name,\n value,\n operator: operator!,\n format,\n class_name,\n };\n conditions.push(condition);\n }\n emit('update:searchConditions', conditions);\n emit('change', conditions);\n if (props.autoRefreshOnSearchChange) {\n delayRefresh();\n }\n};\n\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\n</script>\n\n<style lang=\"less\" scoped>\n.search-field-group-title {\n font-weight: bold;\n padding: 8px 4px 4px 4px;\n margin-bottom: 4px;\n border-bottom: 1px solid #f0f0f0;\n color: var(--primary-color, #1890ff);\n display: flex;\n align-items: center;\n width: 100%;\n flex-basis: 100%;\n &:not(:first-child) {\n margin-top: 8px;\n }\n}\n.search-form-grid-inner {\n display: grid;\n grid-template-columns: repeat(var(--column-count), max-content 1fr);\n gap: 2px 8px;\n align-items: center;\n .search-field-group-title {\n grid-column: span calc(var(--column-count) * 2);\n &:not(:first-child) {\n margin-top: 8px;\n }\n }\n :deep(.ant-form-item) {\n display: contents;\n margin-right: 0;\n margin-bottom: 2px;\n .ant-form-item-row,\n .ant-form-item-label,\n .ant-form-item-control,\n .ant-form-item-control-input,\n .ant-form-item-control-input-content {\n display: contents;\n }\n }\n :deep(.ant-form-item-label) {\n justify-self: end;\n display: flex !important;\n align-items: center;\n max-width: 150px;\n .search-item-label-text {\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n padding-right: 8px;\n text-align: right;\n }\n }\n :deep(.ant-input-group) {\n display: flex !important;\n width: 100%;\n }\n .btn-content {\n grid-column: span calc(var(--column-count) * 2);\n }\n}\n.btn-content {\n padding: 4px;\n margin-left: auto;\n}\n.range {\n padding: 0 4px;\n}\n:deep(.ant-select-selection-placeholder) {\n overflow: hidden;\n width: 100%;\n}\n</style>\n","<template>\n <span\n class=\"search-switch\"\n v-if=\"search_display_type !== 'show_no_hide'\"\n >\n <a-button\n v-if=\"!isShowForm\"\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = true\"\n >\n <span class=\"expand-btn\">{{ $t('展开') }}</span>\n </a-button>\n <div v-else>\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = false\"\n >\n {{ $t('隐藏') }}\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"searchConfigVisible = true\"\n v-if=\"config?.sys_btn?.search_field_config !== false\"\n >\n <a-tooltip :title=\"$t('设置搜索字段')\">\n <SettingOutlined />\n </a-tooltip>\n </a-button>\n </div>\n </span>\n <search-config-modal\n v-model:open=\"searchConfigVisible\"\n :search-config=\"searchConfig\"\n :view-search-config=\"viewSearchConfig\"\n @change=\"handleSearchConfigChange\"\n />\n <search-summary\n v-if=\"!isShowForm\"\n :search-conditions=\"searchConditions\"\n :search-config=\"searchConfig\"\n :event-bus=\"eventBus\"\n :searchType=\"searchType\"\n @update:searchConditions=\"handleUpdateSearchConditions\"\n />\n <div\n v-if=\"isShowForm\"\n class=\"search-form-wraper\"\n :style=\"{\n '--column-count': searchColumnCount || 0,\n }\"\n >\n <SearchForm\n :searchConfig=\"searchConfig\"\n :searchConditions=\"searchConditions\"\n :viewSearchConfig=\"viewSearchConfig\"\n :searchType=\"searchType\"\n :searchColumnCount=\"searchColumnCount\"\n :autoRefreshOnSearchChange=\"autoRefreshOnSearchChange\"\n :eventBus=\"eventBus\"\n :showQueryBtn=\"!searchColumnCount\"\n :disabledShowSearchGroupName=\"disabledShowSearchGroupName\"\n @update:searchConditions=\"handleUpdateSearchConditions\"\n @change=\"handleSearchChange\"\n />\n </div>\n <div\n class=\"bottom-content\"\n v-if=\"searchColumnCount && isShowForm && !autoRefreshOnSearchChange\"\n >\n <a-button\n size=\"small\"\n type=\"primary\"\n @click=\"handleRefresh\"\n >\n 查询\n </a-button>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport searchSummary from './searchSummary.vue';\nimport SearchConfigModal from './tableConfig/searchConfig.vue';\nimport SearchForm from './SearchForm.vue';\nimport {\n SearchConditionItem,\n SearchConfig,\n SearchDisplayType,\n SearchType,\n ViewSearchItem,\n} from './typing';\nimport { useTableProState } from './utils';\n\ndefineOptions({\n name: 'Search',\n});\n\nconst props = defineProps({\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchConditions: {\n type: Array as PropType<SearchConditionItem[]>,\n required: true,\n },\n eventBus: {\n type: Object as any,\n required: false,\n },\n searchType: {\n type: String as () => SearchType,\n default: 'advanced',\n },\n searchDisplayType: {\n type: String as () => SearchDisplayType,\n default: 'hide',\n },\n searchColumnCount: {\n type: Number,\n default: 0,\n },\n viewSearchConfig: {\n type: Object as PropType<Record<string, ViewSearchItem>>,\n default: () => ({}),\n },\n autoRefreshOnSearchChange: {\n type: Boolean,\n default: false,\n },\n disabledShowSearchGroupName: {\n type: Boolean,\n default: false, // 是否显示搜索分组名称,默认不显示\n },\n});\n\nconst emit = defineEmits(['update:searchConditions', 'viewSearchConfigChange', 'change']);\n\nconst { t } = useI18n();\nconst isShowForm = ref(false);\nconst searchConfigVisible = ref(false);\nconst { config } = useTableProState();\n\nconst handleUpdateSearchConditions = (newConditions: Condition[]) => {\n emit('update:searchConditions', newConditions);\n};\n\nconst handleSearchChange = () => {\n emit('change');\n};\n\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\n\nconst handleSearchConfigChange = (data: any) => {\n emit('viewSearchConfigChange', data);\n};\n\nconst search_display_type = computed(() => {\n return props.searchDisplayType || 'hide';\n});\n\nonMounted(() => {\n if (['show', 'show_no_hide'].includes(search_display_type.value)) {\n isShowForm.value = true;\n }\n});\n</script>\n\n<style lang=\"less\" scoped>\n.search-form-wraper {\n padding-left: 2px;\n padding-right: 2px;\n max-height: 300px;\n overflow-y: auto;\n}\n.search-switch {\n display: inline-block;\n line-height: 32px;\n height: 32px;\n}\n.bottom-content {\n display: flex;\n justify-content: flex-end;\n padding: 8px 26px 8px 8px;\n}\n</style>\n","<template>\n <span\n class=\"search-switch\"\n v-if=\"search_display_type !== 'show_no_hide'\"\n >\n <a-button\n v-if=\"!isShowForm\"\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = true\"\n >\n <span class=\"expand-btn\">{{ $t('展开') }}</span>\n </a-button>\n <div v-else>\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = false\"\n >\n {{ $t('隐藏') }}\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"searchConfigVisible = true\"\n v-if=\"config?.sys_btn?.search_field_config !== false\"\n >\n <a-tooltip :title=\"$t('设置搜索字段')\">\n <SettingOutlined />\n </a-tooltip>\n </a-button>\n </div>\n </span>\n <search-config-modal\n v-model:open=\"searchConfigVisible\"\n :search-config=\"searchConfig\"\n :view-search-config=\"viewSearchConfig\"\n @change=\"handleSearchConfigChange\"\n />\n <search-summary\n v-if=\"!isShowForm\"\n :search-conditions=\"searchConditions\"\n :search-config=\"searchConfig\"\n :event-bus=\"eventBus\"\n :searchType=\"searchType\"\n @update:searchConditions=\"handleUpdateSearchConditions\"\n />\n <div\n v-if=\"isShowForm\"\n class=\"search-form-wraper\"\n :style=\"{\n '--column-count': searchColumnCount || 0,\n }\"\n >\n <SearchForm\n :searchConfig=\"searchConfig\"\n :searchConditions=\"searchConditions\"\n :viewSearchConfig=\"viewSearchConfig\"\n :searchType=\"searchType\"\n :searchColumnCount=\"searchColumnCount\"\n :autoRefreshOnSearchChange=\"autoRefreshOnSearchChange\"\n :eventBus=\"eventBus\"\n :showQueryBtn=\"!searchColumnCount\"\n :disabledShowSearchGroupName=\"disabledShowSearchGroupName\"\n @update:searchConditions=\"handleUpdateSearchConditions\"\n @change=\"handleSearchChange\"\n />\n </div>\n <div\n class=\"bottom-content\"\n v-if=\"searchColumnCount && isShowForm && !autoRefreshOnSearchChange\"\n >\n <a-button\n size=\"small\"\n type=\"primary\"\n @click=\"handleRefresh\"\n >\n 查询\n </a-button>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport searchSummary from './searchSummary.vue';\nimport SearchConfigModal from './tableConfig/searchConfig.vue';\nimport SearchForm from './SearchForm.vue';\nimport {\n SearchConditionItem,\n SearchConfig,\n SearchDisplayType,\n SearchType,\n ViewSearchItem,\n} from './typing';\nimport { useTableProState } from './utils';\n\ndefineOptions({\n name: 'Search',\n});\n\nconst props = defineProps({\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchConditions: {\n type: Array as PropType<SearchConditionItem[]>,\n required: true,\n },\n eventBus: {\n type: Object as any,\n required: false,\n },\n searchType: {\n type: String as () => SearchType,\n default: 'advanced',\n },\n searchDisplayType: {\n type: String as () => SearchDisplayType,\n default: 'hide',\n },\n searchColumnCount: {\n type: Number,\n default: 0,\n },\n viewSearchConfig: {\n type: Object as PropType<Record<string, ViewSearchItem>>,\n default: () => ({}),\n },\n autoRefreshOnSearchChange: {\n type: Boolean,\n default: false,\n },\n disabledShowSearchGroupName: {\n type: Boolean,\n default: false, // 是否显示搜索分组名称,默认不显示\n },\n});\n\nconst emit = defineEmits(['update:searchConditions', 'viewSearchConfigChange', 'change']);\n\nconst { t } = useI18n();\nconst isShowForm = ref(false);\nconst searchConfigVisible = ref(false);\nconst { config } = useTableProState();\n\nconst handleUpdateSearchConditions = (newConditions: Condition[]) => {\n emit('update:searchConditions', newConditions);\n};\n\nconst handleSearchChange = () => {\n emit('change');\n};\n\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\n\nconst handleSearchConfigChange = (data: any) => {\n emit('viewSearchConfigChange', data);\n};\n\nconst search_display_type = computed(() => {\n return props.searchDisplayType || 'hide';\n});\n\nonMounted(() => {\n if (['show', 'show_no_hide'].includes(search_display_type.value)) {\n isShowForm.value = true;\n }\n});\n</script>\n\n<style lang=\"less\" scoped>\n.search-form-wraper {\n padding-left: 2px;\n padding-right: 2px;\n max-height: 300px;\n overflow-y: auto;\n}\n.search-switch {\n display: inline-block;\n line-height: 32px;\n height: 32px;\n}\n.bottom-content {\n display: flex;\n justify-content: flex-end;\n padding: 8px 26px 8px 8px;\n}\n</style>\n","<template>\n <span\n class=\"search-switch\"\n v-if=\"search_display_type !== 'show_no_hide'\"\n >\n <a-button\n v-if=\"!isShowForm\"\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = true\"\n >\n <span class=\"expand-btn\">{{ $t('展开') }}</span>\n </a-button>\n <a-button\n v-else\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = false\"\n >\n {{ $t('隐藏') }}\n </a-button>\n </span>\n <!-- 多分组模式:搜索摘要 -->\n <template v-if=\"!isShowForm\">\n <template\n v-for=\"(group, index) in searchGroups\"\n :key=\"group.id\"\n >\n <span\n class=\"group-or-separator\"\n v-if=\"index > 0\"\n >\n OR\n </span>\n <search-summary\n :search-conditions=\"group.conditions\"\n :search-config=\"searchConfig\"\n :event-bus=\"eventBus\"\n :searchType=\"searchType\"\n :showRefresh=\"index === searchGroups.length - 1\"\n @update:searchConditions=\"conditions => handleGroupSummaryUpdate(group.id, conditions)\"\n />\n </template>\n </template>\n <!-- 多分组模式:搜索表单 -->\n <div\n v-if=\"isShowForm\"\n class=\"search-groups-container\"\n >\n <div\n v-for=\"(group, index) in searchGroups\"\n :key=\"group.id\"\n class=\"search-group-item\"\n >\n <div class=\"search-group-header\">\n <span class=\"search-group-header-title\">{{ $t('分组') + ' ' + (index + 1) }}</span>\n <div class=\"search-group-actions\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"searchConfigVisibleMap[group.id] = true\"\n v-if=\"config?.sys_btn?.search_field_config !== false\"\n >\n <a-tooltip :title=\"$t('设置搜索字段')\">\n <SettingOutlined />\n </a-tooltip>\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n danger\n @click=\"handleRemoveGroup(group.id)\"\n v-if=\"searchGroups.length > 1\"\n >\n <a-tooltip :title=\"$t('删除分组')\">\n <DeleteOutlined />\n </a-tooltip>\n </a-button>\n </div>\n </div>\n <div\n class=\"search-form-wraper\"\n :style=\"{\n '--column-count': searchColumnCount || 0,\n }\"\n >\n <SearchForm\n :searchConfig=\"searchConfig\"\n :searchConditions=\"group.conditions\"\n :viewSearchConfig=\"group.viewSearchConfig\"\n :searchType=\"searchType\"\n :searchColumnCount=\"searchColumnCount\"\n :autoRefreshOnSearchChange=\"autoRefreshOnSearchChange\"\n :eventBus=\"eventBus\"\n :disabledShowSearchGroupName=\"disabledShowSearchGroupName\"\n @update:searchConditions=\"conditions => handleGroupConditionsUpdate(group.id, conditions)\"\n @change=\"handleGroupSearchChange\"\n />\n </div>\n <search-config-modal\n v-model:open=\"searchConfigVisibleMap[group.id]\"\n :search-config=\"searchConfig\"\n :view-search-config=\"group.viewSearchConfig\"\n @change=\"config => handleGroupViewSearchConfigChange(group.id, config)\"\n />\n </div>\n <a-button\n class=\"add-group-btn\"\n size=\"small\"\n type=\"dashed\"\n @click=\"handleAddGroup\"\n >\n <PlusOutlined />\n {{ $t('添加分组') }}\n </a-button>\n </div>\n <div\n class=\"bottom-content\"\n v-if=\"isShowForm && !autoRefreshOnSearchChange\"\n >\n <a-button\n size=\"small\"\n type=\"primary\"\n @click=\"handleRefresh\"\n >\n 查询\n </a-button>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport searchSummary from './searchSummary.vue';\nimport SearchConfigModal from './tableConfig/searchConfig.vue';\nimport SearchForm from './SearchForm.vue';\nimport { SearchConfig, SearchGroupItem, SearchType, ViewSearchItem } from './typing';\nimport { useTableProState } from './utils';\nimport { genID } from 'liyu-pc-base/utils/function.js';\n\ndefineOptions({\n name: 'SearchFormGroup',\n});\n\nconst props = defineProps({\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchGroups: {\n type: Array as PropType<SearchGroupItem[]>,\n required: true,\n },\n eventBus: {\n type: Object as any,\n required: false,\n },\n searchType: {\n type: String as () => SearchType,\n default: 'advanced',\n },\n searchDisplayType: {\n type: String,\n default: 'hide',\n },\n searchColumnCount: {\n type: Number,\n default: 0,\n },\n autoRefreshOnSearchChange: {\n type: Boolean,\n default: false,\n },\n disabledShowSearchGroupName: {\n type: Boolean,\n default: false, // 是否显示搜索分组名称,默认不显示\n },\n});\n\nconst emit = defineEmits(['update:searchGroups', 'change']);\n\nconst { t } = useI18n();\nconst isShowForm = ref(false);\nconst searchConfigVisibleMap = ref<Record<string, boolean>>({});\nconst { config } = useTableProState();\n\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\n\nconst handleAddGroup = () => {\n const newGroups = [\n ...props.searchGroups,\n {\n id: genID(),\n conditions: [],\n viewSearchConfig: {},\n },\n ];\n emit('update:searchGroups', newGroups);\n emit('change');\n};\n\nconst handleRemoveGroup = (groupId: string) => {\n const newGroups = props.searchGroups.filter(g => g.id !== groupId);\n emit('update:searchGroups', newGroups);\n emit('change');\n if (props.autoRefreshOnSearchChange) {\n handleRefresh();\n }\n};\n\nconst handleGroupConditionsUpdate = (groupId: string, conditions: any[]) => {\n const newGroups = props.searchGroups.map(g => (g.id === groupId ? { ...g, conditions } : g));\n emit('update:searchGroups', newGroups);\n};\n\nconst handleGroupViewSearchConfigChange = (\n groupId: string,\n viewSearchConfig: Record<string, ViewSearchItem>,\n) => {\n const newGroups = props.searchGroups.map(g =>\n g.id === groupId ? { ...g, viewSearchConfig } : g,\n );\n emit('update:searchGroups', newGroups);\n emit('change');\n};\n\nconst handleGroupSearchChange = () => {\n emit('change');\n};\n\nconst handleGroupSummaryUpdate = (groupId: string, conditions: any[]) => {\n const newGroups = props.searchGroups.map(g => (g.id === groupId ? { ...g, conditions } : g));\n emit('update:searchGroups', newGroups);\n handleRefresh();\n};\n\nconst search_display_type = computed(() => {\n return props.searchDisplayType || 'hide';\n});\n\nonMounted(() => {\n if (['show', 'show_no_hide'].includes(search_display_type.value)) {\n isShowForm.value = true;\n }\n});\n</script>\n\n<style lang=\"less\" scoped>\n.search-switch {\n display: inline-block;\n line-height: 32px;\n height: 32px;\n}\n.search-groups-container {\n max-height: 300px;\n overflow-y: auto;\n padding: 4px 0;\n}\n.search-group-item {\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n padding: 8px;\n margin-bottom: 8px;\n background: #fafafa;\n}\n.search-group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n padding-bottom: 4px;\n border-bottom: 1px dashed #e0e0e0;\n}\n.search-group-header-title {\n font-weight: bold;\n color: var(--primary-color, #1890ff);\n font-size: 13px;\n}\n.search-group-actions {\n display: flex;\n gap: 4px;\n}\n.search-form-wraper {\n padding-left: 2px;\n padding-right: 2px;\n}\n.add-group-btn {\n margin: 4px 0 8px 8px;\n}\n.bottom-content {\n display: flex;\n justify-content: flex-end;\n padding: 8px 26px 8px 8px;\n}\n.group-or-separator {\n display: inline-block;\n padding: 0 8px;\n color: #999;\n font-size: 12px;\n font-weight: bold;\n line-height: 32px;\n}\n</style>\n","<template>\n <span\n class=\"search-switch\"\n v-if=\"search_display_type !== 'show_no_hide'\"\n >\n <a-button\n v-if=\"!isShowForm\"\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = true\"\n >\n <span class=\"expand-btn\">{{ $t('展开') }}</span>\n </a-button>\n <a-button\n v-else\n type=\"link\"\n size=\"small\"\n @click=\"isShowForm = false\"\n >\n {{ $t('隐藏') }}\n </a-button>\n </span>\n <!-- 多分组模式:搜索摘要 -->\n <template v-if=\"!isShowForm\">\n <template\n v-for=\"(group, index) in searchGroups\"\n :key=\"group.id\"\n >\n <span\n class=\"group-or-separator\"\n v-if=\"index > 0\"\n >\n OR\n </span>\n <search-summary\n :search-conditions=\"group.conditions\"\n :search-config=\"searchConfig\"\n :event-bus=\"eventBus\"\n :searchType=\"searchType\"\n :showRefresh=\"index === searchGroups.length - 1\"\n @update:searchConditions=\"conditions => handleGroupSummaryUpdate(group.id, conditions)\"\n />\n </template>\n </template>\n <!-- 多分组模式:搜索表单 -->\n <div\n v-if=\"isShowForm\"\n class=\"search-groups-container\"\n >\n <div\n v-for=\"(group, index) in searchGroups\"\n :key=\"group.id\"\n class=\"search-group-item\"\n >\n <div class=\"search-group-header\">\n <span class=\"search-group-header-title\">{{ $t('分组') + ' ' + (index + 1) }}</span>\n <div class=\"search-group-actions\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"searchConfigVisibleMap[group.id] = true\"\n v-if=\"config?.sys_btn?.search_field_config !== false\"\n >\n <a-tooltip :title=\"$t('设置搜索字段')\">\n <SettingOutlined />\n </a-tooltip>\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n danger\n @click=\"handleRemoveGroup(group.id)\"\n v-if=\"searchGroups.length > 1\"\n >\n <a-tooltip :title=\"$t('删除分组')\">\n <DeleteOutlined />\n </a-tooltip>\n </a-button>\n </div>\n </div>\n <div\n class=\"search-form-wraper\"\n :style=\"{\n '--column-count': searchColumnCount || 0,\n }\"\n >\n <SearchForm\n :searchConfig=\"searchConfig\"\n :searchConditions=\"group.conditions\"\n :viewSearchConfig=\"group.viewSearchConfig\"\n :searchType=\"searchType\"\n :searchColumnCount=\"searchColumnCount\"\n :autoRefreshOnSearchChange=\"autoRefreshOnSearchChange\"\n :eventBus=\"eventBus\"\n :disabledShowSearchGroupName=\"disabledShowSearchGroupName\"\n @update:searchConditions=\"conditions => handleGroupConditionsUpdate(group.id, conditions)\"\n @change=\"handleGroupSearchChange\"\n />\n </div>\n <search-config-modal\n v-model:open=\"searchConfigVisibleMap[group.id]\"\n :search-config=\"searchConfig\"\n :view-search-config=\"group.viewSearchConfig\"\n @change=\"config => handleGroupViewSearchConfigChange(group.id, config)\"\n />\n </div>\n <a-button\n class=\"add-group-btn\"\n size=\"small\"\n type=\"dashed\"\n @click=\"handleAddGroup\"\n >\n <PlusOutlined />\n {{ $t('添加分组') }}\n </a-button>\n </div>\n <div\n class=\"bottom-content\"\n v-if=\"isShowForm && !autoRefreshOnSearchChange\"\n >\n <a-button\n size=\"small\"\n type=\"primary\"\n @click=\"handleRefresh\"\n >\n 查询\n </a-button>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport searchSummary from './searchSummary.vue';\nimport SearchConfigModal from './tableConfig/searchConfig.vue';\nimport SearchForm from './SearchForm.vue';\nimport { SearchConfig, SearchGroupItem, SearchType, ViewSearchItem } from './typing';\nimport { useTableProState } from './utils';\nimport { genID } from 'liyu-pc-base/utils/function.js';\n\ndefineOptions({\n name: 'SearchFormGroup',\n});\n\nconst props = defineProps({\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchGroups: {\n type: Array as PropType<SearchGroupItem[]>,\n required: true,\n },\n eventBus: {\n type: Object as any,\n required: false,\n },\n searchType: {\n type: String as () => SearchType,\n default: 'advanced',\n },\n searchDisplayType: {\n type: String,\n default: 'hide',\n },\n searchColumnCount: {\n type: Number,\n default: 0,\n },\n autoRefreshOnSearchChange: {\n type: Boolean,\n default: false,\n },\n disabledShowSearchGroupName: {\n type: Boolean,\n default: false, // 是否显示搜索分组名称,默认不显示\n },\n});\n\nconst emit = defineEmits(['update:searchGroups', 'change']);\n\nconst { t } = useI18n();\nconst isShowForm = ref(false);\nconst searchConfigVisibleMap = ref<Record<string, boolean>>({});\nconst { config } = useTableProState();\n\nconst handleRefresh = () => {\n props.eventBus.emit('refresh');\n};\n\nconst handleAddGroup = () => {\n const newGroups = [\n ...props.searchGroups,\n {\n id: genID(),\n conditions: [],\n viewSearchConfig: {},\n },\n ];\n emit('update:searchGroups', newGroups);\n emit('change');\n};\n\nconst handleRemoveGroup = (groupId: string) => {\n const newGroups = props.searchGroups.filter(g => g.id !== groupId);\n emit('update:searchGroups', newGroups);\n emit('change');\n if (props.autoRefreshOnSearchChange) {\n handleRefresh();\n }\n};\n\nconst handleGroupConditionsUpdate = (groupId: string, conditions: any[]) => {\n const newGroups = props.searchGroups.map(g => (g.id === groupId ? { ...g, conditions } : g));\n emit('update:searchGroups', newGroups);\n};\n\nconst handleGroupViewSearchConfigChange = (\n groupId: string,\n viewSearchConfig: Record<string, ViewSearchItem>,\n) => {\n const newGroups = props.searchGroups.map(g =>\n g.id === groupId ? { ...g, viewSearchConfig } : g,\n );\n emit('update:searchGroups', newGroups);\n emit('change');\n};\n\nconst handleGroupSearchChange = () => {\n emit('change');\n};\n\nconst handleGroupSummaryUpdate = (groupId: string, conditions: any[]) => {\n const newGroups = props.searchGroups.map(g => (g.id === groupId ? { ...g, conditions } : g));\n emit('update:searchGroups', newGroups);\n handleRefresh();\n};\n\nconst search_display_type = computed(() => {\n return props.searchDisplayType || 'hide';\n});\n\nonMounted(() => {\n if (['show', 'show_no_hide'].includes(search_display_type.value)) {\n isShowForm.value = true;\n }\n});\n</script>\n\n<style lang=\"less\" scoped>\n.search-switch {\n display: inline-block;\n line-height: 32px;\n height: 32px;\n}\n.search-groups-container {\n max-height: 300px;\n overflow-y: auto;\n padding: 4px 0;\n}\n.search-group-item {\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n padding: 8px;\n margin-bottom: 8px;\n background: #fafafa;\n}\n.search-group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n padding-bottom: 4px;\n border-bottom: 1px dashed #e0e0e0;\n}\n.search-group-header-title {\n font-weight: bold;\n color: var(--primary-color, #1890ff);\n font-size: 13px;\n}\n.search-group-actions {\n display: flex;\n gap: 4px;\n}\n.search-form-wraper {\n padding-left: 2px;\n padding-right: 2px;\n}\n.add-group-btn {\n margin: 4px 0 8px 8px;\n}\n.bottom-content {\n display: flex;\n justify-content: flex-end;\n padding: 8px 26px 8px 8px;\n}\n.group-or-separator {\n display: inline-block;\n padding: 0 8px;\n color: #999;\n font-size: 12px;\n font-weight: bold;\n line-height: 32px;\n}\n</style>\n","<template>\n <div style=\"padding: 8px\">\n <a-input-group\n compact\n size=\"small\"\n style=\"margin-bottom: 8px; display: flex\"\n >\n <a-select\n v-if=\"condition.operator_list?.length > 0 && searchType === 'advanced'\"\n :dropdownMatchSelectWidth=\"false\"\n :value=\"condition.operator\"\n @change=\"onChange('operator', $event)\"\n size=\"small\"\n >\n <a-select-option\n v-for=\"item in condition.operator_list\"\n :value=\"item\"\n :key=\"item\"\n >\n {{ operator_type_map[item] }}\n </a-select-option>\n </a-select>\n <template v-if=\"['is_or_not_blank_or_null', 'is_or_not_null'].includes(condition.operator)\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: '',\n }\"\n :formModel=\"formModel\"\n :options=\"is_or_not_blank_or_null_options\"\n :validateInfos=\"validateInfos\"\n @update:value=\"onChange('value', $event)\"\n :field_width=\"condition.field_width || 155\"\n />\n </template>\n <template v-else-if=\"['between', 'notbetween'].includes(condition.operator)\">\n <template v-if=\"condition.type === 'datepicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'rangepicker',\n format: condition.dateFormat,\n }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width ? condition.field_width * 2 : 320\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'monthrangepicker',\n }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width ? condition.field_width * 2 : 320\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue?.[0]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event, 0)\"\n />\n <span\n class=\"range\"\n style=\"padding: 0 4px; line-height: 24px\"\n >\n ~\n </span>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue?.[1]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event, 1)\"\n />\n </template>\n </template>\n <template v-else-if=\"condition.operator === '_eq'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: '',\n }\"\n :formModel=\"formModel\"\n :options=\"options_for_date_eq\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'datepicker' || condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{ ...condition, format: condition.dateFormat }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n name: column.dataIndex,\n }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n </a-input-group>\n <div style=\"display: flex; justify-content: flex-end\">\n <a-button\n type=\"primary\"\n size=\"small\"\n style=\"width: 70px; margin-right: 8px\"\n @click=\"handleSearch\"\n >\n 查询\n </a-button>\n <a-button\n size=\"small\"\n style=\"width: 70px\"\n @click=\"handleReset\"\n >\n 重置\n </a-button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport SmallTableSerarchField from 'liyu-pc-base/components/FormRender/SmallTableSerarchField.vue';\nimport { getParamsCount, operator_type_map, options_for_date_eq } from './utils';\nimport { Column, SearchConditionItem, SearchConfig, SearchType } from './typing';\nimport { isArray } from 'lodash';\nimport { ConditionOperator } from 'liyu-pc-base/api/typing';\n\nconst props = defineProps({\n column: {\n type: Object as PropType<Column>,\n required: true,\n },\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchConditions: {\n type: Array as PropType<SearchConditionItem[]>,\n required: true,\n },\n searchType: {\n type: String as PropType<SearchType>,\n default: 'advanced',\n },\n confirm: {\n type: Function as PropType<() => void>,\n required: true,\n },\n clearFilters: {\n type: Function as PropType<() => void>,\n required: true,\n },\n});\n\nconst emit = defineEmits(['search', 'reset', 'update:searchConditions']);\n\nconst { t } = useI18n();\nconst formModel = ref<Record<string, any>>({});\nconst validateInfos = ref({});\n\nconst is_or_not_blank_or_null_options = ref([\n { value: 'yes', label: t('is_or_not_blank_or_null.yes') },\n { value: 'no', label: t('is_or_not_blank_or_null.no') },\n]);\n\nconst condition = computed<any>(() => {\n const dataIndex = props.column.dataIndex;\n const currentCondition = props.searchConditions.find(item => item.field_name === dataIndex);\n const configItem = props.searchConfig?.find(item => item.field_name === dataIndex);\n if (currentCondition) {\n return {\n ...configItem,\n ...currentCondition,\n };\n }\n return {\n ...configItem,\n operator: configItem?.default_operator || 'eq',\n value: undefined,\n };\n});\n\nconst fieldValue = computed(() => {\n const dataIndex = props.column.dataIndex;\n const configItem = props.searchConfig?.find(item => item.field_name === dataIndex);\n if (!configItem) return undefined;\n const value_count = getParamsCount(condition.value.operator, configItem);\n let value = condition.value.value;\n if (value_count == 1) {\n if (value instanceof Array) {\n return value[0] || undefined;\n } else {\n return value || undefined;\n }\n } else if (value_count == 2) {\n if (value instanceof Array) {\n return [value[0], value[1]];\n } else if (value) {\n return [value, null];\n } else {\n return [null, null];\n }\n } else {\n return value;\n }\n});\n\nconst onChange = (type: 'value' | 'operator', newValue: any, array_index?: number) => {\n const field_name = props.column.dataIndex!;\n const conditions = [...props.searchConditions];\n const configItem = props.searchConfig.find(item => item.field_name === field_name);\n if (!configItem) return;\n let value: any;\n let operator: ConditionOperator;\n let currentCondition = conditions.find(item => item.field_name === field_name);\n\n if (type === 'value') {\n operator = currentCondition\n ? currentCondition.operator\n : (configItem?.default_operator as ConditionOperator) || 'eq';\n if (array_index !== undefined) {\n const value_count = getParamsCount(operator, configItem);\n value = currentCondition?.value;\n if (!value || !isArray(value)) {\n value = new Array(value_count).fill(null);\n } else {\n value = [...value];\n }\n value[array_index] = newValue;\n } else {\n value = isArray(newValue)\n ? newValue\n : newValue !== undefined && newValue !== null\n ? [newValue]\n : [];\n }\n } else {\n operator = newValue as ConditionOperator;\n const value_count = getParamsCount(newValue, configItem);\n value = value_count ? new Array(value_count).fill(null) : undefined;\n }\n\n if (currentCondition) {\n currentCondition.operator = operator;\n currentCondition.value = value;\n } else {\n conditions.push({\n field_name,\n value,\n operator: operator,\n format: configItem?.format || 'input',\n class_name: configItem?.class_name || 'str',\n });\n }\n\n formModel.value[field_name] = isArray(value) ? value[0] : value;\n emit('update:searchConditions', conditions);\n};\n\nconst handleSearch = () => {\n const dataIndex = props.column.dataIndex!;\n const activeCondition = props.searchConditions.find(item => item.field_name === dataIndex);\n let searchVal = '';\n if (activeCondition) {\n searchVal = isArray(activeCondition.value) ? activeCondition.value[0] : activeCondition.value;\n }\n emit('search', { confirm: props.confirm, dataIndex, value: searchVal });\n};\n\nconst handleReset = () => {\n emit('reset', { clearFilters: props.clearFilters, dataIndex: props.column.dataIndex });\n};\n</script>\n","<template>\n <div style=\"padding: 8px\">\n <a-input-group\n compact\n size=\"small\"\n style=\"margin-bottom: 8px; display: flex\"\n >\n <a-select\n v-if=\"condition.operator_list?.length > 0 && searchType === 'advanced'\"\n :dropdownMatchSelectWidth=\"false\"\n :value=\"condition.operator\"\n @change=\"onChange('operator', $event)\"\n size=\"small\"\n >\n <a-select-option\n v-for=\"item in condition.operator_list\"\n :value=\"item\"\n :key=\"item\"\n >\n {{ operator_type_map[item] }}\n </a-select-option>\n </a-select>\n <template v-if=\"['is_or_not_blank_or_null', 'is_or_not_null'].includes(condition.operator)\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: '',\n }\"\n :formModel=\"formModel\"\n :options=\"is_or_not_blank_or_null_options\"\n :validateInfos=\"validateInfos\"\n @update:value=\"onChange('value', $event)\"\n :field_width=\"condition.field_width || 155\"\n />\n </template>\n <template v-else-if=\"['between', 'notbetween'].includes(condition.operator)\">\n <template v-if=\"condition.type === 'datepicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'rangepicker',\n format: condition.dateFormat,\n }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width ? condition.field_width * 2 : 320\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'monthrangepicker',\n }\"\n :formModel=\"formModel\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width ? condition.field_width * 2 : 320\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue?.[0]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event, 0)\"\n />\n <span\n class=\"range\"\n style=\"padding: 0 4px; line-height: 24px\"\n >\n ~\n </span>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue?.[1]\"\n :field=\"condition\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event, 1)\"\n />\n </template>\n </template>\n <template v-else-if=\"condition.operator === '_eq'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n type: 'select',\n mode: 'default',\n format: 'choices',\n class_name: 'str',\n placeholder: '',\n }\"\n :formModel=\"formModel\"\n :options=\"options_for_date_eq\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else-if=\"condition.type === 'datepicker' || condition.type === 'monthpicker'\">\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{ ...condition, format: condition.dateFormat }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n <template v-else>\n <SmallTableSerarchField\n :is_show_label=\"false\"\n :label=\"column.dataIndex\"\n :value=\"fieldValue\"\n :field=\"{\n ...condition,\n name: column.dataIndex,\n }\"\n :formModel=\"formModel\"\n :options=\"condition.options\"\n :validateInfos=\"validateInfos\"\n :field_width=\"condition.field_width || 155\"\n @update:value=\"onChange('value', $event)\"\n />\n </template>\n </a-input-group>\n <div style=\"display: flex; justify-content: flex-end\">\n <a-button\n type=\"primary\"\n size=\"small\"\n style=\"width: 70px; margin-right: 8px\"\n @click=\"handleSearch\"\n >\n 查询\n </a-button>\n <a-button\n size=\"small\"\n style=\"width: 70px\"\n @click=\"handleReset\"\n >\n 重置\n </a-button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, PropType, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport SmallTableSerarchField from 'liyu-pc-base/components/FormRender/SmallTableSerarchField.vue';\nimport { getParamsCount, operator_type_map, options_for_date_eq } from './utils';\nimport { Column, SearchConditionItem, SearchConfig, SearchType } from './typing';\nimport { isArray } from 'lodash';\nimport { ConditionOperator } from 'liyu-pc-base/api/typing';\n\nconst props = defineProps({\n column: {\n type: Object as PropType<Column>,\n required: true,\n },\n searchConfig: {\n type: Array as PropType<SearchConfig[]>,\n required: true,\n },\n searchConditions: {\n type: Array as PropType<SearchConditionItem[]>,\n required: true,\n },\n searchType: {\n type: String as PropType<SearchType>,\n default: 'advanced',\n },\n confirm: {\n type: Function as PropType<() => void>,\n required: true,\n },\n clearFilters: {\n type: Function as PropType<() => void>,\n required: true,\n },\n});\n\nconst emit = defineEmits(['search', 'reset', 'update:searchConditions']);\n\nconst { t } = useI18n();\nconst formModel = ref<Record<string, any>>({});\nconst validateInfos = ref({});\n\nconst is_or_not_blank_or_null_options = ref([\n { value: 'yes', label: t('is_or_not_blank_or_null.yes') },\n { value: 'no', label: t('is_or_not_blank_or_null.no') },\n]);\n\nconst condition = computed<any>(() => {\n const dataIndex = props.column.dataIndex;\n const currentCondition = props.searchConditions.find(item => item.field_name === dataIndex);\n const configItem = props.searchConfig?.find(item => item.field_name === dataIndex);\n if (currentCondition) {\n return {\n ...configItem,\n ...currentCondition,\n };\n }\n return {\n ...configItem,\n operator: configItem?.default_operator || 'eq',\n value: undefined,\n };\n});\n\nconst fieldValue = computed(() => {\n const dataIndex = props.column.dataIndex;\n const configItem = props.searchConfig?.find(item => item.field_name === dataIndex);\n if (!configItem) return undefined;\n const value_count = getParamsCount(condition.value.operator, configItem);\n let value = condition.value.value;\n if (value_count == 1) {\n if (value instanceof Array) {\n return value[0] || undefined;\n } else {\n return value || undefined;\n }\n } else if (value_count == 2) {\n if (value instanceof Array) {\n return [value[0], value[1]];\n } else if (value) {\n return [value, null];\n } else {\n return [null, null];\n }\n } else {\n return value;\n }\n});\n\nconst onChange = (type: 'value' | 'operator', newValue: any, array_index?: number) => {\n const field_name = props.column.dataIndex!;\n const conditions = [...props.searchConditions];\n const configItem = props.searchConfig.find(item => item.field_name === field_name);\n if (!configItem) return;\n let value: any;\n let operator: ConditionOperator;\n let currentCondition = conditions.find(item => item.field_name === field_name);\n\n if (type === 'value') {\n operator = currentCondition\n ? currentCondition.operator\n : (configItem?.default_operator as ConditionOperator) || 'eq';\n if (array_index !== undefined) {\n const value_count = getParamsCount(operator, configItem);\n value = currentCondition?.value;\n if (!value || !isArray(value)) {\n value = new Array(value_count).fill(null);\n } else {\n value = [...value];\n }\n value[array_index] = newValue;\n } else {\n value = isArray(newValue)\n ? newValue\n : newValue !== undefined && newValue !== null\n ? [newValue]\n : [];\n }\n } else {\n operator = newValue as ConditionOperator;\n const value_count = getParamsCount(newValue, configItem);\n value = value_count ? new Array(value_count).fill(null) : undefined;\n }\n\n if (currentCondition) {\n currentCondition.operator = operator;\n currentCondition.value = value;\n } else {\n conditions.push({\n field_name,\n value,\n operator: operator,\n format: configItem?.format || 'input',\n class_name: configItem?.class_name || 'str',\n });\n }\n\n formModel.value[field_name] = isArray(value) ? value[0] : value;\n emit('update:searchConditions', conditions);\n};\n\nconst handleSearch = () => {\n const dataIndex = props.column.dataIndex!;\n const activeCondition = props.searchConditions.find(item => item.field_name === dataIndex);\n let searchVal = '';\n if (activeCondition) {\n searchVal = isArray(activeCondition.value) ? activeCondition.value[0] : activeCondition.value;\n }\n emit('search', { confirm: props.confirm, dataIndex, value: searchVal });\n};\n\nconst handleReset = () => {\n emit('reset', { clearFilters: props.clearFilters, dataIndex: props.column.dataIndex });\n};\n</script>\n","<template>\n <div class=\"columns-config-wrapper\">\n <a-input\n v-model:value=\"searchText\"\n :placeholder=\"$t('搜索列名/字段名')\"\n allow-clear\n style=\"margin-bottom: 8px\"\n />\n <div class=\"table-wrapper\" ref=\"tableWrapperRef\">\n <s-table\n :columns=\"columnsConfig\"\n :rowHeight=\"30\"\n :data-source=\"filteredDataSource\"\n :row-selection=\"rowSelection\"\n v-model:selectedRowKeys=\"selectedRowKeys\"\n expand-row-by-click\n @row-drag-end=\"onRowDragEnd\"\n :row-key=\"'key'\"\n :pagination=\"false\"\n :height=\"tableHeight\"\n >\n <template #bodyCell=\"{ column, index, text, record }\">\n <template v-if=\"column.dataIndex == 'fixed'\">\n <a-radio-group\n v-if=\"!record?.is_children\"\n v-model:value=\"record.fixed\"\n size=\"small\"\n @change=\"change_fixed\"\n >\n <a-radio-button value=\"left\">{{ $t('左') }}</a-radio-button>\n <a-radio-button :value=\"undefined\">{{ $t('不固定') }}</a-radio-button>\n <a-radio-button value=\"right\">{{ $t('右') }}</a-radio-button>\n </a-radio-group>\n </template>\n <template v-if=\"column.dataIndex == 'autoHeight'\">\n <a-switch\n v-if=\"!(record.children && record.children.length > 0)\"\n v-model:checked=\"record.autoHeight\"\n :checked-value=\"true\"\n :un-checked-value=\"false\"\n :checked-children=\"$t('自动')\"\n :un-checked-children=\"$t('固定')\"\n @change=\"change_autoHeight\"\n />\n </template>\n <template v-if=\"column.dataIndex == 'autoWidth'\">\n <a-switch\n v-if=\"!(record.children && record.children.length > 0) && record?.resizable\"\n v-model:checked=\"record.autoWidth\"\n :checked-value=\"true\"\n :un-checked-value=\"false\"\n :checked-children=\"$t('自动')\"\n :un-checked-children=\"$t('固定')\"\n @click=\"change_autoWidth\"\n />\n </template>\n <template v-if=\"column.dataIndex == 'ellipsis'\">\n <a-switch\n v-if=\"!(record.children && record.children.length > 0) && record?.resizable\"\n v-model:checked=\"record.ellipsis\"\n :checked-value=\"true\"\n :un-checked-value=\"false\"\n :checked-children=\"$t('开启')\"\n :un-checked-children=\"$t('关闭')\"\n @click=\"change_ellipsis(record)\"\n />\n </template>\n </template>\n </s-table>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, ref } from 'vue';\nimport { useResizeObserver } from '@vueuse/core';\nimport { useI18n } from 'vue-i18n';\nimport { Column } from '../typing';\n\nexport default defineComponent({\n name: 'ColumnsConfig',\n props: {\n columns: {\n type: Array as () => Array<Column>,\n default: () => [],\n },\n },\n emits: ['change'],\n setup(props, { emit }) {\n const { t } = useI18n();\n const searchText = ref('');\n const tableWrapperRef = ref<HTMLElement | null>(null);\n const tableHeight = ref(500);\n\n // 监听 table-wrapper 高度,让 s-table 自动占满容器\n useResizeObserver(tableWrapperRef, entries => {\n const height = entries[0].contentRect.height;\n if (height > 0) {\n tableHeight.value = height;\n }\n });\n\n const columnsConfig = ref([\n {\n title: t('列名称'),\n dataIndex: 'title',\n key: 'title',\n rowDrag: true,\n width: 270,\n },\n {\n title: t('冻结列'),\n dataIndex: 'fixed',\n key: 'fixed',\n width: 200,\n },\n {\n title: t('行高'),\n dataIndex: 'autoHeight',\n width: 100,\n key: 'autoHeight',\n },\n {\n title: t('列宽'),\n dataIndex: 'autoWidth',\n width: 100,\n key: 'autoWidth',\n },\n {\n title: t('超宽省略'),\n dataIndex: 'ellipsis',\n width: 100,\n key: 'ellipsis',\n },\n ]);\n\n const dataSource = computed(() => {\n return props.columns.filter(item => item.key !== 'index' && item.key !== 'action');\n });\n\n const filteredDataSource = computed(() => {\n if (!searchText.value) return dataSource.value;\n const search = searchText.value.toLowerCase();\n\n const filterNode = (nodes: any[]) => {\n return nodes.reduce((acc: any[], node: any) => {\n const matchSelf =\n node.title?.toLowerCase().includes(search) ||\n node.dataIndex?.toLowerCase().includes(search) ||\n node.key?.toLowerCase().includes(search);\n\n if (node.children && node.children.length > 0) {\n const filteredChildren = filterNode(node.children);\n if (filteredChildren.length > 0) {\n acc.push({ ...node, children: filteredChildren });\n } else if (matchSelf) {\n acc.push({ ...node, children: [] });\n }\n } else if (matchSelf) {\n acc.push(node);\n }\n return acc;\n }, []);\n };\n\n return filterNode(dataSource.value);\n });\n\n const getAllKeysChecked = (children: any) => {\n let allChildrenChecked = true;\n children.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n allChildrenChecked = allChildrenChecked && getAllKeysChecked(item.children);\n } else {\n allChildrenChecked = allChildrenChecked && item?.checked !== false;\n }\n });\n return allChildrenChecked;\n };\n\n const add_selectedRowKeys = (record: any) => {\n let list = [];\n record.children.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n const allChildrenChecked = getAllKeysChecked(item.children);\n if (allChildrenChecked) {\n list.push(item.key);\n }\n list = list.concat(add_selectedRowKeys(item));\n } else {\n if (item?.checked !== false) {\n list.push(item.key);\n }\n }\n });\n return list;\n };\n\n const selectedRowKeys = computed(() => {\n if (props.columns) {\n let list = [];\n props.columns.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n const allChildrenChecked = getAllKeysChecked(item.children);\n if (allChildrenChecked) {\n list.push(item.key);\n }\n list = list.concat(add_selectedRowKeys(item));\n } else {\n if (item?.checked) {\n list.push(item.key);\n }\n }\n });\n return list;\n } else {\n return [];\n }\n });\n\n const rowSelection = ref({\n checkStrictly: false,\n hideSelectAll: true,\n onSelect: (record: any, selected: boolean) => {\n const change_selecte_children_props = (children: any) => {\n children.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n change_selecte_children_props(item.children);\n }\n item.checked = selected;\n });\n };\n\n const change_selecte_props = (obj: any, key: any, is_checked: any) => {\n obj.children.forEach((item: any) => {\n if (item.key == key) {\n if (is_checked && item.children && item.children.length > 0) {\n change_selecte_children_props(item.children);\n }\n if (selected) {\n item.checked = true;\n obj.checked = true;\n if (obj.is_children) {\n change_selecte_dataSource(obj.key, false);\n }\n } else {\n item.checked = false;\n }\n }\n if (item.children && item.children.length > 0) {\n change_selecte_props(item, key, is_checked);\n }\n });\n };\n\n const change_selecte_dataSource = (key: any, is_checked: any) => {\n dataSource.value.forEach((item: any) => {\n if (item.key == key) {\n if (item.children && item.children.length > 0) {\n change_selecte_children_props(item.children);\n }\n item.checked = selected;\n }\n if (item.children && item.children.length > 0) {\n change_selecte_props(item, key, is_checked);\n }\n });\n };\n\n change_selecte_dataSource(record.key, true);\n emit('change', dataSource.value);\n },\n });\n\n const findParent = (data: any[], childKey: any) => {\n let foundParentKey: any;\n const searchParent = (id: any, keys: any[], childKeys: any) => {\n for (const key of keys) {\n if (key.key === childKeys) {\n foundParentKey = id;\n return foundParentKey;\n } else if (key.children && key.children.length > 0) {\n foundParentKey = searchParent(key.key, key.children, childKeys);\n if (foundParentKey !== undefined) return foundParentKey;\n }\n }\n return undefined;\n };\n return searchParent(null, data, childKey);\n };\n\n const onRowDragEnd = ({ record, preTargetInfo, nextTargetInfo }) => {\n return new Promise((reslove, reject) => {\n // 获取当前拖拽节点的父节点ID\n const currentParentId = findParent(dataSource.value, record.key);\n\n if (preTargetInfo) {\n // 如果拖拽到某个节点之后,检查该目标节点的父节点是否与当前节点相同\n const targetParentId = findParent(dataSource.value, preTargetInfo.record.key);\n if (currentParentId === targetParentId) {\n reslove(true);\n } else {\n reject();\n }\n } else if (nextTargetInfo) {\n // 如果拖拽到某个节点之前,检查该目标节点的父节点是否与当前节点相同\n const targetParentId = findParent(dataSource.value, nextTargetInfo.record.key);\n if (currentParentId === targetParentId) {\n reslove(true);\n } else {\n reject();\n }\n } else {\n reject();\n }\n }).then(() => {\n setTimeout(() => {\n emit('change', dataSource.value);\n }, 200);\n });\n };\n\n const change_fixed = () => emit('change', dataSource.value);\n const change_autoHeight = () => emit('change', dataSource.value);\n const change_autoWidth = () => emit('change', dataSource.value);\n\n const recursion_change_ellipsis = (obj: any, record: any) => {\n obj.children.forEach((item: any) => {\n if (item.key == record.key) {\n item.ellipsis = record.ellipsis;\n }\n if (item.children && item.children.length > 0) {\n recursion_change_ellipsis(item, record);\n }\n });\n };\n\n const change_ellipsis = (record: any) => {\n const list = dataSource.value;\n list.forEach((item: any) => {\n if (item.key == record.key) {\n item.ellipsis = record.ellipsis;\n }\n if (item.children && item.children.length > 0) {\n recursion_change_ellipsis(item, record);\n }\n });\n emit('change', list);\n };\n\n return {\n searchText,\n tableWrapperRef,\n tableHeight,\n columnsConfig,\n dataSource,\n filteredDataSource,\n rowSelection,\n selectedRowKeys,\n onRowDragEnd,\n change_fixed,\n change_autoHeight,\n change_autoWidth,\n change_ellipsis,\n };\n },\n});\n</script>\n\n<style lang=\"less\" scoped>\n.columns-config-wrapper {\n display: flex;\n flex-direction: column;\n gap: 12px;\n height: 100%;\n}\n.table-wrapper {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n</style>\n","<template>\n <div class=\"columns-config-wrapper\">\n <a-input\n v-model:value=\"searchText\"\n :placeholder=\"$t('搜索列名/字段名')\"\n allow-clear\n style=\"margin-bottom: 8px\"\n />\n <div class=\"table-wrapper\" ref=\"tableWrapperRef\">\n <s-table\n :columns=\"columnsConfig\"\n :rowHeight=\"30\"\n :data-source=\"filteredDataSource\"\n :row-selection=\"rowSelection\"\n v-model:selectedRowKeys=\"selectedRowKeys\"\n expand-row-by-click\n @row-drag-end=\"onRowDragEnd\"\n :row-key=\"'key'\"\n :pagination=\"false\"\n :height=\"tableHeight\"\n >\n <template #bodyCell=\"{ column, index, text, record }\">\n <template v-if=\"column.dataIndex == 'fixed'\">\n <a-radio-group\n v-if=\"!record?.is_children\"\n v-model:value=\"record.fixed\"\n size=\"small\"\n @change=\"change_fixed\"\n >\n <a-radio-button value=\"left\">{{ $t('左') }}</a-radio-button>\n <a-radio-button :value=\"undefined\">{{ $t('不固定') }}</a-radio-button>\n <a-radio-button value=\"right\">{{ $t('右') }}</a-radio-button>\n </a-radio-group>\n </template>\n <template v-if=\"column.dataIndex == 'autoHeight'\">\n <a-switch\n v-if=\"!(record.children && record.children.length > 0)\"\n v-model:checked=\"record.autoHeight\"\n :checked-value=\"true\"\n :un-checked-value=\"false\"\n :checked-children=\"$t('自动')\"\n :un-checked-children=\"$t('固定')\"\n @change=\"change_autoHeight\"\n />\n </template>\n <template v-if=\"column.dataIndex == 'autoWidth'\">\n <a-switch\n v-if=\"!(record.children && record.children.length > 0) && record?.resizable\"\n v-model:checked=\"record.autoWidth\"\n :checked-value=\"true\"\n :un-checked-value=\"false\"\n :checked-children=\"$t('自动')\"\n :un-checked-children=\"$t('固定')\"\n @click=\"change_autoWidth\"\n />\n </template>\n <template v-if=\"column.dataIndex == 'ellipsis'\">\n <a-switch\n v-if=\"!(record.children && record.children.length > 0) && record?.resizable\"\n v-model:checked=\"record.ellipsis\"\n :checked-value=\"true\"\n :un-checked-value=\"false\"\n :checked-children=\"$t('开启')\"\n :un-checked-children=\"$t('关闭')\"\n @click=\"change_ellipsis(record)\"\n />\n </template>\n </template>\n </s-table>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, ref } from 'vue';\nimport { useResizeObserver } from '@vueuse/core';\nimport { useI18n } from 'vue-i18n';\nimport { Column } from '../typing';\n\nexport default defineComponent({\n name: 'ColumnsConfig',\n props: {\n columns: {\n type: Array as () => Array<Column>,\n default: () => [],\n },\n },\n emits: ['change'],\n setup(props, { emit }) {\n const { t } = useI18n();\n const searchText = ref('');\n const tableWrapperRef = ref<HTMLElement | null>(null);\n const tableHeight = ref(500);\n\n // 监听 table-wrapper 高度,让 s-table 自动占满容器\n useResizeObserver(tableWrapperRef, entries => {\n const height = entries[0].contentRect.height;\n if (height > 0) {\n tableHeight.value = height;\n }\n });\n\n const columnsConfig = ref([\n {\n title: t('列名称'),\n dataIndex: 'title',\n key: 'title',\n rowDrag: true,\n width: 270,\n },\n {\n title: t('冻结列'),\n dataIndex: 'fixed',\n key: 'fixed',\n width: 200,\n },\n {\n title: t('行高'),\n dataIndex: 'autoHeight',\n width: 100,\n key: 'autoHeight',\n },\n {\n title: t('列宽'),\n dataIndex: 'autoWidth',\n width: 100,\n key: 'autoWidth',\n },\n {\n title: t('超宽省略'),\n dataIndex: 'ellipsis',\n width: 100,\n key: 'ellipsis',\n },\n ]);\n\n const dataSource = computed(() => {\n return props.columns.filter(item => item.key !== 'index' && item.key !== 'action');\n });\n\n const filteredDataSource = computed(() => {\n if (!searchText.value) return dataSource.value;\n const search = searchText.value.toLowerCase();\n\n const filterNode = (nodes: any[]) => {\n return nodes.reduce((acc: any[], node: any) => {\n const matchSelf =\n node.title?.toLowerCase().includes(search) ||\n node.dataIndex?.toLowerCase().includes(search) ||\n node.key?.toLowerCase().includes(search);\n\n if (node.children && node.children.length > 0) {\n const filteredChildren = filterNode(node.children);\n if (filteredChildren.length > 0) {\n acc.push({ ...node, children: filteredChildren });\n } else if (matchSelf) {\n acc.push({ ...node, children: [] });\n }\n } else if (matchSelf) {\n acc.push(node);\n }\n return acc;\n }, []);\n };\n\n return filterNode(dataSource.value);\n });\n\n const getAllKeysChecked = (children: any) => {\n let allChildrenChecked = true;\n children.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n allChildrenChecked = allChildrenChecked && getAllKeysChecked(item.children);\n } else {\n allChildrenChecked = allChildrenChecked && item?.checked !== false;\n }\n });\n return allChildrenChecked;\n };\n\n const add_selectedRowKeys = (record: any) => {\n let list = [];\n record.children.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n const allChildrenChecked = getAllKeysChecked(item.children);\n if (allChildrenChecked) {\n list.push(item.key);\n }\n list = list.concat(add_selectedRowKeys(item));\n } else {\n if (item?.checked !== false) {\n list.push(item.key);\n }\n }\n });\n return list;\n };\n\n const selectedRowKeys = computed(() => {\n if (props.columns) {\n let list = [];\n props.columns.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n const allChildrenChecked = getAllKeysChecked(item.children);\n if (allChildrenChecked) {\n list.push(item.key);\n }\n list = list.concat(add_selectedRowKeys(item));\n } else {\n if (item?.checked) {\n list.push(item.key);\n }\n }\n });\n return list;\n } else {\n return [];\n }\n });\n\n const rowSelection = ref({\n checkStrictly: false,\n hideSelectAll: true,\n onSelect: (record: any, selected: boolean) => {\n const change_selecte_children_props = (children: any) => {\n children.forEach((item: any) => {\n if (item.children && item.children.length > 0) {\n change_selecte_children_props(item.children);\n }\n item.checked = selected;\n });\n };\n\n const change_selecte_props = (obj: any, key: any, is_checked: any) => {\n obj.children.forEach((item: any) => {\n if (item.key == key) {\n if (is_checked && item.children && item.children.length > 0) {\n change_selecte_children_props(item.children);\n }\n if (selected) {\n item.checked = true;\n obj.checked = true;\n if (obj.is_children) {\n change_selecte_dataSource(obj.key, false);\n }\n } else {\n item.checked = false;\n }\n }\n if (item.children && item.children.length > 0) {\n change_selecte_props(item, key, is_checked);\n }\n });\n };\n\n const change_selecte_dataSource = (key: any, is_checked: any) => {\n dataSource.value.forEach((item: any) => {\n if (item.key == key) {\n if (item.children && item.children.length > 0) {\n change_selecte_children_props(item.children);\n }\n item.checked = selected;\n }\n if (item.children && item.children.length > 0) {\n change_selecte_props(item, key, is_checked);\n }\n });\n };\n\n change_selecte_dataSource(record.key, true);\n emit('change', dataSource.value);\n },\n });\n\n const findParent = (data: any[], childKey: any) => {\n let foundParentKey: any;\n const searchParent = (id: any, keys: any[], childKeys: any) => {\n for (const key of keys) {\n if (key.key === childKeys) {\n foundParentKey = id;\n return foundParentKey;\n } else if (key.children && key.children.length > 0) {\n foundParentKey = searchParent(key.key, key.children, childKeys);\n if (foundParentKey !== undefined) return foundParentKey;\n }\n }\n return undefined;\n };\n return searchParent(null, data, childKey);\n };\n\n const onRowDragEnd = ({ record, preTargetInfo, nextTargetInfo }) => {\n return new Promise((reslove, reject) => {\n // 获取当前拖拽节点的父节点ID\n const currentParentId = findParent(dataSource.value, record.key);\n\n if (preTargetInfo) {\n // 如果拖拽到某个节点之后,检查该目标节点的父节点是否与当前节点相同\n const targetParentId = findParent(dataSource.value, preTargetInfo.record.key);\n if (currentParentId === targetParentId) {\n reslove(true);\n } else {\n reject();\n }\n } else if (nextTargetInfo) {\n // 如果拖拽到某个节点之前,检查该目标节点的父节点是否与当前节点相同\n const targetParentId = findParent(dataSource.value, nextTargetInfo.record.key);\n if (currentParentId === targetParentId) {\n reslove(true);\n } else {\n reject();\n }\n } else {\n reject();\n }\n }).then(() => {\n setTimeout(() => {\n emit('change', dataSource.value);\n }, 200);\n });\n };\n\n const change_fixed = () => emit('change', dataSource.value);\n const change_autoHeight = () => emit('change', dataSource.value);\n const change_autoWidth = () => emit('change', dataSource.value);\n\n const recursion_change_ellipsis = (obj: any, record: any) => {\n obj.children.forEach((item: any) => {\n if (item.key == record.key) {\n item.ellipsis = record.ellipsis;\n }\n if (item.children && item.children.length > 0) {\n recursion_change_ellipsis(item, record);\n }\n });\n };\n\n const change_ellipsis = (record: any) => {\n const list = dataSource.value;\n list.forEach((item: any) => {\n if (item.key == record.key) {\n item.ellipsis = record.ellipsis;\n }\n if (item.children && item.children.length > 0) {\n recursion_change_ellipsis(item, record);\n }\n });\n emit('change', list);\n };\n\n return {\n searchText,\n tableWrapperRef,\n tableHeight,\n columnsConfig,\n dataSource,\n filteredDataSource,\n rowSelection,\n selectedRowKeys,\n onRowDragEnd,\n change_fixed,\n change_autoHeight,\n change_autoWidth,\n change_ellipsis,\n };\n },\n});\n</script>\n\n<style lang=\"less\" scoped>\n.columns-config-wrapper {\n display: flex;\n flex-direction: column;\n gap: 12px;\n height: 100%;\n}\n.table-wrapper {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n</style>\n","export const u_YEAR = 2;\nexport const u_MONTH = 2 ** 2;\nexport const u_DAY = 2 ** 3;\nexport const u_HOUR = 2 ** 4;\nexport const u_MIN = 2 ** 5;\nexport const u_SEC = 2 ** 6;\nexport const u_DSEC = 2 ** 7; // decisecond\nexport const u_CSEC = 2 ** 8; // centisecond\nexport const u_MSEC = 2 ** 9; // millisecond\n\n// Excel date boundaries\nexport const MIN_S_DATE = 0;\nexport const MAX_S_DATE = 2958466;\n// Google date boundaries\nexport const MIN_L_DATE = -694324;\nexport const MAX_L_DATE = 35830291;\n\n// if more calendars are added, they should conform to MS CALID identifiers\n// https://docs.microsoft.com/en-us/windows/win32/intl/calendar-identifiers\nexport const EPOCH_1904 = -1;\nexport const EPOCH_1900 = 1;\nexport const EPOCH_1317 = 6;\n\nexport const TOKEN_GENERAL = 'general';\nexport const TOKEN_HASH = 'hash';\nexport const TOKEN_ZERO = 'zero';\nexport const TOKEN_QMARK = 'qmark';\nexport const TOKEN_SLASH = 'slash';\nexport const TOKEN_GROUP = 'group';\nexport const TOKEN_SCALE = 'scale';\nexport const TOKEN_COMMA = 'comma';\nexport const TOKEN_BREAK = 'break';\nexport const TOKEN_TEXT = 'text';\nexport const TOKEN_PLUS = 'plus';\nexport const TOKEN_MINUS = 'minus';\nexport const TOKEN_POINT = 'point';\nexport const TOKEN_SPACE = 'space';\nexport const TOKEN_PERCENT = 'percent';\nexport const TOKEN_DIGIT = 'digit';\nexport const TOKEN_CALENDAR = 'calendar';\nexport const TOKEN_ERROR = 'error';\nexport const TOKEN_DATETIME = 'datetime';\nexport const TOKEN_DURATION = 'duration';\nexport const TOKEN_CONDITION = 'condition';\nexport const TOKEN_DBNUM = 'dbnum';\nexport const TOKEN_NATNUM = 'natnum';\nexport const TOKEN_LOCALE = 'locale';\nexport const TOKEN_COLOR = 'color';\nexport const TOKEN_MODIFIER = 'modifier';\nexport const TOKEN_AMPM = 'ampm';\nexport const TOKEN_ESCAPED = 'escaped';\nexport const TOKEN_STRING = 'string';\nexport const TOKEN_SKIP = 'skip';\nexport const TOKEN_EXP = 'exp';\nexport const TOKEN_FILL = 'fill';\nexport const TOKEN_PAREN = 'paren';\nexport const TOKEN_CHAR = 'char';\n\nexport const indexColors = [\n '#000000',\n '#FFFFFF',\n '#FF0000',\n '#00FF00',\n '#0000FF',\n '#FFFF00',\n '#FF00FF',\n '#00FFFF',\n '#800000',\n '#008000',\n '#000080',\n '#808000',\n '#800080',\n '#008080',\n '#C0C0C0',\n '#808080',\n '#9999FF',\n '#993366',\n '#FFFFCC',\n '#CCFFFF',\n '#660066',\n '#FF8080',\n '#0066CC',\n '#CCCCFF',\n '#000080',\n '#FF00FF',\n '#FFFF00',\n '#00FFFF',\n '#800080',\n '#800000',\n '#008080',\n '#0000FF',\n '#00CCFF',\n '#CCFFFF',\n '#CCFFCC',\n '#FFFF99',\n '#99CCFF',\n '#FF99CC',\n '#CC99FF',\n '#FFCC99',\n '#3366FF',\n '#33CCCC',\n '#99CC00',\n '#FFCC00',\n '#FF9900',\n '#FF6600',\n '#666699',\n '#969696',\n '#003366',\n '#339966',\n '#003300',\n '#333300',\n '#993300',\n '#993366',\n '#333399',\n '#333333'\n];\n\nexport const currencySymbols = [\n '¤',\n '$',\n '£',\n '¥',\n '֏',\n '؋',\n '৳',\n '฿',\n '៛',\n '₡',\n '₦',\n '₩',\n '₪',\n '₫',\n '€',\n '₭',\n '₮',\n '₱',\n '₲',\n '₴',\n '₸',\n '₹',\n '₺',\n '₼',\n '₽',\n '₾',\n '₿'\n];\n\nexport const reCurrencySymbols = new RegExp('[' + currencySymbols.join('') + ']');\n","/* eslint-disable @stylistic/indent */\n// https://docs.microsoft.com/en-us/office/vba/api/office.msolanguageid\nexport default Object.freeze({\n 1078: 'af', // Afrikaans\n 1052: 'sq', // Albanian\n 1118: 'am', // Amharic\n 5121: 'ar_DZ', // Arabic - Algeria\n 15361: 'ar_BH', // Arabic - Bahrain\n 3073: 'ar_EG', // Arabic - Egypt\n 2049: 'ar_IQ', // Arabic - Iraq\n 11265: 'ar_JO', // Arabic - Jordan\n 13313: 'ar_KW', // Arabic - Kuwait\n 12289: 'ar_LB', // Arabic - Lebanon\n 4097: 'ar_LY', // Arabic - Libya\n 6145: 'ar_MA', // Arabic - Morocco\n 8193: 'ar_OM', // Arabic - Oman\n 16385: 'ar_QA', // Arabic - Qatar\n 1025: 'ar_SA', // Arabic - Saudi Arabia\n 10241: 'ar_SY', // Arabic - Syria\n 7169: 'ar_TN', // Arabic - Tunisia\n 14337: 'ar_AE', // Arabic - United Arab Emirates\n 9217: 'ar_YE', // Arabic - Yemen\n 1067: 'hy', // Armenian\n 1101: 'as', // Assamese\n 2092: 'az_AZ', // Azeri - Cyrillic\n 1068: 'az_AZ', // Azeri - Latin\n 1069: 'eu', // Basque\n 1059: 'be', // Belarusian\n 2117: 'bn', // Bengali - Bangladesh\n 1093: 'bn_IN', // Bengali - India\n 5146: 'bs', // Bosnian\n 1026: 'bg', // Bulgarian\n 1109: 'my', // Burmese\n 1027: 'ca', // Catalan\n 2052: 'zh_CN', // Chinese - China\n 3076: 'zh_HK', // Chinese - Hong Kong SAR\n 5124: 'zh_MO', // Chinese - Macau SAR\n 4100: 'zh_SG', // Chinese - Singapore\n 1028: 'zh_TW', // Chinese - Taiwan\n 1050: 'hr', // Croatian\n 1029: 'cs', // Czech\n 1030: 'da', // Danish\n 1125: 'dv', // Divehi; Dhivehi; Maldivian\n 2067: 'nl_BE', // Dutch - Belgium\n 1043: 'nl_NL', // Dutch - Netherlands\n 1126: 'bin', // Edo\n 3081: 'en_AU', // English - Australia\n 10249: 'en_BZ', // English - Belize\n 4105: 'en_CA', // English - Canada\n 9225: 'en_CB', // English - Caribbean\n 2057: 'en_GB', // English - Great Britain\n 16393: 'en_IN', // English - India\n 6153: 'en_IE', // English - Ireland\n 8201: 'en_JM', // English - Jamaica\n 5129: 'en_NZ', // English - New Zealand\n 13321: 'en_PH', // English - Phillippines\n 7177: 'en_ZA', // English - Southern Africa\n 11273: 'en_TT', // English - Trinidad\n 1033: 'en_US', // English - United States\n 12297: 'en_ZW', // English - Zimbabwe\n 1061: 'et', // Estonian\n 1071: 'mk', // FYRO Macedonia\n 1080: 'fo', // Faroese\n 1065: 'fa', // Farsi - Persian\n 1124: 'fil', // Filipino\n 1035: 'fi', // Finnish\n 2060: 'fr_BE', // French - Belgium\n 11276: 'fr_CM', // French - Cameroon\n 3084: 'fr_CA', // French - Canada\n 9228: 'fr_CG', // French - Congo\n 12300: 'fr_CI', // French - Cote d'Ivoire\n 1036: 'fr_FR', // French - France\n 5132: 'fr_LU', // French - Luxembourg\n 13324: 'fr_ML', // French - Mali\n 6156: 'fr_MC', // French - Monaco\n 14348: 'fr_MA', // French - Morocco\n 10252: 'fr_SN', // French - Senegal\n 4108: 'fr_CH', // French - Switzerland\n 7180: 'fr', // French - West Indies\n 1122: 'fy_NL', // Frisian - Netherlands\n 2108: 'gd_IE', // Gaelic - Ireland\n 1084: 'gd', // Gaelic - Scotland\n 1110: 'gl', // Galician\n 1079: 'ka', // Georgian\n 3079: 'de_AT', // German - Austria\n 1031: 'de_DE', // German - Germany\n 5127: 'de_LI', // German - Liechtenstein\n 4103: 'de_LU', // German - Luxembourg\n 2055: 'de_CH', // German - Switzerland\n 1032: 'el', // Greek\n 1140: 'gn', // Guarani - Paraguay\n 1095: 'gu', // Gujarati\n 1279: 'en', // HID (Human Interface Device)\n 1037: 'he', // Hebrew\n 1081: 'hi', // Hindi\n 1038: 'hu', // Hungarian\n 1039: 'is', // Icelandic\n 1136: 'ig_NG', // Igbo - Nigeria\n 1057: 'id', // Indonesian\n 1040: 'it_IT', // Italian - Italy\n 2064: 'it_CH', // Italian - Switzerland\n 1041: 'ja', // Japanese\n 1099: 'kn', // Kannada\n 1120: 'ks', // Kashmiri\n 1087: 'kk', // Kazakh\n 1107: 'km', // Khmer\n 1111: 'kok', // Konkani\n 1042: 'ko', // Korean\n 1088: 'ky', // Kyrgyz - Cyrillic\n 1108: 'lo', // Lao\n 1142: 'la', // Latin\n 1062: 'lv', // Latvian\n 1063: 'lt', // Lithuanian\n 2110: 'ms_BN', // Malay - Brunei\n 1086: 'ms_MY', // Malay - Malaysia\n 1100: 'ml', // Malayalam\n 1082: 'mt', // Maltese\n 1112: 'mni', // Manipuri\n 1153: 'mi', // Maori\n 1102: 'mr', // Marathi\n 1104: 'mn', // Mongolian\n 2128: 'mn', // Mongolian\n 1121: 'ne', // Nepali\n 1044: 'no_NO', // Norwegian - Bokml\n 2068: 'no_NO', // Norwegian - Nynorsk\n 1096: 'or', // Oriya\n 1045: 'pl', // Polish\n 1046: 'pt_BR', // Portuguese - Brazil\n 2070: 'pt_PT', // Portuguese - Portugal\n 1094: 'pa', // Punjabi\n 1047: 'rm', // Raeto-Romance\n 2072: 'ro_MO', // Romanian - Moldova\n 1048: 'ro_RO', // Romanian - Romania\n 1049: 'ru', // Russian\n 2073: 'ru_MO', // Russian - Moldova\n 1083: 'se', // Sami Lappish\n 1103: 'sa', // Sanskrit\n 3098: 'sr_SP', // Serbian - Cyrillic\n 2074: 'sr_SP', // Serbian - Latin\n 1072: 'st', // Sesotho (Sutu)\n 1074: 'tn', // Setsuana\n 1113: 'sd', // Sindhi\n 1115: 'si', // Sinhala; Sinhalese\n 1051: 'sk', // Slovak\n 1060: 'sl', // Slovenian\n 1143: 'so', // Somali\n 1070: 'sb', // Sorbian\n 11274: 'es_AR', // Spanish - Argentina\n 16394: 'es_BO', // Spanish - Bolivia\n 13322: 'es_CL', // Spanish - Chile\n 9226: 'es_CO', // Spanish - Colombia\n 5130: 'es_CR', // Spanish - Costa Rica\n 7178: 'es_DO', // Spanish - Dominican Republic\n 12298: 'es_EC', // Spanish - Ecuador\n 17418: 'es_SV', // Spanish - El Salvador\n 4106: 'es_GT', // Spanish - Guatemala\n 18442: 'es_HN', // Spanish - Honduras\n 2058: 'es_MX', // Spanish - Mexico\n 19466: 'es_NI', // Spanish - Nicaragua\n 6154: 'es_PA', // Spanish - Panama\n 15370: 'es_PY', // Spanish - Paraguay\n 10250: 'es_PE', // Spanish - Peru\n 20490: 'es_PR', // Spanish - Puerto Rico\n 1034: 'es_ES', // Spanish - Spain (Traditional)\n 14346: 'es_UY', // Spanish - Uruguay\n 8202: 'es_VE', // Spanish - Venezuela\n 1089: 'sw', // Swahili\n 2077: 'sv_FI', // Swedish - Finland\n 1053: 'sv_SE', // Swedish - Sweden\n 1114: 'syc', // Syriac\n 1064: 'tg', // Tajik\n 1097: 'ta', // Tamil\n 1092: 'tt', // Tatar\n 1098: 'te', // Telugu\n 1054: 'th', // Thai\n 1105: 'bo', // Tibetan\n 1073: 'ts', // Tsonga\n 1055: 'tr', // Turkish\n 1090: 'tk', // Turkmen\n 1058: 'uk', // Ukrainian\n 1056: 'ur', // Urdu\n 2115: 'uz_UZ', // Uzbek - Cyrillic\n 1091: 'uz_UZ', // Uzbek - Latin\n 1075: 've', // Venda\n 1066: 'vi', // Vietnamese\n 1106: 'cy', // Welsh\n 1076: 'xh', // Xhosa\n 1085: 'yi', // Yiddish\n 1077: 'zu' // Zulu\n});\n","import codeToLocale from './codeToLocale.js';\n\n// Locale: [language[_territory][.codeset][@modifier]]\nconst re_locale = /^([a-z\\d]+)(?:[_-]([a-z\\d]+))?(?:\\.([a-z\\d]+))?(?:@([a-z\\d]+))?$/i;\nconst locales = {};\n\n/**\n * Split a semicolon delimited string and replace instances of characters\n * @ignore\n * @param {string} str Semicolon delimited string\n * @param {string} [tilde=''] String to be inserted on every instance of ~\n * @returns {string[]} Array of strings\n */\nconst _ = (str, tilde = '') => str.replace(/~/g, tilde).split(';');\n\n/**\n * Generate mmm and ddd properties as needed for locales. Many of them\n * are straightforward abreviations of mmmm and dddd so we can save some\n * bytes by auto-generating them.\n *\n * Both rule parameters use the same system. If shortform prop is missing:\n *\n * - 0 - use long form array unchanged\n * - 1...9 - shorten to N many characters\n * - 11...19 - shorten to 1...9 characters and add periods\n *\n * @ignore\n * @param {object} o Locale object\n * @param {number} [ml=0] Month list rule\n * @param {number} [dl=0] Day list rule\n * @returns {object} The same input object, but with ddd and mmm filled in.\n */\nconst xm = (o, ml = 0, dl = 0) => {\n if (!o.mmm) {\n o.mmm = ml < 1\n ? o.mmmm.concat()\n : o.mmmm.map(d => {\n const s = d.slice(0, ml % 10);\n return s + (ml < 10 || d === s ? '' : '.');\n });\n }\n if (!o.ddd) {\n o.ddd = dl < 1\n ? o.dddd.concat()\n : o.dddd.map(d => {\n const s = d.slice(0, dl % 10);\n return s + (dl < 10 || d === s ? '' : '.');\n });\n }\n if (!o.mmm6 && o.mmmm6) {\n o.mmm6 = o.mmmm6;\n }\n return o;\n};\n\n/**\n * @typedef {object} LocaleData\n * An object of properties used by a formatter when printing a number in a certain locale.\n * @property {string} group - Symbol used as a grouping separator (`1,000,000` uses `,`)\n * @property {string} decimal - Symbol used to separate integers from fractions (usually `.`)\n * @property {string} positive - Symbol used to indicate positive numbers (usually `+`)\n * @property {string} negative - Symbol used to indicate positive numbers (usually `-`)\n * @property {string} percent - Symbol used to indicate a percentage (usually `%`)\n * @property {string} exponent - Symbol used to indicate an exponent (usually `E`)\n * @property {string} nan - Symbol used to indicate NaN values (`NaN`)\n * @property {string} infinity - Symbol used to indicate infinite values (`∞`)\n * @property {Array<string>} ampm - How AM and PM should be presented\n * @property {Array<string>} mmmm6 - Long month names for the Islamic calendar (`Rajab`)\n * @property {Array<string>} mmm6 - Short month names for the Islamic calendar (`Raj.`)\n * @property {Array<string>} mmmm - Long month names for the Gregorian calendar (`November`)\n * @property {Array<string>} mmm - Short month names for the Gregorian calendar (`Nov`)\n * @property {Array<string>} dddd - Long day names (`Wednesday`)\n * @property {Array<string>} ddd - Shortened day names (`Wed`)\n * @property {Array<string>} bool - How TRUE and FALSE should be presented\n * @property {boolean} preferMDY - Is the prefered date format month first (12/31/2025) or day first (31/12/2025)\n */\n\n/**\n * @ignore\n * @type {LocaleData}\n */\nconst baseLocaleData = {\n group: '\\u00A0',\n decimal: '.',\n positive: '+',\n negative: '-',\n percent: '%',\n exponent: 'E',\n nan: 'NaN',\n infinity: '∞',\n ampm: _('AM;PM'),\n mmmm6: _('Muharram;Safar;Rabiʻ I;Rabiʻ II;Jumada I;Jumada II;Rajab;Shaʻban;Ramadan;Shawwal;Dhuʻl-Qiʻdah;Dhuʻl-Hijjah'),\n mmm6: _('Muh.;Saf.;Rab. I;Rab. II;Jum. I;Jum. II;Raj.;Sha.;Ram.;Shaw.;Dhuʻl-Q.;Dhuʻl-H.'),\n mmmm: _('January;February;March;April;May;June;July;August;September;October;November;December'),\n mmm: _('Jan;Feb;Mar;Apr;May;Jun;Jul;Aug;Sep;Oct;Nov;Dec'),\n dddd: _('Sunday;Monday;Tuesday;Wednesday;Thursday;Friday;Saturday'),\n ddd: _('Sun;Mon;Tue;Wed;Thu;Fri;Sat'),\n bool: _('TRUE;FALSE'),\n preferMDY: false\n};\n\n/**\n * @typedef {object} LocaleToken - An object of properties for a locale tag.\n * @property {string} lang - The basic tag such as `zh_CN` or `fi`\n * @property {string} language - The language section (`zh` for `zh_CN`)\n * @property {string} territory - The territory section (`CN` for `zh_CN`)\n */\n\n/**\n * Parse a regular IETF BCP 47 locale tag and emit an object of its parts.\n * Irregular tags and subtags are not supported.\n *\n * @param {string} locale - A BCP 47 string tag of the locale.\n * @returns {LocaleToken} - An object describing the locale.\n */\nexport function parseLocale (locale) {\n const lm = re_locale.exec(locale);\n if (!lm) {\n throw new SyntaxError(`Malformed locale: ${locale}`);\n }\n return {\n lang: lm[1] + (lm[2] ? '_' + lm[2] : ''),\n language: lm[1],\n territory: lm[2] || ''\n };\n}\n\n// MS code format is: aabbcccc [$-aabbcccc]\n// aa = numerical style (optional, 00 if absent)\n// bb = calendar format (optional, 00 if absent)\n// cc = language code\nexport function resolveLocale (l4e) {\n if (typeof l4e === 'number') {\n return codeToLocale[l4e & 0xffff] || null;\n }\n const wincode = parseInt(l4e, 16);\n if (isFinite(wincode) && codeToLocale[wincode & 0xffff]) {\n return codeToLocale[wincode & 0xffff] || null;\n }\n if (re_locale.test(l4e)) {\n return l4e;\n }\n return null;\n}\n\n/**\n * Used by the formatter to pull a locate from its registered locales. If\n * subtag isn't available but the base language is, the base language is used.\n * So if `en-CA` is not found, the formatter tries to find `en` else it\n * returns a `null`.\n *\n * @param {string} locale - A BCP 47 string tag of the locale, or an Excel locale code.\n * @returns {LocaleData | null} - An object of format date properties.\n */\nexport function getLocale (locale) {\n const tag = resolveLocale(locale);\n let obj = null;\n if (tag) {\n const c = parseLocale(tag);\n obj = locales[c.lang] || locales[c.language] || null;\n }\n return obj;\n}\n\n// creates a new locale options object\nexport function createLocale (data) {\n return Object.assign({}, baseLocaleData, data);\n}\n\n/**\n * Register locale data for a language so for use when formatting.\n *\n * Any partial set of properties may be returned to have the defaults used where properties are missing.\n *\n * @see {LocaleData}\n * @param {object} localeSettings - A collection of settings for a locale.\n * @param {string} [localeSettings.group=\"\\u00a0\"]\n * Symbol used as a grouping separator (`1,000,000` uses `,`)\n * @param {string} [localeSettings.decimal=\".\"]\n * Symbol used to separate integers from fractions (usually `.`)\n * @param {string} [localeSettings.positive=\"+\"]\n * Symbol used to indicate positive numbers (usually `+`)\n * @param {string} [localeSettings.negative=\"-\"]\n * Symbol used to indicate positive numbers (usually `-`)\n * @param {string} [localeSettings.percent=\"%\"]\n * Symbol used to indicate a percentage (usually `%`)\n * @param {string} [localeSettings.exponent=\"E\"]\n * Symbol used to indicate an exponent (usually `E`)\n * @param {string} [localeSettings.nan=\"NaN\"]\n * Symbol used to indicate NaN values (`NaN`)\n * @param {string} [localeSettings.infinity=\"∞\"]\n * Symbol used to indicate infinite values (`∞`)\n * @param {Array<string>} [localeSettings.ampm=[\"AM\",\"PM\"]]\n * How AM and PM should be presented.\n * @param {Array<string>} [localeSettings.mmmm6=[\"Muharram\", \"Safar\", \"Rabiʻ I\", \"Rabiʻ II\", \"Jumada I\", \"Jumada II\", \"Rajab\", \"Shaʻban\", \"Ramadan\", \"Shawwal\", \"Dhuʻl-Qiʻdah\", \"Dhuʻl-Hijjah\"]]\n * Long month names for the Islamic calendar (e.g. `Rajab`)\n * @param {Array<string>} [localeSettings.mmm6=[\"Muh.\", \"Saf.\", \"Rab. I\", \"Rab. II\", \"Jum. I\", \"Jum. II\", \"Raj.\", \"Sha.\", \"Ram.\", \"Shaw.\", \"Dhuʻl-Q.\", \"Dhuʻl-H.\"]]\n * Short month names for the Islamic calendar (e.g. `Raj.`)\n * @param {Array<string>} [localeSettings.mmmm=[\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"]]\n * Long month names for the Gregorian calendar (e.g. `November`)\n * @param {Array<string>} [localeSettings.mmm=[\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]]\n * Short month names for the Gregorian calendar (e.g. `Nov`)\n * @param {Array<string>} [localeSettings.dddd=[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]]\n * Long day names (e.g. `Wednesday`)\n * @param {Array<string>} [localeSettings.ddd=[\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]]\n * Shortened day names (e.g. `Wed`)\n * @param {Array<string>} [localeSettings.bool=[\"TRUE\", \"FALSE\"]]\n * How TRUE and FALSE should be presented.\n * @param {boolean} [localeSettings.preferMDY=false]\n * Is the prefered date format month first (12/31/2025) or day first (31/12/2025)\n * @param {string} l4e - A string BCP 47 tag of the locale.\n * @returns {LocaleData} - A full collection of settings for a locale\n */\nexport function addLocale (localeSettings, l4e) {\n // parse language tag\n const c = typeof l4e === 'object' ? l4e : parseLocale(l4e);\n // add the language\n locales[c.lang] = createLocale(localeSettings);\n // if \"xx_YY\" is added also create \"xx\" if it is missing\n if (c.language !== c.lang && !locales[c.language]) {\n locales[c.language] = createLocale(localeSettings);\n }\n return locales[c.lang];\n}\n\nexport function listLocales () {\n return Object.keys(locales);\n}\n\nexport const defaultLocale = createLocale({ group: ',', preferMDY: true });\ndefaultLocale.isDefault = true;\n\naddLocale({\n group: ',',\n ampm: _('上午;下午'),\n mmmm: _('一月;二月;三月;四月;五月;六月;七月;八月;九月;十月;十一月;十二月'),\n mmm: _('1月;2月;3月;4月;5月;6月;7月;8月;9月;10月;11月;12月'),\n dddd: _('~日;~一;~二;~三;~四;~五;~六', '星期'),\n ddd: _('周日;周一;周二;周三;周四;周五;周六')\n}, 'zh_CN');\nconst _zh = {\n group: ',',\n ampm: _('上午;下午'),\n mmmm: _('1月;2月;3月;4月;5月;6月;7月;8月;9月;10月;11月;12月'),\n mmm: _('1月;2月;3月;4月;5月;6月;7月;8月;9月;10月;11月;12月'),\n dddd: _('~日;~一;~二;~三;~四;~五;~六', '星期'),\n ddd: _('周日;周一;周二;周三;周四;周五;周六')\n};\naddLocale({\n ..._zh,\n nan: '非數值',\n dddd: _('~日;~一;~二;~三;~四;~五;~六', '星期')\n}, 'zh_TW');\naddLocale({\n ..._zh,\n dddd: _('~日;~一;~二;~三;~四;~五;~六', '星期')\n}, 'zh_HK');\n\naddLocale({\n ..._zh,\n ampm: _('午前;午後'),\n dddd: _('日~;月~;火~;水~;木~;金~;土~', '曜日'),\n ddd: _('日;月;火;水;木;金;土')\n}, 'ja');\n\naddLocale({\n group: ',',\n ampm: _('오전;오후'),\n mmmm: _('1월;2월;3월;4월;5월;6월;7월;8월;9월;10월;11월;12월'),\n mmm: _('1월;2월;3월;4월;5월;6월;7월;8월;9월;10월;11월;12월'),\n dddd: _('일요일;월요일;화요일;수요일;목요일;금요일;토요일'),\n ddd: _('일;월;화;수;목;금;토')\n}, 'ko');\n\naddLocale({\n group: ',',\n ampm: _('ก่อนเที่ยง;หลังเที่ยง'),\n mmmm: _('มกร~;กุมภาพันธ์;มีน~;เมษายน;พฤษภ~;มิถุนายน;กรกฎ~;สิงห~;กันยายน;ตุล~;พฤศจิกายน;ธันว~', 'าคม'),\n mmm: _('ม.ค.;ก.พ.;มี.ค.;เม.ย.;พ.ค.;มิ.ย.;ก.ค.;ส.ค.;ก.ย.;ต.ค.;พ.ย.;ธ.ค.'),\n dddd: _('วันอาทิตย์;วันจันทร์;วันอังคาร;วันพุธ;วันพฤหัสบดี;วันศุกร์;วันเสาร์'),\n ddd: _('อา.;จ.;อ.;พ.;พฤ.;ศ.;ส.')\n}, 'th');\n\naddLocale(xm({\n decimal: ',',\n ampm: _('dop.;odp.'),\n mmmm: _('ledna;února;března;dubna;května;června;července;srpna;září;října;listopadu;prosince'),\n mmm: _('I;II;III;IV;V;VI;VII;VIII;IX;X;XI;XII'),\n dddd: _('neděle;pondělí;úterý;středa;čtvrtek;pátek;sobota'),\n bool: _('PRAVDA;NEPRAVDA')\n}, -1, 2), 'cs');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n mmmm: _('januar;februar;marts;april;maj;juni;juli;august;september;oktober;november;december'),\n dddd: _('søn~;man~;tirs~;ons~;tors~;fre~;lør~', 'dag'),\n bool: _('SAND;FALSK')\n}, 13, 13), 'da');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n ampm: _('a.m.;p.m.'),\n mmmm: _('januari;februari;maart;april;mei;juni;juli;augustus;september;oktober;november;december'),\n mmm: _('jan.;feb.;mrt.;apr.;mei;jun.;jul.;aug.;sep.;okt.;nov.;dec.'),\n dddd: _('zondag;maandag;dinsdag;woensdag;donderdag;vrijdag;zaterdag'),\n bool: _('WAAR;ONWAAR')\n}, -1, 2), 'nl');\n\naddLocale({ group: ',', preferMDY: true }, 'en');\naddLocale({ group: ',', preferMDY: true }, 'en_US');\naddLocale({ group: ',' }, 'en_AU');\naddLocale({ group: ',' }, 'en_CA');\naddLocale({ group: ',' }, 'en_GB');\naddLocale({ group: ',', mmm: _('Jan;Feb;Mar;Apr;May;Jun;Jul;Aug;Sept;Oct;Nov;Dec') }, 'en_IE');\n\naddLocale(xm({\n decimal: ',',\n nan: 'epäluku',\n ampm: _('ap.;ip.'),\n mmmm: _('tammi~;helmi~;maalis~;huhti~;touko~;kesä~;heinä~;elo~;syys~;loka~;marras~;joulu~', 'kuuta'),\n mmm: _('tammik.;helmik.;maalisk.;huhtik.;toukok.;kesäk.;heinäk.;elok.;syysk.;lokak.;marrask.;jouluk.'),\n dddd: _('sunnun~;maanan~;tiis~;keskiviikkona;tors~;perjan~;lauan~', 'taina'),\n bool: _('TOSI;EPÄTOSI')\n}, -1, 2), 'fi');\n\nconst _fr = xm({\n group: '\\u202f',\n decimal: ',',\n mmmm: _('janvier;février;mars;avril;mai;juin;juillet;août;septembre;octobre;novembre;décembre'),\n mmm: _('janv.;févr.;mars;avr.;mai;juin;juil.;août;sept.;oct.;nov.;déc.'),\n dddd: _('~manche;lun~;mar~;mercre~;jeu~;vendre~;same~', 'di'),\n bool: _('VRAI;FAUX')\n}, -1, 13);\naddLocale({ ..._fr }, 'fr');\naddLocale({ ..._fr, mmm: _('janv.;févr.;mars;avr.;mai;juin;juill.;août;sept.;oct.;nov.;déc.') }, 'fr_CA');\naddLocale({ group: \"'\", decimal: '.', ..._fr }, 'fr_CH');\n\nconst _de = xm({\n mmmm: _('Januar;Februar;März;April;Mai;Juni;Juli;August;September;Oktober;November;Dezember'),\n mmm: _('Jan.;Feb.;März;Apr.;Mai;Juni;Juli;Aug.;Sept.;Okt.;Nov.;Dez.'),\n dddd: _('Sonn~;Mon~;Diens~;Mittwoch;Donners~;Frei~;Sams~', 'tag'),\n bool: _('WAHR;FALSCH')\n}, -1, 12);\naddLocale({ group: '.', decimal: ',', ..._de }, 'de');\naddLocale({ group: \"'\", decimal: '.', ..._de }, 'de_CH');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n ampm: _('π.μ.;μ.μ.'),\n mmmm: _('Ιανουαρ~;Φεβρουαρ~;Μαρτ~;Απριλ~;Μαΐου;Ιουν~;Ιουλ~;Αυγούστου;Σεπτεμβρ~;Οκτωβρ~;Νοεμβρ~;Δεκεμβρ~', 'ίου'),\n mmm: _('Ιαν;Φεβ;Μαρ;Απρ;Μαΐ;Ιουν;Ιουλ;Αυγ;Σεπ;Οκτ;Νοε;Δεκ'),\n dddd: _('Κυριακή;Δευτέρα;Τρίτη;Τετάρτη;Πέμπτη;Παρασκευή;Σάββατο')\n}, -1, 3), 'el');\n\naddLocale({\n decimal: ',',\n ampm: _('de.;du.'),\n mmmm: _('január;február;március;április;május;június;július;augusztus;szeptember;október;november;december'),\n mmm: _('jan.;febr.;márc.;ápr.;máj.;jún.;júl.;aug.;szept.;okt.;nov.;dec.'),\n dddd: _('vasárnap;hétfő;kedd;szerda;csütörtök;péntek;szombat'),\n ddd: _('V;H;K;Sze;Cs;P;Szo'),\n bool: _('IGAZ;HAMIS')\n}, 'hu');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n ampm: _('f.h.;e.h.'),\n mmmm: _('janúar;febrúar;mars;apríl;maí;júní;júlí;ágúst;september;október;nóvember;desember'),\n dddd: _('sunnu~;mánu~;þriðju~;miðviku~;fimmtu~;föstu~;laugar~', 'dagur')\n}, 13, 13), 'is');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n mmmm: _('Januari;Februari;Maret;April;Mei;Juni;Juli;Agustus;September;Oktober;November;Desember'),\n dddd: _('Minggu;Senin;Selasa;Rabu;Kamis;Jumat;Sabtu')\n}, 3, 3), 'id');\n\nconst _it = xm({\n mmmm: _('gennaio;febbraio;marzo;aprile;maggio;giugno;luglio;agosto;settembre;ottobre;novembre;dicembre'),\n dddd: _('domenica;lunedì;martedì;mercoledì;giovedì;venerdì;sabato'),\n bool: _('VERO;FALSO')\n}, 3, 3);\naddLocale({ group: '.', decimal: ',', ..._it }, 'it');\naddLocale({ group: \"'\", decimal: '.', ..._it }, 'it_CH');\n\nconst _no = {\n decimal: ',',\n ampm: _('a.m.;p.m.'),\n mmmm: _('januar;februar;mars;april;mai;juni;juli;august;september;oktober;november;desember'),\n mmm: _('jan.;feb.;mar.;apr.;mai;jun.;jul.;aug.;sep.;okt.;nov.;des.'),\n dddd: _('søn~;man~;tirs~;ons~;tors~;fre~;lør~', 'dag'),\n bool: _('SANN;USANN')\n};\naddLocale(xm({ ..._no }, -1, 13), 'nb');\naddLocale(xm({ ..._no }, -1, 13), 'no');\n\naddLocale(xm({\n decimal: ',',\n mmmm: _('stycznia;lutego;marca;kwietnia;maja;czerwca;lipca;sierpnia;września;października;listopada;grudnia'),\n dddd: _('niedziela;poniedziałek;wtorek;środa;czwartek;piątek;sobota'),\n ddd: _('niedz.;pon.;wt.;śr.;czw.;pt.;sob.'),\n bool: _('PRAWDA;FAŁSZ')\n}, 3, -1), 'pl');\n\nconst _pt = {\n group: '.',\n decimal: ',',\n mmmm: _('janeiro;fevereiro;março;abril;maio;junho;julho;agosto;setembro;outubro;novembro;dezembro'),\n dddd: _('domingo;segunda-feira;terça-feira;quarta-feira;quinta-feira;sexta-feira;sábado'),\n bool: _('VERDADEIRO;FALSO')\n};\naddLocale(xm(_pt, 13, 13), 'pt');\naddLocale(xm(_pt, 13, 13), 'pt_BR');\n\naddLocale({\n decimal: ',',\n nan: 'не\\u00A0число',\n mmmm: _('января;февраля;марта;апреля;мая;июня;июля;августа;сентября;октября;ноября;декабря'),\n mmm: _('янв.;февр.;мар.;апр.;мая;июн.;июл.;авг.;сент.;окт.;нояб.;дек.'),\n dddd: _('воскресенье;понедельник;вторник;среда;четверг;пятница;суббота'),\n ddd: _('вс;пн;вт;ср;чт;пт;сб'),\n mmmm6: _('рамадан;шавваль;зуль-каада;зуль-хиджжа;мухаррам;раби-уль-авваль;раби-уль-ахир;джумад-уль-авваль;джумад-уль-ахир;раджаб;шаабан;рамадан'),\n mmm6: _('рам.;шав.;зуль-к.;зуль-х.;мух.;раб. I;раб. II;джум. I;джум. II;радж.;шааб.;рам.'),\n bool: _('ИСТИНА;ЛОЖЬ')\n}, 'ru');\n\naddLocale(xm({\n decimal: ',',\n mmmm: _('januára;februára;marca;apríla;mája;júna;júla;augusta;septembra;októbra;novembra;decembra'),\n dddd: _('nedeľa;pondelok;utorok;streda;štvrtok;piatok;sobota')\n}, 3, 2), 'sk');\n\nconst _es = {\n group: '.',\n decimal: ',',\n ampm: _('a.\\u00A0m.;p.\\u00A0m.'),\n mmmm: _('enero;febrero;marzo;abril;mayo;junio;julio;agosto;septiem~;octu~;noviem~;diciem~', 'bre'),\n mmm: _('ene;feb;mar;abr;may;jun;jul;ago;sept;oct;nov;dic'),\n dddd: _('domingo;lunes;martes;miércoles;jueves;viernes;sábado'),\n ddd: _('dom;lun;mar;mié;jue;vie;sáb'),\n bool: _('VERDADERO;FALSO')\n};\nconst _esM3 = _('ene;feb;mar;abr;may;jun;jul;ago;sep;oct;nov;dic');\nconst _esM13 = _('ene.;feb.;mar.;abr.;may.;jun.;jul.;ago.;sept.;oct.;nov.;dic.');\naddLocale({ ..._es }, 'es');\naddLocale({ ..._es }, 'es_AR');\naddLocale({ ..._es }, 'es_BO');\naddLocale({ ..._es }, 'es_CL');\naddLocale({ ..._es }, 'es_CO');\naddLocale({ ..._es }, 'es_EC');\naddLocale({ ..._es, mmm: _esM3, ampm: _('a.m.;p.m.') }, 'es_MX');\naddLocale({ ..._es, mmm: _esM13 }, 'es_PY');\naddLocale({ ..._es, mmm: _esM13 }, 'es_UY');\naddLocale({ ..._es, mmm: _esM13, mmmm: _('enero;febrero;marzo;abril;mayo;junio;julio;agosto;setiembre;octubre;noviembre;diciembre') }, 'es_VE');\n\naddLocale({\n decimal: ',',\n ampm: _('fm;em'),\n mmmm: _('januari;februari;mars;april;maj;juni;juli;augusti;september;oktober;november;december'),\n mmm: _('jan.;feb.;mars;apr.;maj;juni;juli;aug.;sep.;okt.;nov.;dec.'),\n dddd: _('sön~;mån~;tis~;ons~;tors~;fre~;lör~', 'dag'),\n ddd: _('sön;mån;tis;ons;tors;fre;lör')\n}, 'sv');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n ampm: _('ÖÖ;ÖS'),\n mmmm: _('Ocak;Şubat;Mart;Nisan;Mayıs;Haziran;Temmuz;Ağustos;Eylül;Ekim;Kasım;Aralık'),\n mmm: _('Oca;Şub;Mar;Nis;May;Haz;Tem;Ağu;Eyl;Eki;Kas;Ara'),\n dddd: _('Pazar;Pazartesi;Salı;Çarşamba;Perşembe;Cuma;Cumartesi'),\n ddd: _('Paz;Pzt;Sal;Çar;Per;Cum;Cmt'),\n bool: _('DOĞRU;YANLIŞ')\n}, 3, -1), 'tr');\n\naddLocale({\n group: ',',\n ampm: _('yb;yh'),\n mmmm: _('Ionawr;Chwefror;Mawrth;Ebrill;Mai;Mehefin;Gorffennaf;Awst;Medi;Hydref;Tachwedd;Rhagfyr'),\n mmm: _('Ion;Chwef;Maw;Ebr;Mai;Meh;Gorff;Awst;Medi;Hyd;Tach;Rhag'),\n dddd: _('Dydd Sul;Dydd Llun;Dydd Mawrth;Dydd Mercher;Dydd Iau;Dydd Gwener;Dydd Sadwrn'),\n ddd: _('Sul;Llun;Maw;Mer;Iau;Gwen;Sad')\n}, 'cy');\n\naddLocale({\n group: '.',\n decimal: ',',\n mmmm: _('yanvar;fevral;mart;aprel;may;iyun;iyul;avqust;sentyabr;oktyabr;noyabr;dekabr'),\n mmm: _('yan;fev;mar;apr;may;iyn;iyl;avq;sen;okt;noy;dek'),\n dddd: _('bazar;bazar ertəsi;çərşənbə axşamı;çərşənbə;cümə axşamı;cümə;şənbə'),\n ddd: _('B.;B.e.;Ç.a.;Ç.;C.a.;C.;Ş.')\n}, 'az');\n\naddLocale(xm({\n decimal: ',',\n mmmm: _('студзеня;лютага;сакавіка;красавіка;мая;чэрвеня;ліпеня;жніўня;верасня;кастрычніка;лістапада;снежня'),\n dddd: _('нядзеля;панядзелак;аўторак;серада;чацвер;пятніца;субота'),\n ddd: _('нд;пн;аў;ср;чц;пт;сб')\n}, 3, -1), 'be');\n\naddLocale({\n decimal: ',',\n ampm: _('пр.об.;сл.об.'),\n mmmm: _('януари;февруари;март;април;май;юни;юли;август;септември;октомври;ноември;декември'),\n mmm: _('яну;фев;март;апр;май;юни;юли;авг;сеп;окт;ное;дек'),\n dddd: _('неделя;понеделник;вторник;сряда;четвъртък;петък;събота'),\n ddd: _('нд;пн;вт;ср;чт;пт;сб'),\n bool: _('ИСТИНА;ЛОЖЬ')\n}, 'bg');\n\naddLocale({\n group: '.',\n decimal: ',',\n mmmm: _('de gener;de febrer;de març;d’abril;de maig;de juny;de juliol;d’agost;de setembre;d’octubre;de novembre;de desembre'),\n mmm: _('de gen.;de febr.;de març;d’abr.;de maig;de juny;de jul.;d’ag.;de set.;d’oct.;de nov.;de des.'),\n dddd: _('diumenge;dilluns;dimarts;dimecres;dijous;divendres;dissabte'),\n ddd: _('dg.;dl.;dt.;dc.;dj.;dv.;ds.'),\n ampm: _('a.\\u00a0m.;p.\\u00a0m.')\n}, 'ca');\n\naddLocale(xm({\n group: ',',\n decimal: '.',\n mmmm: _('Enero;Pebrero;Marso;Abril;Mayo;Hunyo;Hulyo;Agosto;Setyembre;Oktubre;Nobyembre;Disyembre'),\n dddd: _('Linggo;Lunes;Martes;Miyerkules;Huwebes;Biyernes;Sabado')\n}, 3, 3), 'fil');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('જાન્યુઆરી;ફેબ્રુઆરી;માર્ચ;એપ્રિલ;મે;જૂન;જુલાઈ;ઑગસ્ટ;સપ્ટેમ્બર;ઑક્ટોબર;નવેમ્બર;ડિસેમ્બર'),\n mmm: _('જાન્યુ;ફેબ્રુ;માર્ચ;એપ્રિલ;મે;જૂન;જુલાઈ;ઑગસ્ટ;સપ્ટે;ઑક્ટો;નવે;ડિસે'),\n dddd: _('રવિ~;સોમ~;મંગળ~;બુધ~;ગુરુ~;શુક્ર~;શનિ~', 'વાર'),\n ddd: _('રવિ;સોમ;મંગળ;બુધ;ગુરુ;શુક્ર;શનિ')\n}, 'gu');\n\naddLocale({\n group: ',',\n decimal: '.',\n ampm: _('לפנה״צ;אחה״צ'),\n dddd: _('~ראשון;~שני;~שלישי;~רביעי;~חמישי;~שישי;~שבת', 'יום '),\n ddd: _('~א׳;~ב׳;~ג׳;~ד׳;~ה׳;~ו׳;שבת', 'יום '),\n mmmm: _('ינואר;פברואר;מרץ;אפריל;מאי;יוני;יולי;אוגוסט;ספטמבר;אוקטובר;נובמבר;דצמבר'),\n mmm: _('ינו׳;פבר׳;מרץ;אפר׳;מאי;יוני;יולי;אוג׳;ספט׳;אוק׳;נוב׳;דצמ׳'),\n mmmm6: _('רמדאן;שוואל;ד׳ו אל־קעדה;ד׳ו אל־חיג׳ה;מוחרם;רביע אל־אוול;רביע א־ת׳אני;ג׳ומאדא אל־אולא;ג׳ומאדא א־ת׳אניה;רג׳ב;שעבאן;רמדאן'),\n mmm6: _('רמדאן;שוואל;ד׳ו אל־קעדה;ד׳ו אל־חיג׳ה;מוחרם;רביע א׳;רביע ב׳;ג׳ומאדא א׳;ג׳ומאדא ב׳;רג׳ב;שעבאן;רמדאן')\n}, 'he');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n mmmm: _('siječnja;veljače;ožujka;travnja;svibnja;lipnja;srpnja;kolovoza;rujna;listopada;studenoga;prosinca'),\n mmm: _('sij;velj;ožu;tra;svi;lip;srp;kol;ruj;lis;stu;pro'),\n dddd: _('nedjelja;ponedjeljak;utorak;srijeda;četvrtak;petak;subota')\n}, -1, 3), 'hr');\n\naddLocale({\n decimal: ',',\n mmmm: _('հունվարի;փետրվարի;մարտի;ապրիլի;մայիսի;հունիսի;հուլիսի;օգոստոսի;սեպտեմբերի;հոկտեմբերի;նոյեմբերի;դեկտեմբերի'),\n mmm: _('հնվ;փտվ;մրտ;ապր;մյս;հնս;հլս;օգս;սեպ;հոկ;նոյ;դեկ'),\n dddd: _('կիրակի;երկուշաբթի;երեքշաբթի;չորեքշաբթի;հինգշաբթի;ուրբաթ;շաբաթ'),\n ddd: _('կիր;երկ;երք;չրք;հնգ;ուր;շբթ')\n}, 'hy');\n\naddLocale(xm({\n decimal: ',',\n mmmm: _('იანვარი;თებერვალი;მარტი;აპრილი;მაისი;ივნისი;ივლისი;აგვისტო;სექტემბერი;ოქტომბერი;ნოემბერი;დეკემბერი'),\n dddd: _('კვირა;ორშაბათი;სამშაბათი;ოთხშაბათი;ხუთშაბათი;პარასკევი;შაბათი')\n}, 3, 3), 'ka');\n\naddLocale(xm({\n decimal: ',',\n mmmm: _('қаңтар;ақпан;наурыз;сәуір;мамыр;маусым;шілде;тамыз;қыркүйек;қазан;қараша;желтоқсан'),\n dddd: _('жексенбі;дүйсенбі;сейсенбі;сәрсенбі;бейсенбі;жұма;сенбі'),\n ddd: _('жс;дс;сс;ср;бс;жм;сб')\n}, 13, -1), 'kk');\n\naddLocale({\n group: ',',\n mmmm: _('ಜನವರಿ;ಫೆಬ್ರವರಿ;ಮಾರ್ಚ್;ಏಪ್ರಿಲ್;ಮೇ;ಜೂನ್;ಜುಲೈ;ಆಗಸ್ಟ್;ಸೆಪ್ಟೆಂಬರ್;ಅಕ್ಟೋಬರ್;ನವೆಂಬರ್;ಡಿಸೆಂಬರ್'),\n mmm: _('ಜನವರಿ;ಫೆಬ್ರವರಿ;ಮಾರ್ಚ್;ಏಪ್ರಿ;ಮೇ;ಜೂನ್;ಜುಲೈ;ಆಗಸ್ಟ್;ಸೆಪ್ಟೆಂ;ಅಕ್ಟೋ;ನವೆಂ;ಡಿಸೆಂ'),\n dddd: _('ಭಾನು~;ಸೋಮ~;ಮಂಗಳ~;ಬುಧ~;ಗುರು~;ಶುಕ್ರ~;ಶನಿ~', 'ವಾರ'),\n ddd: _('ಭಾನು;ಸೋಮ;ಮಂಗಳ;ಬುಧ;ಗುರು;ಶುಕ್ರ;ಶನಿ'),\n ampm: _('ಪೂರ್ವಾಹ್ನ;ಅಪರಾಹ್ನ')\n}, 'kn');\n\naddLocale({\n decimal: ',',\n mmmm: _('sausio;vasario;kovo;balandžio;gegužės;birželio;liepos;rugpjūčio;rugsėjo;spalio;lapkričio;gruodžio'),\n mmm: _('saus.;vas.;kov.;bal.;geg.;birž.;liep.;rugp.;rugs.;spal.;lapkr.;gruod.'),\n dddd: _('sekmadienis;pirmadienis;antradienis;trečiadienis;ketvirtadienis;penktadienis;šeštadienis'),\n ddd: _('sk;pr;an;tr;kt;pn;št'),\n ampm: _('priešpiet;popiet')\n}, 'lt');\n\naddLocale({\n decimal: ',',\n mmmm: _('janvāris;februāris;marts;aprīlis;maijs;jūnijs;jūlijs;augusts;septembris;oktobris;novembris;decembris'),\n mmm: _('janv.;febr.;marts;apr.;maijs;jūn.;jūl.;aug.;sept.;okt.;nov.;dec.'),\n dddd: _('svētdiena;pirmdiena;otrdiena;trešdiena;ceturtdiena;piektdiena;sestdiena'),\n ddd: _('svētd.;pirmd.;otrd.;trešd.;ceturtd.;piektd.;sestd.'),\n ampm: _('priekšpusdienā;pēcpusdienā')\n}, 'lv');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('ജനുവരി;ഫെബ്രുവരി;മാർച്ച്;ഏപ്രിൽ;മേയ്;ജൂൺ;ജൂലൈ;ഓഗസ്റ്റ്;സെപ്റ്റംബർ;ഒക്ടോബർ;നവംബർ;ഡിസംബർ'),\n mmm: _('ജനു;ഫെബ്രു;മാർ;ഏപ്രി;മേയ്;ജൂൺ;ജൂലൈ;ഓഗ;സെപ്റ്റം;ഒക്ടോ;നവം;ഡിസം'),\n dddd: _('ഞായറാഴ്ച;തിങ്കളാഴ്ച;ചൊവ്വാഴ്ച;ബുധനാഴ്ച;വ്യാഴാഴ്ച;വെള്ളിയാഴ്ച;ശനിയാഴ്ച'),\n ddd: _('ഞായർ;തിങ്കൾ;ചൊവ്വ;ബുധൻ;വ്യാഴം;വെള്ളി;ശനി')\n}, 'ml');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('нэгдүгээ~;хоёрдугаа~;гуравдугаа~;дөрөвдүгээ~;тавдугаа~;зургаадугаа~;долоодугаа~;наймдугаа~;есдүгээ~;аравдугаа~;арван нэгдүгээ~;арван хоёрдугаа~', 'р сар'),\n mmm: _('1~;2~;3~;4~;5~;6~;7~;8~;9~;10~;11~;12~', '-р сар'),\n dddd: _('ням;даваа;мягмар;лхагва;пүрэв;баасан;бямба'),\n ddd: _('Ня;Да;Мя;Лх;Пү;Ба;Бя'),\n ampm: _('ү.ө.;ү.х.')\n}, 'mn');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('जानेवारी;फेब्रुवारी;मार्च;एप्रिल;मे;जून;जुलै;ऑगस्ट;सप्टेंबर;ऑक्टोबर;नोव्हेंबर;डिसेंबर'),\n mmm: _('जाने;फेब्रु;मार्च;एप्रि;मे;जून;जुलै;ऑग;सप्टें;ऑक्टो;नोव्हें;डिसें'),\n dddd: _('रविवार;सोमवार;मंगळवार;बुधवार;गुरुवार;शुक्रवार;शनिवार'),\n ddd: _('रवि;सोम;मंगळ;बुध;गुरु;शुक्र;शनि')\n}, 'mr');\n\naddLocale(xm({\n group: ',',\n decimal: '.',\n mmmm: _('ဇန်နဝါရီ;ဖေဖော်ဝါရီ;မတ်;ဧပြီ;မေ;ဇွန်;ဇူလိုင်;ဩဂုတ်;စက်တင်ဘာ;အောက်တိုဘာ;နိုဝင်ဘာ;ဒီဇင်ဘာ'),\n mmm: _('ဇန်;ဖေ;မတ်;ဧ;မေ;ဇွန်;ဇူ;ဩ;စက်;အောက်;နို;ဒီ'),\n dddd: _('တနင်္ဂနွေ;တနင်္လာ;အင်္ဂါ;ဗုဒ္ဓဟူး;ကြာသပတေး;သောကြာ;စနေ'),\n ampm: _('နံနက်;ညနေ')\n}, -1, 0), 'my');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('ਜਨਵਰੀ;ਫ਼ਰਵਰੀ;ਮਾਰਚ;ਅਪ੍ਰੈਲ;ਮਈ;ਜੂਨ;ਜੁਲਾਈ;ਅਗਸਤ;ਸਤੰਬਰ;ਅਕਤੂਬਰ;ਨਵੰਬਰ;ਦਸੰਬਰ'),\n mmm: _('ਜਨ;ਫ਼ਰ;ਮਾਰਚ;ਅਪ੍ਰੈ;ਮਈ;ਜੂਨ;ਜੁਲਾ;ਅਗ;ਸਤੰ;ਅਕਤੂ;ਨਵੰ;ਦਸੰ'),\n dddd: _('ਐਤਵਾਰ;ਸੋਮਵਾਰ;ਮੰਗਲਵਾਰ;ਬੁੱਧਵਾਰ;ਵੀਰਵਾਰ;ਸ਼ੁੱਕਰਵਾਰ;ਸ਼ਨਿੱਚਰਵਾਰ'),\n ddd: _('ਐਤ;ਸੋਮ;ਮੰਗਲ;ਬੁੱਧ;ਵੀਰ;ਸ਼ੁੱਕਰ;ਸ਼ਨਿੱਚਰ'),\n ampm: _('ਪੂ.ਦੁ.;ਬਾ.ਦੁ.')\n}, 'pa');\n\naddLocale({\n group: '.',\n decimal: ',',\n mmmm: _('ianuarie;februarie;martie;aprilie;mai;iunie;iulie;august;septem~;octom~;noiem~;decem~', 'brie'),\n mmm: _('ian.;feb.;mar.;apr.;mai;iun.;iul.;aug.;sept.;oct.;nov.;dec.'),\n dddd: _('duminică;luni;marți;miercuri;joi;vineri;sâmbătă'),\n ddd: _('dum.;lun.;mar.;mie.;joi;vin.;sâm.'),\n ampm: _('a.m.;p.m.')\n}, 'ro');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n mmmm: _('januar;februar;marec;april;maj;junij;julij;avgust;september;oktober;november;december'),\n mmm: _('jan.;feb.;mar.;apr.;maj;jun.;jul.;avg.;sep.;okt.;nov.;dec.'),\n dddd: _('nedelja;ponedeljek;torek;sreda;četrtek;petek;sobota'),\n ampm: _('dop.;pop.')\n}, -1, 13), 'sl');\n\naddLocale(xm({\n group: '.',\n decimal: ',',\n mmmm: _('јануар;фебруар;март;април;мај;јун;јул;август;септембар;октобар;новембар;децембар'),\n dddd: _('недеља;понедељак;уторак;среда;четвртак;петак;субота')\n}, 3, 3), 'sr');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('ஜனவரி;பிப்ரவரி;மார்ச்;ஏப்ரல்;மே;ஜூன்;ஜூலை;ஆகஸ்ட்;செப்டம்பர்;அக்டோபர்;நவம்பர்;டிசம்பர்'),\n mmm: _('ஜன.;பிப்.;மார்.;ஏப்.;மே;ஜூன்;ஜூலை;ஆக.;செப்.;அக்.;நவ.;டிச.'),\n dddd: _('ஞாயிறு;திங்கள்;செவ்வாய்;புதன்;வியாழன்;வெள்ளி;சனி'),\n ddd: _('ஞாயி.;திங்.;செவ்.;புத.;வியா.;வெள்.;சனி')\n}, 'ta');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('జనవరి;ఫిబ్రవరి;మార్చి;ఏప్రిల్;మే;జూన్;జులై;ఆగస్టు;సెప్టెంబర్;అక్టోబర్;నవంబర్;డిసెంబర్'),\n mmm: _('జన;ఫిబ్ర;మార్చి;ఏప్రి;మే;జూన్;జులై;ఆగ;సెప్టెం;అక్టో;నవం;డిసెం'),\n dddd: _('ఆదివారం;సోమవారం;మంగళవారం;బుధవారం;గురువారం;శుక్రవారం;శనివారం'),\n ddd: _('ఆది;సోమ;మంగళ;బుధ;గురు;శుక్ర;శని')\n}, 'te');\n\naddLocale({\n decimal: ',',\n mmmm: _('січня;лютого;березня;квітня;травня;червня;липня;серпня;вересня;жовтня;листопада;грудня'),\n mmm: _('січ.;лют.;бер.;квіт.;трав.;черв.;лип.;серп.;вер.;жовт.;лист.;груд.'),\n dddd: _('неділю;понеділок;вівторок;середу;четвер;пʼятницю;суботу'),\n ddd: _('нд;пн;вт;ср;чт;пт;сб'),\n ampm: _('дп;пп')\n}, 'uk');\n\naddLocale({\n group: '.',\n decimal: ',',\n mmmm: _('~1;~2;~3;~4;~5;~6;~7;~8;~9;~10;~11;~12', 'tháng '),\n mmm: _('~1;~2;~3;~4;~5;~6;~7;~8;~9;~10;~11;~12', 'thg '),\n dddd: _('Chủ Nhật;Thứ Hai;Thứ Ba;Thứ Tư;Thứ Năm;Thứ Sáu;Thứ Bảy'),\n ddd: _('CN;Th 2;Th 3;Th 4;Th 5;Th 6;Th 7'),\n ampm: _('SA;CH')\n}, 'vi');\n\naddLocale(xm({\n group: '٬',\n decimal: '٫',\n ampm: _('ص;م'),\n mmmm: _('يناير;فبراير;مارس;أبريل;مايو;يونيو;يوليو;أغسطس;سبتمبر;أكتوبر;نوفمبر;ديسمبر'),\n dddd: _('الأحد;الاثنين;الثلاثاء;الأربعاء;الخميس;الجمعة;السبت'),\n mmmm6: _('رمضان;شوال;ذو القعدة;ذو الحجة;محرم;ربيع الأول;ربيع الآخرة;جمادى الأولى;جمادى الآخرة;رجب;شعبان;رمضان')\n}, 0, 0), 'ar');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('জানুয়ারী;ফেব্রুয়ারী;মার্চ;এপ্রিল;মে;জুন;জুলাই;আগস্ট;সেপ্টেম্বর;অক্টোবর;নভেম্বর;ডিসেম্বর'),\n mmm: _('জানু;ফেব;মার্চ;এপ্রি;মে;জুন;জুল;আগ;সেপ্টেঃ;অক্টোঃ;নভেঃ;ডিসেঃ'),\n dddd: _('রবিবার;সোমবার;মঙ্গলবার;বুধবার;বৃহস্পতিবার;শুক্রবার;শনিবার'),\n ddd: _('রবি;সোম;মঙ্গল;বুধ;বৃহস্পতি;শুক্র;শনি')\n}, 'bn');\n\naddLocale({\n group: ',',\n decimal: '.',\n mmmm: _('जनवरी;फ़रवरी;मार्च;अप्रैल;मई;जून;जुलाई;अगस्त;सितंबर;अक्तूबर;नवंबर;दिसंबर'),\n mmm: _('जन॰;फ़र॰;मार्च;अप्रैल;मई;जून;जुल॰;अग॰;सित॰;अक्तू॰;नव॰;दिस॰'),\n dddd: _('रविवार;सोमवार;मंगलवार;बुधवार;गुरुवार;शुक्रवार;शनिवार'),\n ddd: _('रवि;सोम;मंगल;बुध;गुरु;शुक्र;शनि'),\n ampm: _('am;pm')\n}, 'hi');\n\n","export const defaultOptions = {\n // Overflow error string\n overflow: '######', // dateErrorThrow needs to be off! [prev in locale]\n // Should it throw when there is an overflow error?\n dateErrorThrows: false,\n // Should it emit a number when date has an overflow error? (Sheets does this)\n dateErrorNumber: true, // dateErrorThrow needs to be off!\n // Should it emit a number when bigint has an is an overflow error?\n bigintErrorNumber: false,\n // Sheets mode (see #3)\n dateSpanLarge: true,\n // Simulate the Lotus 1-2-3 leap year bug\n leap1900: true,\n // Emit regular vs. non-breaking spaces\n nbsp: false,\n // Robust/throw mode\n throws: true,\n // What is emitted when robust mode fails to parse (###### currently)\n invalid: '######',\n // Locale\n locale: '',\n // Don't adjust dates to UTC when converting them to serial time\n ignoreTimezone: false,\n // Integer digit grouping\n grouping: [ 3, 3 ],\n // resolve indexed colors to hex\n indexColors: true,\n // Skip-next signifier character\n skipChar: '',\n // Repear-next signifier character\n repeatChar: ''\n};\n","/**\n * Return a number rounded to the specified amount of places. This is the\n * rounding function used internally by the formatter (symmetric arithmetic\n * rounding).\n *\n * @param {number} number - The number to round.\n * @param {number} [places=0] - The number of decimals to round to.\n * @returns {number} A rounded number.\n */\nexport function round (number, places = 0) {\n if (typeof number !== 'number') {\n return number;\n }\n if (number < 0) {\n return -round(-number, places);\n }\n if (places) {\n const p = 10 ** (places) || 1;\n return round(number * p, 0) / p;\n }\n return Math.round(number);\n}\n","// https://web.archive.org/web/20110813042636/http://homepage.smc.edu/kennedy_john/DEC2FRAC.PDF\nconst PRECISION = 1e-13;\n\n/**\n * Split a fractional number into a numerator and denominator for display as\n * vulgar fractions.\n *\n * @ignore\n * @param {number} number The value to split\n * @param {number} [numeratorMaxDigits=2] The maxdigits number\n * @param {number} [denominatorMaxDigits=2] The maxdigits de\n * @returns {Array<number>} Array of two numbers, numerator and denominator.\n */\nexport function dec2frac (number, numeratorMaxDigits = 2, denominatorMaxDigits = 2) {\n const sign = (number < 0) ? -1 : 1;\n const maxdigits_n = 10 ** (numeratorMaxDigits || 2);\n const maxdigits_d = 10 ** (denominatorMaxDigits || 2);\n let z = Math.abs(number);\n let last_d = 0;\n let last_n = 0;\n let curr_n = 0;\n let curr_d = 1;\n let tmp;\n let r;\n number = z;\n if (number % 1 === 0) {\n // handles exact integers including 0\n r = [ number * sign, 1 ];\n }\n else if (number < 1e-19) {\n r = [ sign, 1e+19 ];\n }\n else if (number > 1e+19) {\n r = [ 1e+19 * sign, 1 ];\n }\n else {\n do {\n z = 1 / (z - Math.floor(z));\n tmp = curr_d;\n curr_d = (curr_d * Math.floor(z)) + last_d;\n last_d = tmp;\n last_n = curr_n;\n curr_n = Math.floor(number * curr_d + 0.5); // round\n if (curr_n >= maxdigits_n || curr_d >= maxdigits_d) {\n return [ sign * last_n, last_d ];\n }\n }\n while (Math.abs(number - (curr_n / curr_d)) >= PRECISION && z !== Math.floor(z));\n r = [ sign * curr_n, curr_d ];\n }\n return r;\n}\n","import { EPOCH_1317, EPOCH_1904 } from './constants.js';\nconst floor = Math.floor;\n\n// https://www.codeproject.com/Articles/2750/Excel-Serial-Date-to-Day-Month-Year-and-Vice-Versa\nexport function toYMD_1900 (ord, leap1900 = true) {\n if (leap1900 && ord >= 0) {\n if (ord === 0) {\n return [ 1900, 1, 0 ];\n }\n if (ord === 60) {\n return [ 1900, 2, 29 ];\n }\n if (ord < 60) {\n return [ 1900, (ord < 32 ? 1 : 2), ((ord - 1) % 31) + 1 ];\n }\n }\n let l = ord + 68569 + 2415019;\n const n = floor((4 * l) / 146097);\n l = l - floor((146097 * n + 3) / 4);\n const i = floor((4000 * (l + 1)) / 1461001);\n l = l - floor((1461 * i) / 4) + 31;\n const j = floor((80 * l) / 2447);\n const nDay = l - floor((2447 * j) / 80);\n l = floor(j / 11);\n const nMonth = j + 2 - (12 * l);\n const nYear = 100 * (n - 49) + i + l;\n return [ nYear | 0, nMonth | 0, nDay | 0 ];\n}\n\nexport function toYMD_1904 (ord) {\n return toYMD_1900(ord + 1462);\n}\n\n// https://web.archive.org/web/20080209173858/https://www.microsoft.com/globaldev/DrIntl/columns/002/default.mspx\n// > [algorithm] is used in many Microsoft products, including all\n// > operating systems that support Arabic locales, Microsoft Office,\n// > COM, Visual Basic, VBA, and SQL Server 2000.\nexport function toYMD_1317 (ord) {\n if (ord === 60) {\n throw new Error('#VALUE!');\n }\n if (ord <= 1) {\n return [ 1317, 8, 29 ];\n }\n if (ord < 60) {\n return [ 1317, (ord < 32 ? 9 : 10), 1 + ((ord - 2) % 30) ];\n }\n const y = 10631 / 30;\n const shift1 = 8.01 / 60;\n let z = ord + 466935;\n const cyc = floor(z / 10631);\n z = z - 10631 * cyc;\n const j = floor((z - shift1) / y);\n z = z - floor(j * y + shift1);\n const m = floor((z + 28.5001) / 29.5);\n if (m === 13) {\n return [ 30 * cyc + j, 12, 30 ];\n }\n return [ 30 * cyc + j, m, z - floor(29.5001 * m - 29) ];\n}\n\nexport function toYMD (ord, system = 0, leap1900 = true) {\n const int = floor(ord);\n if (system === EPOCH_1317) {\n return toYMD_1317(int);\n }\n if (system === EPOCH_1904) {\n return toYMD_1904(int);\n }\n return toYMD_1900(int, leap1900);\n}\n","import { toYMD } from './toYMD.js';\nconst floor = Math.floor;\nconst DAYSIZE = 86400;\n\n/**\n * Convert a native JavaScript Date, or array to an spreadsheet serial date.\n *\n * Returns a serial date number if input was a Date object or an array of\n * numbers, a null.\n *\n * ```js\n * // input as Date\n * dateToSerial(new Date(1978, 5, 17)); // 28627\n * // input as [ Y, M, D, h, m, s ]\n * dateToSerial([ 1978, 5, 17 ]); // 28627\n * // other input\n * dateToSerial(\"something else\"); // null\n * ````\n *\n * @param {Date | Array<number>} date The date\n * @param {object} [options={}] Options\n * @param {boolean} [options.ignoreTimezone=false]\n * Normally time zone will be taken into account. This makes the conversion to\n * serial date ignore the timezone offset.\n * @returns {number | null} The date as a spreadsheet serial date, or null.\n */\nexport function dateToSerial (date, options) {\n let ts = null;\n if (Array.isArray(date)) {\n const [ y, m, d, hh, mm, ss ] = date;\n ts = Date.UTC(y, m == null ? 0 : m - 1, d ?? 1, hh || 0, mm || 0, ss || 0);\n }\n // dates are changed to serial\n else if (date instanceof Date) {\n ts = date * 1;\n if (!options?.ignoreTimezone) {\n // Many timezones are offset in seconds but getTimezoneOffset() returns\n // time \"rounded\" to minutes so it is basically usable. 😿\n const dt = new Date();\n dt.setUTCFullYear(\n date.getFullYear(),\n date.getMonth(),\n date.getDate()\n );\n dt.setUTCHours(\n date.getHours(),\n date.getMinutes(),\n date.getSeconds(),\n date.getMilliseconds()\n );\n // timestamp\n ts = dt * 1;\n }\n }\n if (ts != null && isFinite(ts)) {\n const d = (ts / 864e5);\n return d - (d <= -25509 ? -25568 : -25569);\n }\n return null;\n}\n\n/**\n * Convert a spreadsheet serial date to an array of date parts.\n * Accurate to a second.\n *\n * ```js\n * // output as [ Y, M, D, h, m, s ]\n * dateFromSerial(28627); // [ 1978, 5, 17, 0, 0, 0 ]\n * ````\n *\n * @param {number} serialDate The date\n * @param {object} [options={}] The options\n * @param {boolean} [options.leap1900=true]\n * Simulate the Lotus 1-2-3 [1900 leap year bug](https://docs.microsoft.com/en-us/office/troubleshoot/excel/wrongly-assumes-1900-is-leap-year).\n * @returns {Array<number>} returns an array of date parts\n */\nexport function dateFromSerial (serialDate, options) {\n let date = (serialDate | 0);\n const t = DAYSIZE * (serialDate - date);\n let time = floor(t); // in seconds\n // date \"epsilon\" correction\n if ((t - time) > 0.9999) {\n time += 1;\n if (time === DAYSIZE) {\n time = 0;\n date += 1;\n }\n }\n // serial date/time to gregorian calendar\n const x = (time < 0) ? DAYSIZE + time : time;\n const [ y, m, d ] = toYMD(serialDate, 0, options?.leap1900);\n const hh = floor((x / 60) / 60) % 60;\n const mm = floor(x / 60) % 60;\n const ss = floor(x) % 60;\n // return the parts\n return [ y, m, d, hh, mm, ss ];\n}\n","import { currencySymbols, reCurrencySymbols } from './constants.js';\nimport { defaultLocale, getLocale } from './locale.js';\n\n/**\n * @typedef {object} ParseData\n * @property {number | boolean} v - the value\n * @property {string} [z] - number format pattern\n */\n\n/*\nThis is a list of the allowed date formats. The test file contains\nthe full list of permuations and the resulting values and formats.\n\nLegend:\n \"-\" - Date separator (any of \"/\" | \"-\" | \" \" | \".\"⁽¹⁾ | \", \"⁽²⁾)\n \" \" - Whitespace\n \"j\" - Day without leading zero (1-31)\n \"d\" - Day with leading zero (00-31)\n \"D\" - Abbreviated day name (\"Sun\"-\"Sat\")\n \"l\" - Full day name (\"Sunday\"-\"Saturday\")\n \"n\" - Month without leading zero (1-12)\n \"m\" - Month with leading zero (01-12)\n \"F\" - Full month name (\"January\"-\"December\")\n \"M\" - Abbreviated month name (\"Jan\"-\"Dec\")\n \"y\" - Year without century (00-99)\n \"Y\" - Year of our lord (1900-9999)\n \"x\" - Time of day (all formats: \"10 PM\", \"10:11:12\", ...)\n \"!\" - Only use in \"date-first\" locales: 4.2.2000 = 4 feb.\n \"?\" - Only use in \"month-first\" locales: 2.4.2000 = 4 feb.\n\n¹ Only considered valid if there are three or more sections to the date.\n² Comma is only allowed if followed by a space.\n\nTime is appended to each of these as they are inserted into the\ncollection of valid dates below.\n*/\nconst okDateFormats = [\n // day-month-year\n '!d-m-y', '!d-m-Y', '!j-m-y', '!j-m-Y',\n '!d-n-y', '!d-n-Y', '!j-n-y', '!j-n-Y',\n // month-day-year\n '?m-d-y', '?m-d-Y', '?m-j-y', '?m-j-Y',\n '?n-d-y', '?n-d-Y', '?n-j-y', '?n-j-Y',\n // unab\n 'd-M-y', 'd-M-Y', 'j-M-y', 'j-M-Y',\n 'M-d-y', 'M-d-Y', 'M-j-y', 'M-j-Y',\n 'd-F-y', 'd-F-Y', 'F-d-y', 'F-d-Y',\n 'F-j-y', 'F-j-Y', 'j-F-y', 'j-F-Y',\n 'y-F-d', 'y-F-j', 'y-M-d', 'y-M-j',\n 'Y-F-d', 'Y-F-j', 'Y-M-d', 'Y-m-d',\n 'Y-M-j', 'Y-m-j', 'Y-n-d', 'Y-n-j',\n 'j-F', // 2-April\n 'j-M', // 2-Apr\n 'd-F', // 02-April\n 'd-M', // 02-Apr\n 'n-d', // 4-02\n 'n-j', // 4-2\n 'n-Y', // 4-1908\n 'm-d', // 04-02\n 'm-j', // 04-2\n 'm-Y', // 04-1908\n 'M-Y', // Apr-1908\n 'M-y', // Apr-08\n 'F-y', // April-08\n 'F-Y', // April-1908\n 'Y-M', // 1908-Apr\n 'Y-n', // 1908-4\n 'Y-m', // 1908-04\n 'Y-F', // 1908-April\n 'Y-M' // 1908-Apr\n];\n\n// letter to excel\nconst tx0 = { j: 'd', d: 'd', D: 'ddd', l: 'dddd', n: 'm', m: 'm', M: 'mmm', F: 'mmmm', y: 'yy', Y: 'yyyy' };\nconst tx00 = { j: 'dd', d: 'dd', D: 'ddd', l: 'dddd', n: 'mm', m: 'mm', M: 'mmm', F: 'mmmm', y: 'yy', Y: 'yyyy' };\n\n// date formats are stored as a token-tree in a trie\n// for minimal looping and branching while parsing\nconst dateTrieDM = {};\nconst dateTrieMD = {};\nfunction packDate (f, node, allowType = 1) {\n if (f) {\n const char = f[0];\n const next = f.slice(1);\n if (char === '!') {\n packDate(next, node, 4);\n }\n else if (char === '?') {\n packDate(next, node, 2);\n }\n else {\n node[char] = node[char] || {};\n packDate(next, node[char], allowType);\n }\n }\n else {\n node.$ = allowType;\n }\n}\nfunction addFormatToTrie (fmt, trie) {\n // add date to token tree\n packDate(fmt, trie);\n // add a variant of the date with time suffixed\n // Excel allows time first, but Sheets and GRID do not\n packDate(fmt + ' x', trie);\n // add a variant of the date with weekdays pre-/suffixed\n packDate(fmt + ' l', trie);\n packDate(fmt + ' l x', trie);\n packDate('l ' + fmt, trie);\n packDate('l ' + fmt + ' x', trie);\n packDate(fmt + ' D', trie);\n packDate(fmt + ' D x', trie);\n packDate('D ' + fmt, trie);\n packDate('D ' + fmt + ' x', trie);\n}\nokDateFormats.forEach(fmt => {\n if (!fmt.startsWith('?')) addFormatToTrie(fmt, dateTrieDM);\n if (!fmt.startsWith('!')) addFormatToTrie(fmt, dateTrieMD);\n});\n\nconst currentYear = new Date().getUTCFullYear();\n\nconst PT = '.';\nconst CM = ',';\nconst SP = ' ';\nconst NS = ' ';\nconst NN = ' ';\nconst AP = \"'\";\nconst AG = '٬';\nconst dec2group = {\n '.': [ CM, NS, NN, AP, AG ],\n ',': [ PT, NS, NN, AP, AG ],\n '٫': [ PT, NS, NN, AP, AG ]\n};\nconst isDigit = d => d?.length === 1 && d >= '0' && d <= '9';\n\n/**\n * Parse a numeric string input and return its value and format. If the input\n * was not recognized or valid, the function returns a `null`, for valid input\n * it returns an object with two properties:\n *\n * * `v`: the parsed value.\n * * `z`: the number format of the input (if applicable).\n *\n * @see parseValue\n * @param {string} value The number to parse\n * @param {object} [options={}] Options\n * @param {string} [options.locale=\"\"]\n * A BCP 47 string tag. Locale default is english with a `\\u00a0`\n * grouping symbol (see [addLocale](#addLocale))\n * @returns {ParseData | null} An object of the parsed value and a corresponding format string\n */\nexport function parseNumber (value, options = {}) {\n const l10n = getLocale(options.locale || '') || defaultLocale;\n // we base everything on the decimal separator\n const dec = l10n.decimal;\n // base allowed grouping chars on decimal\n const grp = [ ...(dec2group[dec] || [ AP, AG ]) ];\n if (!grp.includes(l10n.group) && l10n.group !== SP && l10n.group !== dec) {\n grp.push(l10n.group);\n }\n let num = '';\n let exp = '';\n let sign = 1;\n let format = '';\n let minus = false;\n let openParen = false;\n let closeParen = false;\n let percent = false;\n let currency = false;\n let currencySymbol = null;\n let currencyTrailing = false;\n let i = 0;\n // prefix\n const prefixChars = [ SP, NS, NN, '+', '%', '(', '-' ].concat(currencySymbols);\n while (prefixChars.includes(value[i])) {\n const char = value[i];\n if (char === '-') {\n if (minus || openParen) {\n return null;\n }\n minus = true;\n sign = -1;\n }\n else if (reCurrencySymbols.test(char)) {\n if (currency) {\n return null;\n }\n currency = true;\n currencySymbol = char;\n }\n else if (char === '(') {\n if (openParen || minus) {\n return null;\n }\n openParen = true;\n sign = -1;\n }\n else if (char === '%') {\n if (percent) {\n return null;\n }\n percent = true;\n }\n i++;\n }\n // number\n let haveDecimal = false;\n let g;\n if (value[i] === dec || isDigit(value[i])) {\n while (i < value.length) {\n const ch = value[i];\n // can maybe allow space as the grouping operator if we find that it is\n // immediately followed by a digit or decimal?\n if (!g && grp.includes(ch)) {\n g = ch;\n // skip\n }\n else if (g && g === ch) {\n // skip\n }\n else if (ch === dec) {\n if (haveDecimal) {\n break;\n }\n num += '.';\n haveDecimal = true;\n }\n else if (isDigit(ch)) {\n num += ch;\n }\n else {\n break;\n }\n i++;\n }\n }\n // exponent\n if (value[i] === 'e' || value[i] === 'E') {\n exp += value[i];\n i++;\n if (value[i] === '+' || value[i] === '-') {\n exp += value[i];\n i++;\n }\n const d = i;\n while (isDigit(value[i])) {\n exp += value[i];\n i++;\n }\n if (d === i) {\n // contains no digits\n return null;\n }\n }\n // suffix\n const suffixChars = [ SP, NS, NN, '%', '$', ')' ].concat(currencySymbols);\n while (suffixChars.includes(value[i])) {\n const char = value[i];\n // only 1 occurance of these is allowed\n if (reCurrencySymbols.test(char)) {\n if (currency) {\n return null;\n }\n currency = true;\n currencySymbol = char;\n currencyTrailing = true;\n }\n else if (char === ')') {\n if (closeParen || !openParen) {\n return null;\n }\n closeParen = true;\n }\n else if (char === '%') {\n if (percent) {\n return null;\n }\n percent = true;\n }\n i++;\n }\n\n if (i !== value.length) {\n return null;\n }\n\n // is number ok?\n let numberValue = parseFloat(num + exp);\n if (!isFinite(numberValue)) {\n return null;\n }\n\n if (exp) {\n if (percent || currency) {\n return null;\n }\n // allow parens and minus, but not %$\n format = '0.00E+00';\n }\n else if (percent) {\n if (currency) {\n // Sheets allows this: $123% => $1.23 (Excel does not)\n return null;\n }\n // numpart dictates how \"deep\" the format is: \"0\" vs \"0.00\"\n format = num.includes('.')\n ? '0.00%'\n : '0%';\n numberValue *= 0.01;\n }\n else if (currency) {\n // numpart dictates how \"deep\" the format is: \"0\" vs \"0.00\"\n const currencyFormat = num.includes('.')\n ? '#,##0.00'\n : '#,##0';\n if (currencyTrailing) {\n format = currencyFormat + currencySymbol;\n }\n else {\n format = currencySymbol + currencyFormat;\n }\n }\n else if (g) {\n format = num.includes('.')\n ? '#,##0.00'\n : '#,##0';\n }\n // we may want to lower the fidelity of the number: +num.toFixed(13)\n const ret = { v: numberValue * sign };\n if (format) {\n ret.z = format;\n }\n return ret;\n}\n\nexport function isValidDate (y, m, d) {\n // day can't be 0\n if (d < 1) {\n return false;\n }\n // month must be 1-12\n if (m < 1 || m > 12) {\n return false;\n }\n // february\n if (m === 2) {\n const isLeapYear = (((y % 4 === 0) && (y % 100 !== 0)) || (y % 400 === 0));\n // 1900 is a leap year in Excel\n const febDays = (isLeapYear || y === 1900) ? 29 : 28;\n if (d > febDays) {\n return false;\n }\n }\n // test any other month\n else if (\n ((m === 4 || m === 6 || m === 9 || m === 11) && d > 30) ||\n ((m === 1 || m === 3 || m === 5 || m === 7 || m === 8 || m === 10 || m === 12) && d > 31)) {\n return false;\n }\n return true;\n}\n\n// should really match { ’' } and all whitespace\nconst matchRec = (str, data, skipPeriod = false) => {\n for (const item of data) {\n if (str.startsWith(item[0])) {\n // if the match is followed by a \".\" we'll skip it if the abbr. is by\n // convention abbreviated in the locale.\n let l = item[0].length;\n if (skipPeriod && (item[2] === 'D' || item[2] === 'M') && str[l] === '.') {\n l++;\n }\n return [ str.slice(0, l), item ];\n }\n }\n return [ '', null ];\n};\n\nconst nextToken = (str, node, data, lData) => {\n const path = data.path || '';\n const matchOrder = Object.keys(node);\n for (const t of matchOrder) {\n let r;\n if (!node[t]) {\n continue;\n }\n if (t === '$' || t === '€') {\n // if string is done, then we can return\n if (!str) {\n r = data;\n }\n }\n else if (t === '-') {\n const m = /^(\\s*([./-]|,\\s)\\s*|\\s+)/.exec(str);\n if (m) {\n const sep = (m[1] === '-' || m[1] === '/' || m[1] === '.') ? m[1] : ' ';\n // don't allow mixing date separators\n if (!data.sep || data.sep === sep) {\n const s = m[0].replace(/\\s+/g, ' ');\n r = nextToken(str.slice(m[0].length), node[t], { ...data, sep, path: path + s }, lData);\n }\n }\n }\n else if (t === ' ') {\n const m = /^[,.]?\\s+/.exec(str);\n if (m) {\n const s = m[0].replace(/\\s+/g, ' ');\n r = nextToken(str.slice(m[0].length), node[t], { ...data, path: path + s }, lData);\n }\n }\n else if (t === 'j' || t === 'd') {\n const m = /^(0?[1-9]|1\\d|2\\d|3[01])\\b/.exec(str);\n if (m) {\n r = nextToken(str.slice(m[0].length), node[t], { ...data, day: m[0], path: path + t }, lData);\n }\n }\n else if (t === 'n' || t === 'm') {\n const m = /^(0?[1-9]|1[012])\\b/.exec(str);\n if (m) {\n r = nextToken(str.slice(m[0].length), node[t], { ...data, month: +m[0], _mon: m[0], path: path + t }, lData);\n }\n }\n else if (t === 'F' || t === 'M') {\n const [ m, match ] = matchRec(str, lData.mon, lData.mp);\n if (match?.[2] === t) {\n r = nextToken(str.slice(m.length), node[t],\n { ...data, month: match[1], _mon: m, path: path + t }, lData);\n }\n }\n else if (t === 'l' || t === 'D') {\n const [ m, match ] = matchRec(str, lData.day, lData.dp);\n if (match?.[2] === t) {\n // the value is ignored\n r = nextToken(str.slice(m.length), node[t], { ...data, path: path + t }, lData);\n }\n }\n else if (t === 'y') {\n const m = /^\\d\\d\\b/.exec(str);\n if (m) {\n const y = (+m[0] >= 30) ? +m[0] + 1900 : +m[0] + 2000;\n r = nextToken(str.slice(m[0].length), node[t], { ...data, year: y, path: path + t }, lData);\n }\n }\n else if (t === 'Y') {\n const m = /^\\d\\d\\d\\d\\b/.exec(str);\n if (m) {\n r = nextToken(str.slice(m[0].length), node[t], { ...data, year: +m[0], path: path + t }, lData);\n }\n }\n else if (t === 'x') {\n const time = parseTime(str, { locale: lData.locale });\n if (time) {\n r = nextToken('', node[t], { ...data, time: time.v, tf: time.z, path: path + t }, lData);\n }\n }\n else {\n throw new Error(`Unknown date token \"${t}\"`);\n }\n if (r) {\n // reject invalid dates so we continue traversing the tree\n if (isValidDate(data.year || 1916, data.month || 1, data.day ? +data.day : 1)) {\n return r;\n }\n }\n }\n};\n\nconst normDateStr = s => (\n s.replace(/\\s+/g, ' ').trim()\n .replace(/’/, \"'\")\n .replace(/\\.$/, '')\n .toLowerCase()\n);\n\nconst getLookups = (arr, sym) => {\n const s = arr.map((d, i) => [ normDateStr(d), i + 1, sym ]);\n s.sort((a, b) => b[0].length - a[0].length);\n return s;\n};\n\n/**\n * Parse a date or datetime string input and return its value and format. If\n * the input was not recognized or valid, the function returns a `null`, for\n * valid input it returns an object with two properties:\n *\n * - `v`: the parsed value.\n * - `z`: the number format of the input (if applicable).\n *\n * @see parseValue\n * @param {string} value The date to parse\n * @param {object} [options={}] Options\n * @param {string} [options.locale=\"\"]\n * A BCP 47 string tag. Locale default is english with a `\\u00a0`\n * grouping symbol (see [addLocale](#addLocale))\n * @returns {ParseData | null} An object of the parsed value and a corresponding format string\n */\nexport function parseDate (value, options = {}) {\n const l10n = getLocale(options.locale || '') || defaultLocale;\n const lData = {\n mon: getLookups(l10n.mmmm, 'F').concat(getLookups(l10n.mmm, 'M')),\n mp: l10n.mmm[0].at(-1) === '.',\n day: getLookups(l10n.dddd, 'l').concat(getLookups(l10n.ddd, 'D')),\n dp: l10n.ddd[0].at(-1) === '.',\n locale: options.locale\n };\n // possible shortcut: quickly dismiss if there isn't a number?\n const date = nextToken(\n normDateStr(value),\n l10n.preferMDY ? dateTrieMD : dateTrieDM,\n { path: '' },\n lData\n );\n if (date) {\n // disallow matches where two tokens are separated by a period\n if (date.sep === '.' && date.path.length === 3) {\n return null;\n }\n const year = +(date.year ?? currentYear);\n if (!date.day) {\n date.day = 1;\n }\n let epoch = -Infinity;\n if (year < 1900) {\n return null;\n }\n else if (year <= 1900 && date.month <= 2) {\n epoch = 25568;\n }\n else if (year < 10000) {\n epoch = 25569;\n }\n const dateValue = (Date.UTC(year, date.month - 1, date.day) / 864e5) + epoch + (date.time || 0);\n if (dateValue >= 0 && dateValue <= 2958465) {\n const lead0 = (\n // either has a leading zero\n (date._mon[0] === '0' || date.day[0] === '0') ||\n // both are 2-digits long\n (date._mon.length === 2 && date.day.length === 2)\n );\n const format = date.path.replace(/[jdlDnmMFyYx]/g, a => {\n if (a === 'x') {\n return date.tf || '';\n }\n return (lead0 ? tx00[a] : tx0[a]) || a;\n });\n return { v: dateValue, z: format };\n }\n }\n return null;\n}\n\nconst normAMPMStr = s => (\n s.replace(/\\s+/g, '').trim()\n .replace(/\\./g, '')\n .toLowerCase()\n);\n\n/**\n * Parse a time string input and return its value and format. If the input was\n * not recognized or valid, the function returns a `null`, for valid input it\n * returns an object with two properties:\n *\n * - `v`: the parsed value.\n * - `z`: the number format of the input (if applicable).\n *\n * @see parseValue\n * @param {string} value The date to parse\n * @param {object} [options={}] Options\n * @param {string} [options.locale=\"\"]\n * A BCP 47 string tag. Locale default is english with a `\\u00a0`\n * grouping symbol (see [addLocale](#addLocale))\n * @returns {ParseData | null} An object of the parsed value and a corresponding format string\n */\nexport function parseTime (value, options = {}) {\n const l10n = getLocale(options.locale || '') || defaultLocale;\n const parts = /^\\s*([10]?\\d|2[0-4])(?::([0-5]\\d|\\d))?(?::([0-5]\\d|\\d))?(\\.\\d{1,10})?(?=\\s*[^\\s\\d]|$)/.exec(value);\n let ampm = '';\n if (parts) {\n const tail = normAMPMStr(value.slice(parts[0].length));\n if (tail === normAMPMStr(l10n.ampm[0]) || tail === 'a' || tail === 'am') {\n ampm = 'a';\n }\n else if (tail === normAMPMStr(l10n.ampm[1]) || tail === 'p' || tail === 'pm') {\n ampm = 'p';\n }\n else if (tail === ':') {\n if (!parts[3]) {\n parts[3] = '0';\n }\n if (!parts[2]) {\n parts[2] = '0';\n }\n }\n else if (tail) {\n return null;\n }\n }\n if (parts) {\n const [ , h, m, s, f ] = parts;\n // don't allow milliseconds without seconds\n if (f && !s) {\n return null;\n }\n // single number must also include AM/PM part\n if (!ampm && !m && !s) {\n return null;\n }\n // AM/PM part must align with hours\n let hrs = +(h || 0) * 1;\n if (ampm) {\n if (hrs >= 13) {\n return null;\n }\n // 00:00 AM - 12:00 AM\n if (ampm === 'a') {\n if (hrs === 12) {\n hrs = 0;\n } // midnight\n }\n else if (ampm === 'p') {\n if (hrs !== 12) {\n hrs += 12;\n } // afternoon\n }\n }\n const min = +(m || 0) * 1;\n const sec = +(s || 0) * 1;\n const mss = +(f || 0) * 1;\n\n return {\n v: ((hrs * 60 * 60) + (min * 60) + sec + mss) / (60 * 60 * 24),\n z: (\n (h.length === 2 ? 'hh' : 'h') +\n ':mm' +\n (s ? ':ss' : '') +\n (ampm ? ' AM/PM' : '')\n )\n };\n }\n return null;\n}\n\n/**\n * Parse a string input and return its boolean value. If the input was not\n * recognized or valid, the function returns a `null`, for valid input it\n * returns an object with one property:\n *\n * - `v`: the parsed value.\n *\n * @see parseValue\n * @param {string} value The supposed boolean to parse\n * @param {object} [options={}] Options\n * @param {string} [options.locale=\"\"]\n * A BCP 47 string tag. Locale default is english with a `\\u00a0`\n * grouping symbol (see [addLocale](#addLocale))\n * @returns {ParseData | null} An object of the parsed value and a corresponding format string\n */\nexport function parseBool (value, options = {}) {\n const l10n = getLocale(options.locale || '') || defaultLocale;\n const v = value.trim().toLowerCase();\n const bT = l10n.bool[0].toLowerCase();\n if (v === 'true' || v === bT) {\n return { v: true };\n }\n const bF = l10n.bool[1].toLowerCase();\n if (v === 'false' || v === bF) {\n return { v: false };\n }\n return null;\n}\n\n/**\n * Attempt to parse a \"spreadsheet input\" string input and return its value and\n * format. If the input was not recognized or valid, the function returns a\n * `null`, for valid input it returns an object with two properties:\n *\n * - `v`: The parsed value. For dates, this will be an Excel style serial date.\n * - `z`: (Optionally) the number format string of the input. This property will\n * not be present if it amounts to the `General` format.\n *\n * `parseValue()` recognizes a wide range of dates and date-times, times,\n * numbers, and booleans. Some examples:\n *\n * ```js\n * // basic number\n * parseValue(\"-123\");// { v: -123 }\n * // formatted number\n * parseValue(\"$1,234\"); // { v: 1234, z: \"$#,##0\" }\n * // a percent\n * parseValue(\"12.3%\"); // { v: 0.123, z: \"0.00%\" }\n * // a date\n * parseValue(\"07 October 1984\"); // { v: 30962, z: 'dd mmmm yyyy' }\n * // an ISO formatted date-time\n * parseValue(\"1984-09-10 11:12:13.1234\"); // { v: 30935.46681855787, z: \"yyyy-mm-dd hh:mm:ss\" }\n * // a boolean\n * parseValue(\"false\"); // { v: false }\n * ```\n *\n * The formatting string outputted may not correspond exactly to the input.\n * Rather, is it composed of certain elements which the input controls. This is\n * comparable to how Microsoft Excel and Google Sheets parse pasted input. Some\n * things you may expect:\n *\n * - Whitespace is ignored.\n * - Decimal fractions are always represented by `.00` regardless of how many\n * digits were shown in the input.\n * - Negatives denoted by parentheses [`(1,234)`] will not include the\n * parentheses in the format string (the value will still by negative.)\n * - All \"scientific notation\" returns the same format: `0.00E+00`.\n *\n * Internally the parser calls, `parseNumber`, `parseDate`,\n * `parseTime` and `parseBool`. They work in the same way except\n * with a more limited scope. You may want those function if you are limiting\n * input to a smaller scope.\n *\n * Be warned that the parser do not (yet) take locale into account so all input\n * is assumed to be in \"en-US\". This means that `1,234.5` will parse, but\n * `1.234,5` will not. Similarly, the order of date parts will be US centric.\n * This may change in the future so be careful what options you pass the\n * functions.\n *\n * @param {string} value The value to parse\n * @param {object} [options={}] Options\n * @param {string} [options.locale=\"\"]\n * A BCP 47 string tag. Locale default is english with a `\\u00a0`\n * grouping symbol (see [addLocale](#addLocale))\n * @returns {ParseData | null} An object of the parsed value and a corresponding format string\n */\nexport function parseValue (value, options) {\n return (\n parseNumber(value, options) ??\n parseDate(value, options) ??\n parseTime(value, options) ??\n parseBool(value, options)\n );\n}\n","import { resolveLocale } from './locale.js';\nimport {\n u_YEAR, u_MONTH, u_DAY, u_HOUR, u_MIN, u_SEC, u_DSEC, u_CSEC, u_MSEC,\n EPOCH_1900, EPOCH_1317,\n TOKEN_AMPM, TOKEN_BREAK, TOKEN_CALENDAR, TOKEN_CHAR, TOKEN_COLOR, TOKEN_COMMA, TOKEN_CONDITION,\n TOKEN_DATETIME, TOKEN_DBNUM, TOKEN_DIGIT, TOKEN_DURATION, TOKEN_ERROR, TOKEN_ESCAPED, TOKEN_EXP,\n TOKEN_FILL, TOKEN_GENERAL, TOKEN_GROUP, TOKEN_HASH, TOKEN_LOCALE, TOKEN_MINUS, TOKEN_NATNUM,\n TOKEN_PAREN, TOKEN_PERCENT, TOKEN_PLUS, TOKEN_POINT, TOKEN_QMARK, TOKEN_SCALE, TOKEN_SKIP,\n TOKEN_SLASH, TOKEN_SPACE, TOKEN_STRING, TOKEN_TEXT, TOKEN_ZERO\n} from './constants.js';\n\nfunction minMaxPad (str, part, prefix) {\n part[prefix + '_max'] = str.length;\n part[prefix + '_min'] = str.replace(/#/g, '').length;\n return part;\n}\n\nfunction add (s, tokens) {\n // allow adding string tokens without wrapping\n if (typeof s === 'string') {\n tokens.push({ type: 'string', value: s });\n }\n else {\n tokens.push(s);\n }\n}\n\nfunction isNumOp (token, activePattern) {\n const type = token?.type;\n return (\n (type === TOKEN_HASH || type === TOKEN_ZERO || type === TOKEN_QMARK) ||\n (type === TOKEN_DIGIT && activePattern === 'den')\n );\n}\n\nexport function parseFormatSection (inputTokens) {\n const outputTokens = [];\n\n const part = {\n scale: 1,\n percent: false,\n text: false,\n date: 0,\n date_eval: false,\n date_system: EPOCH_1900,\n sec_decimals: 0,\n general: false,\n clock: 24,\n int_pattern: [],\n frac_pattern: [],\n man_pattern: [],\n den_pattern: [],\n num_pattern: [],\n tokens: outputTokens\n };\n\n let currentPattern = 'int';\n let lastNumberChunk = null;\n const dateChunks = [];\n let last;\n let haveLocale = false;\n\n let index = -1;\n let partOver = false;\n let patternSource = '';\n let haveSlash = false;\n\n while (++index < inputTokens.length && !partOver) {\n const token = inputTokens[index];\n const type = token.type || TOKEN_ERROR;\n patternSource += token.raw;\n\n if (type === TOKEN_GENERAL) {\n part.general = true;\n add(token, outputTokens);\n }\n\n // new partition\n else if (isNumOp(token, currentPattern)) {\n const pt = part[currentPattern + '_pattern'];\n if (isNumOp(last, currentPattern) || last?.type === TOKEN_GROUP) {\n // append to current\n pt.push((pt.pop() || '') + token.value);\n lastNumberChunk.num += token.value;\n }\n else {\n // new number section\n pt.push(token.value);\n lastNumberChunk = { type: currentPattern, num: token.value };\n add(lastNumberChunk, outputTokens);\n }\n }\n\n else if (type === TOKEN_PAREN) {\n if (token.value === '(') {\n part.parens = true;\n }\n add(token.value, outputTokens);\n }\n else if (type === TOKEN_DIGIT) {\n // just print it\n add(token.value, outputTokens);\n }\n\n // vulgar fractions\n else if (type === TOKEN_SLASH) {\n haveSlash = true;\n if (part[currentPattern + '_pattern'].length) {\n if (!lastNumberChunk) { // need to have a numerator present\n throw new SyntaxError('Format pattern is missing a numerator');\n }\n part.fractions = true;\n // ... we just passed the numerator - correct that item\n part.num_pattern.push(part[currentPattern + '_pattern'].pop());\n lastNumberChunk.type = 'num';\n // next up... the denominator\n currentPattern = 'den';\n add({ type: 'div' }, outputTokens);\n }\n else {\n add(token.value, outputTokens);\n }\n }\n\n else if (type === TOKEN_COMMA) {\n add(',', outputTokens);\n }\n else if (type === TOKEN_SCALE) {\n part.scale = 0.001 ** token.raw.length;\n }\n else if (type === TOKEN_GROUP) {\n if (currentPattern === 'int') {\n part.grouping = true;\n }\n if (currentPattern === 'den') {\n throw new SyntaxError('Cannot group denominator digits');\n }\n // else we just ignore it!\n }\n\n else if (type === TOKEN_SPACE) {\n add(token, outputTokens);\n }\n\n else if (type === TOKEN_BREAK) {\n partOver = true;\n break; // leave the \";\" hanging\n }\n\n else if (type === TOKEN_TEXT) { // @\n part.text = true;\n add(token, outputTokens);\n }\n else if (type === TOKEN_PLUS || type === TOKEN_MINUS) {\n add(token, outputTokens);\n }\n\n // [h] [m] [s]\n // else if ((m = /^(?:\\[(h+|m+|s+)\\])/i.exec(s))) {\n else if (type === TOKEN_DURATION) {\n const tokenValue = token.value.toLowerCase(); // deal with in tokenizer\n const startsWith = tokenValue[0];\n const bit = { type: '', size: 0, date: 1, pad: tokenValue.length };\n if (startsWith === 'h') {\n bit.size = u_HOUR;\n bit.type = 'hour-elap';\n }\n else if (startsWith === 'm') {\n bit.size = u_MIN;\n bit.type = 'min-elap';\n }\n else {\n bit.size = u_SEC;\n bit.type = 'sec-elap';\n }\n // signal date calc and track smallest needed unit\n part.date = part.date | bit.size;\n dateChunks.push(bit);\n add(bit, outputTokens);\n }\n // Note: In locales where decimal symbol is set to \",\" Excel will expect\n // \",\" rather than a \".\". This must be solved by re-localizing the\n // pattern before using it.\n // .0 .00 .000\n else if (part.date && type === TOKEN_POINT && inputTokens[index + 1]?.type === TOKEN_ZERO) {\n let dec = 1;\n index++;\n let raw = '0';\n if (inputTokens[index + 1]?.type === TOKEN_ZERO) {\n raw += '0';\n dec = 2;\n index++;\n }\n if (inputTokens[index + 1]?.type === TOKEN_ZERO) {\n raw += '0';\n dec = 3;\n index++;\n }\n patternSource += raw;\n const size = [ u_SEC, u_DSEC, u_CSEC, u_MSEC ][dec];\n part.date = part.date | size;\n part.date_eval = true;\n part.sec_decimals = Math.max(part.sec_decimals, dec);\n add({\n type: 'subsec',\n size: size,\n decimals: dec,\n date: 1\n }, outputTokens);\n }\n\n else if (type === TOKEN_CALENDAR) {\n // signal date system (ignored if defined with [$-xxx])\n if (!haveLocale) {\n // Use Hijri calendar system\n if (token.value === 'B2' || token.value === 'b2') {\n // TODO: B2 does more than this\n // it switches locale to [$-060401] (ar) which affects display (RTL)\n part.date_system = EPOCH_1317;\n }\n // Use Gregorian calendar system\n else { // B1 | b1\n // signal date system (ignored if defined with [$-xxx])\n part.date_system = EPOCH_1900;\n }\n }\n }\n\n // hh:mm:ss YYYY-MM-DD\n else if (type === TOKEN_DATETIME) {\n // Excel is \"mostly\" case insensitive here except it checks the last used\n // date token. Which, if it was s or h, minutes is used. The same is true\n // if we hit m or s, and last is m.\n // m and mm are spurious, mmm is always month\n const bit = { type: '', size: 0, date: 1 };\n const value = token.value.toLowerCase(); // deal with in tokenizer?\n const startsWith = value[0];\n if (value === 'y' || value === 'yy') {\n bit.size = u_YEAR;\n bit.type = 'year-short';\n }\n else if (startsWith === 'y' || startsWith === 'e') {\n bit.size = u_YEAR;\n bit.type = 'year';\n }\n else if (value === 'b' || value === 'bb') {\n bit.size = u_YEAR;\n bit.type = 'b-year-short';\n }\n else if (startsWith === 'b') {\n bit.size = u_YEAR;\n bit.type = 'b-year';\n }\n else if (value === 'd' || value === 'dd') {\n bit.size = u_DAY;\n bit.type = 'day';\n bit.pad = /dd/.test(value);\n }\n else if (value === 'ddd' || value === 'aaa') {\n bit.size = u_DAY;\n bit.type = 'weekday-short';\n }\n else if (startsWith === 'd' || startsWith === 'a') {\n bit.size = u_DAY;\n bit.type = 'weekday';\n }\n else if (startsWith === 'h') {\n bit.size = u_HOUR;\n bit.type = 'hour';\n bit.pad = /hh/i.test(value);\n }\n else if (startsWith === 'm') {\n if (value.length === 3) {\n bit.size = u_MONTH;\n bit.type = 'monthname-short';\n }\n else if (value.length === 5) {\n bit.size = u_MONTH;\n bit.type = 'monthname-single';\n }\n else if (value.length >= 4) {\n bit.size = u_MONTH;\n bit.type = 'monthname';\n }\n // m or mm can be either minute or month based on context\n const last_date_chunk = dateChunks[dateChunks.length - 1];\n if (!bit.type && last_date_chunk &&\n !last_date_chunk.used &&\n (last_date_chunk.size & (u_HOUR | u_SEC))) {\n // if this value follows hour or second, it is a minute\n last_date_chunk.used = true;\n bit.size = u_MIN;\n bit.type = 'min';\n bit.pad = /mm/.test(value);\n }\n // if we still don't know, we treat as a month\n // and defer, a later 'sec' value may switch it\n if (!bit.type) {\n bit.size = u_MONTH;\n bit.type = 'month';\n bit.pad = /mm/.test(value);\n bit.indeterminate = true;\n }\n }\n else if (startsWith === 's') {\n bit.size = u_SEC;\n bit.type = 'sec';\n bit.pad = /ss/.test(value);\n // if last date chunk was m, flag this used\n const last_date_chunk = dateChunks[dateChunks.length - 1];\n if (last_date_chunk && last_date_chunk.size & u_MIN) {\n bit.used = true;\n }\n // if last date chunk is undecided, we know that it is a minute\n else if (last_date_chunk?.indeterminate) {\n delete last_date_chunk.indeterminate;\n last_date_chunk.size = u_MIN;\n last_date_chunk.type = 'min';\n bit.used = true;\n }\n }\n else if (startsWith === 'g') {\n // TODO: Don't know what this does? (yet!)\n }\n // signal date calc and track smallest needed unit\n part.date = part.date | bit.size;\n part.date_eval = true;\n dateChunks.push(bit);\n add(bit, outputTokens);\n }\n\n // AM/PM\n // See: https://github.com/SheetJS/sheetjs/issues/676\n else if (type === TOKEN_AMPM) {\n part.clock = 12;\n part.date = part.date | u_HOUR;\n part.date_eval = true;\n // deal with in tokenizer?\n token.short = token.value === 'A/P';\n add(token, outputTokens);\n }\n\n // escaped character, string\n else if (type === TOKEN_STRING || type === TOKEN_ESCAPED || type === TOKEN_CHAR) {\n add(token.value, outputTokens);\n }\n\n // condition\n else if (type === TOKEN_CONDITION) {\n part.condition = [\n token.value[0], // operator\n parseFloat(token.value[1]) // operand\n ];\n }\n\n // locale code -- we allow std. \"en-US\" style codes\n // https://stackoverflow.com/questions/54134729/what-does-the-130000-in-excel-locale-code-130000-mean/54540455#54540455\n else if (type === TOKEN_LOCALE) {\n const bits = token.value.split('-');\n const code = bits.length < 2 ? '' : bits.slice(1).join('-');\n\n const currency = bits[0];\n if (currency) {\n add(currency, outputTokens);\n }\n\n const l4e = resolveLocale(code);\n if (l4e) { part.locale = l4e; }\n const wincode = parseInt(code, 16);\n if (isFinite(wincode) && (wincode & 0xff0000)) {\n const cal = (wincode >> 16) & 0xff;\n // only Hijri is supported atm.\n if (cal === 6) { part.date_system = EPOCH_1317; }\n }\n\n haveLocale = true; // ignore any B2 & B1 tokens\n }\n\n // color\n else if (type === TOKEN_COLOR) {\n let cm;\n let v = token.value.toLowerCase();\n if ((cm = /^color\\s*(\\d+)$/i.exec(v))) {\n v = parseInt(cm[1], 10);\n }\n part.color = v;\n }\n\n // percentage\n else if (type === TOKEN_PERCENT) {\n part.scale = 100;\n part.percent = true;\n add('%', outputTokens);\n }\n\n // decimal fraction\n else if (type === TOKEN_POINT) {\n add(token, outputTokens);\n if (!part.date) {\n part.dec_fractions = true;\n currentPattern = 'frac';\n }\n }\n\n // exponent\n else if (type === TOKEN_EXP) {\n // Exponent pattern requires symbol to directly follow \"E\" but the\n // signature symbol, however, prefixes the first digit of the mantissa\n part.exponential = true;\n part.exp_plus = token.value.includes('+');\n currentPattern = 'man';\n add({ type: 'exp', plus: part.exp_plus }, outputTokens);\n }\n\n // skip width\n else if (type === TOKEN_SKIP) {\n add(token, outputTokens);\n }\n\n // fill space with next char\n else if (type === TOKEN_FILL) {\n add(token, outputTokens);\n }\n\n else if (type === TOKEN_DBNUM || type === TOKEN_NATNUM) {\n // UNSUPPORTED:\n // - DBNum1 = NatNum4\n // - DBNum2 = NatNum5\n // - DBNum3 = either NatNum6 or NatNum3?\n // - DBNum3 = NatNum10\n // NatNum: https://www.openoffice.org/api/docs/common/ref/com/sun/star/i18n/NativeNumberMode.html\n }\n\n else if (type === TOKEN_ERROR) {\n throw new SyntaxError(`Illegal character: ${patternSource}`);\n }\n\n else {\n throw new SyntaxError(`Unknown token ${type} in ${patternSource}`);\n }\n\n // advance parser\n last = token;\n }\n part.tokensUsed = index;\n part.pattern = patternSource;\n\n // Quickly determine if this pattern is condition only\n // if so, then add String(value) but using the condition\n if (/^((?:\\[[^\\]]+\\])+)(;|$)/.test(part.pattern) && !/^\\[(?:h+|m+|s+)\\]/.test(part.pattern)) {\n add({ type: 'text' }, outputTokens);\n }\n\n // Make sure we don't have an illegal pattern. We could support some of this\n // but we side with Excel and don't because they make no sense.\n if (\n (part.fractions && part.dec_fractions) ||\n (part.grouping && !part.int_pattern.length) ||\n (part.fractions && part.exponential) ||\n (part.fractions && (part.den_pattern.length * part.num_pattern.length) === 0) ||\n (haveSlash && !part.fractions && !part.date) ||\n (part.exponential && ((part.int_pattern.length || part.frac_pattern.length) * part.man_pattern.length) === 0)\n ) {\n throw new SyntaxError(`Invalid pattern: ${patternSource}`);\n }\n\n const intPattern = part.int_pattern.join('');\n const manPattern = part.man_pattern.join('');\n const fracPattern = part.frac_pattern.join('');\n minMaxPad(intPattern, part, 'int');\n let min = 0;\n for (let i = 0; i < intPattern.length; i++) {\n const ch = intPattern[intPattern.length - 1 - i];\n if (/^[0-9?]/.test(ch)) {\n min = i + 1;\n }\n }\n part.int_min = min;\n\n minMaxPad(fracPattern, part, 'frac');\n minMaxPad(manPattern, part, 'man');\n\n let num_pat = part.num_pattern.join('');\n // let den_pat = part.den_pattern.join('');\n let den_pat = part.den_pattern[0] || '';\n const enforce_padded = den_pat.includes('?') || num_pat.includes('?');\n // numerical denominator padding type is inherited from numerator padding type\n if (enforce_padded) {\n den_pat = den_pat.replace(/\\d/g, '?');\n den_pat = den_pat.replace(/#$/g, '?');\n minMaxPad(num_pat, part, 'num');\n minMaxPad(den_pat, part, 'den');\n num_pat = num_pat.replace(/#$/g, '?');\n }\n else {\n minMaxPad(num_pat, part, 'num');\n minMaxPad(den_pat, part, 'den');\n }\n\n part.int_p = intPattern;\n part.man_p = manPattern;\n part.num_p = num_pat;\n part.den_p = den_pat;\n\n if (part.den_pattern.length) {\n // detect and set rounding factor for denominator\n part.denominator = parseInt(part.den_pattern.join('').replace(/\\D/g, ''), 10);\n }\n\n part.integer = !!intPattern.length;\n\n if (!part.integer && !part.exponential && fracPattern.length) {\n // if no integer has been found, we inject one\n const pointIdx = part.tokens.findIndex(d => d.type === 'point');\n part.tokens.splice(pointIdx, 0, { type: 'int', value: '#' });\n part.integer = true;\n part.int_pattern = [ '#' ];\n part.int_p = '#';\n }\n\n // extra whitespace rules for vulgar fractions\n if (part.fractions) {\n // fragment bits affect surrounding whitespace\n // if either bit is \"#\", the whitespace around it, and\n // the div symbol, is removed if the bit is not shown\n for (let i = 0; i < outputTokens.length - 1; i++) {\n const tok = outputTokens[i];\n if (tok.type !== 'string' && tok.type !== 'space') {\n continue;\n }\n const nextType = outputTokens[i + 1].type;\n if (nextType === 'num') {\n tok.rule = 'num+int';\n }\n else if (nextType === 'div') {\n tok.rule = 'num';\n }\n else if (nextType === 'den') {\n tok.rule = 'den';\n }\n else {\n // tok.rule = '???';\n }\n }\n }\n\n // if the number is fragmented, grouping should not be applied\n if (part.grouping) {\n if (part.int_pattern.length > 1) {\n part.grouping = false;\n }\n }\n\n return part;\n}\n","export function clamp (number) {\n if (number === 0) {\n return number;\n }\n const d = Math.ceil(Math.log10(number < 0 ? -number : number));\n const mag = 10 ** (16 - Math.floor(d));\n return isFinite(mag) ? Math.round(number * mag) / mag : 0;\n}\n","export function getExponent (num, int_max = 0) {\n const exp = Math.floor(Math.log10(num));\n return (int_max > 1)\n ? Math.floor(exp / int_max) * int_max\n : exp;\n}\n\nexport function getSignificand (n, exp = 1) {\n if (exp < -300) {\n return parseFloat(n.toExponential().split('e')[0]);\n }\n return n * (10 ** -exp);\n}\n","import { round } from './round.js';\n\nconst zero = {\n total: 1,\n sign: 0,\n period: 0,\n int: 1,\n frac: 0\n};\n\n// returns the count of digits (including - and .) need to represent the number\nexport default function numdec (value, incl_sign = true) {\n const v = Math.abs(value);\n\n // shortcut zero\n if (!v) { return zero; }\n\n const signSize = (incl_sign && value < 0) ? 1 : 0;\n const intPart = Math.floor(v);\n const intSize = Math.floor(Math.log10(v) + 1);\n let periodSize = 0;\n let fracSize = 0;\n\n // is not an integer\n if (intPart !== v) {\n periodSize = 1;\n\n // B: this has turned out to be much faster than all pure math\n // based solutions I was able to come up with ¯\\_(ツ)_/¯\n const n = String(\n round(v * (10 ** -intSize), 15)\n );\n let f = n.length;\n let z = true;\n let i = 0;\n while (i <= n.length) {\n if (n[i] === '.') {\n // discount period\n f--;\n break;\n }\n else if (n[i] === '0' && z) {\n // leading zeros before period are discounted\n f--;\n }\n else {\n // non-zero digit\n z = false;\n }\n i++;\n }\n fracSize = f - intSize;\n\n if (fracSize < 0) {\n // the number is not representable [by Excel]\n // this would be something like 1000.0000000000001\n // it would normally get truncated to 15 significant figures and\n // end up in the same place as the following does:\n fracSize = 0;\n periodSize = 0;\n }\n }\n\n return {\n total: signSize + Math.max(intSize, 1) + periodSize + fracSize,\n digits: Math.max(intSize, 0) + fracSize,\n sign: signSize,\n period: periodSize,\n int: Math.max(intSize, 1),\n frac: fracSize\n };\n}\n","import { getExponent, getSignificand } from './numberProps.js';\nimport numdec from './numdec.js';\nimport { round } from './round.js';\n\nconst fixLocale = (s, l10n) => {\n return s.replace(/\\./, l10n.decimal);\n};\n\nconst getExp = (n, exp, l10n) => {\n const x = Math.abs(exp);\n let m;\n if (n === 1) {\n m = n;\n }\n else {\n m = round(n, 5);\n }\n return [\n fixLocale(m + '', l10n),\n l10n.exponent,\n (exp < 0 ? l10n.negative : l10n.positive),\n x < 10 ? '0' : '',\n x\n ];\n};\n\nexport function general (ret, part, value, l10n) {\n const int = value | 0;\n\n if (typeof value === 'string') {\n // special case for: [<-25]General;[>25]General;General;General\n ret.push(value);\n }\n else if (value === int) {\n ret.push(Math.abs(int));\n }\n else {\n const v = Math.abs(value);\n let exp = getExponent(v);\n let n = getSignificand(v, exp);\n if (n === 10) {\n n = 1;\n exp++;\n }\n\n // The application shall attempt to display the full number\n // up to 11 digits (inc. decimal point).\n const num_dig = numdec(v);\n if (exp >= -4 && exp <= -1) {\n const o = v.toPrecision(10 + exp).replace(/\\.?0+$/, '');\n ret.push(fixLocale(o, l10n));\n }\n else if (exp === 10) {\n const o = v.toFixed(10)\n .slice(0, 12)\n .replace(/\\.$/, '');\n ret.push(fixLocale(o, l10n));\n }\n else if (Math.abs(exp) <= 9) {\n if (num_dig.total <= 11) {\n const o = round(v, 9).toFixed(num_dig.frac);\n ret.push(fixLocale(o, l10n));\n }\n else if (exp === 9) {\n ret.push(Math.floor(v));\n }\n else if (exp >= 0 && exp < 9) {\n ret.push(fixLocale(String(round(v, 9 - exp)), l10n));\n }\n else {\n ret.push(...getExp(n, exp, l10n));\n }\n }\n else if (num_dig.total >= 12) {\n ret.push(...getExp(n, exp, l10n));\n }\n else {\n ret.push(fixLocale(round(v, 9).toFixed(num_dig.frac), l10n));\n }\n }\n return ret;\n}\n","/**\n * @ignore\n * @param {string} c Instruction character (['#', '?', '0'])\n * @param {boolean} [nbsp=false] Use nonbreaking space or not?\n * @returns {string} Padding character\n */\nexport function pad (c, nbsp = false) {\n if (c === '0') {\n return '0';\n }\n else if (c === '?') {\n return nbsp ? '\\u00a0' : ' ';\n }\n // if (c === '#')\n return '';\n}\n","import { round } from './round.js';\nimport { clamp } from './clamp.js';\nimport { dec2frac } from './dec2frac.js';\nimport { general } from './general.js';\nimport { toYMD } from './toYMD.js';\nimport { defaultLocale } from './locale.js';\nimport {\n u_DSEC, u_CSEC, u_MSEC,\n EPOCH_1317,\n MIN_S_DATE, MAX_S_DATE,\n MIN_L_DATE, MAX_L_DATE\n} from './constants.js';\nimport { pad } from './pad.js';\nimport { getExponent, getSignificand } from './numberProps.js';\n\nconst DAYSIZE = 86400;\n\nconst dateOverflows = (inputValue, roundedValue, bigRange) => {\n if (bigRange) {\n return (inputValue < MIN_L_DATE || roundedValue >= MAX_L_DATE);\n }\n return (inputValue < MIN_S_DATE || roundedValue >= MAX_S_DATE);\n};\n\nexport function runPart (value, part, opts, l10n_) {\n let mantissa = '';\n let mantissa_sign = '';\n let numerator = '';\n let denominator = '';\n let fraction = '';\n let integer = '';\n let exp = 0;\n\n let date = 0;\n if (typeof value === 'bigint') {\n if (value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER) {\n value = Number(value);\n }\n else {\n return opts.bigintErrorNumber\n ? String(value)\n : opts.overflow;\n }\n date = value;\n }\n else {\n date = Math.trunc(value);\n }\n let time = 0;\n let year = 0;\n let month = 1;\n let day = 0;\n let weekday = 0;\n let hour = 0;\n let minute = 0;\n let second = 0;\n let subsec = 0;\n\n const l10n = l10n_ || defaultLocale;\n\n // scale number\n if (!part.text && isFinite(part.scale) && part.scale !== 1) {\n value = clamp(value * part.scale);\n }\n\n // calc exponent\n if (part.exponential) {\n let v = Math.abs(value);\n if (v) {\n exp = getExponent(v, part.int_max);\n }\n if (value && !part.integer) {\n // when there isn't an integer part, the exp gets shifted by 1\n exp++;\n }\n v = getSignificand(v, exp);\n if (part.int_max === 1 && round(v, part.frac_max) === 10) {\n v = 1;\n exp++;\n }\n value = (value < 0) ? -v : v;\n mantissa += Math.abs(exp);\n }\n\n // integer to text\n if (part.integer) {\n const i = Math.abs(round(value, part.fractions ? 1 : part.frac_max));\n integer += (i < 1) ? '' : Math.floor(i);\n }\n // integer grouping\n const group_pri = opts.grouping[0] ?? 3;\n const group_sec = opts.grouping[1] ?? group_pri;\n\n // fraction to text\n if (part.dec_fractions) {\n fraction = String(round(value, part.frac_max)).split('.')[1] || '';\n }\n\n // using vulgar fractions\n const fixed_slash = !part.error && (part.num_p.includes('0') || part.den_p.includes('0'));\n\n let have_fraction = fixed_slash;\n if (part.fractions) {\n have_fraction = fixed_slash || !!(value % 1);\n const _dec = Math.abs(part.integer ? value % 1 : value);\n if (_dec) {\n have_fraction = true;\n if (part.denominator && isFinite(part.denominator)) {\n // predefined denominator\n denominator += part.denominator;\n numerator += round(_dec * part.denominator);\n if (numerator === '0') {\n numerator = '';\n denominator = '';\n have_fraction = fixed_slash;\n }\n }\n else {\n const frt = dec2frac(_dec, Infinity, part.den_max);\n numerator += frt[0];\n denominator += frt[1];\n if (part.integer && numerator === '0') {\n numerator = '';\n denominator = '';\n have_fraction = fixed_slash;\n }\n }\n }\n else if (!value && !part.integer) {\n have_fraction = true;\n numerator = '0';\n denominator = '1';\n }\n if (part.integer && !have_fraction && !Math.trunc(value)) {\n integer = '0';\n }\n }\n\n // using date/time\n if (part.date) {\n date = Math.trunc(value);\n const t = DAYSIZE * (value - date);\n time = Math.floor(t); // in seconds\n // \"epsilon\" correction\n subsec = t - time;\n if (Math.abs(subsec) < 1e-6) { // 0.000001\n subsec = 0;\n }\n else if (subsec > 0.9999) {\n subsec = 0;\n time += 1;\n if (time === DAYSIZE) {\n time = 0;\n date += 1;\n }\n }\n if (subsec) {\n // round time based on smallest used unit\n const minU = part.date & u_MSEC || part.date & u_CSEC || part.date & u_DSEC;\n if (\n (minU === u_MSEC && subsec > 0.9995) ||\n (minU === u_CSEC && subsec > 0.995) ||\n (minU === u_DSEC && subsec > 0.95) ||\n (!minU && subsec >= 0.5)\n ) {\n time++;\n subsec = 0;\n }\n }\n // serial date/time to gregorian calendar\n if (date || part.date_system) {\n const dout = toYMD(value, part.date_system, opts.leap1900);\n year = dout[0];\n month = dout[1];\n day = dout[2];\n }\n if (time) {\n const x = (time < 0) ? DAYSIZE + time : time;\n second = Math.floor(x) % 60;\n minute = Math.floor(x / 60) % 60;\n hour = Math.floor((x / 60) / 60) % 60;\n }\n weekday = (6 + date) % 7;\n if (part.date_eval && dateOverflows(value, date + (time / DAYSIZE), opts.dateSpanLarge)) {\n // if value is out of bounds and formatting is date Excel emits a\n // stream of \"######\" that fills the cell width.\n // This doesn't happen, if the only date tokens are \"elapsed time\" tokens.\n // Code instead follows the TEXT function which emits a #VALUE! error.\n if (opts.dateErrorThrows) {\n throw new Error('Date out of bounds');\n }\n if (opts.dateErrorNumber) {\n const _ret = value < 0 ? [ l10n.negative ] : [];\n return general(_ret, {}, value, l10n).join('');\n }\n return opts.overflow;\n }\n }\n\n const padQ = pad('?', opts.nbsp);\n\n // mantissa sign\n if (exp < 0) {\n mantissa_sign = '-';\n }\n else if (part.exp_plus) {\n mantissa_sign = '+';\n }\n\n const ret = [];\n\n const digitsStart = (numstr, pattern, prt, offset) => {\n const l = (!offset && numstr.length > pattern.length)\n ? prt.length + numstr.length - pattern.length\n : prt.length;\n if (numstr.length < pattern.length) {\n offset += numstr.length - pattern.length;\n }\n for (let i = 0; i < l; i++) {\n ret.push(numstr[i + offset] || pad(prt[i], opts.nbsp));\n }\n return l;\n };\n\n let denominator_fixed = false;\n const counter = { int: 0, frac: 0, man: 0, num: 0, den: 0 };\n for (let ti = 0, tl = part.tokens.length; ti < tl; ti++) {\n const tok = part.tokens[ti];\n const tokenType = tok.type;\n const len = tok.num ? tok.num.length : 0;\n\n if (tokenType === 'string') {\n // special rules may apply if next or prev is numerator or denominator\n if (tok.rule) {\n if (tok.rule === 'num') {\n if (have_fraction) {\n ret.push(tok.value.replace(/ /g, padQ));\n }\n else if (part.num_min > 0 || part.den_min > 0) {\n // FIXME: ret.push(''.repeat(tok.value.length))\n ret.push(tok.value.replace(/./g, padQ));\n }\n }\n else if (tok.rule === 'num+int') {\n if (have_fraction && integer) {\n ret.push(tok.value.replace(/ /g, padQ));\n }\n else if ((part.den_min > 0) && (integer || part.num_min)) {\n ret.push(tok.value.replace(/./g, padQ));\n }\n }\n else if (tok.rule === 'den') {\n if (have_fraction) {\n ret.push(tok.value.replace(/ /g, padQ));\n }\n else if (part.den_min > 0 || part.den_min > 0) {\n ret.push(tok.value.replace(/./g, padQ));\n }\n }\n }\n else {\n ret.push(tok.value.replace(/ /g, padQ));\n }\n }\n else if (tokenType === 'space') {\n if (tok.rule === 'num+int') {\n if (\n (have_fraction || part.num_min || part.den_min) &&\n (integer || part.num_min)\n ) {\n ret.push(padQ);\n }\n }\n else {\n ret.push(padQ);\n }\n }\n else if (tokenType === 'error') {\n // token used to define invalid pattern\n ret.push(opts.invalid);\n }\n else if (tokenType === 'point') {\n // Excel always emits a period: TEXT(0, \"#.#\") => \".\"\n ret.push(part.date ? tok.value : l10n.decimal);\n }\n else if (tokenType === 'general') {\n general(ret, part, value, l10n);\n }\n else if (tokenType === 'exp') {\n ret.push(l10n.exponent);\n }\n else if (tokenType === 'minus') {\n if (tok.volatile && part.date) {\n // don't emit the prepended minus if this is a date\n }\n else if (tok.volatile && (value >= 0 || typeof value !== 'number')) {\n // don't emit volatile minus for positive numbers\n }\n else if (tok.volatile && !part.fractions && (part.integer || part.dec_fractions)) {\n // minus is only shown if there is a non-zero digit present\n if (value < 0 && (integer && integer !== '0') || fraction) {\n ret.push(l10n.negative);\n }\n }\n else {\n ret.push(l10n.negative);\n }\n }\n else if (tokenType === 'plus') {\n ret.push(l10n.positive);\n }\n else if (tokenType === 'text') {\n ret.push(value);\n }\n else if (tokenType === 'fill') {\n // If user has provided a token to signal that next char is a fill char,\n // then emit the that plus the fill char. By default this does what the\n // TEXT function does in this case: Emits nothing.\n if (opts.fillChar) {\n ret.push(opts.fillChar, tok.value);\n }\n }\n else if (tokenType === 'skip') {\n // If user has provided a token to signal that next char is a fill char,\n // then emit the that plus the fill char. By default this does what the\n // TEXT function does in this case: Emits a space.\n if (opts.skipChar) {\n ret.push(opts.skipChar, tok.value);\n }\n else {\n ret.push(opts.nbsp ? '\\u00a0' : ' ');\n }\n }\n else if (tokenType === 'div') {\n if (have_fraction) {\n ret.push('/');\n }\n else if (part.num_min > 0 || part.den_min > 0) {\n ret.push(padQ);\n }\n else {\n ret.push(pad('#', opts.nbsp));\n }\n }\n else if (tokenType === 'int') {\n // number isn't fragmented\n if (part.int_pattern.length === 1) {\n const pt = part.int_p;\n const l = Math.max(part.int_min, integer.length);\n let digits = '';\n for (let i = l; i > 0; i--) {\n const d = integer.charAt(integer.length - i);\n const p = d ? '' : pt.charAt(pt.length - i) || pt[0];\n let sep = '';\n if (part.grouping) {\n const n = (i - 1) - group_pri;\n if (n >= 0 && !(n % group_sec)) {\n sep = (d || p === '0')\n ? l10n.group\n : pad('?', opts.nbsp);\n }\n }\n digits += (d || pad(p, opts.nbsp)) + sep;\n }\n ret.push(digits);\n }\n else {\n counter.int += digitsStart(integer, part.int_p, tok.num, counter.int);\n }\n }\n else if (tokenType === 'frac') {\n const o = counter.frac;\n for (let i = 0; i < len; i++) {\n ret.push(fraction[i + o] || pad(tok.num[i], opts.nbsp));\n }\n counter.frac += len;\n }\n else if (tokenType === 'man') {\n // mantissa sign is attached to the first digit, not the exponent symbol\n // \"0E+ 0\" will print as \"1E +12\"\n if (!counter[tokenType] && !counter.man) {\n ret.push(mantissa_sign);\n }\n counter.man += digitsStart(mantissa, part.man_p, tok.num, counter.man);\n }\n else if (tokenType === 'num') {\n counter.num += digitsStart(numerator, part.num_p, tok.num, counter.num);\n }\n else if (tokenType === 'den') {\n const o = counter.den;\n for (let i = 0; i < len; i++) {\n let digit = denominator[i + o];\n if (!digit) {\n const ch = tok.num[i];\n if (\n '123456789'.includes(ch) ||\n (denominator_fixed && ch === '0')\n ) {\n denominator_fixed = true;\n digit = opts.nbsp ? '\\u00a0' : ' ';\n }\n else if (\n !denominator_fixed &&\n (i === len - 1) &&\n ch === '0' &&\n !denominator\n ) {\n digit = '1';\n }\n else {\n digit = pad(ch, opts.nbsp);\n }\n }\n ret.push(digit);\n }\n counter.den += len;\n }\n else if (tokenType === 'year') {\n if (year < 0) { ret.push(l10n.negative); }\n ret.push(String(Math.abs(year)).padStart(4, '0'));\n }\n else if (tokenType === 'year-short') {\n const y = year % 100;\n ret.push(y < 10 ? '0' : '', y);\n }\n else if (tokenType === 'month') {\n ret.push((tok.pad && month < 10 ? '0' : ''), month);\n }\n else if (tokenType === 'monthname-single') {\n // This is what Excel does.\n // The Vietnamese list goes:\n // from [\"Tháng 1\", \"Tháng 2\", ... ] to [ \"T\", \"T\", ... ]\n // Simplified Chinese goes:\n // from [ 1月, ... 9月, 10月, 11月, 12月 ] to [ 1, ... 9, 1, 1, 1 ]\n if (part.date_system === EPOCH_1317) {\n ret.push(l10n.mmmm6[month - 1].charAt(0));\n }\n else {\n ret.push(l10n.mmmm[month - 1].charAt(0));\n }\n }\n else if (tokenType === 'monthname-short') {\n if (part.date_system === EPOCH_1317) {\n ret.push(l10n.mmm6[month - 1]);\n }\n else {\n ret.push(l10n.mmm[month - 1]);\n }\n }\n else if (tokenType === 'monthname') {\n if (part.date_system === EPOCH_1317) {\n ret.push(l10n.mmmm6[month - 1]);\n }\n else {\n ret.push(l10n.mmmm[month - 1]);\n }\n }\n else if (tok.type === 'weekday-short') {\n ret.push(l10n.ddd[weekday]);\n }\n else if (tokenType === 'weekday') {\n ret.push(l10n.dddd[weekday]);\n }\n else if (tokenType === 'day') {\n ret.push((tok.pad && day < 10 ? '0' : ''), day);\n }\n else if (tokenType === 'hour') {\n const h = hour % part.clock || (part.clock < 24 ? part.clock : 0);\n ret.push((tok.pad && h < 10 ? '0' : ''), h);\n }\n else if (tokenType === 'min') {\n ret.push((tok.pad && minute < 10 ? '0' : ''), minute);\n }\n else if (tokenType === 'sec') {\n ret.push((tok.pad && second < 10 ? '0' : ''), second);\n }\n else if (tokenType === 'subsec') {\n ret.push(l10n.decimal);\n // decimals is pre-determined by longest subsec token\n // but the number emitted is per-token\n const f = subsec.toFixed(part.sec_decimals);\n ret.push(f.slice(2, 2 + tok.decimals));\n }\n else if (tokenType === 'ampm') {\n const idx = hour < 12 ? 0 : 1;\n if (tok.short && !l10n_) {\n ret.push('AP'[idx]);\n }\n else {\n ret.push(l10n.ampm[idx]);\n }\n }\n else if (tokenType === 'hour-elap') {\n if (value < 0) { ret.push(l10n.negative); }\n const hh = (date * 24) + Math.floor(Math.abs(time) / (60 * 60));\n ret.push(String(Math.abs(hh)).padStart(tok.pad, '0'));\n }\n else if (tokenType === 'min-elap') {\n if (value < 0) { ret.push(l10n.negative); }\n const mm = (date * 1440) + Math.floor(Math.abs(time) / 60);\n ret.push(String(Math.abs(mm)).padStart(tok.pad, '0'));\n }\n else if (tokenType === 'sec-elap') {\n if (value < 0) { ret.push(l10n.negative); }\n const ss = (date * DAYSIZE) + Math.abs(time);\n ret.push(String(Math.abs(ss)).padStart(tok.pad, '0'));\n }\n else if (tokenType === 'b-year') {\n ret.push(year + 543);\n }\n else if (tokenType === 'b-year-short') {\n const y = (year + 543) % 100;\n ret.push(y < 10 ? '0' : '', y);\n }\n }\n return ret.join('');\n}\n","import { TOKEN_TEXT, indexColors } from './constants.js';\nimport { defaultLocale, getLocale } from './locale.js';\nimport { parseFormatSection } from './parseFormatSection.js';\nimport { runPart } from './runPart.js';\n\nconst default_text = parseFormatSection([\n { type: TOKEN_TEXT, value: '@', raw: '@' }\n]);\n\nfunction getPart (value, parts) {\n for (let pi = 0; pi < 3; pi++) {\n const part = parts[pi];\n if (part) {\n let cond;\n if (part.condition) {\n const operator = part.condition[0];\n const operand = part.condition[1];\n if (operator === '=') { cond = (value === operand); }\n else if (operator === '>') { cond = (value > operand); }\n else if (operator === '<') { cond = (value < operand); }\n else if (operator === '>=') { cond = (value >= operand); }\n else if (operator === '<=') { cond = (value <= operand); }\n else if (operator === '<>') { cond = (value !== operand); }\n }\n else {\n cond = true;\n }\n if (cond) {\n return part;\n }\n }\n }\n return undefined;\n}\n\nexport function formatColor (value, parseData, opts) {\n const parts = parseData.partitions;\n let part = parts[3];\n let color = null;\n if ((typeof value === 'number' || typeof value === 'bigint') && isFinite(value)) {\n part = getPart(value, parts);\n }\n if (part?.color) {\n color = part.color;\n }\n if (color && typeof color === 'number' && opts.indexColors) {\n color = indexColors[color - 1] || '#000';\n }\n return color;\n}\n\nexport function formatValue (value, parseData, opts) {\n const parts = parseData.partitions;\n const l10n = getLocale(parseData.locale || opts.locale);\n // not a number?\n const text_part = parts[3] ? parts[3] : default_text;\n if (typeof value === 'boolean') {\n const loc = l10n || defaultLocale;\n value = loc.bool[value ? 0 : 1];\n }\n if (value == null) {\n return '';\n }\n const n = typeof value === 'bigint';\n if (typeof value !== 'number' && !n) {\n return runPart(value, text_part, opts, l10n);\n }\n // guard against non-finite numbers:\n if (!n && !isFinite(value)) {\n const loc = l10n || defaultLocale;\n if (isNaN(value)) { return loc.nan; }\n return (value < 0 ? loc.negative : '') + loc.infinity;\n }\n // find and run the pattern part that applies to this number\n const part = getPart(value, parts);\n return part ? runPart(value, part, opts, l10n) : opts.overflow;\n}\n","import {\n TOKEN_GENERAL, TOKEN_HASH, TOKEN_ZERO, TOKEN_QMARK, TOKEN_SLASH, TOKEN_GROUP, TOKEN_SCALE,\n TOKEN_COMMA, TOKEN_BREAK, TOKEN_TEXT, TOKEN_PLUS, TOKEN_MINUS, TOKEN_POINT, TOKEN_SPACE,\n TOKEN_PERCENT, TOKEN_DIGIT, TOKEN_CALENDAR, TOKEN_ERROR, TOKEN_DATETIME, TOKEN_DURATION,\n TOKEN_CONDITION, TOKEN_DBNUM, TOKEN_NATNUM, TOKEN_LOCALE, TOKEN_COLOR, TOKEN_MODIFIER,\n TOKEN_AMPM, TOKEN_ESCAPED, TOKEN_STRING, TOKEN_SKIP, TOKEN_EXP, TOKEN_FILL, TOKEN_PAREN,\n TOKEN_CHAR\n} from './constants.js';\n\nconst tokenHandlers = [\n [ TOKEN_GENERAL, /^General/i, 0 ],\n [ TOKEN_HASH, /^#/, 0 ],\n [ TOKEN_ZERO, /^0/, 0 ],\n [ TOKEN_QMARK, /^\\?/, 0 ],\n [ TOKEN_SLASH, /^\\//, 0 ],\n // Commas are dealt with as a special case in the tokenizer but will end up\n // as one of these:\n // [ TOKEN_GROUP, /^(,),*/, 1 ],\n // [ TOKEN_SCALE, /^(,),*/, 1 ],\n // [ TOKEN_COMMA, /^(,),*/, 1 ],\n [ TOKEN_BREAK, /^;/, 0 ],\n [ TOKEN_TEXT, /^@/, 0 ],\n [ TOKEN_PLUS, /^\\+/, 0 ],\n [ TOKEN_MINUS, /^-/, 0 ],\n [ TOKEN_POINT, /^\\./, 0 ],\n [ TOKEN_SPACE, /^ /, 0 ],\n [ TOKEN_PERCENT, /^%/, 0 ],\n [ TOKEN_DIGIT, /^[1-9]/, 0 ],\n [ TOKEN_CALENDAR, /^(?:B[12])/i, 0 ],\n [ TOKEN_ERROR, /^B$/, 0 ], // pattern must not end in a \"B\"\n [ TOKEN_DATETIME, /^(?:[hH]+|[mM]+|[sS]+|[yY]+|[bB]+|[dD]+|[gG]+|[aA]{3,}|e+)/, 0 ],\n [ TOKEN_DURATION, /^(?:\\[(h+|m+|s+)\\])/i, 1 ],\n [ TOKEN_CONDITION, /^\\[(<[=>]?|>=?|=)\\s*(-?[.\\d]+)\\]/, [ 1, 2 ] ],\n [ TOKEN_DBNUM, /^\\[(DBNum[0-4]?\\d)\\]/i, 1 ],\n [ TOKEN_NATNUM, /^\\[(NatNum[0-4]?\\d)\\]/i, 1 ],\n [ TOKEN_LOCALE, /^\\[\\$([^\\]]+)\\]/, 1 ],\n [ TOKEN_COLOR, /^\\[(black|blue|cyan|green|magenta|red|white|yellow|color\\s*\\d+)\\]/i, 1 ],\n // conditionally allow these open ended directions?\n [ TOKEN_MODIFIER, /^\\[([^\\]]+)\\]/, 1 ],\n [ TOKEN_AMPM, /^(?:AM\\/PM|am\\/pm|A\\/P)/, 0 ],\n [ TOKEN_ESCAPED, /^\\\\(.)/, 1 ],\n [ TOKEN_STRING, /^\"([^\"]*?)\"/, 1 ],\n [ TOKEN_SKIP, /^_(\\\\.|.)/, 1 ],\n // Google Sheets and Excel diverge on \"e\": Excel only accepts E.\n [ TOKEN_EXP, /^[Ee]([+-])/, 1 ],\n [ TOKEN_FILL, /^\\*(\\\\.|.)/, 1 ],\n [ TOKEN_PAREN, /^[()]/, 0 ],\n [ TOKEN_ERROR, /^[EÈÉÊËèéêëĒēĔĕĖėĘęĚěȄȅȆȇȨȩNnÑñŃńŅņŇňǸǹ[\"*/\\\\_]/, 0 ],\n [ TOKEN_CHAR, /^./, 0 ]\n];\n\nconst CODE_QMRK = 63;\nconst CODE_HASH = 35;\nconst CODE_ZERO = 48;\nconst CODE_NINE = 57;\nconst isNumOp = char => {\n const c = (char || '\\0').charCodeAt(0);\n return (c === CODE_QMRK || c === CODE_HASH || (c >= CODE_ZERO && c <= CODE_NINE));\n};\n\n/**\n * @typedef {object} FormatToken\n * @property {string} type Token type.\n * @property {any} value The value of the token, cleaned of extra characters.\n * @property {string} raw Raw token source.\n */\n\n/**\n * Breaks a format pattern string into a list of tokens.\n *\n * The returned output will be an array of objects representing the tokens:\n *\n * ```js\n * [\n * { type: 'zero', value: '0', raw: '0' },\n * { type: 'point', value: '.', raw: '.' },\n * { type: 'zero', value: '0', raw: '0' },\n * { type: 'percent', value: '%', raw: '%' }\n * ]\n * ```\n *\n * Token types may be found as an Object as the\n * [`tokenTypes` export]{@link tokenTypes} of the package.\n *\n * @param {string} pattern The format pattern\n * @returns {FormatToken[]} a list of tokens\n */\nexport function tokenize (pattern) {\n let i = 0;\n const tokens = [];\n const unresolvedCommas = [];\n while (i < pattern.length) {\n const curr = pattern.slice(i);\n let step = 0;\n // A comma is context sensitive and needs to be handled as a special case.\n // This needs to happen in the tokenizer because to be able to re-localize\n // the pattern we'll need to know what each comma means.\n const mComma = /^(,+)(.)?/.exec(curr);\n if (mComma) {\n // comma depends on what it follows\n const raw = mComma[1];\n step = raw.length;\n const lookBehind = pattern[i - 1] || '';\n let maybeGROUP = false;\n let maybeSCALE = false;\n if (isNumOp(lookBehind)) { // 0-9, '#', or '?': may be GROUP or SCALE\n maybeGROUP = true;\n maybeSCALE = true;\n }\n else if (lookBehind === '.') { // '.': may be SCALE only\n maybeSCALE = true;\n }\n // if at the end of the pattern or section, then this can't be a GROUP op\n const lookAhead = mComma[2] || '';\n if (maybeGROUP && (!lookAhead || lookAhead === ';')) {\n maybeGROUP = false;\n }\n // if next char is a num token, then this cannot be a SCALE op\n if (maybeSCALE && isNumOp(lookAhead)) {\n maybeSCALE = false;\n }\n if (maybeGROUP && !maybeSCALE) {\n tokens.push({ type: TOKEN_GROUP, value: ',', raw });\n }\n else if (!maybeGROUP && maybeSCALE) {\n tokens.push({ type: TOKEN_SCALE, value: ',', raw });\n }\n else if (maybeGROUP && maybeSCALE) {\n // this token will be set to scale, but switched to group if we hit a\n // num token later on in the pattern...\n const t = { type: TOKEN_SCALE, value: ',', raw };\n tokens.push(t);\n unresolvedCommas.push(t);\n }\n else {\n tokens.push({ type: TOKEN_COMMA, value: ',', raw });\n }\n }\n // all other symbols are matched using\n else {\n let token;\n for (const [ type, expr, group ] of tokenHandlers) {\n const m = expr.exec(curr);\n if (m) {\n const value = Array.isArray(group)\n ? group.map(d => m[d])\n : m[group || 0];\n token = { type, value, raw: m[0] };\n tokens.push(token);\n step = m[0].length;\n break;\n }\n }\n // if we just matched a break, then deal with any unresolved commas\n if (unresolvedCommas.length && token.raw === ';') {\n unresolvedCommas.length = 0;\n }\n // if we just matched a num operator, then deal with any unresolved commas\n if (unresolvedCommas.length && isNumOp(token.raw)) {\n unresolvedCommas.forEach(d => (d.type = TOKEN_GROUP));\n unresolvedCommas.length = 0;\n }\n }\n if (!step) {\n const raw = curr[0];\n step = 1;\n tokens.push({ type: TOKEN_CHAR, value: raw, raw });\n }\n i += step;\n }\n return tokens;\n}\n","import { resolveLocale } from './locale.js';\nimport { parseFormatSection } from './parseFormatSection.js';\nimport { tokenize } from './tokenize.js';\n\nconst maybeAddMinus = part => {\n const [ op, val ] = part.condition ?? [];\n const exception = (\n (val < 0 && (op === '<' || op === '<=' || op === '=')) ||\n (val === 0 && (op === '<'))\n );\n if (!exception) {\n part.tokens.unshift({\n type: 'minus',\n volatile: true\n });\n }\n};\n\nconst clonePart = (part, prefixToken = null) => {\n const r = {};\n for (const key in part) {\n if (Array.isArray(part[key])) {\n r[key] = [ ...part[key] ];\n }\n else {\n r[key] = part[key];\n }\n }\n if (prefixToken) {\n r.tokens.unshift(prefixToken);\n }\n r.generated = true;\n return r;\n};\n\nexport function parsePattern (pattern) {\n const partitions = [];\n let conditional = false;\n let l10n_override;\n let text_partition = null;\n let more = 0;\n let part = false;\n let i = 0;\n let conditions = 0;\n let tokens = tokenize(pattern);\n do {\n part = parseFormatSection(tokens);\n // Dates cannot blend with non-date tokens\n // General cannot blend with non-date tokens\n // -- This is does not match Excel 100% which\n // seems to allow \",\" as a text token with General\n // -- Excel also does something strange when mixing\n // General with dates (but that can hardly be expected to work)\n if (\n (part.date || part.general) &&\n (part.int_pattern.length || part.frac_pattern.length || part.scale !== 1 || part.text)\n ) {\n throw new Error('Illegal format');\n }\n if (part.condition) {\n conditions++;\n conditional = true;\n }\n if (part.text) {\n // only one text partition is allowed per pattern\n if (text_partition) {\n throw new Error('Unexpected partition');\n }\n text_partition = part;\n }\n if (part.locale) {\n l10n_override = resolveLocale(part.locale);\n }\n partitions.push(part);\n\n more = tokens[part.tokensUsed]?.type === 'break' ? 1 : 0;\n tokens = tokens.slice(part.tokensUsed + more);\n i++;\n }\n while (more && i < 4 && conditions < 3);\n\n // No more than 4 sections are allowed\n if (more) {\n throw new Error('Unexpected partition');\n }\n // Only 2 conditional statements are allowed: \"1;2;else;txt\"\n if (conditions > 2) {\n throw new Error('Unexpected condition');\n }\n // 3rd part must be text of neutral if it is present\n const part3 = partitions[3];\n if (part3 && (part3.int_pattern.length || part3.frac_pattern.length || part3.date)) {\n throw new Error('Unexpected partition');\n }\n\n if (conditional) {\n // When a pattern has a single partition which exists in the second\n // partition, We must also set the first partition to `[>0]`.\n if (!partitions[0].condition) {\n partitions[0].condition = [ '>', 0 ];\n }\n\n // conditional patterns get a volatile minus on the \"else\" partitions\n const numParts = partitions.length;\n if (numParts === 1) {\n // provide a fallback pattern if there isn't one\n partitions[1] = parseFormatSection(tokenize('General'));\n partitions[1].generated = true;\n }\n if (numParts <= 2) {\n // what happens when [<10]0;[>10]0 <=> 3 or -3?\n // => pattern is \"valid\" but won't match anything runtime, so errors\n }\n // 1 and 2 part conditionals\n if (numParts < 3) {\n const part1 = partitions[0];\n const part2 = partitions[1];\n // first part follows standard < <= = rules\n maybeAddMinus(part1);\n // second part uses standard as well *if it has conditions*\n if (part2.condition) {\n maybeAddMinus(part2);\n }\n else {\n // ...else it *seems* to follow logic based on first condition\n const cond = part1.condition;\n if (\n cond[0] === '=' ||\n (cond[1] >= 0 && (cond[0] === '>' || cond[0] === '>='))\n ) {\n part2.tokens.unshift({\n type: 'minus',\n volatile: true\n });\n }\n }\n }\n else {\n // 3 and 4 part patterns\n partitions.forEach(maybeAddMinus);\n }\n }\n // if this is not a conditional, then we ensure we have all 4 partitions\n else {\n // if we have less than 4 partitions and one of them is .text,\n // we need to use it as the text one\n if (partitions.length < 4 && text_partition) {\n for (let pi = 0, pl = partitions.length; pi < pl; pi++) {\n if (partitions[pi] === text_partition) {\n partitions.splice(pi, 1);\n }\n }\n }\n // missing positive\n if (partitions.length < 1 && text_partition) {\n partitions[0] = parseFormatSection(tokenize('General'));\n partitions[0].generated = true;\n }\n // missing negative\n if (partitions.length < 2) {\n // the volatile minus only happens if there is a single pattern\n const volMinus = { type: 'minus', volatile: true };\n partitions.push(clonePart(partitions[0], volMinus));\n }\n // missing zero\n if (partitions.length < 3) {\n partitions.push(clonePart(partitions[0]));\n }\n // missing text\n if (partitions.length < 4) {\n if (text_partition) {\n partitions.push(text_partition);\n }\n else {\n const p = parseFormatSection(tokenize('@'));\n p.generated = true;\n partitions.push(p);\n }\n }\n\n partitions[0].condition = [ '>', 0 ];\n partitions[1].condition = [ '<', 0 ];\n partitions[2].condition = null;\n }\n\n return {\n pattern: pattern,\n partitions: partitions,\n locale: l10n_override\n };\n}\n\nexport function parseCatch (pattern) {\n try {\n return parsePattern(pattern);\n }\n catch (err) {\n const errPart = {\n tokens: [ { type: 'error' } ],\n error: err.message\n };\n return {\n pattern: pattern,\n partitions: [ errPart, errPart, errPart, errPart ],\n error: err.message,\n locale: null\n };\n }\n}\n","import {\n TOKEN_GENERAL, TOKEN_HASH, TOKEN_ZERO, TOKEN_QMARK, TOKEN_SLASH, TOKEN_GROUP, TOKEN_SCALE,\n TOKEN_COMMA, TOKEN_BREAK, TOKEN_TEXT, TOKEN_PLUS, TOKEN_MINUS, TOKEN_POINT, TOKEN_SPACE,\n TOKEN_PERCENT, TOKEN_DIGIT, TOKEN_CALENDAR, TOKEN_ERROR, TOKEN_DATETIME, TOKEN_DURATION,\n TOKEN_CONDITION, TOKEN_DBNUM, TOKEN_NATNUM, TOKEN_LOCALE, TOKEN_COLOR, TOKEN_MODIFIER,\n TOKEN_AMPM, TOKEN_ESCAPED, TOKEN_STRING, TOKEN_SKIP, TOKEN_EXP, TOKEN_FILL, TOKEN_PAREN,\n TOKEN_CHAR\n} from './constants.js';\n\nexport {\n getLocale,\n parseLocale,\n addLocale\n} from './locale.js';\n\nimport { defaultOptions } from './options.js';\n\nexport { round } from './round.js';\nexport { dec2frac } from './dec2frac.js';\n\nimport { dateToSerial as handleDates } from './serialDate.js';\nexport { dateToSerial, dateFromSerial } from './serialDate.js';\n\nexport {\n parseNumber,\n parseDate,\n parseTime,\n parseBool,\n parseValue\n} from './parseValue.js';\n\nimport { formatColor as fmtColor, formatValue as fmtValue } from './formatNumber.js';\nimport { info, dateInfo, isDate, isPercent, isText } from './formatInfo.js';\nimport { parsePattern } from './parsePattern.js';\n\nexport { tokenize } from './tokenize.js';\n\nconst _parseDataCache = Object.create({});\nfunction prepareFormatterData (pattern, shouldThrow = false) {\n if (!pattern) { pattern = 'General'; }\n\n let parseData = _parseDataCache[pattern];\n if (!parseData) {\n try {\n parseData = parsePattern(pattern);\n _parseDataCache[pattern] = parseData;\n }\n catch (err) {\n // if the options say to throw errors, then do so\n if (shouldThrow) {\n throw err;\n }\n // else we set the parsedata to error\n const errPart = {\n tokens: [ { type: 'error' } ],\n error: err.message\n };\n parseData = {\n pattern: pattern,\n partitions: [ errPart, errPart, errPart, errPart ],\n error: err.message,\n locale: null\n };\n }\n }\n return parseData;\n}\n\n/**\n * Formats a value as a string and returns the result.\n *\n * - Dates are normalized to spreadsheet style serial dates and then formatted.\n * - Booleans are emitted as uppercase \"TRUE\" or \"FALSE\".\n * - Null and Undefined will return an empty string \"\".\n * - Any non number values will be stringified and passed through the text section of the format pattern.\n * - NaNs and infinites will use the corresponding strings from the active locale.\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @param {*} value - The value to format.\n * @param {object} [options={}] Options\n * @param {string} [options.locale=\"\"]\n * A BCP 47 string tag. Locale default is english with a `\\u00a0`\n * grouping symbol (see [addLocale](#addLocale))\n * @param {string} [options.overflow=\"######\"]\n * The string emitted when a formatter fails to format a date that is out\n * of bounds.\n * @param {string} [options.invalid=\"######\"]\n * The string emitted when no-throw mode fails to parse a pattern.\n * @param {boolean} [options.throws=true]\n * Should the formatter throw an error if a provided pattern is invalid.\n * If false, a formatter will be constructed which instead outputs an error\n * string (see _invalid_ in this table).\n * @param {boolean} [options.nbsp=false]\n * By default the output will use a regular space, but in many cases you\n * may desire a non-breaking-space instead.\n * @param {boolean} [options.leap1900=true]\n * Simulate the Lotus 1-2-3 [1900 leap year bug](https://docs.microsoft.com/en-us/office/troubleshoot/excel/wrongly-assumes-1900-is-leap-year).\n * It is a requirement in the Ecma OOXML specification so it is on by default.\n * @param {boolean} [options.dateErrorThrows=false]\n * Should the formatter throw an error when trying to format a date that is\n * out of bounds?\n * @param {boolean} [options.dateErrorNumber=true]\n * Should the formatter switch to a General number format when trying to\n * format a date that is out of bounds?\n * @param {boolean} [options.bigintErrorNumber=false]\n * Should the formatter switch to a plain string number format when trying to\n * format a bigint that is out of bounds?\n * @param {boolean} [options.dateSpanLarge=true]\n * Extends the allowed range of dates from Excel bounds (1900–9999) to\n * Google Sheet bounds (0–99999).\n * @param {boolean} [options.ignoreTimezone=false]\n * Normally when date objects are used with the formatter, time zone is taken\n * into account. This makes the formatter ignore the timezone offset.\n * @param {boolean} [options.skipChar='']\n * When the formatter encounters `_` it normally emits a single space instead\n * of the `_` and the next character (like Excel TEXT function does). Setting\n * this to a character will make the formatter emit that followed by the next\n * one.\n * @param {boolean} [options.fillChar='']\n * When the formatter encounters `*` it normally emits nothing instead of the\n * `*` and the next character (like Excel TEXT function does). Setting this\n * to a character will make the formatter emit that followed by the next one.\n * @returns {string} A formatted value\n */\nexport function format (pattern, value, options = {}) {\n const opts = Object.assign({}, defaultOptions, options);\n const data = prepareFormatterData(pattern, opts.throws);\n const val = handleDates(value, opts) ?? value;\n return fmtValue(val, data, opts);\n}\n\n/**\n * Find the color appropriate to a value as dictated by a format pattern.\n *\n * If the pattern defines colors, this function will emit the color appropriate\n * to the value. If no colors were specified this function returns `undefined`.\n *\n * ```js\n * const color = formatColor(\"[green]#,##0;[red]-#,##0\", -10);\n * console.log(color); // \"red\"\n * const color = formatColor(\"[green]#,##0;-#,##0\", -10);\n * console.log(color); // null\n * ```\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @param {*} value - The value to format.\n * @param {object} [options={}] Options\n * @param {boolean} [options.throws=true]\n * Should the formatter throw an error if a provided pattern is invalid.\n * If false, a formatter will be constructed which instead outputs an error\n * string (see _invalid_ in this table).\n * @param {boolean} [options.ignoreTimezone=false]\n * Normally when date objects are used with the formatter, time zone is taken\n * into account. This makes the formatter ignore the timezone offset.\n * @param {boolean} [options.indexColors=true]\n * When indexed color modifiers are used (`[Color 1]`) the formatter will\n * convert the index into the corresponding hex color of the default palette.\n * When this option is set to false, the number will instead by emitted\n * allowing you to index against a custom palette.\n * @returns {string|number|null}\n * A string color value as described by the pattern or a number if the\n * indexColors option has been set to false.\n */\nexport function formatColor (pattern, value, options) {\n const opts = Object.assign({}, defaultOptions, options);\n const data = prepareFormatterData(pattern, opts.throws);\n const val = handleDates(value, opts) ?? value;\n return fmtColor(val, data, opts);\n}\n\n// FIXME: what is a a section?...\n/**\n * Determine if a given format pattern is a date pattern.\n *\n * The pattern is considered a date pattern if any of its sections contain a\n * date symbol (such as `Y` or `H`). Each section is restricted to be\n * _either_ a number or date format.\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @returns {boolean} True if the specified pattern is date pattern, False otherwise.\n */\nexport function isDateFormat (pattern) {\n const data = prepareFormatterData(pattern, false);\n return isDate(data.partitions);\n}\n\n/**\n * Determine if a given format pattern is a percentage pattern.\n *\n * The pattern is considered a percentage pattern if any of its sections\n * contains an unescaped percentage symbol.\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @returns {boolean} True if the specified pattern is date pattern, False otherwise.\n */\nexport function isPercentFormat (pattern) {\n const data = prepareFormatterData(pattern, false);\n return isPercent(data.partitions);\n}\n\n/**\n * Determine if a given format pattern is a text only pattern.\n *\n * The pattern is considered text only if its definition is composed of a single\n * section that includes that text symbol (`@`).\n *\n * For example `@` or `@\" USD\"` are text patterns but `#;@` is not.\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @returns {boolean} True if the specified pattern is date pattern, False otherwise.\n */\nexport function isTextFormat (pattern) {\n const data = prepareFormatterData(pattern, false);\n return isText(data.partitions);\n}\n\n/**\n * Determine if a given format pattern is valid.\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @returns {boolean} True if the specified pattern is valid, False otherwise.\n */\nexport function isValidFormat (pattern) {\n try {\n prepareFormatterData(pattern, true);\n return true;\n }\n catch (err) {\n return false;\n }\n}\n\n/**\n * Returns an object detailing the properties and internals of a format parsed\n * format pattern.\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @param {object} [options={}] Options\n * @param {string} [options.currency]\n * Limit the patterns identified as currency to those that use the give string.\n * If nothing is provided, patterns will be tagged as currency if one of the\n * following currency symbols is used: ¤$£¥֏؋৳฿៛₡₦₩₪₫€₭₮₱₲₴₸₹₺₼₽₾₿\n * @returns {FormatInfo} An object of format properties.\n */\nexport function getFormatInfo (pattern, options = {}) {\n const data = prepareFormatterData(pattern, false);\n if (!data.info) {\n data.info = info(data.partitions, options?.currency);\n }\n return data.info;\n}\n\n/**\n * Gets information about date codes use in a format string.\n *\n * @param {string} pattern - A format pattern in the ECMA-376 number format.\n * @returns {FormatDateInfo} An object of format date properties.\n */\nexport function getFormatDateInfo (pattern) {\n const data = prepareFormatterData(pattern, false);\n if (!data.dateInfo) {\n data.dateInfo = dateInfo(data.partitions);\n }\n return data.dateInfo;\n}\n\n/**\n * A dictionary of the types used to identify token variants.\n *\n * @readonly\n * @constant {Object<string>} tokenTypes\n * @property {string} AMPM - AM/PM operator (`AM/PM`, `A/P`)\n * @property {string} BREAK - Semicolon operator indicating a break between format sections (`;`)\n * @property {string} CALENDAR - Calendar modifier (`B2`)\n * @property {string} CHAR - Single non-operator character (`m`)\n * @property {string} COLOR - Color modifier (`[Black]`, `[color 5]`)\n * @property {string} COMMA - Plain non-operator comma (`,`)\n * @property {string} CONDITION - Condition modifier for a section (`[>=10]`)\n * @property {string} DATETIME - Date-time operator (`mmmm`, `YY`)\n * @property {string} DBNUM - Number display modifier (`[DBNum23]`)\n * @property {string} DIGIT - A digit between 1 and 9 (`3`)\n * @property {string} DURATION - Time duration (`[ss]`)\n * @property {string} ERROR - Unidentifiable or illegal character (`Ň`)\n * @property {string} ESCAPED - Escaped character (`\\E`)\n * @property {string} EXP - Exponent operator (`E+`)\n * @property {string} FILL - Fill with char operator and operand (`*_`)\n * @property {string} GENERAL - General format operator (`General`)\n * @property {string} GROUP - Number grouping operator (`,`)\n * @property {string} HASH - Hash operator (digit if available) (`#`)\n * @property {string} LOCALE - Locale modifier (`[$-1E020404]`)\n * @property {string} MINUS - Minus sign (`-`)\n * @property {string} MODIFIER - An unidentified modifier (`[Schwarz]`)\n * @property {string} NATNUM - Number display modifier (`[NatNum3]`)\n * @property {string} PAREN - Parenthesis character (`)`)\n * @property {string} PERCENT - Percent operator (`%`)\n * @property {string} PLUS - Plus sign (`+`)\n * @property {string} POINT - Decimal point operator (`.`)\n * @property {string} QMARK - Question mark operator (digit or space if not available) (`?`)\n * @property {string} SCALE - Scaling operator (`,`)\n * @property {string} SKIP - Skip with char operator and operand (`*_`)\n * @property {string} SLASH - Slash operator (`/`)\n * @property {string} SPACE - Space (` `)\n * @property {string} STRING - Quoted string (`\"days\"`)\n * @property {string} TEXT - Text output operator (`@`)\n * @property {string} ZERO - Zero operator (digit or zero if not available) (`0`)\n * @see tokenize\n */\nexport const tokenTypes = Object.freeze({\n AMPM: TOKEN_AMPM,\n BREAK: TOKEN_BREAK,\n CALENDAR: TOKEN_CALENDAR,\n CHAR: TOKEN_CHAR,\n COLOR: TOKEN_COLOR,\n COMMA: TOKEN_COMMA,\n CONDITION: TOKEN_CONDITION,\n DATETIME: TOKEN_DATETIME,\n DBNUM: TOKEN_DBNUM,\n DIGIT: TOKEN_DIGIT,\n DURATION: TOKEN_DURATION,\n ERROR: TOKEN_ERROR,\n ESCAPED: TOKEN_ESCAPED,\n EXP: TOKEN_EXP,\n FILL: TOKEN_FILL,\n GENERAL: TOKEN_GENERAL,\n GROUP: TOKEN_GROUP,\n HASH: TOKEN_HASH,\n LOCALE: TOKEN_LOCALE,\n MINUS: TOKEN_MINUS,\n MODIFIER: TOKEN_MODIFIER,\n NATNUM: TOKEN_NATNUM,\n PAREN: TOKEN_PAREN,\n PERCENT: TOKEN_PERCENT,\n PLUS: TOKEN_PLUS,\n POINT: TOKEN_POINT,\n QMARK: TOKEN_QMARK,\n SCALE: TOKEN_SCALE,\n SKIP: TOKEN_SKIP,\n SLASH: TOKEN_SLASH,\n SPACE: TOKEN_SPACE,\n STRING: TOKEN_STRING,\n TEXT: TOKEN_TEXT,\n ZERO: TOKEN_ZERO\n});\n","/**\n * 透视表 行/列 维度「数据处理函数」注册表\n * ----------------------------------------------------------------\n * 用途:在透视表配置中,为某个维度字段指定一个处理函数,\n * 在数据进入 VTable 前对原始值做转换(典型场景:把时间字符串\n * 处理成「年 / 年月 / 年月日」,从而实现按年、按月分组)。\n *\n * 设计原则:\n * - 配置中只保存函数的「key 字符串」(可 JSON 序列化,便于存入视图);\n * 运行时通过 getPivotFieldProcessFunc(key) 取到真正的函数。\n * - 新增处理函数只需在 PIVOT_FIELD_PROCESS_FUNCS 中追加一项,UI 自动出现。\n */\n\nimport dayjs from 'dayjs';\nimport * as numfmt from 'numfmt';\n\n/** 数据处理函数签名:输入原始值,输出处理后的值 */\nexport type PivotFieldProcessFunc = (value: any) => any;\n\n/** 单个处理函数的元信息(用于 UI 下拉与运行时查找) */\nexport interface PivotProcessFuncDef {\n key: string; // 配置中保存的函数标识\n label: string; // 下拉框展示名称\n func: PivotFieldProcessFunc; // 实际处理函数\n}\n\n/**\n * 将各种形态的时间值归一化为 dayjs 对象\n * 支持:Date 对象、秒/毫秒时间戳、'YYYY-MM-DD'、'YYYY-MM-DD HH:mm:ss'、\n * ISO 字符串(含 T 与时区)等。\n */\nconst normalizeDate = (value: any): dayjs.Dayjs | null => {\n if (value === null || value === undefined || value === '') return null;\n if (value instanceof Date) return dayjs(value);\n if (typeof value === 'number') {\n if (!isFinite(value)) return null;\n // 10 位秒级时间戳 → 毫秒\n const ms = value < 1e12 ? value * 1000 : value;\n const d = dayjs(ms);\n return d.isValid() ? d : null;\n }\n const s = String(value).trim();\n if (!s) return null;\n // 处理 ISO(2026-07-07T00:00:00.000Z)→ 取前 19 位并按本地时间解析\n const normalized = s.includes('T') ? s.replace('T', ' ').slice(0, 19) : s;\n const d = dayjs(normalized);\n return d.isValid() ? d : null;\n};\n\n/** 季度(1-4),dayjs 核心未内置 quarter,手动计算 */\nconst quarterOf = (d: dayjs.Dayjs): number => Math.floor(d.month() / 3) + 1;\n\nexport const PIVOT_FIELD_PROCESS_FUNCS: PivotProcessFuncDef[] = [\n {\n key: 'none',\n label: '不处理',\n func: v => v,\n },\n {\n key: 'date_year',\n label: '日期 → 年',\n func: v => {\n const d = normalizeDate(v);\n return d ? d.format('YYYY') : v;\n },\n },\n {\n key: 'date_year_quarter',\n label: '日期 → 年·季度',\n func: v => {\n const d = normalizeDate(v);\n return d ? `${d.format('YYYY')}Q${quarterOf(d)}` : v;\n },\n },\n {\n key: 'date_year_month',\n label: '日期 → 年月',\n func: v => {\n const d = normalizeDate(v);\n return d ? d.format('YYYY-MM') : v;\n },\n },\n {\n key: 'date_year_month_day',\n label: '日期 → 年月日',\n func: v => {\n const d = normalizeDate(v);\n return d ? d.format('YYYY-MM-DD') : v;\n },\n },\n {\n key: 'date_month',\n label: '日期 → 月',\n func: v => {\n const d = normalizeDate(v);\n return d ? d.format('MM') : v;\n },\n },\n {\n key: 'date_day',\n label: '日期 → 日',\n func: v => {\n const d = normalizeDate(v);\n return d ? d.format('DD') : v;\n },\n },\n];\n\nconst FUNC_MAP = new Map<string, PivotProcessFuncDef>(\n PIVOT_FIELD_PROCESS_FUNCS.map(d => [d.key, d]),\n);\n\n/**\n * 按 key 获取处理函数。\n * - key 为空或等于 'none' 时返回 null(表示无需处理,数据原样使用)。\n * - key 未注册时返回 null(容错,避免配置异常导致整表崩溃)。\n */\nexport function getPivotFieldProcessFunc(key?: string | null): PivotFieldProcessFunc | null {\n if (!key || key === 'none') return null;\n return FUNC_MAP.get(key)?.func ?? null;\n}\n\n/**\n * 值字段「显示格式」注册表\n * ----------------------------------------------------------------\n * 用途:在透视表值配置中,为聚合后的值指定显示格式。\n * 三类格式:\n * - duration_seconds:秒 → 时间间隔(3天2小时15分钟40秒)\n * - duration_minutes:分钟 → 时间间隔\n * - numfmt:通用数字格式,使用 numfmt 库(ECMA-376 / Excel 标准格式串),\n * 具体格式串保存在值字段的 formatPattern 中(如 '#,##0.00'、'0.00%')。\n * 兼容旧配置:历史保存的 key('0.00' / '#,##0' / '¥0.00' 等)本身就是合法的\n * numfmt 格式串,formatPivotValue 会将未注册的 key 直接当作格式串处理。\n */\n\nexport interface PivotValueFormatDef {\n key: string; // 配置中保存的格式标识\n label: string; // 下拉框展示名称\n format: (value: number, pattern?: string) => string; // 格式化函数(numfmt 类需要 pattern)\n}\n\n/**\n * 将秒数转为可读的时间间隔,例如:3天2小时15分钟40秒500毫秒\n * - 支持负值(返回 \"-X天...\" 形式)。\n * - 输入为小数时,不足 1 秒的部分以「毫秒」呈现(四舍五入至整数毫秒),\n * 保证各分段之和尽量逼近原始秒数。\n */\nconst formatDurationFromSeconds = (totalSeconds: number | string): string => {\n if (totalSeconds === null || totalSeconds === undefined || totalSeconds === '')\n return String(totalSeconds);\n if (typeof totalSeconds !== 'number' || !isFinite(totalSeconds)) return String(totalSeconds);\n const isNeg = totalSeconds < 0;\n const totalMs = Math.round(Math.abs(totalSeconds) * 1000);\n const days = Math.floor(totalMs / 86400000);\n let rem = totalMs - days * 86400000;\n const hours = Math.floor(rem / 3600000);\n rem -= hours * 3600000;\n const minutes = Math.floor(rem / 60000);\n rem -= minutes * 60000;\n const seconds = Math.floor(rem / 1000);\n rem -= seconds * 1000;\n const ms = rem;\n const parts: string[] = [];\n if (days) parts.push(`${days}天`);\n if (hours) parts.push(`${hours}小时`);\n if (minutes) parts.push(`${minutes}分钟`);\n if (seconds) parts.push(`${seconds}秒`);\n if (ms) parts.push(`${ms}毫秒`);\n if (parts.length === 0) return '0秒';\n return (isNeg ? '-' : '') + parts.join('');\n};\n\n/** 通用数字格式的 key(选择此项后可通过 formatPattern 进一步配置格式串) */\nexport const NUMFMT_FORMAT_KEY = 'numfmt';\n\n/** 通用数字格式的缺省格式串 */\nexport const DEFAULT_NUMFMT_PATTERN = '#,##0.00';\n\n/**\n * 通用数字格式的常用格式串预设(供 a-auto-complete 下拉选择,也可自定义输入)。\n * 全部为 numfmt 库支持的 ECMA-376 / Excel 标准格式串。\n */\nexport const NUMFMT_PATTERN_PRESETS: { value: string; label?: string }[] = [\n { value: '0', label: '0(整数)' },\n { value: '0.0', label: '0.0(1 位小数)' },\n { value: '0.00', label: '0.00(2 位小数)' },\n { value: '0.000', label: '0.000(3 位小数)' },\n { value: '#,##0', label: '#,##0(千分位整数)' },\n { value: '#,##0.00', label: '#,##0.00(千分位 2 位小数)' },\n { value: '0%', label: '0%(百分比整数)' },\n { value: '0.00%', label: '0.00%(百分比 2 位小数)' },\n { value: '¥#,##0.00', label: '¥#,##0.00(人民币)' },\n { value: '$#,##0.00', label: '$#,##0.00(美元)' },\n { value: '0.00E+00', label: '0.00E+00(科学计数)' },\n { value: '# ?/?', label: '# ?/?(分数)' },\n { value: '#,##0;[Red]-#,##0', label: '#,##0;[Red]-#,##0(负数标红)' },\n];\n\n/**\n * 用 numfmt 库按格式串格式化数值。\n * - 格式串非法或格式化异常时回退为 String(value),避免整表崩溃。\n */\nconst numfmtFormat = (value: number, pattern?: string): string => {\n const p = (pattern || '').trim() || DEFAULT_NUMFMT_PATTERN;\n try {\n return numfmt.format(p, value);\n } catch {\n return String(value);\n }\n};\n\n/** 校验 numfmt 格式串是否合法(供配置界面即时提示) */\nexport function isValidNumfmtPattern(pattern?: string | null): boolean {\n if (!pattern || !pattern.trim()) return false;\n try {\n return numfmt.isValidFormat(pattern.trim());\n } catch {\n return false;\n }\n}\n\nexport const VALUE_FORMATS: PivotValueFormatDef[] = [\n {\n key: NUMFMT_FORMAT_KEY,\n label: '通用数字格式',\n format: (v, pattern) => numfmtFormat(v, pattern),\n },\n {\n key: 'duration_seconds',\n label: '秒 → 时间间隔',\n format: v => formatDurationFromSeconds(v),\n },\n {\n key: 'duration_minutes',\n label: '分钟 → 时间间隔',\n format: v => formatDurationFromSeconds((typeof v === 'number' ? v : Number(v)) * 60),\n },\n];\n\nconst FORMAT_MAP = new Map<string, PivotValueFormatDef>(VALUE_FORMATS.map(d => [d.key, d]));\n\n/** 按 key 获取格式定义 */\nexport function getPivotValueFormatDef(key?: string | null): PivotValueFormatDef | null {\n if (!key) return null;\n return FORMAT_MAP.get(key) ?? null;\n}\n\n/**\n * 统一的值格式化入口。\n * - formatFun 为空 → 原样字符串。\n * - duration_seconds / duration_minutes → 时间间隔。\n * - numfmt → 用 formatPattern(缺省 DEFAULT_NUMFMT_PATTERN)经 numfmt 库格式化。\n * - 其他任意 key(旧配置保存的 '0.00' / '#,##0' / '¥0.00' 等)→ 视为 numfmt\n * 格式串直接格式化,天然兼容历史数据。\n */\nexport function formatPivotValue(\n value: number,\n formatFun?: string | null,\n formatPattern?: string | null,\n): string {\n if (value === null || value === undefined) return '';\n if (typeof value !== 'number' || !isFinite(value)) return String(value);\n if (!formatFun) return String(value);\n const def = FORMAT_MAP.get(formatFun);\n if (def) return def.format(value, formatPattern ?? undefined);\n // 兼容旧配置:未注册的 key 当作 numfmt 格式串\n return numfmtFormat(value, formatFun);\n}\n\n/**\n * 按 key 获取格式化函数(兼容旧调用方)。\n * 未注册的 key 视为 numfmt 格式串返回对应格式化函数。\n */\nexport function getPivotValueFormatFunc(key?: string | null): ((value: number) => string) | null {\n if (!key) return null;\n const def = FORMAT_MAP.get(key);\n if (def) return v => def.format(v, undefined);\n return v => numfmtFormat(v, key);\n}\n\n/** 按 key 获取展示名称 */\nexport function getPivotValueFormatLabel(key?: string | null): string {\n if (!key) return '';\n return FORMAT_MAP.get(key)?.label ?? key;\n}\n\n/**\n * 兼容旧配置:把历史保存的格式串 key('0.00' / '#,##0' / '¥0.00' 等)\n * 归一为「通用数字格式 + formatPattern」,使配置界面下拉正确显示。\n * 已是注册 key(duration_* / numfmt)时不做任何处理。\n */\nexport function normalizeValueFormat(v: { formatFun?: string; formatPattern?: string }): void {\n if (!v.formatFun || FORMAT_MAP.has(v.formatFun)) return;\n v.formatPattern = v.formatPattern || v.formatFun;\n v.formatFun = NUMFMT_FORMAT_KEY;\n}\n","<template>\n <div class=\"column-edit-view-wrapper\">\n <template v-if=\"valueField.useExpression && expressionTokens.length\">\n <span class=\"vf-display-expr\">\n <span\n v-for=\"(t, i) in expressionTokens\"\n :key=\"i\"\n class=\"vf-display-token\"\n :class=\"tokenClass(t)\"\n >\n {{ tokenText(t) }}\n </span>\n </span>\n <a-tag\n v-if=\"!exprValid\"\n color=\"error\"\n class=\"vf-display-warn\"\n >\n {{ exprError || $t('表达式不完整') }}\n </a-tag>\n </template>\n <span\n v-else\n class=\"pivot-zone-item-label\"\n >\n {{ fieldLabel }}\n </span>\n\n <!-- 过滤条件徽标 -->\n <template v-if=\"filterTokens.length\">\n <a-tag\n class=\"vf-display-filter\"\n :color=\"filterValid ? 'blue' : 'error'\"\n >\n ⚑ {{ filterPreview }}\n </a-tag>\n <a-tag\n v-if=\"!filterValid\"\n color=\"error\"\n class=\"vf-display-warn\"\n >\n {{ filterError || $t('过滤条件不完整') }}\n </a-tag>\n </template>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { PivotValueField, PivotSubExpression } from '../typing';\nimport { validateValueExpression } from '../utils';\n\ninterface Props {\n valueField: PivotValueField;\n fieldOptions?: { field: string; label: string }[];\n // 当前配置中全部子表达式(供按 id 解析 subexpr token 的显示名)\n subExpressions?: PivotSubExpression[];\n}\nconst props = defineProps<Props>();\n\nconst fieldLabel = computed(() => props.valueField.label || props.valueField.field || '');\n\nconst expressionTokens = computed(() => props.valueField.expression || []);\nconst filterTokens = computed(() => props.valueField.filter || []);\n\nconst labelOf = (field: string) => {\n const f = (props.fieldOptions || []).find(x => x.field === field);\n return f ? f.label : field;\n};\n\nconst subExprNameOf = (id: string) => {\n const s = (props.subExpressions || []).find(x => x.id === id);\n return s ? s.name : id;\n};\n\nconst opDisplay: Record<string, string> = {\n '*': '×',\n '/': '÷',\n '&&': '且',\n '||': '或',\n '>=': '≥',\n '<=': '≤',\n '==': '=',\n '!=': '≠',\n '?': '?',\n ':': ':',\n};\n\nconst tokenText = (t: { type: string; value: string }): string => {\n switch (t.type) {\n case 'field':\n return `${labelOf(t.value)}(${t.value})`;\n case 'operator':\n return opDisplay[t.value] || t.value;\n case 'number':\n return t.value;\n case 'string':\n return `'${t.value}'`;\n case 'bool':\n return t.value;\n case 'lparen':\n return '(';\n case 'rparen':\n return ')';\n case 'subexpr':\n return subExprNameOf(t.value);\n default:\n return '';\n }\n};\n\nconst tokenClass = (t: { type: string }) => {\n if (t.type === 'field') return 'vf-token-field';\n if (t.type === 'number') return 'vf-token-number';\n if (t.type === 'string') return 'vf-token-string';\n if (t.type === 'bool') return 'vf-token-bool';\n if (t.type === 'lparen' || t.type === 'rparen') return 'vf-token-paren';\n if (t.type === 'subexpr') return 'vf-token-subexpr';\n return 'vf-token-op';\n};\n\nconst exprValid = computed(() => validateValueExpression(props.valueField.expression).valid);\nconst exprError = computed(() => validateValueExpression(props.valueField.expression).message || '');\n\nconst filterValid = computed(() => validateValueExpression(props.valueField.filter).valid);\nconst filterError = computed(() => validateValueExpression(props.valueField.filter).message || '');\nconst filterPreview = computed(() =>\n filterTokens.value\n .map(t => {\n if (t.type === 'field') return labelOf(t.value);\n if (t.type === 'operator') return opDisplay[t.value] || t.value;\n if (t.type === 'number') return t.value;\n if (t.type === 'string') return `'${t.value}'`;\n if (t.type === 'bool') return t.value;\n if (t.type === 'lparen') return '(';\n if (t.type === 'rparen') return ')';\n if (t.type === 'subexpr') return subExprNameOf(t.value);\n return '';\n })\n .join(' '),\n);\n</script>\n\n<style lang=\"less\" scoped>\n.pivot-zone-item-label {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.vf-display-expr {\n display: inline-flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 2px;\n}\n\n.vf-display-token {\n padding: 0 4px;\n border-radius: 3px;\n font-size: 12px;\n &.vf-token-field {\n color: #0958d9;\n background: #e6f7ff;\n }\n &.vf-token-op {\n font-weight: 700;\n color: #333;\n }\n &.vf-token-number {\n color: #389e0d;\n background: #f6ffed;\n }\n &.vf-token-string {\n color: #c41d7f;\n background: #fff0f6;\n }\n &.vf-token-bool {\n color: #722ed1;\n background: #f9f0ff;\n }\n &.vf-token-paren {\n font-weight: 700;\n color: #d46b08;\n }\n &.vf-token-subexpr {\n color: #722ed1;\n background: #f9f0ff;\n }\n}\n\n.vf-display-filter {\n margin-left: 4px;\n}\n\n.vf-display-warn {\n margin-left: 4px;\n}\n</style>\n","<template>\n <div class=\"column-edit-view-wrapper\">\n <template v-if=\"valueField.useExpression && expressionTokens.length\">\n <span class=\"vf-display-expr\">\n <span\n v-for=\"(t, i) in expressionTokens\"\n :key=\"i\"\n class=\"vf-display-token\"\n :class=\"tokenClass(t)\"\n >\n {{ tokenText(t) }}\n </span>\n </span>\n <a-tag\n v-if=\"!exprValid\"\n color=\"error\"\n class=\"vf-display-warn\"\n >\n {{ exprError || $t('表达式不完整') }}\n </a-tag>\n </template>\n <span\n v-else\n class=\"pivot-zone-item-label\"\n >\n {{ fieldLabel }}\n </span>\n\n <!-- 过滤条件徽标 -->\n <template v-if=\"filterTokens.length\">\n <a-tag\n class=\"vf-display-filter\"\n :color=\"filterValid ? 'blue' : 'error'\"\n >\n ⚑ {{ filterPreview }}\n </a-tag>\n <a-tag\n v-if=\"!filterValid\"\n color=\"error\"\n class=\"vf-display-warn\"\n >\n {{ filterError || $t('过滤条件不完整') }}\n </a-tag>\n </template>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { PivotValueField, PivotSubExpression } from '../typing';\nimport { validateValueExpression } from '../utils';\n\ninterface Props {\n valueField: PivotValueField;\n fieldOptions?: { field: string; label: string }[];\n // 当前配置中全部子表达式(供按 id 解析 subexpr token 的显示名)\n subExpressions?: PivotSubExpression[];\n}\nconst props = defineProps<Props>();\n\nconst fieldLabel = computed(() => props.valueField.label || props.valueField.field || '');\n\nconst expressionTokens = computed(() => props.valueField.expression || []);\nconst filterTokens = computed(() => props.valueField.filter || []);\n\nconst labelOf = (field: string) => {\n const f = (props.fieldOptions || []).find(x => x.field === field);\n return f ? f.label : field;\n};\n\nconst subExprNameOf = (id: string) => {\n const s = (props.subExpressions || []).find(x => x.id === id);\n return s ? s.name : id;\n};\n\nconst opDisplay: Record<string, string> = {\n '*': '×',\n '/': '÷',\n '&&': '且',\n '||': '或',\n '>=': '≥',\n '<=': '≤',\n '==': '=',\n '!=': '≠',\n '?': '?',\n ':': ':',\n};\n\nconst tokenText = (t: { type: string; value: string }): string => {\n switch (t.type) {\n case 'field':\n return `${labelOf(t.value)}(${t.value})`;\n case 'operator':\n return opDisplay[t.value] || t.value;\n case 'number':\n return t.value;\n case 'string':\n return `'${t.value}'`;\n case 'bool':\n return t.value;\n case 'lparen':\n return '(';\n case 'rparen':\n return ')';\n case 'subexpr':\n return subExprNameOf(t.value);\n default:\n return '';\n }\n};\n\nconst tokenClass = (t: { type: string }) => {\n if (t.type === 'field') return 'vf-token-field';\n if (t.type === 'number') return 'vf-token-number';\n if (t.type === 'string') return 'vf-token-string';\n if (t.type === 'bool') return 'vf-token-bool';\n if (t.type === 'lparen' || t.type === 'rparen') return 'vf-token-paren';\n if (t.type === 'subexpr') return 'vf-token-subexpr';\n return 'vf-token-op';\n};\n\nconst exprValid = computed(() => validateValueExpression(props.valueField.expression).valid);\nconst exprError = computed(() => validateValueExpression(props.valueField.expression).message || '');\n\nconst filterValid = computed(() => validateValueExpression(props.valueField.filter).valid);\nconst filterError = computed(() => validateValueExpression(props.valueField.filter).message || '');\nconst filterPreview = computed(() =>\n filterTokens.value\n .map(t => {\n if (t.type === 'field') return labelOf(t.value);\n if (t.type === 'operator') return opDisplay[t.value] || t.value;\n if (t.type === 'number') return t.value;\n if (t.type === 'string') return `'${t.value}'`;\n if (t.type === 'bool') return t.value;\n if (t.type === 'lparen') return '(';\n if (t.type === 'rparen') return ')';\n if (t.type === 'subexpr') return subExprNameOf(t.value);\n return '';\n })\n .join(' '),\n);\n</script>\n\n<style lang=\"less\" scoped>\n.pivot-zone-item-label {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.vf-display-expr {\n display: inline-flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 2px;\n}\n\n.vf-display-token {\n padding: 0 4px;\n border-radius: 3px;\n font-size: 12px;\n &.vf-token-field {\n color: #0958d9;\n background: #e6f7ff;\n }\n &.vf-token-op {\n font-weight: 700;\n color: #333;\n }\n &.vf-token-number {\n color: #389e0d;\n background: #f6ffed;\n }\n &.vf-token-string {\n color: #c41d7f;\n background: #fff0f6;\n }\n &.vf-token-bool {\n color: #722ed1;\n background: #f9f0ff;\n }\n &.vf-token-paren {\n font-weight: 700;\n color: #d46b08;\n }\n &.vf-token-subexpr {\n color: #722ed1;\n background: #f9f0ff;\n }\n}\n\n.vf-display-filter {\n margin-left: 4px;\n}\n\n.vf-display-warn {\n margin-left: 4px;\n}\n</style>\n","<template>\n <a-input\n v-bind=\"passthroughAttrs\"\n :value=\"innerVal\"\n @update:value=\"onUpdate\"\n >\n <template\n v-for=\"(slotContent, slotName) in $slots\"\n :key=\"slotName\"\n #[slotName]=\"slotProps\"\n >\n <slot :name=\"slotName\" v-bind=\"slotProps\" />\n </template>\n </a-input>\n</template>\n\n<script setup lang=\"ts\">\nimport { debounce } from 'lodash';\nimport { computed, onBeforeUnmount, ref, useAttrs, watch } from 'vue';\n\ninterface Props {\n /** 绑定值(v-model 或 v-model:value 均可),等价于 a-input 的 value */\n value?: string;\n modelValue?: string;\n /** 防抖延迟(毫秒),默认 300 */\n debounce?: number;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n value: '',\n modelValue: '',\n debounce: 300,\n});\n\nconst emit = defineEmits<{\n (e: 'update:value', val: string): void;\n (e: 'update:modelValue', val: string): void;\n /** 防抖后触发,等价于 a-input 的 change,供父级监听(如 onLabelInput / onValueConfigChange) */\n (e: 'change', val: string): void;\n}>();\n\n// 关闭属性继承:父级传入的 @change / v-model 事件由本组件自行防抖派发,\n// 不能透传给内部 a-input,否则每次按键都会直接触发父级 handler(失去防抖意义)。\ndefineOptions({ inheritAttrs: false });\n\nconst attrs = useAttrs();\n\n// 仅透传非事件类属性(allow-clear、size、placeholder、class、style、onClick 等),\n// 过滤掉本组件自行管理的 onChange / onUpdate:value / onUpdate:modelValue。\nconst passthroughAttrs = computed(() => {\n const rest: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (k === 'onChange' || k === 'onUpdate:value' || k === 'onUpdate:modelValue') continue;\n rest[k] = v;\n }\n return rest;\n});\n\n// 本地即时值:输入时立即回显,保证手感;真正向外派发受 lodash.debounce 控制。\nconst innerVal = ref<string>((props.modelValue || props.value || '') as string);\n\n// 用 lodash.debounce 实现防抖:仅在停止输入 debounce 毫秒后才向外派发,\n// 避免每次按键都触发父级 handler(如 onLabelInput / onValueConfigChange → 接口调用)。\nconst emitDebounced = debounce((val: string) => {\n emit('update:value', val);\n emit('update:modelValue', val);\n emit('change', val);\n}, props.debounce);\n\n// 外部(如父级回填、重置)更新绑定值时,同步内部显示。\nwatch(\n () => (props.value || props.modelValue || '') as string,\n (next) => {\n if (next !== innerVal.value) innerVal.value = next;\n },\n);\n\nconst onUpdate = (val: string) => {\n innerVal.value = val;\n emitDebounced(val);\n};\n\nonBeforeUnmount(() => {\n emitDebounced.cancel();\n});\n</script>\n","<template>\n <a-input\n v-bind=\"passthroughAttrs\"\n :value=\"innerVal\"\n @update:value=\"onUpdate\"\n >\n <template\n v-for=\"(slotContent, slotName) in $slots\"\n :key=\"slotName\"\n #[slotName]=\"slotProps\"\n >\n <slot :name=\"slotName\" v-bind=\"slotProps\" />\n </template>\n </a-input>\n</template>\n\n<script setup lang=\"ts\">\nimport { debounce } from 'lodash';\nimport { computed, onBeforeUnmount, ref, useAttrs, watch } from 'vue';\n\ninterface Props {\n /** 绑定值(v-model 或 v-model:value 均可),等价于 a-input 的 value */\n value?: string;\n modelValue?: string;\n /** 防抖延迟(毫秒),默认 300 */\n debounce?: number;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n value: '',\n modelValue: '',\n debounce: 300,\n});\n\nconst emit = defineEmits<{\n (e: 'update:value', val: string): void;\n (e: 'update:modelValue', val: string): void;\n /** 防抖后触发,等价于 a-input 的 change,供父级监听(如 onLabelInput / onValueConfigChange) */\n (e: 'change', val: string): void;\n}>();\n\n// 关闭属性继承:父级传入的 @change / v-model 事件由本组件自行防抖派发,\n// 不能透传给内部 a-input,否则每次按键都会直接触发父级 handler(失去防抖意义)。\ndefineOptions({ inheritAttrs: false });\n\nconst attrs = useAttrs();\n\n// 仅透传非事件类属性(allow-clear、size、placeholder、class、style、onClick 等),\n// 过滤掉本组件自行管理的 onChange / onUpdate:value / onUpdate:modelValue。\nconst passthroughAttrs = computed(() => {\n const rest: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (k === 'onChange' || k === 'onUpdate:value' || k === 'onUpdate:modelValue') continue;\n rest[k] = v;\n }\n return rest;\n});\n\n// 本地即时值:输入时立即回显,保证手感;真正向外派发受 lodash.debounce 控制。\nconst innerVal = ref<string>((props.modelValue || props.value || '') as string);\n\n// 用 lodash.debounce 实现防抖:仅在停止输入 debounce 毫秒后才向外派发,\n// 避免每次按键都触发父级 handler(如 onLabelInput / onValueConfigChange → 接口调用)。\nconst emitDebounced = debounce((val: string) => {\n emit('update:value', val);\n emit('update:modelValue', val);\n emit('change', val);\n}, props.debounce);\n\n// 外部(如父级回填、重置)更新绑定值时,同步内部显示。\nwatch(\n () => (props.value || props.modelValue || '') as string,\n (next) => {\n if (next !== innerVal.value) innerVal.value = next;\n },\n);\n\nconst onUpdate = (val: string) => {\n innerVal.value = val;\n emitDebounced(val);\n};\n\nonBeforeUnmount(() => {\n emitDebounced.cancel();\n});\n</script>\n","<template>\n <div class=\"vee-wrap\">\n <!-- 运算符 / 字面量面板 -->\n <div class=\"vee-ops\">\n <span class=\"vee-ops-label\">{{ $t('运算符') }}:</span>\n <a-button\n v-for=\"op in panelOperators\"\n :key=\"op.value\"\n size=\"small\"\n class=\"vee-op-btn\"\n draggable=\"true\"\n @dragstart=\"onOpDragStart(op.value)\"\n @dragend=\"onDragEnd\"\n @click=\"appendOp(op.value)\"\n :title=\"op.label\"\n >\n {{ op.display }}\n </a-button>\n\n <a-button\n size=\"small\"\n class=\"vee-op-btn\"\n draggable=\"true\"\n @dragstart=\"onOpDragStart('()')\"\n @dragend=\"onDragEnd\"\n @click=\"appendOp('()')\"\n :title=\"$t('括号(成对插入)')\"\n >\n ( )\n </a-button>\n <a-button\n size=\"small\"\n class=\"vee-op-btn\"\n draggable=\"true\"\n @dragstart=\"onOpDragStart('?:')\"\n @dragend=\"onDragEnd\"\n @click=\"appendOp('?:')\"\n :title=\"$t('三目:条件 ? 真值 : 假值(成对插入)')\"\n >\n ? :\n </a-button>\n\n <a-button\n size=\"small\"\n class=\"vee-op-btn vee-bool-btn\"\n draggable=\"true\"\n @click=\"appendBool('true')\"\n @dragstart=\"onBoolDragStart('true')\"\n @dragend=\"onDragEnd\"\n >\n true\n </a-button>\n <a-button\n size=\"small\"\n class=\"vee-op-btn vee-bool-btn\"\n draggable=\"true\"\n @click=\"appendBool('false')\"\n @dragstart=\"onBoolDragStart('false')\"\n @dragend=\"onDragEnd\"\n >\n false\n </a-button>\n <div class=\"vee-input-wrap\">\n <div\n :draggable=\"numTextValid\"\n @dragstart=\"onNumDragStart\"\n @dragend=\"onDragEnd\"\n :class=\"{ 'vee-op-item': numTextValid }\"\n >\n <a-input-search\n v-model:value=\"numText\"\n size=\"small\"\n class=\"vee-num-input\"\n :placeholder=\"$t('数字')\"\n @pressEnter=\"addNumber\"\n >\n <template #enterButton>\n <a-button\n size=\"small\"\n :disabled=\"!numTextValid\"\n @click=\"addNumber\"\n >\n {{ $t('添加数字') }}\n </a-button>\n </template>\n </a-input-search>\n </div>\n <div\n :draggable=\"strTextValid\"\n @dragstart=\"onStrDragStart\"\n @dragend=\"onDragEnd\"\n :class=\"{ 'vee-op-item': strTextValid }\"\n >\n <a-input-search\n v-model:value=\"strText\"\n size=\"small\"\n class=\"vee-str-input\"\n :placeholder=\"$t('字符串')\"\n @pressEnter=\"addString\"\n >\n <template #enterButton>\n <a-button\n size=\"small\"\n :disabled=\"!strTextValid\"\n @click=\"addString\"\n >\n {{ $t('添加字符串') }}\n </a-button>\n </template>\n </a-input-search>\n </div>\n\n <!-- 时间日期立即数:switch 开启带时间(日期时间),否则仅日期;拖拽/点击生成对应格式的字符串字面量 -->\n <div\n class=\"vee-date-row\"\n :draggable=\"dateValid\"\n @dragstart=\"onDateDragStart\"\n @dragend=\"onDragEnd\"\n :class=\"{ 'vee-op-item': dateValid }\"\n >\n <span class=\"vee-date-mode-label\">{{ $t('带时间') }}:</span>\n <a-switch\n v-model:checked=\"withTime\"\n size=\"small\"\n class=\"vee-date-mode\"\n />\n\n <a-date-picker\n v-model:value=\"dateValue\"\n :show-time=\"withTime\"\n :format=\"withTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'\"\n size=\"small\"\n class=\"vee-date-picker\"\n :placeholder=\"$t('选择时间')\"\n />\n <a-button\n size=\"small\"\n :disabled=\"!dateValid\"\n >\n {{ $t('添加时间') }}\n </a-button>\n </div>\n <a-button\n size=\"small\"\n type=\"text\"\n danger\n @click=\"clearExpr\"\n >\n {{ $t('清空表达式') }}\n </a-button>\n </div>\n </div>\n\n <!-- 可复用子表达式面板:点击/拖拽把子表达式名作为 subexpr token 加入表达式 -->\n <div\n v-if=\"availableSubExprs.length\"\n class=\"vee-subexpr-panel\"\n >\n <span class=\"vee-ops-label\">{{ $t('子表达式') }}:</span>\n <span\n v-for=\"se in availableSubExprs\"\n :key=\"se.id\"\n class=\"vee-subexpr-chip\"\n draggable=\"true\"\n @dragstart=\"onSubExprDragStart(se)\"\n @dragend=\"onDragEnd\"\n @click=\"appendSubExpr(se.id)\"\n :title=\"$t('点击或拖拽引用子表达式') + ':' + se.name\"\n >\n {{ se.name }}\n </span>\n </div>\n\n <!-- 表达式画布(拖拽落点) -->\n <div\n class=\"vee-canvas\"\n :class=\"{ 'drag-over': dragOverIndex >= 0 }\"\n @dragover.prevent=\"onCanvasDragOver\"\n @dragleave=\"onCanvasDragLeave\"\n @drop.stop=\"onCanvasDrop\"\n >\n <span\n v-if=\"!tokens.length\"\n class=\"vee-canvas-placeholder\"\n >\n {{ placeholder || $t('从左侧拖拽字段,或拖拽/点击运算符、数字、字符串到此处') }}\n </span>\n <span\n v-for=\"(t, i) in tokens\"\n :key=\"i\"\n class=\"vee-token\"\n :class=\"tokenClass(t)\"\n draggable=\"true\"\n @dragstart.stop=\"onTokenDragStart(i)\"\n @dragend=\"onDragEnd\"\n >\n {{ tokenText(t) }}\n <close-outlined\n class=\"vee-token-remove\"\n @click.stop=\"removeToken(i)\"\n />\n </span>\n <!-- 插入指示线:绝对定位覆盖层,不参与 flex 布局,避免拖拽时反复重排导致抖动 -->\n <span\n v-if=\"dragIndicator\"\n class=\"vee-insert-line\"\n :style=\"{\n left: dragIndicator.left + 'px',\n top: dragIndicator.top + 'px',\n height: dragIndicator.height + 'px',\n }\"\n />\n </div>\n\n <!-- 预览 + 校验 -->\n <div class=\"vee-preview-row\">\n <span class=\"vee-preview-label\">{{ $t('预览') }}:</span>\n <span\n class=\"vee-preview\"\n :class=\"{ 'vee-preview-invalid': !valid }\"\n >\n {{ preview || '—' }}\n </span>\n <span\n v-if=\"!valid\"\n class=\"vee-warn\"\n >\n {{ errorMsg || $t('表达式不完整(括号不匹配或缺少操作数)') }}\n </span>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, inject, ref, watch, type Ref } from 'vue';\nimport { CloseOutlined } from '@ant-design/icons-vue';\nimport {\n PivotValueExprToken,\n PivotFieldOption,\n PivotSubExpression,\n ExprLocalDrag,\n} from '../typing';\nimport { validateValueExpression } from '../utils';\n\ninterface Props {\n modelValue?: PivotValueExprToken[];\n fieldOptions: PivotFieldOption[];\n placeholder?: string;\n // 当前配置中所有子表达式(供编辑器展示可拖入的子表达式名称、以及按 id 解析显示名)\n subExpressions?: PivotSubExpression[];\n // 编辑某条子表达式时,排除其自身以避免直接自引用(递归展开由后端深度上限兜底环形依赖)\n excludeSubExprId?: string;\n}\nconst props = defineProps<Props>();\nconst emit = defineEmits<{\n (e: 'update:modelValue', v: PivotValueExprToken[]): void;\n (e: 'change'): void;\n}>();\n\n// 当前可引用的子表达式列表(排除正在编辑的自身)\nconst availableSubExprs = computed<PivotSubExpression[]>(() => {\n const list = props.subExpressions || [];\n if (props.excludeSubExprId) {\n return list.filter(s => s.id !== props.excludeSubExprId);\n }\n return list;\n});\n// 子表达式 id → 名称(用于 token 展示解析)\nconst subExprName = (id: string): string => {\n const s = (props.subExpressions || []).find(x => x.id === id);\n return s ? s.name : id;\n};\n\n// 运算符清单:value 为线上实际符号,display 为界面展示,label 为悬停说明\nconst operators = [\n { value: '+', display: '+', label: '加' },\n { value: '-', display: '−', label: '减' },\n { value: '*', display: '×', label: '乘' },\n { value: '/', display: '÷', label: '除' },\n { value: '^', display: '^', label: '幂(右结合)' },\n { value: '>', display: '>', label: '大于' },\n { value: '<', display: '<', label: '小于' },\n { value: '>=', display: '≥', label: '大于等于' },\n { value: '<=', display: '≤', label: '小于等于' },\n { value: '==', display: '=', label: '等于' },\n { value: '!=', display: '≠', label: '不等于' },\n { value: '&&', display: '且', label: '逻辑与' },\n { value: '||', display: '或', label: '逻辑或' },\n { value: '!', display: '非', label: '逻辑非(一元前缀)' },\n {\n value: 'abs',\n display: 'abs',\n label: '绝对值(一元前缀,作用于其后的表达式,如 abs(x) 或 abs(x-3))',\n },\n { value: '?', display: '?', label: '三目:条件 ? 真值 : 假值' },\n { value: ':', display: ':', label: '三目:分隔真/假值' },\n];\n\n// 面板按钮:'?' 与 ':' 由独立的「? :」三目按钮成对插入,此处过滤掉,避免单独拖入。\nconst panelOperators = operators.filter(o => o.value !== '?' && o.value !== ':');\n\nconst localDrag = inject<Ref<ExprLocalDrag | null>>(\n 'vfLocalDrag',\n ref(null) as Ref<ExprLocalDrag | null>,\n);\nconst dragOverIndex = ref<number>(-1);\n// 插入指示线的绝对定位坐标(相对画布内容原点);null 表示不显示。\nconst dragIndicator = ref<{ left: number; top: number; height: number } | null>(null);\nconst numText = ref('');\nconst strText = ref('');\n// 时间日期立即数:switch 开启带时间(YYYY-MM-DD HH:mm:ss),否则仅日期(YYYY-MM-DD),\n// 拖拽/点击生成一个对应格式的字符串字面量 token。\nconst withTime = ref(false);\nconst dateValue = ref<any>(null);\nconst formattedDate = computed(() => {\n if (!dateValue.value) return '';\n const fmt = withTime.value ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD';\n try {\n return dateValue.value.format(fmt);\n } catch {\n return '';\n }\n});\nconst dateValid = computed(() => formattedDate.value !== '');\n\nconst fieldLabel = (field: string): string => {\n const f = props.fieldOptions.find(x => x.field === field);\n return f ? f.label : field;\n};\n\n// 本地权威副本:所有增删先改本地 ref 再 emit 一次。\n// 不能用 computed 代理 props.modelValue——props 在同步代码内不会立即刷新,\n// 连续两次 insertToken 会读到过期数组而相互覆盖(括号/三目成对插入只显示后一个 token 的根因)。\nconst tokens = ref<PivotValueExprToken[]>(props.modelValue ? [...props.modelValue] : []);\nwatch(\n () => props.modelValue,\n v => {\n tokens.value = v ? [...v] : [];\n },\n { immediate: true, deep: false },\n);\n\nconst numTextValid = computed(() => {\n const s = (numText.value || '').trim();\n return s !== '' && !isNaN(Number(s));\n});\nconst strTextValid = computed(() => (strText.value || '').trim() !== '');\n\n// 预览与校验\nconst validation = computed(() => validateValueExpression(tokens.value));\nconst valid = computed(() => validation.value.valid);\nconst errorMsg = computed(() => validation.value.message || '');\nconst preview = computed(() =>\n tokens.value\n .map(t => {\n if (t.type === 'field') return fieldLabel(t.value);\n if (t.type === 'operator') return displayOp(t.value);\n if (t.type === 'number') return t.value;\n if (t.type === 'string') return `'${t.value}'`;\n if (t.type === 'bool') return t.value;\n if (t.type === 'lparen') return '(';\n if (t.type === 'rparen') return ')';\n if (t.type === 'subexpr') return subExprName(t.value);\n return '';\n })\n .join(' '),\n);\n\nconst displayOp = (v: string) => operators.find(o => o.value === v)?.display ?? v;\n\nconst tokenText = (t: PivotValueExprToken): string => {\n switch (t.type) {\n case 'field':\n return `${fieldLabel(t.value)}(${t.value})`;\n case 'operator':\n return displayOp(t.value);\n case 'number':\n return t.value;\n case 'string':\n return `'${t.value}'`;\n case 'bool':\n return t.value;\n case 'lparen':\n return '(';\n case 'rparen':\n return ')';\n case 'subexpr':\n return subExprName(t.value);\n default:\n return '';\n }\n};\n\nconst tokenClass = (t: PivotValueExprToken): string => {\n if (t.type === 'field') return 'vee-token-field';\n if (t.type === 'number') return 'vee-token-number';\n if (t.type === 'string') return 'vee-token-string';\n if (t.type === 'bool') return 'vee-token-bool';\n if (t.type === 'lparen' || t.type === 'rparen') return 'vee-token-paren';\n if (t.type === 'subexpr') return 'vee-token-subexpr';\n return 'vee-token-op';\n};\n\nconst emitChange = () => emit('change');\n\n// 统一提交:本地 tokens 始终更新(保证编辑体验,画布即时反映增删);\n// 但仅当表达式为「空」或「语法合法」时才向外 emit——\n// 非空且语法错误的中间态不外发,从而不会触发父组件的接口调用;\n// 空表达式属于合法的「无表达式/无过滤」状态,需要外发以便清除已有配置。\nconst commit = (arr: PivotValueExprToken[]) => {\n tokens.value = arr;\n const emitable = arr.length === 0 || validateValueExpression(arr).valid;\n if (emitable) {\n emit('update:modelValue', arr);\n emitChange();\n }\n};\n\n// ===== 拖拽起始 =====\nconst onOpDragStart = (op: string) => {\n localDrag.value = { kind: 'op', op };\n};\nconst onNumDragStart = (event: DragEvent) => {\n const s = (numText.value || '').trim();\n if (s === '' || isNaN(Number(s))) {\n event.preventDefault();\n return;\n }\n localDrag.value = { kind: 'number', value: s };\n};\nconst onStrDragStart = (event: DragEvent) => {\n const s = (strText.value || '').trim();\n if (s === '') {\n event.preventDefault();\n return;\n }\n localDrag.value = { kind: 'string', value: s };\n};\nconst onBoolDragStart = (value: string) => {\n localDrag.value = { kind: 'bool', value };\n};\nconst onTokenDragStart = (index: number) => {\n localDrag.value = { kind: 'token', index };\n};\nconst onSubExprDragStart = (se: PivotSubExpression) => {\n localDrag.value = { kind: 'subexpr', id: se.id, name: se.name };\n};\nconst onDragEnd = () => {\n localDrag.value = null;\n dragOverIndex.value = -1;\n dragIndicator.value = null;\n};\n\n// ===== 画布落点 =====\nconst computeInsertIndex = (event: DragEvent): number => {\n const container = event.currentTarget as HTMLElement;\n const chips = Array.from(container.querySelectorAll('.vee-token')) as HTMLElement[];\n if (chips.length === 0) return 0;\n const y = event.clientY;\n const rects = chips.map(c => c.getBoundingClientRect());\n const rows: number[][] = [];\n rects.forEach((r, i) => {\n const ri = rows.findIndex(rowIdxs => Math.abs(rects[rowIdxs[0]].top - r.top) < 8);\n if (ri === -1) {\n rows.push([i]);\n } else {\n rows[ri].push(i);\n }\n });\n let targetRow = 0;\n let best = Infinity;\n rows.forEach((rowIdxs, ri) => {\n const r0 = rects[rowIdxs[0]];\n const dist = y < r0.top ? r0.top - y : y > r0.top + r0.height ? y - (r0.top + r0.height) : 0;\n if (dist < best) {\n best = dist;\n targetRow = ri;\n }\n });\n const rowIdxs = rows[targetRow];\n for (const i of rowIdxs) {\n const r = rects[i];\n if (event.clientX < r.left + r.width / 2) return i;\n }\n return rowIdxs[rowIdxs.length - 1] + 1;\n};\n\n// 根据插入索引,计算指示线相对画布内容原点的像素坐标。\n// 用现有 chip 的 rect 推算,因指示线为绝对定位、不参与布局,故不会引起重排反馈。\nconst computeIndicatorPos = (\n container: HTMLElement,\n idx: number,\n): { left: number; top: number; height: number } => {\n const chips = Array.from(container.querySelectorAll('.vee-token')) as HTMLElement[];\n const crect = container.getBoundingClientRect();\n // 画布内容原点:需叠加滚动量与内边距,使坐标落在内容坐标系而非可视坐标系。\n const offX = -crect.left + container.scrollLeft;\n const offY = -crect.top + container.scrollTop;\n if (chips.length === 0) {\n return { left: 10, top: 10, height: 28 };\n }\n if (idx >= chips.length) {\n const r = chips[chips.length - 1].getBoundingClientRect();\n return { left: r.right + offX + 2, top: r.top + offY, height: r.height };\n }\n const r = chips[idx].getBoundingClientRect();\n return { left: r.left + offX - 4, top: r.top + offY, height: r.height };\n};\n\nconst onCanvasDragOver = (event: DragEvent) => {\n if (!localDrag.value) return;\n const container = event.currentTarget as HTMLElement;\n const idx = computeInsertIndex(event);\n dragOverIndex.value = idx;\n dragIndicator.value = computeIndicatorPos(container, idx);\n};\nconst onCanvasDragLeave = (event: DragEvent) => {\n // dragleave 会在移动到子元素(token chip)时误触发,导致指示线闪烁。\n // 仅当指针真正离开画布外沿时才清除,避免抖动。\n const container = event.currentTarget as HTMLElement;\n const related = event.relatedTarget as Node | null;\n if (related && container.contains(related)) return;\n dragOverIndex.value = -1;\n dragIndicator.value = null;\n};\nconst onCanvasDrop = () => {\n const d = localDrag.value;\n const insertAt = dragOverIndex.value < 0 ? tokens.value.length : dragOverIndex.value;\n dragOverIndex.value = -1;\n dragIndicator.value = null;\n if (!d) return;\n if (d.kind === 'field') {\n insertToken(insertAt, { type: 'field', value: d.field });\n } else if (d.kind === 'op') {\n if (d.op === '()') {\n // 括号成对插入:左右括号一起,光标可继续往中间填内容\n insertTokens(insertAt, [\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else if (d.op === '?:') {\n // 三目成对插入:问号与冒号一起\n insertTokens(insertAt, [\n { type: 'operator', value: '?' },\n { type: 'operator', value: ':' },\n ]);\n } else if (d.op === 'abs') {\n // 绝对值成对插入:abs 前缀 + 一对括号,用户往括号中间填被取绝对值的表达式\n insertTokens(insertAt, [\n { type: 'operator', value: 'abs' },\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else {\n insertToken(insertAt, { type: 'operator', value: d.op });\n }\n } else if (d.kind === 'number') {\n insertToken(insertAt, { type: 'number', value: d.value });\n } else if (d.kind === 'string') {\n insertToken(insertAt, { type: 'string', value: d.value });\n } else if (d.kind === 'bool') {\n insertToken(insertAt, { type: 'bool', value: d.value });\n } else if (d.kind === 'token') {\n const arr = tokens.value.slice();\n const [moved] = arr.splice(d.index, 1);\n let target = insertAt;\n if (d.index < insertAt) target -= 1;\n arr.splice(target, 0, moved);\n commit(arr);\n } else if (d.kind === 'subexpr') {\n // 引用子表达式:以 subexpr token 加入(value 为子表达式 id),后端递归内联展开\n insertToken(insertAt, { type: 'subexpr', value: d.id });\n }\n};\n\nconst appendSubExpr = (id: string) => {\n insertToken(tokens.value.length, { type: 'subexpr', value: id });\n};\n\nconst insertTokens = (index: number, toks: PivotValueExprToken[]) => {\n const arr = tokens.value.slice();\n arr.splice(index, 0, ...toks);\n commit(arr);\n};\nconst insertToken = (index: number, tok: PivotValueExprToken) => {\n insertTokens(index, [tok]);\n};\nconst removeToken = (index: number) => {\n const arr = tokens.value.slice();\n arr.splice(index, 1);\n commit(arr);\n};\n\nconst appendOp = (op: string) => {\n if (op === '()') {\n // 括号成对追加\n insertTokens(tokens.value.length, [\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else if (op === '?:') {\n // 三目问号 + 冒号成对追加\n insertTokens(tokens.value.length, [\n { type: 'operator', value: '?' },\n { type: 'operator', value: ':' },\n ]);\n } else if (op === 'abs') {\n // 绝对值:abs 前缀 + 一对括号成对追加,用户往括号中间填被取绝对值的表达式\n insertTokens(tokens.value.length, [\n { type: 'operator', value: 'abs' },\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else {\n insertToken(tokens.value.length, { type: 'operator', value: op });\n }\n};\nconst addNumber = () => {\n const s = (numText.value || '').trim();\n if (s === '' || isNaN(Number(s))) return;\n insertToken(tokens.value.length, { type: 'number', value: s });\n numText.value = '';\n};\nconst addString = () => {\n const s = (strText.value || '').trim();\n if (s === '') return;\n insertToken(tokens.value.length, { type: 'string', value: s });\n strText.value = '';\n};\nconst onDateDragStart = (event: DragEvent) => {\n if (!dateValid.value) {\n event.preventDefault();\n return;\n }\n localDrag.value = { kind: 'string', value: formattedDate.value };\n};\nconst addDate = () => {\n if (!dateValid.value) return;\n insertToken(tokens.value.length, { type: 'string', value: formattedDate.value });\n};\nconst appendBool = (v: string) => {\n insertToken(tokens.value.length, { type: 'bool', value: v });\n};\nconst clearExpr = () => {\n commit([]);\n};\n</script>\n\n<style lang=\"less\" scoped>\n.vee-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.vee-ops {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n .vee-ops-label {\n font-size: 13px;\n color: #666;\n }\n .vee-op-btn {\n min-width: 32px;\n font-weight: 600;\n cursor: grab;\n }\n .vee-bool-btn {\n font-family: monospace;\n }\n .vee-num-input {\n width: 200px;\n }\n .vee-str-input {\n width: 200px;\n }\n}\n\n.vee-canvas {\n position: relative;\n min-height: 64px;\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n padding: 10px;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n transition: all 0.2s;\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n .vee-canvas-placeholder {\n color: #bbb;\n font-size: 12px;\n }\n .vee-insert-line {\n position: absolute;\n width: 0;\n border-left: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n z-index: 2;\n }\n .vee-token {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 13px;\n cursor: grab;\n user-select: none;\n border: 1px solid transparent;\n &.vee-token-field {\n background: #e6f7ff;\n border-color: #91d5ff;\n color: #0958d9;\n }\n &.vee-token-op {\n background: #fafafa;\n border-color: #d9d9d9;\n font-weight: 700;\n }\n &.vee-token-number {\n background: #f6ffed;\n border-color: #b7eb8f;\n color: #389e0d;\n }\n &.vee-token-string {\n background: #fff0f6;\n border-color: #ffadd2;\n color: #c41d7f;\n }\n &.vee-token-bool {\n background: #f9f0ff;\n border-color: #d3adf7;\n color: #722ed1;\n font-family: monospace;\n }\n &.vee-token-paren {\n background: #fff7e6;\n border-color: #ffd591;\n font-weight: 700;\n }\n .vee-token-remove {\n color: #999;\n font-size: 10px;\n cursor: pointer;\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n}\n\n.vee-preview-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n font-size: 13px;\n .vee-preview-label {\n color: #666;\n }\n .vee-preview {\n font-weight: 600;\n color: #333;\n background: #f5f5f5;\n padding: 2px 8px;\n border-radius: 4px;\n &.vee-preview-invalid {\n color: #cf1322;\n background: #fff1f0;\n }\n }\n .vee-warn {\n color: #fa8c16;\n font-size: 12px;\n }\n}\n.vee-input-wrap {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n flex-direction: row;\n flex-wrap: wrap;\n width: 100%;\n}\n\n.vee-subexpr-panel {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n .vee-subexpr-chip {\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 12px;\n background: #f9f0ff;\n border: 1px solid #d3adf7;\n color: #722ed1;\n cursor: grab;\n user-select: none;\n &:hover {\n border-color: #9254de;\n background: #efdbff;\n }\n &:active {\n cursor: grabbing;\n }\n }\n}\n.vee-token-subexpr {\n background: #f9f0ff;\n border-color: #d3adf7;\n color: #722ed1;\n}\n\n.vee-date-row {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: nowrap;\n .vee-date-mode {\n flex-shrink: 0;\n }\n .vee-date-mode-label {\n font-size: 13px;\n color: #666;\n flex-shrink: 0;\n }\n .vee-date-picker {\n width: 220px;\n }\n}\n.vee-op-item {\n cursor: grab;\n}\n</style>\n","<template>\n <div class=\"vee-wrap\">\n <!-- 运算符 / 字面量面板 -->\n <div class=\"vee-ops\">\n <span class=\"vee-ops-label\">{{ $t('运算符') }}:</span>\n <a-button\n v-for=\"op in panelOperators\"\n :key=\"op.value\"\n size=\"small\"\n class=\"vee-op-btn\"\n draggable=\"true\"\n @dragstart=\"onOpDragStart(op.value)\"\n @dragend=\"onDragEnd\"\n @click=\"appendOp(op.value)\"\n :title=\"op.label\"\n >\n {{ op.display }}\n </a-button>\n\n <a-button\n size=\"small\"\n class=\"vee-op-btn\"\n draggable=\"true\"\n @dragstart=\"onOpDragStart('()')\"\n @dragend=\"onDragEnd\"\n @click=\"appendOp('()')\"\n :title=\"$t('括号(成对插入)')\"\n >\n ( )\n </a-button>\n <a-button\n size=\"small\"\n class=\"vee-op-btn\"\n draggable=\"true\"\n @dragstart=\"onOpDragStart('?:')\"\n @dragend=\"onDragEnd\"\n @click=\"appendOp('?:')\"\n :title=\"$t('三目:条件 ? 真值 : 假值(成对插入)')\"\n >\n ? :\n </a-button>\n\n <a-button\n size=\"small\"\n class=\"vee-op-btn vee-bool-btn\"\n draggable=\"true\"\n @click=\"appendBool('true')\"\n @dragstart=\"onBoolDragStart('true')\"\n @dragend=\"onDragEnd\"\n >\n true\n </a-button>\n <a-button\n size=\"small\"\n class=\"vee-op-btn vee-bool-btn\"\n draggable=\"true\"\n @click=\"appendBool('false')\"\n @dragstart=\"onBoolDragStart('false')\"\n @dragend=\"onDragEnd\"\n >\n false\n </a-button>\n <div class=\"vee-input-wrap\">\n <div\n :draggable=\"numTextValid\"\n @dragstart=\"onNumDragStart\"\n @dragend=\"onDragEnd\"\n :class=\"{ 'vee-op-item': numTextValid }\"\n >\n <a-input-search\n v-model:value=\"numText\"\n size=\"small\"\n class=\"vee-num-input\"\n :placeholder=\"$t('数字')\"\n @pressEnter=\"addNumber\"\n >\n <template #enterButton>\n <a-button\n size=\"small\"\n :disabled=\"!numTextValid\"\n @click=\"addNumber\"\n >\n {{ $t('添加数字') }}\n </a-button>\n </template>\n </a-input-search>\n </div>\n <div\n :draggable=\"strTextValid\"\n @dragstart=\"onStrDragStart\"\n @dragend=\"onDragEnd\"\n :class=\"{ 'vee-op-item': strTextValid }\"\n >\n <a-input-search\n v-model:value=\"strText\"\n size=\"small\"\n class=\"vee-str-input\"\n :placeholder=\"$t('字符串')\"\n @pressEnter=\"addString\"\n >\n <template #enterButton>\n <a-button\n size=\"small\"\n :disabled=\"!strTextValid\"\n @click=\"addString\"\n >\n {{ $t('添加字符串') }}\n </a-button>\n </template>\n </a-input-search>\n </div>\n\n <!-- 时间日期立即数:switch 开启带时间(日期时间),否则仅日期;拖拽/点击生成对应格式的字符串字面量 -->\n <div\n class=\"vee-date-row\"\n :draggable=\"dateValid\"\n @dragstart=\"onDateDragStart\"\n @dragend=\"onDragEnd\"\n :class=\"{ 'vee-op-item': dateValid }\"\n >\n <span class=\"vee-date-mode-label\">{{ $t('带时间') }}:</span>\n <a-switch\n v-model:checked=\"withTime\"\n size=\"small\"\n class=\"vee-date-mode\"\n />\n\n <a-date-picker\n v-model:value=\"dateValue\"\n :show-time=\"withTime\"\n :format=\"withTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'\"\n size=\"small\"\n class=\"vee-date-picker\"\n :placeholder=\"$t('选择时间')\"\n />\n <a-button\n size=\"small\"\n :disabled=\"!dateValid\"\n >\n {{ $t('添加时间') }}\n </a-button>\n </div>\n <a-button\n size=\"small\"\n type=\"text\"\n danger\n @click=\"clearExpr\"\n >\n {{ $t('清空表达式') }}\n </a-button>\n </div>\n </div>\n\n <!-- 可复用子表达式面板:点击/拖拽把子表达式名作为 subexpr token 加入表达式 -->\n <div\n v-if=\"availableSubExprs.length\"\n class=\"vee-subexpr-panel\"\n >\n <span class=\"vee-ops-label\">{{ $t('子表达式') }}:</span>\n <span\n v-for=\"se in availableSubExprs\"\n :key=\"se.id\"\n class=\"vee-subexpr-chip\"\n draggable=\"true\"\n @dragstart=\"onSubExprDragStart(se)\"\n @dragend=\"onDragEnd\"\n @click=\"appendSubExpr(se.id)\"\n :title=\"$t('点击或拖拽引用子表达式') + ':' + se.name\"\n >\n {{ se.name }}\n </span>\n </div>\n\n <!-- 表达式画布(拖拽落点) -->\n <div\n class=\"vee-canvas\"\n :class=\"{ 'drag-over': dragOverIndex >= 0 }\"\n @dragover.prevent=\"onCanvasDragOver\"\n @dragleave=\"onCanvasDragLeave\"\n @drop.stop=\"onCanvasDrop\"\n >\n <span\n v-if=\"!tokens.length\"\n class=\"vee-canvas-placeholder\"\n >\n {{ placeholder || $t('从左侧拖拽字段,或拖拽/点击运算符、数字、字符串到此处') }}\n </span>\n <span\n v-for=\"(t, i) in tokens\"\n :key=\"i\"\n class=\"vee-token\"\n :class=\"tokenClass(t)\"\n draggable=\"true\"\n @dragstart.stop=\"onTokenDragStart(i)\"\n @dragend=\"onDragEnd\"\n >\n {{ tokenText(t) }}\n <close-outlined\n class=\"vee-token-remove\"\n @click.stop=\"removeToken(i)\"\n />\n </span>\n <!-- 插入指示线:绝对定位覆盖层,不参与 flex 布局,避免拖拽时反复重排导致抖动 -->\n <span\n v-if=\"dragIndicator\"\n class=\"vee-insert-line\"\n :style=\"{\n left: dragIndicator.left + 'px',\n top: dragIndicator.top + 'px',\n height: dragIndicator.height + 'px',\n }\"\n />\n </div>\n\n <!-- 预览 + 校验 -->\n <div class=\"vee-preview-row\">\n <span class=\"vee-preview-label\">{{ $t('预览') }}:</span>\n <span\n class=\"vee-preview\"\n :class=\"{ 'vee-preview-invalid': !valid }\"\n >\n {{ preview || '—' }}\n </span>\n <span\n v-if=\"!valid\"\n class=\"vee-warn\"\n >\n {{ errorMsg || $t('表达式不完整(括号不匹配或缺少操作数)') }}\n </span>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, inject, ref, watch, type Ref } from 'vue';\nimport { CloseOutlined } from '@ant-design/icons-vue';\nimport {\n PivotValueExprToken,\n PivotFieldOption,\n PivotSubExpression,\n ExprLocalDrag,\n} from '../typing';\nimport { validateValueExpression } from '../utils';\n\ninterface Props {\n modelValue?: PivotValueExprToken[];\n fieldOptions: PivotFieldOption[];\n placeholder?: string;\n // 当前配置中所有子表达式(供编辑器展示可拖入的子表达式名称、以及按 id 解析显示名)\n subExpressions?: PivotSubExpression[];\n // 编辑某条子表达式时,排除其自身以避免直接自引用(递归展开由后端深度上限兜底环形依赖)\n excludeSubExprId?: string;\n}\nconst props = defineProps<Props>();\nconst emit = defineEmits<{\n (e: 'update:modelValue', v: PivotValueExprToken[]): void;\n (e: 'change'): void;\n}>();\n\n// 当前可引用的子表达式列表(排除正在编辑的自身)\nconst availableSubExprs = computed<PivotSubExpression[]>(() => {\n const list = props.subExpressions || [];\n if (props.excludeSubExprId) {\n return list.filter(s => s.id !== props.excludeSubExprId);\n }\n return list;\n});\n// 子表达式 id → 名称(用于 token 展示解析)\nconst subExprName = (id: string): string => {\n const s = (props.subExpressions || []).find(x => x.id === id);\n return s ? s.name : id;\n};\n\n// 运算符清单:value 为线上实际符号,display 为界面展示,label 为悬停说明\nconst operators = [\n { value: '+', display: '+', label: '加' },\n { value: '-', display: '−', label: '减' },\n { value: '*', display: '×', label: '乘' },\n { value: '/', display: '÷', label: '除' },\n { value: '^', display: '^', label: '幂(右结合)' },\n { value: '>', display: '>', label: '大于' },\n { value: '<', display: '<', label: '小于' },\n { value: '>=', display: '≥', label: '大于等于' },\n { value: '<=', display: '≤', label: '小于等于' },\n { value: '==', display: '=', label: '等于' },\n { value: '!=', display: '≠', label: '不等于' },\n { value: '&&', display: '且', label: '逻辑与' },\n { value: '||', display: '或', label: '逻辑或' },\n { value: '!', display: '非', label: '逻辑非(一元前缀)' },\n {\n value: 'abs',\n display: 'abs',\n label: '绝对值(一元前缀,作用于其后的表达式,如 abs(x) 或 abs(x-3))',\n },\n { value: '?', display: '?', label: '三目:条件 ? 真值 : 假值' },\n { value: ':', display: ':', label: '三目:分隔真/假值' },\n];\n\n// 面板按钮:'?' 与 ':' 由独立的「? :」三目按钮成对插入,此处过滤掉,避免单独拖入。\nconst panelOperators = operators.filter(o => o.value !== '?' && o.value !== ':');\n\nconst localDrag = inject<Ref<ExprLocalDrag | null>>(\n 'vfLocalDrag',\n ref(null) as Ref<ExprLocalDrag | null>,\n);\nconst dragOverIndex = ref<number>(-1);\n// 插入指示线的绝对定位坐标(相对画布内容原点);null 表示不显示。\nconst dragIndicator = ref<{ left: number; top: number; height: number } | null>(null);\nconst numText = ref('');\nconst strText = ref('');\n// 时间日期立即数:switch 开启带时间(YYYY-MM-DD HH:mm:ss),否则仅日期(YYYY-MM-DD),\n// 拖拽/点击生成一个对应格式的字符串字面量 token。\nconst withTime = ref(false);\nconst dateValue = ref<any>(null);\nconst formattedDate = computed(() => {\n if (!dateValue.value) return '';\n const fmt = withTime.value ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD';\n try {\n return dateValue.value.format(fmt);\n } catch {\n return '';\n }\n});\nconst dateValid = computed(() => formattedDate.value !== '');\n\nconst fieldLabel = (field: string): string => {\n const f = props.fieldOptions.find(x => x.field === field);\n return f ? f.label : field;\n};\n\n// 本地权威副本:所有增删先改本地 ref 再 emit 一次。\n// 不能用 computed 代理 props.modelValue——props 在同步代码内不会立即刷新,\n// 连续两次 insertToken 会读到过期数组而相互覆盖(括号/三目成对插入只显示后一个 token 的根因)。\nconst tokens = ref<PivotValueExprToken[]>(props.modelValue ? [...props.modelValue] : []);\nwatch(\n () => props.modelValue,\n v => {\n tokens.value = v ? [...v] : [];\n },\n { immediate: true, deep: false },\n);\n\nconst numTextValid = computed(() => {\n const s = (numText.value || '').trim();\n return s !== '' && !isNaN(Number(s));\n});\nconst strTextValid = computed(() => (strText.value || '').trim() !== '');\n\n// 预览与校验\nconst validation = computed(() => validateValueExpression(tokens.value));\nconst valid = computed(() => validation.value.valid);\nconst errorMsg = computed(() => validation.value.message || '');\nconst preview = computed(() =>\n tokens.value\n .map(t => {\n if (t.type === 'field') return fieldLabel(t.value);\n if (t.type === 'operator') return displayOp(t.value);\n if (t.type === 'number') return t.value;\n if (t.type === 'string') return `'${t.value}'`;\n if (t.type === 'bool') return t.value;\n if (t.type === 'lparen') return '(';\n if (t.type === 'rparen') return ')';\n if (t.type === 'subexpr') return subExprName(t.value);\n return '';\n })\n .join(' '),\n);\n\nconst displayOp = (v: string) => operators.find(o => o.value === v)?.display ?? v;\n\nconst tokenText = (t: PivotValueExprToken): string => {\n switch (t.type) {\n case 'field':\n return `${fieldLabel(t.value)}(${t.value})`;\n case 'operator':\n return displayOp(t.value);\n case 'number':\n return t.value;\n case 'string':\n return `'${t.value}'`;\n case 'bool':\n return t.value;\n case 'lparen':\n return '(';\n case 'rparen':\n return ')';\n case 'subexpr':\n return subExprName(t.value);\n default:\n return '';\n }\n};\n\nconst tokenClass = (t: PivotValueExprToken): string => {\n if (t.type === 'field') return 'vee-token-field';\n if (t.type === 'number') return 'vee-token-number';\n if (t.type === 'string') return 'vee-token-string';\n if (t.type === 'bool') return 'vee-token-bool';\n if (t.type === 'lparen' || t.type === 'rparen') return 'vee-token-paren';\n if (t.type === 'subexpr') return 'vee-token-subexpr';\n return 'vee-token-op';\n};\n\nconst emitChange = () => emit('change');\n\n// 统一提交:本地 tokens 始终更新(保证编辑体验,画布即时反映增删);\n// 但仅当表达式为「空」或「语法合法」时才向外 emit——\n// 非空且语法错误的中间态不外发,从而不会触发父组件的接口调用;\n// 空表达式属于合法的「无表达式/无过滤」状态,需要外发以便清除已有配置。\nconst commit = (arr: PivotValueExprToken[]) => {\n tokens.value = arr;\n const emitable = arr.length === 0 || validateValueExpression(arr).valid;\n if (emitable) {\n emit('update:modelValue', arr);\n emitChange();\n }\n};\n\n// ===== 拖拽起始 =====\nconst onOpDragStart = (op: string) => {\n localDrag.value = { kind: 'op', op };\n};\nconst onNumDragStart = (event: DragEvent) => {\n const s = (numText.value || '').trim();\n if (s === '' || isNaN(Number(s))) {\n event.preventDefault();\n return;\n }\n localDrag.value = { kind: 'number', value: s };\n};\nconst onStrDragStart = (event: DragEvent) => {\n const s = (strText.value || '').trim();\n if (s === '') {\n event.preventDefault();\n return;\n }\n localDrag.value = { kind: 'string', value: s };\n};\nconst onBoolDragStart = (value: string) => {\n localDrag.value = { kind: 'bool', value };\n};\nconst onTokenDragStart = (index: number) => {\n localDrag.value = { kind: 'token', index };\n};\nconst onSubExprDragStart = (se: PivotSubExpression) => {\n localDrag.value = { kind: 'subexpr', id: se.id, name: se.name };\n};\nconst onDragEnd = () => {\n localDrag.value = null;\n dragOverIndex.value = -1;\n dragIndicator.value = null;\n};\n\n// ===== 画布落点 =====\nconst computeInsertIndex = (event: DragEvent): number => {\n const container = event.currentTarget as HTMLElement;\n const chips = Array.from(container.querySelectorAll('.vee-token')) as HTMLElement[];\n if (chips.length === 0) return 0;\n const y = event.clientY;\n const rects = chips.map(c => c.getBoundingClientRect());\n const rows: number[][] = [];\n rects.forEach((r, i) => {\n const ri = rows.findIndex(rowIdxs => Math.abs(rects[rowIdxs[0]].top - r.top) < 8);\n if (ri === -1) {\n rows.push([i]);\n } else {\n rows[ri].push(i);\n }\n });\n let targetRow = 0;\n let best = Infinity;\n rows.forEach((rowIdxs, ri) => {\n const r0 = rects[rowIdxs[0]];\n const dist = y < r0.top ? r0.top - y : y > r0.top + r0.height ? y - (r0.top + r0.height) : 0;\n if (dist < best) {\n best = dist;\n targetRow = ri;\n }\n });\n const rowIdxs = rows[targetRow];\n for (const i of rowIdxs) {\n const r = rects[i];\n if (event.clientX < r.left + r.width / 2) return i;\n }\n return rowIdxs[rowIdxs.length - 1] + 1;\n};\n\n// 根据插入索引,计算指示线相对画布内容原点的像素坐标。\n// 用现有 chip 的 rect 推算,因指示线为绝对定位、不参与布局,故不会引起重排反馈。\nconst computeIndicatorPos = (\n container: HTMLElement,\n idx: number,\n): { left: number; top: number; height: number } => {\n const chips = Array.from(container.querySelectorAll('.vee-token')) as HTMLElement[];\n const crect = container.getBoundingClientRect();\n // 画布内容原点:需叠加滚动量与内边距,使坐标落在内容坐标系而非可视坐标系。\n const offX = -crect.left + container.scrollLeft;\n const offY = -crect.top + container.scrollTop;\n if (chips.length === 0) {\n return { left: 10, top: 10, height: 28 };\n }\n if (idx >= chips.length) {\n const r = chips[chips.length - 1].getBoundingClientRect();\n return { left: r.right + offX + 2, top: r.top + offY, height: r.height };\n }\n const r = chips[idx].getBoundingClientRect();\n return { left: r.left + offX - 4, top: r.top + offY, height: r.height };\n};\n\nconst onCanvasDragOver = (event: DragEvent) => {\n if (!localDrag.value) return;\n const container = event.currentTarget as HTMLElement;\n const idx = computeInsertIndex(event);\n dragOverIndex.value = idx;\n dragIndicator.value = computeIndicatorPos(container, idx);\n};\nconst onCanvasDragLeave = (event: DragEvent) => {\n // dragleave 会在移动到子元素(token chip)时误触发,导致指示线闪烁。\n // 仅当指针真正离开画布外沿时才清除,避免抖动。\n const container = event.currentTarget as HTMLElement;\n const related = event.relatedTarget as Node | null;\n if (related && container.contains(related)) return;\n dragOverIndex.value = -1;\n dragIndicator.value = null;\n};\nconst onCanvasDrop = () => {\n const d = localDrag.value;\n const insertAt = dragOverIndex.value < 0 ? tokens.value.length : dragOverIndex.value;\n dragOverIndex.value = -1;\n dragIndicator.value = null;\n if (!d) return;\n if (d.kind === 'field') {\n insertToken(insertAt, { type: 'field', value: d.field });\n } else if (d.kind === 'op') {\n if (d.op === '()') {\n // 括号成对插入:左右括号一起,光标可继续往中间填内容\n insertTokens(insertAt, [\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else if (d.op === '?:') {\n // 三目成对插入:问号与冒号一起\n insertTokens(insertAt, [\n { type: 'operator', value: '?' },\n { type: 'operator', value: ':' },\n ]);\n } else if (d.op === 'abs') {\n // 绝对值成对插入:abs 前缀 + 一对括号,用户往括号中间填被取绝对值的表达式\n insertTokens(insertAt, [\n { type: 'operator', value: 'abs' },\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else {\n insertToken(insertAt, { type: 'operator', value: d.op });\n }\n } else if (d.kind === 'number') {\n insertToken(insertAt, { type: 'number', value: d.value });\n } else if (d.kind === 'string') {\n insertToken(insertAt, { type: 'string', value: d.value });\n } else if (d.kind === 'bool') {\n insertToken(insertAt, { type: 'bool', value: d.value });\n } else if (d.kind === 'token') {\n const arr = tokens.value.slice();\n const [moved] = arr.splice(d.index, 1);\n let target = insertAt;\n if (d.index < insertAt) target -= 1;\n arr.splice(target, 0, moved);\n commit(arr);\n } else if (d.kind === 'subexpr') {\n // 引用子表达式:以 subexpr token 加入(value 为子表达式 id),后端递归内联展开\n insertToken(insertAt, { type: 'subexpr', value: d.id });\n }\n};\n\nconst appendSubExpr = (id: string) => {\n insertToken(tokens.value.length, { type: 'subexpr', value: id });\n};\n\nconst insertTokens = (index: number, toks: PivotValueExprToken[]) => {\n const arr = tokens.value.slice();\n arr.splice(index, 0, ...toks);\n commit(arr);\n};\nconst insertToken = (index: number, tok: PivotValueExprToken) => {\n insertTokens(index, [tok]);\n};\nconst removeToken = (index: number) => {\n const arr = tokens.value.slice();\n arr.splice(index, 1);\n commit(arr);\n};\n\nconst appendOp = (op: string) => {\n if (op === '()') {\n // 括号成对追加\n insertTokens(tokens.value.length, [\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else if (op === '?:') {\n // 三目问号 + 冒号成对追加\n insertTokens(tokens.value.length, [\n { type: 'operator', value: '?' },\n { type: 'operator', value: ':' },\n ]);\n } else if (op === 'abs') {\n // 绝对值:abs 前缀 + 一对括号成对追加,用户往括号中间填被取绝对值的表达式\n insertTokens(tokens.value.length, [\n { type: 'operator', value: 'abs' },\n { type: 'lparen', value: '' },\n { type: 'rparen', value: '' },\n ]);\n } else {\n insertToken(tokens.value.length, { type: 'operator', value: op });\n }\n};\nconst addNumber = () => {\n const s = (numText.value || '').trim();\n if (s === '' || isNaN(Number(s))) return;\n insertToken(tokens.value.length, { type: 'number', value: s });\n numText.value = '';\n};\nconst addString = () => {\n const s = (strText.value || '').trim();\n if (s === '') return;\n insertToken(tokens.value.length, { type: 'string', value: s });\n strText.value = '';\n};\nconst onDateDragStart = (event: DragEvent) => {\n if (!dateValid.value) {\n event.preventDefault();\n return;\n }\n localDrag.value = { kind: 'string', value: formattedDate.value };\n};\nconst addDate = () => {\n if (!dateValid.value) return;\n insertToken(tokens.value.length, { type: 'string', value: formattedDate.value });\n};\nconst appendBool = (v: string) => {\n insertToken(tokens.value.length, { type: 'bool', value: v });\n};\nconst clearExpr = () => {\n commit([]);\n};\n</script>\n\n<style lang=\"less\" scoped>\n.vee-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.vee-ops {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n .vee-ops-label {\n font-size: 13px;\n color: #666;\n }\n .vee-op-btn {\n min-width: 32px;\n font-weight: 600;\n cursor: grab;\n }\n .vee-bool-btn {\n font-family: monospace;\n }\n .vee-num-input {\n width: 200px;\n }\n .vee-str-input {\n width: 200px;\n }\n}\n\n.vee-canvas {\n position: relative;\n min-height: 64px;\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n padding: 10px;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n transition: all 0.2s;\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n .vee-canvas-placeholder {\n color: #bbb;\n font-size: 12px;\n }\n .vee-insert-line {\n position: absolute;\n width: 0;\n border-left: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n z-index: 2;\n }\n .vee-token {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 13px;\n cursor: grab;\n user-select: none;\n border: 1px solid transparent;\n &.vee-token-field {\n background: #e6f7ff;\n border-color: #91d5ff;\n color: #0958d9;\n }\n &.vee-token-op {\n background: #fafafa;\n border-color: #d9d9d9;\n font-weight: 700;\n }\n &.vee-token-number {\n background: #f6ffed;\n border-color: #b7eb8f;\n color: #389e0d;\n }\n &.vee-token-string {\n background: #fff0f6;\n border-color: #ffadd2;\n color: #c41d7f;\n }\n &.vee-token-bool {\n background: #f9f0ff;\n border-color: #d3adf7;\n color: #722ed1;\n font-family: monospace;\n }\n &.vee-token-paren {\n background: #fff7e6;\n border-color: #ffd591;\n font-weight: 700;\n }\n .vee-token-remove {\n color: #999;\n font-size: 10px;\n cursor: pointer;\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n}\n\n.vee-preview-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n font-size: 13px;\n .vee-preview-label {\n color: #666;\n }\n .vee-preview {\n font-weight: 600;\n color: #333;\n background: #f5f5f5;\n padding: 2px 8px;\n border-radius: 4px;\n &.vee-preview-invalid {\n color: #cf1322;\n background: #fff1f0;\n }\n }\n .vee-warn {\n color: #fa8c16;\n font-size: 12px;\n }\n}\n.vee-input-wrap {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n flex-direction: row;\n flex-wrap: wrap;\n width: 100%;\n}\n\n.vee-subexpr-panel {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n .vee-subexpr-chip {\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 12px;\n background: #f9f0ff;\n border: 1px solid #d3adf7;\n color: #722ed1;\n cursor: grab;\n user-select: none;\n &:hover {\n border-color: #9254de;\n background: #efdbff;\n }\n &:active {\n cursor: grabbing;\n }\n }\n}\n.vee-token-subexpr {\n background: #f9f0ff;\n border-color: #d3adf7;\n color: #722ed1;\n}\n\n.vee-date-row {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: nowrap;\n .vee-date-mode {\n flex-shrink: 0;\n }\n .vee-date-mode-label {\n font-size: 13px;\n color: #666;\n flex-shrink: 0;\n }\n .vee-date-picker {\n width: 220px;\n }\n}\n.vee-op-item {\n cursor: grab;\n}\n</style>\n","<template>\n <div class=\"se-wrap\">\n <!-- 左侧:所有字段(拖拽源) -->\n <div class=\"se-left\">\n <div class=\"se-section-title\">{{ $t('字段') }}</div>\n <a-input\n class=\"se-field-search\"\n v-model:value=\"fieldSearch\"\n size=\"small\"\n allow-clear\n :placeholder=\"$t('搜索字段')\"\n >\n <template #prefix>\n <search-outlined />\n </template>\n </a-input>\n <div class=\"se-field-pool\">\n <div\n v-for=\"f in displayFieldRows\"\n :key=\"f.key\"\n :class=\"['se-field-item', { 'se-field-group': !f.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + f.depth * 16}px` }\"\n :draggable=\"f.isLeaf\"\n @dragstart=\"f.isLeaf ? onFieldDragStart(f.option) : undefined\"\n @dragend=\"f.isLeaf ? onDragEnd() : undefined\"\n :title=\"f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label\"\n >\n <database-outlined\n v-if=\"f.isLeaf\"\n class=\"se-field-icon\"\n />\n <span\n v-else\n class=\"se-field-branch-dot\"\n />\n <span class=\"se-field-label\">\n {{ f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label }}\n </span>\n </div>\n <div\n v-if=\"displayFieldRows.length === 0\"\n class=\"se-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 右侧:表达式名称 + 表达式编辑器 -->\n <div class=\"se-right\">\n <div class=\"se-name-row\">\n <span class=\"se-name-label\">{{ $t('表达式名称') }}</span>\n <a-input\n v-model:value=\"local.name\"\n size=\"small\"\n class=\"se-name-input\"\n :placeholder=\"$t('请输入子表达式名称(将作为可拖入的引用名)')\"\n @change=\"emitChange\"\n />\n </div>\n <valueExprEditor\n v-model=\"local.expression\"\n :field-options=\"flatFieldOptions\"\n :sub-expressions=\"availableSubExprs\"\n :exclude-sub-expr-id=\"local.id\"\n :placeholder=\"$t('从左侧拖拽字段,或拖拽左侧「子表达式」名称,构建可复用的表达式片段')\"\n @change=\"emitChange\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, provide, ref, watch } from 'vue';\nimport { cloneDeep } from 'lodash';\nimport { DatabaseOutlined, SearchOutlined } from '@ant-design/icons-vue';\nimport {\n PivotSubExpression,\n PivotFieldOption,\n ExprLocalDrag,\n} from '../typing';\nimport ValueExprEditor from './valueExprEditor.vue';\n\ninterface Props {\n modelValue: PivotSubExpression;\n fieldOptions: PivotFieldOption[];\n // 当前配置中全部子表达式(供引用其它子表达式);编辑自身时由 excludeSubExprId 排除\n subExpressions?: PivotSubExpression[];\n}\nconst props = defineProps<Props>();\nconst emit = defineEmits<{\n (e: 'update:modelValue', v: PivotSubExpression): void;\n (e: 'change'): void;\n}>();\n\n// 跨子组件共享的拖拽负载(字段从左侧池拖出,落到编辑器画布)\nconst localDrag = ref<ExprLocalDrag | null>(null);\nprovide('vfLocalDrag', localDrag);\n\n// 可引用子表达式:排除正在编辑的自身(避免直接自引用;递归展开由后端深度上限兜底)\nconst availableSubExprs = computed<PivotSubExpression[]>(() => {\n const list = props.subExpressions || [];\n if (props.modelValue?.id) {\n return list.filter(s => s.id !== props.modelValue.id);\n }\n return list;\n});\n\nconst local = ref<PivotSubExpression>(cloneDeep(props.modelValue));\nwatch(\n () => props.modelValue,\n v => {\n if (v) local.value = cloneDeep(v);\n },\n { immediate: true, deep: false },\n);\n\nconst flatFieldOptions = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(props.fieldOptions || []);\n});\n\nconst fieldSearch = ref('');\nconst filteredFieldTree = computed<PivotFieldOption[]>(() => {\n const q = fieldSearch.value.trim().toLowerCase();\n if (!q) return props.fieldOptions;\n\n const filterTree = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(q) || field.field.toLowerCase().includes(q);\n const children = field.children?.length ? filterTree(field.children) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n\n return filterTree(props.fieldOptions);\n});\n\nconst displayFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(filteredFieldTree.value);\n return list;\n});\n\nconst emitChange = () => {\n emit('update:modelValue', cloneDeep(local.value));\n emit('change');\n};\n\nconst onFieldDragStart = (f: PivotFieldOption) => {\n localDrag.value = { kind: 'field', field: f.field, label: f.label };\n};\nconst onDragEnd = () => {\n localDrag.value = null;\n};\n\n// 对外暴露:是否具备可保存条件(名称非空)\ndefineExpose({\n isValid: () => !!(local.value.name && local.value.name.trim()),\n});\n</script>\n\n<style lang=\"less\" scoped>\n.se-wrap {\n display: flex;\n gap: 16px;\n height: 100%;\n min-height: 360px;\n}\n\n.se-left {\n width: 240px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .se-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n }\n .se-field-search {\n margin-bottom: 8px;\n flex-shrink: 0;\n }\n .se-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n .se-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n &:active {\n cursor: grabbing;\n }\n .se-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n .se-field-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n .se-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n }\n .se-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n flex-shrink: 0;\n }\n .se-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.se-right {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n overflow-y: auto;\n\n .se-name-row {\n display: flex;\n align-items: center;\n gap: 8px;\n .se-name-label {\n font-size: 13px;\n color: #333;\n flex-shrink: 0;\n font-weight: 600;\n }\n .se-name-input {\n flex: 1;\n max-width: 360px;\n }\n }\n}\n</style>\n","<template>\n <div class=\"se-wrap\">\n <!-- 左侧:所有字段(拖拽源) -->\n <div class=\"se-left\">\n <div class=\"se-section-title\">{{ $t('字段') }}</div>\n <a-input\n class=\"se-field-search\"\n v-model:value=\"fieldSearch\"\n size=\"small\"\n allow-clear\n :placeholder=\"$t('搜索字段')\"\n >\n <template #prefix>\n <search-outlined />\n </template>\n </a-input>\n <div class=\"se-field-pool\">\n <div\n v-for=\"f in displayFieldRows\"\n :key=\"f.key\"\n :class=\"['se-field-item', { 'se-field-group': !f.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + f.depth * 16}px` }\"\n :draggable=\"f.isLeaf\"\n @dragstart=\"f.isLeaf ? onFieldDragStart(f.option) : undefined\"\n @dragend=\"f.isLeaf ? onDragEnd() : undefined\"\n :title=\"f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label\"\n >\n <database-outlined\n v-if=\"f.isLeaf\"\n class=\"se-field-icon\"\n />\n <span\n v-else\n class=\"se-field-branch-dot\"\n />\n <span class=\"se-field-label\">\n {{ f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label }}\n </span>\n </div>\n <div\n v-if=\"displayFieldRows.length === 0\"\n class=\"se-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 右侧:表达式名称 + 表达式编辑器 -->\n <div class=\"se-right\">\n <div class=\"se-name-row\">\n <span class=\"se-name-label\">{{ $t('表达式名称') }}</span>\n <a-input\n v-model:value=\"local.name\"\n size=\"small\"\n class=\"se-name-input\"\n :placeholder=\"$t('请输入子表达式名称(将作为可拖入的引用名)')\"\n @change=\"emitChange\"\n />\n </div>\n <valueExprEditor\n v-model=\"local.expression\"\n :field-options=\"flatFieldOptions\"\n :sub-expressions=\"availableSubExprs\"\n :exclude-sub-expr-id=\"local.id\"\n :placeholder=\"$t('从左侧拖拽字段,或拖拽左侧「子表达式」名称,构建可复用的表达式片段')\"\n @change=\"emitChange\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, provide, ref, watch } from 'vue';\nimport { cloneDeep } from 'lodash';\nimport { DatabaseOutlined, SearchOutlined } from '@ant-design/icons-vue';\nimport {\n PivotSubExpression,\n PivotFieldOption,\n ExprLocalDrag,\n} from '../typing';\nimport ValueExprEditor from './valueExprEditor.vue';\n\ninterface Props {\n modelValue: PivotSubExpression;\n fieldOptions: PivotFieldOption[];\n // 当前配置中全部子表达式(供引用其它子表达式);编辑自身时由 excludeSubExprId 排除\n subExpressions?: PivotSubExpression[];\n}\nconst props = defineProps<Props>();\nconst emit = defineEmits<{\n (e: 'update:modelValue', v: PivotSubExpression): void;\n (e: 'change'): void;\n}>();\n\n// 跨子组件共享的拖拽负载(字段从左侧池拖出,落到编辑器画布)\nconst localDrag = ref<ExprLocalDrag | null>(null);\nprovide('vfLocalDrag', localDrag);\n\n// 可引用子表达式:排除正在编辑的自身(避免直接自引用;递归展开由后端深度上限兜底)\nconst availableSubExprs = computed<PivotSubExpression[]>(() => {\n const list = props.subExpressions || [];\n if (props.modelValue?.id) {\n return list.filter(s => s.id !== props.modelValue.id);\n }\n return list;\n});\n\nconst local = ref<PivotSubExpression>(cloneDeep(props.modelValue));\nwatch(\n () => props.modelValue,\n v => {\n if (v) local.value = cloneDeep(v);\n },\n { immediate: true, deep: false },\n);\n\nconst flatFieldOptions = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(props.fieldOptions || []);\n});\n\nconst fieldSearch = ref('');\nconst filteredFieldTree = computed<PivotFieldOption[]>(() => {\n const q = fieldSearch.value.trim().toLowerCase();\n if (!q) return props.fieldOptions;\n\n const filterTree = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(q) || field.field.toLowerCase().includes(q);\n const children = field.children?.length ? filterTree(field.children) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n\n return filterTree(props.fieldOptions);\n});\n\nconst displayFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(filteredFieldTree.value);\n return list;\n});\n\nconst emitChange = () => {\n emit('update:modelValue', cloneDeep(local.value));\n emit('change');\n};\n\nconst onFieldDragStart = (f: PivotFieldOption) => {\n localDrag.value = { kind: 'field', field: f.field, label: f.label };\n};\nconst onDragEnd = () => {\n localDrag.value = null;\n};\n\n// 对外暴露:是否具备可保存条件(名称非空)\ndefineExpose({\n isValid: () => !!(local.value.name && local.value.name.trim()),\n});\n</script>\n\n<style lang=\"less\" scoped>\n.se-wrap {\n display: flex;\n gap: 16px;\n height: 100%;\n min-height: 360px;\n}\n\n.se-left {\n width: 240px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .se-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n }\n .se-field-search {\n margin-bottom: 8px;\n flex-shrink: 0;\n }\n .se-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n .se-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n &:active {\n cursor: grabbing;\n }\n .se-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n .se-field-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n .se-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n }\n .se-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n flex-shrink: 0;\n }\n .se-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.se-right {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n overflow-y: auto;\n\n .se-name-row {\n display: flex;\n align-items: center;\n gap: 8px;\n .se-name-label {\n font-size: 13px;\n color: #333;\n flex-shrink: 0;\n font-weight: 600;\n }\n .se-name-input {\n flex: 1;\n max-width: 360px;\n }\n }\n}\n</style>\n","<template>\n <div class=\"pivot-config-wrapper\">\n <div class=\"pivot-config-main\">\n <!-- 字段列表区域 -->\n <div class=\"pivot-field-list\">\n <div class=\"pivot-section-title\">{{ $t('字段列表') }}</div>\n <a-input\n v-model:value=\"searchText\"\n :placeholder=\"$t('搜索字段')\"\n allow-clear\n size=\"small\"\n style=\"margin-bottom: 8px\"\n />\n <div class=\"pivot-field-pool\">\n <div\n v-for=\"field in availableFieldRows\"\n :key=\"field.key\"\n :class=\"['pivot-field-item', { 'pivot-field-group': !field.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + field.depth * 16}px` }\"\n :draggable=\"field.isLeaf\"\n @dragstart=\"field.isLeaf ? onDragStart($event, field.option) : undefined\"\n @dragend=\"field.isLeaf ? onDragEnd() : undefined\"\n @dblclick=\"field.isLeaf ? addToValues(field.option) : undefined\"\n >\n <span class=\"pivot-field-icon\">\n <database-outlined v-if=\"field.isLeaf\" />\n <span\n v-else\n class=\"pivot-field-branch-dot\"\n />\n </span>\n <a-tooltip\n :title=\"\n field.isLeaf\n ? field.option.label + '(' + field.option.field + ')'\n : field.option.label\n \"\n >\n <span class=\"pivot-field-label\">\n {{ field.isLeaf ? `${field.option.label}(${field.option.field})` : field.option.label }}\n </span>\n </a-tooltip>\n <span\n v-if=\"field.isLeaf && isFieldInRowOrCol(field.option.field)\"\n class=\"pivot-field-tag\"\n >\n {{ getFieldLocation(field.option.field) }}\n </span>\n </div>\n <div\n v-if=\"availableFieldRows.length === 0\"\n class=\"pivot-field-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 拖放区域 -->\n <div class=\"pivot-drop-zones\">\n <!-- 列区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'columns' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('columns', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('columns', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <swap-outlined />\n {{ $t('列') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为列维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.columns\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'columns' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'columns' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'columns' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'columns')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('columns', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('columns', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('columns', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'columns' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'columns' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'columns', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('columns', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('columns', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('columns', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.columns.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 行区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'rows' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('rows', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('rows', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <bars-outlined />\n {{ $t('行') }}\n </span>\n\n <a-tooltip :title=\"$t('拖拽字段到此处作为行维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.rows\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'rows' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'rows' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'rows' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'rows')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('rows', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('rows', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('rows', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'rows' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'rows' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'rows', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('rows', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('rows', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('rows', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.rows.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 值区域 -->\n <div\n class=\"pivot-drop-zone pivot-drop-zone-values\"\n :class=\"{ 'drag-over': dragOverZone === 'values' }\"\n @dragover.prevent=\"onDragOver('values')\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onValueDrop\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <calculator-outlined />\n {{ $t('值') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为值,双击字段可快速添加')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(item, index) in localConfig.values\"\n :key=\"item.id\"\n class=\"pivot-zone-item pivot-value-item\"\n :class=\"{\n 'drag-before': dragOverZone === 'values' && dragInsertIndex === index,\n 'drag-after': dragOverZone === 'values' && dragInsertIndex === index + 1,\n }\"\n draggable=\"true\"\n @dragstart=\"onValueDragStart($event, item)\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onValueItemDragOver(index, $event)\"\n >\n <div class=\"value-item-header\">\n <DebouncedInput\n v-model:value=\"item.title\"\n size=\"small\"\n style=\"width: 100px\"\n :placeholder=\"$t('显示名称')\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onOpenValueConfig(item)\"\n >\n 高级配置\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeValue(index)\"\n />\n </div>\n <div class=\"value-item-content\">\n <valueFieldDisplay\n :valueField=\"item\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div class=\"value-item-content\">\n <a-select\n v-model:value=\"item.aggregationType\"\n size=\"small\"\n style=\"width: 90px\"\n :options=\"aggregationOptions\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"item.formatFun\"\n size=\"small\"\n style=\"width: 140px\"\n :options=\"valueFormatOptions\"\n :placeholder=\"$t('显示格式')\"\n :allow-clear=\"true\"\n @change=\"onFormatFunChange(item)\"\n @click.stop\n />\n <!-- 通用数字格式:可选预设格式串,也可自定义输入(numfmt / Excel 标准格式) -->\n <div\n v-if=\"item.formatFun === NUMFMT_FORMAT_KEY\"\n class=\"value-item-content\"\n >\n <a-auto-complete\n v-model:value=\"item.formatPattern\"\n size=\"small\"\n style=\"width: 234px\"\n :options=\"numfmtPatternOptions\"\n :placeholder=\"$t('数据格式,如 #,##0.00')\"\n @change=\"onPatternChange\"\n @click.stop\n />\n </div>\n </div>\n </div>\n <div\n v-if=\"localConfig.values.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 子表达式区域:可复用表达式片段,供任意值字段的表达式/过滤条件引用 -->\n <div class=\"pivot-drop-zone pivot-drop-zone-subexpr\">\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <function-outlined />\n {{ $t('子表达式') }}\n </span>\n <div class=\"header-item\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onAddSubExpr\"\n >\n {{ $t('添加子表达式') }}\n </a-button>\n <a-tooltip\n :title=\"\n $t(\n '子表达式是可复用的表达式片段,可在值字段表达式/过滤条件中以名称引用,后端递归内联展开',\n )\n \"\n >\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n </div>\n <div class=\"pivot-zone-items subexpr-items\">\n <div\n v-for=\"(se, i) in localConfig.subExpressions\"\n :key=\"se.id\"\n class=\"pivot-subexpr-card\"\n >\n <div class=\"subexpr-card-head\">\n <span class=\"subexpr-card-name\">{{ se.name || $t('(未命名)') }}</span>\n <span class=\"subexpr-card-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onEditSubExpr(i)\"\n >\n {{ $t('编辑') }}\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeSubExpr(i)\"\n />\n </span>\n </div>\n <valueFieldDisplay\n :valueField=\"subExprAsValueField(se)\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div\n v-if=\"!localConfig.subExpressions.length\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('暂无子表达式,点击「添加子表达式」创建可复用的表达式片段') }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 选项区域 -->\n <div class=\"pivot-options\">\n <div class=\"pivot-options-group\">\n <span class=\"pivot-options-label\">{{ $t('结构') }}</span>\n <a-radio-group\n v-model:value=\"localConfig.indicatorsAsCol\"\n size=\"small\"\n option-type=\"button\"\n button-style=\"solid\"\n @change=\"onConfigChange\"\n >\n <a-radio-button :value=\"true\">{{ $t('指标在列') }}</a-radio-button>\n <a-radio-button :value=\"false\">{{ $t('指标在行') }}</a-radio-button>\n </a-radio-group>\n <a-radio-group\n v-model:value=\"localConfig.rowHierarchyType\"\n size=\"small\"\n option-type=\"button\"\n button-style=\"solid\"\n @change=\"onConfigChange\"\n >\n <a-radio-button value=\"grid\">{{ $t('扁平') }}</a-radio-button>\n <a-radio-button\n :disabled=\"!localConfig.rows?.length || localConfig.rows.length < 2\"\n value=\"tree\"\n >\n {{ $t('树形') }}\n </a-radio-button>\n <a-radio-button\n :disabled=\"!localConfig.rows?.length || localConfig.rows.length < 2\"\n value=\"grid-tree\"\n >\n {{ $t('网格树形') }}\n </a-radio-button>\n </a-radio-group>\n </div>\n <div class=\"pivot-options-group\">\n <span class=\"pivot-options-label\">{{ $t('汇总') }}</span>\n <label class=\"pivot-option-switch\">\n <a-switch\n v-model:checked=\"localConfig.columnShowSubTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('列小计') }}</span>\n </label>\n <label class=\"pivot-option-switch\">\n <a-switch\n v-model:checked=\"localConfig.columnShowGrandTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('列合计') }}</span>\n </label>\n <label\n class=\"pivot-option-switch\"\n v-if=\"localConfig.rowHierarchyType === 'grid'\"\n >\n <a-switch\n v-model:checked=\"localConfig.rowShowSubTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('行小计') }}</span>\n </label>\n <label\n class=\"pivot-option-switch\"\n v-if=\"localConfig.rowHierarchyType === 'grid'\"\n >\n <a-switch\n v-model:checked=\"localConfig.rowShowGrandTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('行合计') }}</span>\n </label>\n </div>\n </div>\n\n <!-- 子表达式编辑弹窗 -->\n <a-modal\n v-model:open=\"subExprModalOpen\"\n :title=\"editingIndex >= 0 ? $t('编辑子表达式') : $t('添加子表达式')\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n @ok=\"onSubExprOk\"\n @cancel=\"subExprModalOpen = false\"\n >\n <sub-expression-editor\n v-if=\"subExprModalOpen\"\n v-model=\"editingSubExpr\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n <template #footer>\n <a-button\n type=\"primary\"\n :disabled=\"!editingSubExpr.name || !editingSubExpr.name.trim()\"\n @click=\"onSubExprOk\"\n >\n {{ $t('确定') }}\n </a-button>\n <a-button @click=\"subExprModalOpen = false\">\n {{ $t('取消') }}\n </a-button>\n </template>\n </a-modal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n DatabaseOutlined,\n SwapOutlined,\n BarsOutlined,\n CalculatorOutlined,\n CloseOutlined,\n InfoCircleOutlined,\n FunctionOutlined,\n} from '@ant-design/icons-vue';\nimport { cloneDeep, debounce } from 'lodash';\nimport { computed, reactive, ref, watch } from 'vue';\nimport {\n Column,\n PivotConfig,\n PivotFieldConfig,\n PivotFieldOption,\n PivotValueField,\n PivotSubExpression,\n SettingMode,\n} from '../typing';\nimport {\n PIVOT_FIELD_PROCESS_FUNCS,\n VALUE_FORMATS,\n NUMFMT_FORMAT_KEY,\n NUMFMT_PATTERN_PRESETS,\n DEFAULT_NUMFMT_PATTERN,\n normalizeValueFormat,\n} from '../func';\nimport { genID } from 'liyu-pc-base/utils/function';\nimport { combineLabel } from '../utils';\nimport valueFieldDisplay from './valueFieldDisplay.vue';\nimport DebouncedInput from './DebouncedInput.vue';\nimport subExpressionEditor from './subExpressionEditor.vue';\ninterface Props {\n columns: Column[];\n modelValue?: PivotConfig;\n settingMode: SettingMode;\n}\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: PivotConfig): void;\n (e: 'change', value: PivotConfig): void;\n (e: 'openValueConfig', value: PivotValueField): void;\n}>();\n\nconst searchText = ref('');\n\n// 拖拽负载(判别联合)\ntype DragPayload =\n | { kind: 'pool'; field: PivotFieldOption }\n | { kind: 'group'; zone: 'rows' | 'columns'; groupId: string }\n | { kind: 'field'; zone: 'rows' | 'columns'; groupId: string; fieldIndex: number }\n | { kind: 'value'; field: PivotValueField };\nconst dragData = ref<DragPayload | null>(null);\nconst dragOverZone = ref<string>('');\nconst dragOverGroupZone = ref<string>('');\nconst dragOverGroupIndex = ref<number>(-1);\nconst dragInsertIndex = ref<number>(-1); // 值区域拖拽插入位置索引\n// 组内字段重排指示:拖拽字段经过某 chip 时,记录插入位置(前/后)\nconst dragFieldTarget = ref<{\n zone: string;\n groupIndex: number;\n fieldIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n// 组间插入指示:拖拽到组外(组间间隙)新建/插入/移动维度组时,在相邻组的上/下边框高亮\nconst dragGroupInsert = ref<{\n zone: string;\n refIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n\n// 维度组「显示名」是否为用户手动自定义:自定义名在字段增删/重排时不被自动覆盖\nconst customLabels = reactive(new Set<string>());\n\n// 重建 custom 标记:外部/旧配置中已带 label 的组视为自定义(保留),不调用\n// defaultGroupLabel 以避免 setup 阶段访问尚未初始化的依赖。\nconst syncCustomFlags = (config: PivotConfig) => {\n customLabels.clear();\n const dims = [...(config.rows || []), ...(config.columns || [])];\n dims.forEach(g => {\n if (g.id && g.label) customLabels.add(g.id);\n });\n};\n\n// 用户手动输入显示名:标记为自定义,后续字段变化不再自动覆盖\nconst onLabelInput = (group: PivotFieldConfig) => {\n if (group.id) customLabels.add(group.id);\n onConfigChange();\n};\n\nconst defaultConfig: PivotConfig = {\n rows: [],\n columns: [],\n values: [],\n indicatorsAsCol: true,\n rowHierarchyType: 'grid',\n};\n\nconst localConfig = ref<PivotConfig>(cloneDeep(props.modelValue || defaultConfig));\n\n// 兼容旧数据:把「单字段」维度 {field,label,processFunc} 归一为「维度组」{id,fields:[...],linkType}\nconst normalizeDim = (d: any): PivotFieldConfig => {\n if (d && Array.isArray(d.fields)) {\n return { ...d, id: d.id || genID() };\n }\n return {\n id: d?.id || genID(),\n fields: [{ field: d?.field, label: d?.label, processFunc: d?.processFunc }],\n label: d?.label, // 旧单字段配置的显示名提升到组级(兼容旧数据)\n linkType: d?.linkType || '_',\n subTotalAggregationType: d?.subTotalAggregationType,\n sortOrder: d?.sortOrder,\n sortBy: d?.sortBy,\n sortProcessFunc: d?.sortProcessFunc,\n };\n};\n// 确保每个值字段都有唯一 id(兼容旧数据);同时把旧格式 key('0.00' 等)\n// 归一为「通用数字格式 + formatPattern」\nconst ensureValueIds = (config: PivotConfig) => {\n config.values.forEach(v => {\n if (!v.id && v.field) {\n v.id = `${v.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;\n }\n normalizeValueFormat(v);\n });\n};\nconst normalizeConfig = (config: PivotConfig) => {\n config.rows = (config.rows || []).map(normalizeDim);\n config.columns = (config.columns || []).map(normalizeDim);\n if (!Array.isArray(config.subExpressions)) config.subExpressions = [];\n ensureValueIds(config);\n};\nnormalizeConfig(localConfig.value);\nsyncCustomFlags(localConfig.value);\n\n// 自身 emit 引起的 modelValue 回灌无需再 cloneDeep+重渲染(localConfig 已是最新),\n// 用 skipWatch 跳过本次,避免一次 drop 触发两次整面板重渲染导致的卡顿。\nlet skipWatch = false;\n\nwatch(\n () => props.modelValue,\n val => {\n if (skipWatch) {\n skipWatch = false;\n return;\n }\n if (val) {\n const cloned = cloneDeep(val);\n normalizeConfig(cloned);\n syncCustomFlags(cloned);\n localConfig.value = cloned;\n }\n },\n { deep: true },\n);\n\n// 所有可用字段列表\nconst allFields = computed<PivotFieldOption[]>(() => {\n const buildTree = (cols: Column[]): PivotFieldOption[] => {\n return cols\n .map(col => {\n const label = col.title || col.dataIndex || col.key || '';\n const children = col.children?.length ? buildTree(col.children) : [];\n const isLeaf = children.length === 0;\n if (isLeaf) {\n if (!col.dataIndex || col.dataIndex === 'index' || col.dataIndex === 'action') {\n return null;\n }\n return {\n field: col.dataIndex,\n label,\n };\n }\n return {\n field: col.dataIndex || col.key || label,\n label,\n children,\n };\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n return buildTree(props.columns || []);\n});\n\nconst flatAllFields = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(allFields.value);\n});\n\nconst availableFieldTree = computed<PivotFieldOption[]>(() => {\n const filterTree = (fields: PivotFieldOption[], search: string): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(search) || field.field.toLowerCase().includes(search);\n const children = field.children?.length ? filterTree(field.children, search) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n\n const search = searchText.value.trim().toLowerCase();\n if (!search) return allFields.value;\n return filterTree(allFields.value, search);\n});\n\nconst availableFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(availableFieldTree.value);\n return list;\n});\n\n// 检查字段是否已在任意行/列维度组中\nconst isFieldInRowOrCol = (field: string) => {\n return (\n localConfig.value.rows.some(g => g.fields.some(f => f.field === field)) ||\n localConfig.value.columns.some(g => g.fields.some(f => f.field === field))\n );\n};\n\n// 获取字段所在位置标签(可能同时出现在行、列)\nconst getFieldLocation = (field: string) => {\n const locs: string[] = [];\n if (localConfig.value.rows.some(g => g.fields.some(f => f.field === field))) locs.push('行');\n if (localConfig.value.columns.some(g => g.fields.some(f => f.field === field))) locs.push('列');\n return locs.join('·');\n};\n\nconst aggregationOptions = [\n { label: '求和', value: 'SUM' },\n { label: '计数', value: 'COUNT' },\n { label: '数值计数', value: 'COUNTNUM' },\n { label: '平均值', value: 'AVG' },\n { label: '最小值', value: 'MIN' },\n { label: '最大值', value: 'MAX' },\n { label: '乘积', value: 'PRODUCT' },\n { label: '方差', value: 'VAR' },\n { label: '总体方差', value: 'VARP' },\n { label: '标准偏差', value: 'STD' },\n { label: '总体标准偏差', value: 'STDP' },\n { label: '无', value: 'NONE' },\n];\n\n// 行/列维度「数据处理函数」下拉选项(来自 func.ts 注册表,新增函数后自动出现)\nconst processFuncOptions = computed(() =>\n PIVOT_FIELD_PROCESS_FUNCS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 值字段「显示格式」下拉选项(来自 func.ts 注册表)\nconst valueFormatOptions = computed(() =>\n VALUE_FORMATS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 通用数字格式的常用格式串预设(a-auto-complete 选项,支持自定义输入)\nconst numfmtPatternOptions = NUMFMT_PATTERN_PRESETS.map(p => ({\n value: p.value,\n label: p.label || p.value,\n}));\n\n// 切换显示格式:选中「通用数字格式」且尚无格式串时填入缺省值\nconst onFormatFunChange = (item: PivotValueField) => {\n if (item.formatFun === NUMFMT_FORMAT_KEY && !item.formatPattern) {\n item.formatPattern = DEFAULT_NUMFMT_PATTERN;\n }\n onValueConfigChange();\n};\n\n// 格式串输入防抖:避免逐字符输入触发接口调用\nconst onPatternChange = debounce(() => onValueConfigChange(), 500);\n\n// 维度组「连接符」下拉选项:'_'(默认)/ '-' / '()'\nconst linkTypeOptions = [\n { label: '_', value: '_' },\n { label: '-', value: '-' },\n { label: '( )', value: '()' },\n];\n\n// 维度「排序方向」下拉选项\nconst sortOrderOptions = [\n { label: '升序', value: 'asc' },\n { label: '降序', value: 'desc' },\n];\n\n// 维度组「排序依据字段」下拉选项:先列出本组各字段(按原始 field 名,Rust 侧已为组内\n// 每个字段登记单字段解析列,sort_by 指向该 field 即按「组内该字段」排序),再列出其余所有\n// 可用字段(含值字段、其它维度、未参与维度但存在于数据中的字段)。\n// 与 Rust 侧 resolve_sort 对齐:sort_by 现可解析任意存在于数据中的字段(见 pivot.rs\n// 中按 sort_by 新增 __s* 辅助列的逻辑),因此不再有「只能指向已选列 / 更早维度」的限制。\nconst getSortByOptions = (group: PivotFieldConfig) => {\n const own = group.fields.map(f => ({\n label: `${fieldLabel(f.field)}(${f.field})`,\n value: f.field,\n }));\n const ownSet = new Set(group.fields.map(f => f.field));\n const others = flatAllFields.value\n .filter(f => !ownSet.has(f.field))\n .map(f => ({ label: `${f.label}(${f.field})`, value: f.field }));\n return [...own, ...others];\n};\n\n// 排序依据字段下拉的搜索过滤(按 label 模糊匹配)\nconst filterFieldOption = (input: string, option: { label?: string }) =>\n (option?.label ?? '').toLowerCase().includes(input.toLowerCase());\n\n// 排序依据字段变化:清空后同步清除其处理函数(处理函数依赖排序依据字段存在)\nconst onGroupSortByChange = (group: PivotFieldConfig) => {\n if (!group.sortBy) {\n group.sortProcessFunc = undefined;\n }\n onConfigChange();\n};\n\n// 维度组默认显示名:按字段名以下划线连接(缺省值,用户可在配置中覆盖 group.label)\nconst defaultGroupLabel = (group: PivotFieldConfig): string => {\n if (!group.fields.length) return '';\n const labelList = group.fields.map(f => f.label || fieldLabel(f.field));\n return combineLabel(labelList, group.linkType);\n};\n\n// 字段显示名:优先用字段列表中的 title,缺省回退到字段名\nconst fieldLabel = (field: string): string => {\n const f = flatAllFields.value.find(x => x.field === field);\n return f ? f.label : field;\n};\n\n// ===== 拖拽 =====\nconst onDragStart = (event: DragEvent, field: PivotFieldOption) => {\n dragData.value = { kind: 'pool', field };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', field.field);\n }\n};\n\nconst onGroupDragStart = (event: DragEvent, group: PivotFieldConfig, zone: 'rows' | 'columns') => {\n dragData.value = { kind: 'group', zone, groupId: group.id! };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onFieldDragStart = (\n event: DragEvent,\n group: PivotFieldConfig,\n zone: 'rows' | 'columns',\n fieldIndex: number,\n) => {\n dragData.value = { kind: 'field', zone, groupId: group.id!, fieldIndex };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onDragEnd = () => clearDrag();\n\nconst clearDrag = () => {\n dragData.value = null;\n dragOverZone.value = '';\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragInsertIndex.value = -1;\n dragFieldTarget.value = null;\n dragGroupInsert.value = null;\n};\n\nconst onDragOver = (zone: string) => {\n dragOverZone.value = zone;\n dragFieldTarget.value = null;\n};\n\nconst onDragLeave = () => {\n // 只在离开整个区域时清除\n};\n\nconst onGroupDragOver = (zone: 'rows' | 'columns', index: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n event.stopPropagation();\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';\n\n if (d.kind === 'group') {\n // 拖拽整组进行排序:始终显示单边框插入指示(上/下),不显示 4 边框高亮。\n // 悬停在自身组上:不显示插入指示(原地不动)。\n const groupId = localConfig.value[zone][index]?.id;\n const overSelf = d.zone === zone && d.groupId === groupId;\n let nextIns: { zone: 'rows' | 'columns'; refIndex: number; pos: 'before' | 'after' } | null =\n null;\n if (!overSelf) {\n // 指针在组的上半区→插入到该组之前(高亮上边框);下半区→之后(高亮下边框)\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n nextIns = { zone, refIndex: index, pos: before ? 'before' : 'after' };\n }\n applyGroupInsert(nextIns);\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n dragOverZone.value = '';\n return;\n }\n\n // 其它情况(字段 / 池字段):悬停整组内部 → 4 边框高亮(把字段添加进该组)\n dragOverGroupZone.value = zone;\n dragOverGroupIndex.value = index;\n dragFieldTarget.value = null; // 悬停整组(非具体字段)时清除字段级指示\n dragGroupInsert.value = null; // 悬停整组(内部)时清除组间插入指示\n dragOverZone.value = ''; // 悬停整组(内部)时清除整区高亮,避免与组内 4 边框叠加\n};\n\nconst findGroupIndex = (zone: 'rows' | 'columns', groupId: string) =>\n localConfig.value[zone].findIndex(g => g.id === groupId);\n\n// 拖到区域空白:池字段→新建维度组;组→移到该区域末尾;字段→从原组移除\nconst onDropZone = (zone: 'rows' | 'columns') => {\n const d = dragData.value;\n if (!d) return;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].push(ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n localConfig.value[zone].push(g);\n }\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n localConfig.value[d.zone][srcIdx].fields.splice(d.fieldIndex, 1);\n if (localConfig.value[d.zone][srcIdx].fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖到具体维度组:池字段→追加;字段→跨组移动;组→组内重排 / 跨区移动\nconst onDropOnGroup = (zone: 'rows' | 'columns', gIndex: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) return;\n\n if (d.kind === 'pool') {\n targetGroup.fields.push({ field: d.field.field, label: d.field.label });\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n targetGroup.fields.push(field);\n }\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n let tIdx = gIndex;\n if (d.zone === zone && srcIdx < gIndex) tIdx -= 1;\n localConfig.value[zone].splice(before ? tIdx : tIdx + 1, 0, g);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 区域级拖拽悬浮:把探测范围提升到 .pivot-drop-zone(覆盖整片区域),\n// 既设置整区高亮(dragOverZone),又按指针相对各组位置计算「组间插入」指示\n// (dragGroupInsert)。关键点:第一组上方与末组下方的留白属于区域 padding/header,\n// 原本不在 .pivot-zone-items 内,导致那两块区域只显示整区边框、且 drop 落到末尾;\n// 现在由本函数统一探测,使「第一组上方」→ 高亮组0上边框并插入到首个位置,\n// 「末组下方」→ 高亮末组下边框并插入到末尾。组内拖拽由 .pivot-dim-group 的\n// .stop 拦截,仍显示 4 边框(添加字段)。\nconst onZoneDragOver = (zone: 'rows' | 'columns', event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n onDragOver(zone); // 整区高亮\n computeGroupInsert(zone, event);\n};\n\n// 计算「组间插入」指示:第一组上方→before(0);末组下方→after(末);组间间隙→after(i);\n// 命中组内部由 .stop 拦截不会进入本函数,故此处无需处理组内情况。\n// 仅在插入目标(zone/refIndex/pos)真正变化时才更新 dragGroupInsert,\n// 避免拖拽过程中每个 dragover 都触发整面板重渲染(卡顿来源之一)。\nconst applyGroupInsert = (\n next: { zone: 'rows' | 'columns'; refIndex: number; pos: 'before' | 'after' } | null,\n) => {\n const cur = dragGroupInsert.value;\n if (\n cur &&\n next &&\n cur.zone === next.zone &&\n cur.refIndex === next.refIndex &&\n cur.pos === next.pos\n )\n return;\n if (!cur && !next) return;\n dragGroupInsert.value = next;\n};\n\nconst computeGroupInsert = (zone: 'rows' | 'columns', event: DragEvent) => {\n const d = dragData.value;\n if (!d) {\n applyGroupInsert(null);\n return;\n }\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n const container = event.currentTarget as HTMLElement;\n const groups = Array.from(container.querySelectorAll('.pivot-dim-group')) as HTMLElement[];\n if (groups.length === 0) {\n applyGroupInsert(null); // 无组(空白区)由区域层级高亮处理\n return;\n }\n const y = event.clientY;\n const firstTop = groups[0].getBoundingClientRect().top;\n if (y < firstTop) {\n applyGroupInsert({ zone, refIndex: 0, pos: 'before' });\n return;\n }\n const lastBottom = groups[groups.length - 1].getBoundingClientRect().bottom;\n if (y > lastBottom) {\n applyGroupInsert({ zone, refIndex: groups.length - 1, pos: 'after' });\n return;\n }\n for (let i = 0; i < groups.length - 1; i++) {\n const top = groups[i].getBoundingClientRect();\n const nxt = groups[i + 1].getBoundingClientRect();\n if (y >= top.bottom && y <= nxt.top) {\n // 落在组 i 与组 i+1 之间:在组 i 之后插入(高亮组 i 下边框)\n applyGroupInsert({ zone, refIndex: i, pos: 'after' });\n return;\n }\n }\n applyGroupInsert(null);\n};\n\n// 在组间间隙释放:新建(池字段)/ 插入(整组重排)/ 移出(字段独立成组)维度组到对应位置。\n// 未命中组间插入目标(如空白区)时回退到区域空白逻辑。\nconst onZoneItemsDrop = (zone: 'rows' | 'columns', _event: DragEvent) => {\n const d = dragData.value;\n const ins = dragGroupInsert.value;\n if (!d || !ins || ins.zone !== zone) {\n onDropZone(zone);\n return;\n }\n let insIndex = ins.pos === 'before' ? ins.refIndex : ins.refIndex + 1;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].splice(insIndex, 0, ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n if (d.zone === zone && srcIdx < insIndex) insIndex -= 1;\n localConfig.value[zone].splice(insIndex, 0, g);\n }\n } else if (d.kind === 'field') {\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < insIndex) insIndex -= 1;\n }\n const ng: PivotFieldConfig = { id: genID(), fields: [field], linkType: '_' };\n localConfig.value[zone].splice(insIndex, 0, ng);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖拽字段经过组内某个 chip:记录插入位置(上边框=前 / 下边框=后),用于高亮提示\nconst onChipDragOver = (\n zone: 'rows' | 'columns',\n gIndex: number,\n fIndex: number,\n event: DragEvent,\n) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 仅字段重排时处理,其它拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n dragGroupInsert.value = null; // 悬停具体字段 chip 时清除组间插入指示\n const group = localConfig.value[zone][gIndex];\n if (!group) return;\n // 悬停在被拖拽的源字段自身上时,不显示指示线(避免原地误判)\n if (d.zone === zone && d.groupId === group.id && d.fieldIndex === fIndex) {\n dragFieldTarget.value = null;\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n dragFieldTarget.value = {\n zone,\n groupIndex: gIndex,\n fieldIndex: fIndex,\n pos: before ? 'before' : 'after',\n };\n};\n\n// 在组内某字段的上/下边界释放:把被拖字段插入到对应位置(支持组内重排与跨组定位)\nconst onChipDrop = (zone: 'rows' | 'columns', gIndex: number, fIndex: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 非字段拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) {\n clearDrag();\n return;\n }\n // 直接落在源字段自身:视为无移动\n if (d.zone === zone && d.groupId === targetGroup.id && d.fieldIndex === fIndex) {\n clearDrag();\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx < 0) {\n clearDrag();\n return;\n }\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n\n const sameGroup = d.zone === zone && srcGroupIdx === gIndex;\n let targetGroupIdx = gIndex;\n let insertAt = before ? fIndex : fIndex + 1;\n // 同组内、源在目标之前时被移除会导致下标左移一位\n if (sameGroup && d.fieldIndex < fIndex) insertAt -= 1;\n\n // 跨组移动且源组已空:移除源组,并按需要修正目标组下标\n if (srcGroup.fields.length === 0 && !sameGroup) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < targetGroupIdx) targetGroupIdx -= 1;\n }\n localConfig.value[zone][targetGroupIdx].fields.splice(insertAt, 0, field);\n clearDrag();\n onConfigChange();\n};\n\n// 值区域拖放:仅支持从字段池拖入新增值\nconst onDropValues = () => {\n const d = dragData.value;\n if (d && d.kind === 'pool') {\n addToValues(d.field);\n }\n clearDrag();\n};\n\n// 值字段拖拽排序\nconst onValueDragStart = (event: DragEvent, item: PivotValueField) => {\n dragData.value = { kind: 'value', field: item };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', item.id);\n }\n};\n\nconst onValueItemDragOver = (index: number, event: DragEvent) => {\n dragOverZone.value = 'values';\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n if (event.clientY < midpoint) {\n dragInsertIndex.value = index;\n } else {\n dragInsertIndex.value = index + 1;\n }\n};\n\nconst onValueDrop = () => {\n const d = dragData.value;\n if (!d) return;\n\n // 从字段池拖入:创建新值\n if (d.kind === 'pool') {\n const insertIndex =\n dragInsertIndex.value >= 0 ? dragInsertIndex.value : localConfig.value.values.length;\n const valueField: PivotValueField = {\n id: `${d.field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: d.field.field,\n label: d.field.label,\n aggregationType: 'SUM',\n title: d.field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.splice(insertIndex, 0, valueField);\n clearDrag();\n onConfigChange();\n return;\n }\n\n // 值区域内重排\n if (d.kind !== 'value') return;\n const list = localConfig.value.values;\n const fromIndex = list.findIndex(f => f.id === (d as any).field.id);\n if (fromIndex === -1) {\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n return;\n }\n let toIndex = dragInsertIndex.value;\n if (toIndex < 0) toIndex = list.length;\n if (toIndex > fromIndex) toIndex -= 1;\n if (toIndex >= list.length) toIndex = list.length - 1;\n if (toIndex < 0) toIndex = 0;\n const [item] = list.splice(fromIndex, 1);\n list.splice(toIndex, 0, item);\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n onConfigChange();\n};\n\n// 双击字段快速添加到值区域(允许重复添加,不限制是否已在行/列中)\nconst addToValues = (field: PivotFieldOption) => {\n const valueField: PivotValueField = {\n id: `${field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: field.field,\n label: field.label,\n aggregationType: 'SUM',\n title: field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.push(valueField);\n onConfigChange();\n};\n\n// 移除整个维度组\nconst removeGroup = (zone: 'rows' | 'columns', index: number) => {\n localConfig.value[zone].splice(index, 1);\n onConfigChange();\n};\n\n// 从维度组移除单个字段;若组变空则一并移除该组\nconst removeFieldFromGroup = (zone: 'rows' | 'columns', gIndex: number, fIndex: number) => {\n const g = localConfig.value[zone][gIndex];\n g.fields.splice(fIndex, 1);\n if (g.fields.length === 0) {\n localConfig.value[zone].splice(gIndex, 1);\n }\n onConfigChange();\n};\n\n// 移除值字段\nconst removeValue = (index: number) => {\n localConfig.value.values.splice(index, 1);\n onConfigChange();\n};\n\nconst onValueConfigChange = () => {\n onConfigChange();\n};\n\n// ===== 子表达式(可复用表达式片段)管理 =====\nconst subExprModalOpen = ref(false);\nconst editingIndex = ref(-1); // >=0 表示编辑已有项,-1 表示新增\nconst editingSubExpr = ref<PivotSubExpression>({ id: '', name: '', expression: [] });\n\n// 把子表达式映射为一个「伪值字段」,复用 valueFieldDisplay 渲染其表达式预览\nconst subExprAsValueField = (se: PivotSubExpression): PivotValueField => ({\n id: se.id,\n field: '',\n aggregationType: 'NONE',\n useExpression: true,\n expression: se.expression || [],\n filter: [],\n});\n\nconst onAddSubExpr = () => {\n editingIndex.value = -1;\n editingSubExpr.value = { id: genID(), name: '', expression: [] };\n subExprModalOpen.value = true;\n};\nconst onEditSubExpr = (i: number) => {\n editingIndex.value = i;\n editingSubExpr.value = cloneDeep(localConfig.value.subExpressions[i]);\n subExprModalOpen.value = true;\n};\nconst onSubExprOk = () => {\n const se = editingSubExpr.value;\n if (!se.name || !se.name.trim()) return;\n if (editingIndex.value >= 0) {\n localConfig.value.subExpressions[editingIndex.value] = cloneDeep(se);\n } else {\n localConfig.value.subExpressions.push(cloneDeep(se));\n }\n subExprModalOpen.value = false;\n onConfigChange();\n};\nconst removeSubExpr = (i: number) => {\n localConfig.value.subExpressions.splice(i, 1);\n onConfigChange();\n};\n\nconst onConfigChange = () => {\n skipWatch = true; // 标记本次 emit 引起的 modelValue 回灌,watch 中应跳过\n const config = cloneDeep(localConfig.value);\n emit('update:modelValue', config);\n emit('change', config);\n};\nconst onOpenValueConfig = (valueField: PivotValueField) => {\n emit('openValueConfig', valueField);\n};\n\n// 供父级(tableConfig/index.vue)在「高级配置」抽屉内修改值字段后,触发一次完整配置回传\n// getSubExpressions:把当前(含本会话内新增/编辑的)子表达式列表暴露给 index.vue,\n// 使「高级配置」里的表达式编辑器能引用已配置的子表达式(不依赖上层 props 的实时回灌)。\ndefineExpose({\n forceEmit: onConfigChange,\n getSubExpressions: () => localConfig.value.subExpressions,\n});\n</script>\n\n<style lang=\"less\" scoped>\n.pivot-config-wrapper {\n display: flex;\n flex-direction: column;\n gap: 12px;\n height: 100%;\n}\n\n.pivot-config-main {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n}\n\n.pivot-field-list {\n width: 220px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .pivot-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n color: #333;\n }\n\n .pivot-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n\n .pivot-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n\n .pivot-field-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-field-tag {\n flex-shrink: 0;\n font-size: 10px;\n color: #fff;\n background: #1890ff;\n border-radius: 8px;\n padding: 1px 6px;\n line-height: 16px;\n }\n }\n\n .pivot-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n\n .pivot-field-icon {\n color: #bfbfbf;\n }\n }\n\n .pivot-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n }\n\n .pivot-field-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.pivot-drop-zones {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n overflow-y: auto;\n}\n\n.pivot-drop-zone {\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n padding: 8px;\n transition: all 0.2s;\n min-height: 80px;\n flex-shrink: 0;\n\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n .pivot-zone-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n\n .pivot-zone-title {\n font-weight: 600;\n font-size: 13px;\n color: #333;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .pivot-zone-tip {\n color: #999;\n font-size: 12px;\n cursor: help;\n }\n }\n\n .pivot-zone-items {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n min-height: 32px;\n }\n\n .pivot-zone-placeholder {\n color: #bbb;\n font-size: 12px;\n padding: 4px 8px;\n width: 100%;\n text-align: center;\n }\n}\n\n.pivot-zone-item {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n background: #fafafa;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n max-width: 100%;\n box-sizing: border-box;\n position: relative;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-zone-item-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n margin-left: 2px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n}\n\n.pivot-value-item {\n background: #fff7e6;\n border-color: #ffd591;\n flex-wrap: wrap;\n width: 100%;\n max-width: 100%;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n cursor: grab;\n user-select: none;\n\n &:hover {\n border-color: #fa8c16;\n background: #fff7e6;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n &.drag-before {\n box-shadow: 0 -2px 0 0 #1890ff;\n }\n\n &.drag-after {\n box-shadow: 0 2px 0 0 #1890ff;\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n\n :deep(.ant-input) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n}\n\n.pivot-drop-zone-values {\n /* 值区域根据内容自动扩展,不再用 flex: 1 限制高度 */\n min-height: 80px;\n}\n\n.pivot-dim-group {\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n border-radius: 6px;\n padding: 6px 8px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 6px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n position: relative;\n\n &:hover {\n border-color: #52c41a;\n background: #f6ffed;\n }\n\n &.drag-over {\n border-color: #52c41a;\n box-shadow: 0 0 0 2px #52c41a inset;\n }\n\n // 组间插入指示线:在相邻组的上/下边框高亮(蓝),提示将在此处新建/插入维度组;\n // 与「组内 4 边框高亮(绿,添加字段到该组)」区分。\n &.drop-group-before::before,\n &.drop-group-after::after {\n content: '';\n position: absolute;\n left: -2px;\n right: -2px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n z-index: 2;\n }\n\n &.drop-group-before::before {\n top: -4px;\n }\n\n &.drop-group-after::after {\n bottom: -4px;\n }\n\n .pivot-dim-group-header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n\n .pivot-group-label-input {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .pivot-group-remove {\n color: #999;\n cursor: pointer;\n font-size: 11px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n\n .pivot-dim-group-fields {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 4px;\n width: 100%;\n }\n\n .pivot-field-chip {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n background: #fff;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n position: relative;\n\n // 组内字段重排指示线:上/下边框高亮,提示释放后的插入位置\n &.drop-before::before,\n &.drop-after::after {\n content: '';\n position: absolute;\n left: -1px;\n right: -1px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n }\n\n &.drop-before::before {\n top: -3px;\n }\n\n &.drop-after::after {\n bottom: -3px;\n }\n\n &:hover {\n border-color: #52c41a;\n }\n\n .pivot-chip-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-chip-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 80px;\n }\n }\n\n .pivot-sort-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n width: 100%;\n padding-top: 6px;\n border-top: 1px dashed #d9d9d9;\n\n .pivot-sort-label {\n font-size: 11px;\n color: #999;\n flex-shrink: 0;\n }\n\n :deep(.ant-select) {\n flex: 0 1 auto;\n min-width: 0;\n }\n }\n}\n\n.pivot-options {\n flex-shrink: 0;\n padding: 10px 0 4px;\n border-top: 1px solid #f0f0f0;\n display: flex;\n flex-direction: column;\n gap: 10px;\n\n .pivot-options-group {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px 12px;\n\n .pivot-options-label {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n flex-shrink: 0;\n }\n\n .pivot-option-switch {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n color: #333;\n cursor: pointer;\n user-select: none;\n\n span {\n white-space: nowrap;\n }\n }\n }\n}\n.value-item-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n gap: 6px;\n width: 100%;\n}\n.value-item-content {\n display: flex;\n flex-direction: row;\n gap: 6px;\n align-items: center;\n}\n\n.pivot-drop-zone-subexpr {\n background: #fafaff;\n}\n\n.subexpr-items {\n align-items: stretch;\n}\n\n.pivot-subexpr-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 10px;\n background: #fff;\n border: 1px solid #efd6ff;\n border-radius: 6px;\n min-width: 220px;\n max-width: 100%;\n\n .subexpr-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n\n .subexpr-card-name {\n font-weight: 600;\n font-size: 13px;\n color: #722ed1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .subexpr-card-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n }\n }\n}\n</style>\n","<template>\n <div class=\"pivot-config-wrapper\">\n <div class=\"pivot-config-main\">\n <!-- 字段列表区域 -->\n <div class=\"pivot-field-list\">\n <div class=\"pivot-section-title\">{{ $t('字段列表') }}</div>\n <a-input\n v-model:value=\"searchText\"\n :placeholder=\"$t('搜索字段')\"\n allow-clear\n size=\"small\"\n style=\"margin-bottom: 8px\"\n />\n <div class=\"pivot-field-pool\">\n <div\n v-for=\"field in availableFieldRows\"\n :key=\"field.key\"\n :class=\"['pivot-field-item', { 'pivot-field-group': !field.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + field.depth * 16}px` }\"\n :draggable=\"field.isLeaf\"\n @dragstart=\"field.isLeaf ? onDragStart($event, field.option) : undefined\"\n @dragend=\"field.isLeaf ? onDragEnd() : undefined\"\n @dblclick=\"field.isLeaf ? addToValues(field.option) : undefined\"\n >\n <span class=\"pivot-field-icon\">\n <database-outlined v-if=\"field.isLeaf\" />\n <span\n v-else\n class=\"pivot-field-branch-dot\"\n />\n </span>\n <a-tooltip\n :title=\"\n field.isLeaf\n ? field.option.label + '(' + field.option.field + ')'\n : field.option.label\n \"\n >\n <span class=\"pivot-field-label\">\n {{ field.isLeaf ? `${field.option.label}(${field.option.field})` : field.option.label }}\n </span>\n </a-tooltip>\n <span\n v-if=\"field.isLeaf && isFieldInRowOrCol(field.option.field)\"\n class=\"pivot-field-tag\"\n >\n {{ getFieldLocation(field.option.field) }}\n </span>\n </div>\n <div\n v-if=\"availableFieldRows.length === 0\"\n class=\"pivot-field-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 拖放区域 -->\n <div class=\"pivot-drop-zones\">\n <!-- 列区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'columns' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('columns', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('columns', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <swap-outlined />\n {{ $t('列') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为列维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.columns\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'columns' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'columns' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'columns' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'columns')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('columns', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('columns', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('columns', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'columns' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'columns' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'columns', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('columns', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('columns', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('columns', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.columns.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 行区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'rows' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('rows', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('rows', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <bars-outlined />\n {{ $t('行') }}\n </span>\n\n <a-tooltip :title=\"$t('拖拽字段到此处作为行维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.rows\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'rows' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'rows' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'rows' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'rows')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('rows', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('rows', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('rows', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'rows' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'rows' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'rows', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('rows', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('rows', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('rows', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.rows.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 值区域 -->\n <div\n class=\"pivot-drop-zone pivot-drop-zone-values\"\n :class=\"{ 'drag-over': dragOverZone === 'values' }\"\n @dragover.prevent=\"onDragOver('values')\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onValueDrop\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <calculator-outlined />\n {{ $t('值') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为值,双击字段可快速添加')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(item, index) in localConfig.values\"\n :key=\"item.id\"\n class=\"pivot-zone-item pivot-value-item\"\n :class=\"{\n 'drag-before': dragOverZone === 'values' && dragInsertIndex === index,\n 'drag-after': dragOverZone === 'values' && dragInsertIndex === index + 1,\n }\"\n draggable=\"true\"\n @dragstart=\"onValueDragStart($event, item)\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onValueItemDragOver(index, $event)\"\n >\n <div class=\"value-item-header\">\n <DebouncedInput\n v-model:value=\"item.title\"\n size=\"small\"\n style=\"width: 100px\"\n :placeholder=\"$t('显示名称')\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onOpenValueConfig(item)\"\n >\n 高级配置\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeValue(index)\"\n />\n </div>\n <div class=\"value-item-content\">\n <valueFieldDisplay\n :valueField=\"item\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div class=\"value-item-content\">\n <a-select\n v-model:value=\"item.aggregationType\"\n size=\"small\"\n style=\"width: 90px\"\n :options=\"aggregationOptions\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"item.formatFun\"\n size=\"small\"\n style=\"width: 140px\"\n :options=\"valueFormatOptions\"\n :placeholder=\"$t('显示格式')\"\n :allow-clear=\"true\"\n @change=\"onFormatFunChange(item)\"\n @click.stop\n />\n <!-- 通用数字格式:可选预设格式串,也可自定义输入(numfmt / Excel 标准格式) -->\n <div\n v-if=\"item.formatFun === NUMFMT_FORMAT_KEY\"\n class=\"value-item-content\"\n >\n <a-auto-complete\n v-model:value=\"item.formatPattern\"\n size=\"small\"\n style=\"width: 234px\"\n :options=\"numfmtPatternOptions\"\n :placeholder=\"$t('数据格式,如 #,##0.00')\"\n @change=\"onPatternChange\"\n @click.stop\n />\n </div>\n </div>\n </div>\n <div\n v-if=\"localConfig.values.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 子表达式区域:可复用表达式片段,供任意值字段的表达式/过滤条件引用 -->\n <div class=\"pivot-drop-zone pivot-drop-zone-subexpr\">\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <function-outlined />\n {{ $t('子表达式') }}\n </span>\n <div class=\"header-item\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onAddSubExpr\"\n >\n {{ $t('添加子表达式') }}\n </a-button>\n <a-tooltip\n :title=\"\n $t(\n '子表达式是可复用的表达式片段,可在值字段表达式/过滤条件中以名称引用,后端递归内联展开',\n )\n \"\n >\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n </div>\n <div class=\"pivot-zone-items subexpr-items\">\n <div\n v-for=\"(se, i) in localConfig.subExpressions\"\n :key=\"se.id\"\n class=\"pivot-subexpr-card\"\n >\n <div class=\"subexpr-card-head\">\n <span class=\"subexpr-card-name\">{{ se.name || $t('(未命名)') }}</span>\n <span class=\"subexpr-card-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onEditSubExpr(i)\"\n >\n {{ $t('编辑') }}\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeSubExpr(i)\"\n />\n </span>\n </div>\n <valueFieldDisplay\n :valueField=\"subExprAsValueField(se)\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div\n v-if=\"!localConfig.subExpressions.length\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('暂无子表达式,点击「添加子表达式」创建可复用的表达式片段') }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 选项区域 -->\n <div class=\"pivot-options\">\n <div class=\"pivot-options-group\">\n <span class=\"pivot-options-label\">{{ $t('结构') }}</span>\n <a-radio-group\n v-model:value=\"localConfig.indicatorsAsCol\"\n size=\"small\"\n option-type=\"button\"\n button-style=\"solid\"\n @change=\"onConfigChange\"\n >\n <a-radio-button :value=\"true\">{{ $t('指标在列') }}</a-radio-button>\n <a-radio-button :value=\"false\">{{ $t('指标在行') }}</a-radio-button>\n </a-radio-group>\n <a-radio-group\n v-model:value=\"localConfig.rowHierarchyType\"\n size=\"small\"\n option-type=\"button\"\n button-style=\"solid\"\n @change=\"onConfigChange\"\n >\n <a-radio-button value=\"grid\">{{ $t('扁平') }}</a-radio-button>\n <a-radio-button\n :disabled=\"!localConfig.rows?.length || localConfig.rows.length < 2\"\n value=\"tree\"\n >\n {{ $t('树形') }}\n </a-radio-button>\n <a-radio-button\n :disabled=\"!localConfig.rows?.length || localConfig.rows.length < 2\"\n value=\"grid-tree\"\n >\n {{ $t('网格树形') }}\n </a-radio-button>\n </a-radio-group>\n </div>\n <div class=\"pivot-options-group\">\n <span class=\"pivot-options-label\">{{ $t('汇总') }}</span>\n <label class=\"pivot-option-switch\">\n <a-switch\n v-model:checked=\"localConfig.columnShowSubTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('列小计') }}</span>\n </label>\n <label class=\"pivot-option-switch\">\n <a-switch\n v-model:checked=\"localConfig.columnShowGrandTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('列合计') }}</span>\n </label>\n <label\n class=\"pivot-option-switch\"\n v-if=\"localConfig.rowHierarchyType === 'grid'\"\n >\n <a-switch\n v-model:checked=\"localConfig.rowShowSubTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('行小计') }}</span>\n </label>\n <label\n class=\"pivot-option-switch\"\n v-if=\"localConfig.rowHierarchyType === 'grid'\"\n >\n <a-switch\n v-model:checked=\"localConfig.rowShowGrandTotals\"\n size=\"small\"\n @change=\"onConfigChange\"\n />\n <span>{{ $t('行合计') }}</span>\n </label>\n </div>\n </div>\n\n <!-- 子表达式编辑弹窗 -->\n <a-modal\n v-model:open=\"subExprModalOpen\"\n :title=\"editingIndex >= 0 ? $t('编辑子表达式') : $t('添加子表达式')\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n @ok=\"onSubExprOk\"\n @cancel=\"subExprModalOpen = false\"\n >\n <sub-expression-editor\n v-if=\"subExprModalOpen\"\n v-model=\"editingSubExpr\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n <template #footer>\n <a-button\n type=\"primary\"\n :disabled=\"!editingSubExpr.name || !editingSubExpr.name.trim()\"\n @click=\"onSubExprOk\"\n >\n {{ $t('确定') }}\n </a-button>\n <a-button @click=\"subExprModalOpen = false\">\n {{ $t('取消') }}\n </a-button>\n </template>\n </a-modal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n DatabaseOutlined,\n SwapOutlined,\n BarsOutlined,\n CalculatorOutlined,\n CloseOutlined,\n InfoCircleOutlined,\n FunctionOutlined,\n} from '@ant-design/icons-vue';\nimport { cloneDeep, debounce } from 'lodash';\nimport { computed, reactive, ref, watch } from 'vue';\nimport {\n Column,\n PivotConfig,\n PivotFieldConfig,\n PivotFieldOption,\n PivotValueField,\n PivotSubExpression,\n SettingMode,\n} from '../typing';\nimport {\n PIVOT_FIELD_PROCESS_FUNCS,\n VALUE_FORMATS,\n NUMFMT_FORMAT_KEY,\n NUMFMT_PATTERN_PRESETS,\n DEFAULT_NUMFMT_PATTERN,\n normalizeValueFormat,\n} from '../func';\nimport { genID } from 'liyu-pc-base/utils/function';\nimport { combineLabel } from '../utils';\nimport valueFieldDisplay from './valueFieldDisplay.vue';\nimport DebouncedInput from './DebouncedInput.vue';\nimport subExpressionEditor from './subExpressionEditor.vue';\ninterface Props {\n columns: Column[];\n modelValue?: PivotConfig;\n settingMode: SettingMode;\n}\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: PivotConfig): void;\n (e: 'change', value: PivotConfig): void;\n (e: 'openValueConfig', value: PivotValueField): void;\n}>();\n\nconst searchText = ref('');\n\n// 拖拽负载(判别联合)\ntype DragPayload =\n | { kind: 'pool'; field: PivotFieldOption }\n | { kind: 'group'; zone: 'rows' | 'columns'; groupId: string }\n | { kind: 'field'; zone: 'rows' | 'columns'; groupId: string; fieldIndex: number }\n | { kind: 'value'; field: PivotValueField };\nconst dragData = ref<DragPayload | null>(null);\nconst dragOverZone = ref<string>('');\nconst dragOverGroupZone = ref<string>('');\nconst dragOverGroupIndex = ref<number>(-1);\nconst dragInsertIndex = ref<number>(-1); // 值区域拖拽插入位置索引\n// 组内字段重排指示:拖拽字段经过某 chip 时,记录插入位置(前/后)\nconst dragFieldTarget = ref<{\n zone: string;\n groupIndex: number;\n fieldIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n// 组间插入指示:拖拽到组外(组间间隙)新建/插入/移动维度组时,在相邻组的上/下边框高亮\nconst dragGroupInsert = ref<{\n zone: string;\n refIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n\n// 维度组「显示名」是否为用户手动自定义:自定义名在字段增删/重排时不被自动覆盖\nconst customLabels = reactive(new Set<string>());\n\n// 重建 custom 标记:外部/旧配置中已带 label 的组视为自定义(保留),不调用\n// defaultGroupLabel 以避免 setup 阶段访问尚未初始化的依赖。\nconst syncCustomFlags = (config: PivotConfig) => {\n customLabels.clear();\n const dims = [...(config.rows || []), ...(config.columns || [])];\n dims.forEach(g => {\n if (g.id && g.label) customLabels.add(g.id);\n });\n};\n\n// 用户手动输入显示名:标记为自定义,后续字段变化不再自动覆盖\nconst onLabelInput = (group: PivotFieldConfig) => {\n if (group.id) customLabels.add(group.id);\n onConfigChange();\n};\n\nconst defaultConfig: PivotConfig = {\n rows: [],\n columns: [],\n values: [],\n indicatorsAsCol: true,\n rowHierarchyType: 'grid',\n};\n\nconst localConfig = ref<PivotConfig>(cloneDeep(props.modelValue || defaultConfig));\n\n// 兼容旧数据:把「单字段」维度 {field,label,processFunc} 归一为「维度组」{id,fields:[...],linkType}\nconst normalizeDim = (d: any): PivotFieldConfig => {\n if (d && Array.isArray(d.fields)) {\n return { ...d, id: d.id || genID() };\n }\n return {\n id: d?.id || genID(),\n fields: [{ field: d?.field, label: d?.label, processFunc: d?.processFunc }],\n label: d?.label, // 旧单字段配置的显示名提升到组级(兼容旧数据)\n linkType: d?.linkType || '_',\n subTotalAggregationType: d?.subTotalAggregationType,\n sortOrder: d?.sortOrder,\n sortBy: d?.sortBy,\n sortProcessFunc: d?.sortProcessFunc,\n };\n};\n// 确保每个值字段都有唯一 id(兼容旧数据);同时把旧格式 key('0.00' 等)\n// 归一为「通用数字格式 + formatPattern」\nconst ensureValueIds = (config: PivotConfig) => {\n config.values.forEach(v => {\n if (!v.id && v.field) {\n v.id = `${v.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;\n }\n normalizeValueFormat(v);\n });\n};\nconst normalizeConfig = (config: PivotConfig) => {\n config.rows = (config.rows || []).map(normalizeDim);\n config.columns = (config.columns || []).map(normalizeDim);\n if (!Array.isArray(config.subExpressions)) config.subExpressions = [];\n ensureValueIds(config);\n};\nnormalizeConfig(localConfig.value);\nsyncCustomFlags(localConfig.value);\n\n// 自身 emit 引起的 modelValue 回灌无需再 cloneDeep+重渲染(localConfig 已是最新),\n// 用 skipWatch 跳过本次,避免一次 drop 触发两次整面板重渲染导致的卡顿。\nlet skipWatch = false;\n\nwatch(\n () => props.modelValue,\n val => {\n if (skipWatch) {\n skipWatch = false;\n return;\n }\n if (val) {\n const cloned = cloneDeep(val);\n normalizeConfig(cloned);\n syncCustomFlags(cloned);\n localConfig.value = cloned;\n }\n },\n { deep: true },\n);\n\n// 所有可用字段列表\nconst allFields = computed<PivotFieldOption[]>(() => {\n const buildTree = (cols: Column[]): PivotFieldOption[] => {\n return cols\n .map(col => {\n const label = col.title || col.dataIndex || col.key || '';\n const children = col.children?.length ? buildTree(col.children) : [];\n const isLeaf = children.length === 0;\n if (isLeaf) {\n if (!col.dataIndex || col.dataIndex === 'index' || col.dataIndex === 'action') {\n return null;\n }\n return {\n field: col.dataIndex,\n label,\n };\n }\n return {\n field: col.dataIndex || col.key || label,\n label,\n children,\n };\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n return buildTree(props.columns || []);\n});\n\nconst flatAllFields = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(allFields.value);\n});\n\nconst availableFieldTree = computed<PivotFieldOption[]>(() => {\n const filterTree = (fields: PivotFieldOption[], search: string): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(search) || field.field.toLowerCase().includes(search);\n const children = field.children?.length ? filterTree(field.children, search) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n\n const search = searchText.value.trim().toLowerCase();\n if (!search) return allFields.value;\n return filterTree(allFields.value, search);\n});\n\nconst availableFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(availableFieldTree.value);\n return list;\n});\n\n// 检查字段是否已在任意行/列维度组中\nconst isFieldInRowOrCol = (field: string) => {\n return (\n localConfig.value.rows.some(g => g.fields.some(f => f.field === field)) ||\n localConfig.value.columns.some(g => g.fields.some(f => f.field === field))\n );\n};\n\n// 获取字段所在位置标签(可能同时出现在行、列)\nconst getFieldLocation = (field: string) => {\n const locs: string[] = [];\n if (localConfig.value.rows.some(g => g.fields.some(f => f.field === field))) locs.push('行');\n if (localConfig.value.columns.some(g => g.fields.some(f => f.field === field))) locs.push('列');\n return locs.join('·');\n};\n\nconst aggregationOptions = [\n { label: '求和', value: 'SUM' },\n { label: '计数', value: 'COUNT' },\n { label: '数值计数', value: 'COUNTNUM' },\n { label: '平均值', value: 'AVG' },\n { label: '最小值', value: 'MIN' },\n { label: '最大值', value: 'MAX' },\n { label: '乘积', value: 'PRODUCT' },\n { label: '方差', value: 'VAR' },\n { label: '总体方差', value: 'VARP' },\n { label: '标准偏差', value: 'STD' },\n { label: '总体标准偏差', value: 'STDP' },\n { label: '无', value: 'NONE' },\n];\n\n// 行/列维度「数据处理函数」下拉选项(来自 func.ts 注册表,新增函数后自动出现)\nconst processFuncOptions = computed(() =>\n PIVOT_FIELD_PROCESS_FUNCS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 值字段「显示格式」下拉选项(来自 func.ts 注册表)\nconst valueFormatOptions = computed(() =>\n VALUE_FORMATS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 通用数字格式的常用格式串预设(a-auto-complete 选项,支持自定义输入)\nconst numfmtPatternOptions = NUMFMT_PATTERN_PRESETS.map(p => ({\n value: p.value,\n label: p.label || p.value,\n}));\n\n// 切换显示格式:选中「通用数字格式」且尚无格式串时填入缺省值\nconst onFormatFunChange = (item: PivotValueField) => {\n if (item.formatFun === NUMFMT_FORMAT_KEY && !item.formatPattern) {\n item.formatPattern = DEFAULT_NUMFMT_PATTERN;\n }\n onValueConfigChange();\n};\n\n// 格式串输入防抖:避免逐字符输入触发接口调用\nconst onPatternChange = debounce(() => onValueConfigChange(), 500);\n\n// 维度组「连接符」下拉选项:'_'(默认)/ '-' / '()'\nconst linkTypeOptions = [\n { label: '_', value: '_' },\n { label: '-', value: '-' },\n { label: '( )', value: '()' },\n];\n\n// 维度「排序方向」下拉选项\nconst sortOrderOptions = [\n { label: '升序', value: 'asc' },\n { label: '降序', value: 'desc' },\n];\n\n// 维度组「排序依据字段」下拉选项:先列出本组各字段(按原始 field 名,Rust 侧已为组内\n// 每个字段登记单字段解析列,sort_by 指向该 field 即按「组内该字段」排序),再列出其余所有\n// 可用字段(含值字段、其它维度、未参与维度但存在于数据中的字段)。\n// 与 Rust 侧 resolve_sort 对齐:sort_by 现可解析任意存在于数据中的字段(见 pivot.rs\n// 中按 sort_by 新增 __s* 辅助列的逻辑),因此不再有「只能指向已选列 / 更早维度」的限制。\nconst getSortByOptions = (group: PivotFieldConfig) => {\n const own = group.fields.map(f => ({\n label: `${fieldLabel(f.field)}(${f.field})`,\n value: f.field,\n }));\n const ownSet = new Set(group.fields.map(f => f.field));\n const others = flatAllFields.value\n .filter(f => !ownSet.has(f.field))\n .map(f => ({ label: `${f.label}(${f.field})`, value: f.field }));\n return [...own, ...others];\n};\n\n// 排序依据字段下拉的搜索过滤(按 label 模糊匹配)\nconst filterFieldOption = (input: string, option: { label?: string }) =>\n (option?.label ?? '').toLowerCase().includes(input.toLowerCase());\n\n// 排序依据字段变化:清空后同步清除其处理函数(处理函数依赖排序依据字段存在)\nconst onGroupSortByChange = (group: PivotFieldConfig) => {\n if (!group.sortBy) {\n group.sortProcessFunc = undefined;\n }\n onConfigChange();\n};\n\n// 维度组默认显示名:按字段名以下划线连接(缺省值,用户可在配置中覆盖 group.label)\nconst defaultGroupLabel = (group: PivotFieldConfig): string => {\n if (!group.fields.length) return '';\n const labelList = group.fields.map(f => f.label || fieldLabel(f.field));\n return combineLabel(labelList, group.linkType);\n};\n\n// 字段显示名:优先用字段列表中的 title,缺省回退到字段名\nconst fieldLabel = (field: string): string => {\n const f = flatAllFields.value.find(x => x.field === field);\n return f ? f.label : field;\n};\n\n// ===== 拖拽 =====\nconst onDragStart = (event: DragEvent, field: PivotFieldOption) => {\n dragData.value = { kind: 'pool', field };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', field.field);\n }\n};\n\nconst onGroupDragStart = (event: DragEvent, group: PivotFieldConfig, zone: 'rows' | 'columns') => {\n dragData.value = { kind: 'group', zone, groupId: group.id! };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onFieldDragStart = (\n event: DragEvent,\n group: PivotFieldConfig,\n zone: 'rows' | 'columns',\n fieldIndex: number,\n) => {\n dragData.value = { kind: 'field', zone, groupId: group.id!, fieldIndex };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onDragEnd = () => clearDrag();\n\nconst clearDrag = () => {\n dragData.value = null;\n dragOverZone.value = '';\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragInsertIndex.value = -1;\n dragFieldTarget.value = null;\n dragGroupInsert.value = null;\n};\n\nconst onDragOver = (zone: string) => {\n dragOverZone.value = zone;\n dragFieldTarget.value = null;\n};\n\nconst onDragLeave = () => {\n // 只在离开整个区域时清除\n};\n\nconst onGroupDragOver = (zone: 'rows' | 'columns', index: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n event.stopPropagation();\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';\n\n if (d.kind === 'group') {\n // 拖拽整组进行排序:始终显示单边框插入指示(上/下),不显示 4 边框高亮。\n // 悬停在自身组上:不显示插入指示(原地不动)。\n const groupId = localConfig.value[zone][index]?.id;\n const overSelf = d.zone === zone && d.groupId === groupId;\n let nextIns: { zone: 'rows' | 'columns'; refIndex: number; pos: 'before' | 'after' } | null =\n null;\n if (!overSelf) {\n // 指针在组的上半区→插入到该组之前(高亮上边框);下半区→之后(高亮下边框)\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n nextIns = { zone, refIndex: index, pos: before ? 'before' : 'after' };\n }\n applyGroupInsert(nextIns);\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n dragOverZone.value = '';\n return;\n }\n\n // 其它情况(字段 / 池字段):悬停整组内部 → 4 边框高亮(把字段添加进该组)\n dragOverGroupZone.value = zone;\n dragOverGroupIndex.value = index;\n dragFieldTarget.value = null; // 悬停整组(非具体字段)时清除字段级指示\n dragGroupInsert.value = null; // 悬停整组(内部)时清除组间插入指示\n dragOverZone.value = ''; // 悬停整组(内部)时清除整区高亮,避免与组内 4 边框叠加\n};\n\nconst findGroupIndex = (zone: 'rows' | 'columns', groupId: string) =>\n localConfig.value[zone].findIndex(g => g.id === groupId);\n\n// 拖到区域空白:池字段→新建维度组;组→移到该区域末尾;字段→从原组移除\nconst onDropZone = (zone: 'rows' | 'columns') => {\n const d = dragData.value;\n if (!d) return;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].push(ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n localConfig.value[zone].push(g);\n }\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n localConfig.value[d.zone][srcIdx].fields.splice(d.fieldIndex, 1);\n if (localConfig.value[d.zone][srcIdx].fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖到具体维度组:池字段→追加;字段→跨组移动;组→组内重排 / 跨区移动\nconst onDropOnGroup = (zone: 'rows' | 'columns', gIndex: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) return;\n\n if (d.kind === 'pool') {\n targetGroup.fields.push({ field: d.field.field, label: d.field.label });\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n targetGroup.fields.push(field);\n }\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n let tIdx = gIndex;\n if (d.zone === zone && srcIdx < gIndex) tIdx -= 1;\n localConfig.value[zone].splice(before ? tIdx : tIdx + 1, 0, g);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 区域级拖拽悬浮:把探测范围提升到 .pivot-drop-zone(覆盖整片区域),\n// 既设置整区高亮(dragOverZone),又按指针相对各组位置计算「组间插入」指示\n// (dragGroupInsert)。关键点:第一组上方与末组下方的留白属于区域 padding/header,\n// 原本不在 .pivot-zone-items 内,导致那两块区域只显示整区边框、且 drop 落到末尾;\n// 现在由本函数统一探测,使「第一组上方」→ 高亮组0上边框并插入到首个位置,\n// 「末组下方」→ 高亮末组下边框并插入到末尾。组内拖拽由 .pivot-dim-group 的\n// .stop 拦截,仍显示 4 边框(添加字段)。\nconst onZoneDragOver = (zone: 'rows' | 'columns', event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n onDragOver(zone); // 整区高亮\n computeGroupInsert(zone, event);\n};\n\n// 计算「组间插入」指示:第一组上方→before(0);末组下方→after(末);组间间隙→after(i);\n// 命中组内部由 .stop 拦截不会进入本函数,故此处无需处理组内情况。\n// 仅在插入目标(zone/refIndex/pos)真正变化时才更新 dragGroupInsert,\n// 避免拖拽过程中每个 dragover 都触发整面板重渲染(卡顿来源之一)。\nconst applyGroupInsert = (\n next: { zone: 'rows' | 'columns'; refIndex: number; pos: 'before' | 'after' } | null,\n) => {\n const cur = dragGroupInsert.value;\n if (\n cur &&\n next &&\n cur.zone === next.zone &&\n cur.refIndex === next.refIndex &&\n cur.pos === next.pos\n )\n return;\n if (!cur && !next) return;\n dragGroupInsert.value = next;\n};\n\nconst computeGroupInsert = (zone: 'rows' | 'columns', event: DragEvent) => {\n const d = dragData.value;\n if (!d) {\n applyGroupInsert(null);\n return;\n }\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n const container = event.currentTarget as HTMLElement;\n const groups = Array.from(container.querySelectorAll('.pivot-dim-group')) as HTMLElement[];\n if (groups.length === 0) {\n applyGroupInsert(null); // 无组(空白区)由区域层级高亮处理\n return;\n }\n const y = event.clientY;\n const firstTop = groups[0].getBoundingClientRect().top;\n if (y < firstTop) {\n applyGroupInsert({ zone, refIndex: 0, pos: 'before' });\n return;\n }\n const lastBottom = groups[groups.length - 1].getBoundingClientRect().bottom;\n if (y > lastBottom) {\n applyGroupInsert({ zone, refIndex: groups.length - 1, pos: 'after' });\n return;\n }\n for (let i = 0; i < groups.length - 1; i++) {\n const top = groups[i].getBoundingClientRect();\n const nxt = groups[i + 1].getBoundingClientRect();\n if (y >= top.bottom && y <= nxt.top) {\n // 落在组 i 与组 i+1 之间:在组 i 之后插入(高亮组 i 下边框)\n applyGroupInsert({ zone, refIndex: i, pos: 'after' });\n return;\n }\n }\n applyGroupInsert(null);\n};\n\n// 在组间间隙释放:新建(池字段)/ 插入(整组重排)/ 移出(字段独立成组)维度组到对应位置。\n// 未命中组间插入目标(如空白区)时回退到区域空白逻辑。\nconst onZoneItemsDrop = (zone: 'rows' | 'columns', _event: DragEvent) => {\n const d = dragData.value;\n const ins = dragGroupInsert.value;\n if (!d || !ins || ins.zone !== zone) {\n onDropZone(zone);\n return;\n }\n let insIndex = ins.pos === 'before' ? ins.refIndex : ins.refIndex + 1;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].splice(insIndex, 0, ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n if (d.zone === zone && srcIdx < insIndex) insIndex -= 1;\n localConfig.value[zone].splice(insIndex, 0, g);\n }\n } else if (d.kind === 'field') {\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < insIndex) insIndex -= 1;\n }\n const ng: PivotFieldConfig = { id: genID(), fields: [field], linkType: '_' };\n localConfig.value[zone].splice(insIndex, 0, ng);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖拽字段经过组内某个 chip:记录插入位置(上边框=前 / 下边框=后),用于高亮提示\nconst onChipDragOver = (\n zone: 'rows' | 'columns',\n gIndex: number,\n fIndex: number,\n event: DragEvent,\n) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 仅字段重排时处理,其它拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n dragGroupInsert.value = null; // 悬停具体字段 chip 时清除组间插入指示\n const group = localConfig.value[zone][gIndex];\n if (!group) return;\n // 悬停在被拖拽的源字段自身上时,不显示指示线(避免原地误判)\n if (d.zone === zone && d.groupId === group.id && d.fieldIndex === fIndex) {\n dragFieldTarget.value = null;\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n dragFieldTarget.value = {\n zone,\n groupIndex: gIndex,\n fieldIndex: fIndex,\n pos: before ? 'before' : 'after',\n };\n};\n\n// 在组内某字段的上/下边界释放:把被拖字段插入到对应位置(支持组内重排与跨组定位)\nconst onChipDrop = (zone: 'rows' | 'columns', gIndex: number, fIndex: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 非字段拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) {\n clearDrag();\n return;\n }\n // 直接落在源字段自身:视为无移动\n if (d.zone === zone && d.groupId === targetGroup.id && d.fieldIndex === fIndex) {\n clearDrag();\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx < 0) {\n clearDrag();\n return;\n }\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n\n const sameGroup = d.zone === zone && srcGroupIdx === gIndex;\n let targetGroupIdx = gIndex;\n let insertAt = before ? fIndex : fIndex + 1;\n // 同组内、源在目标之前时被移除会导致下标左移一位\n if (sameGroup && d.fieldIndex < fIndex) insertAt -= 1;\n\n // 跨组移动且源组已空:移除源组,并按需要修正目标组下标\n if (srcGroup.fields.length === 0 && !sameGroup) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < targetGroupIdx) targetGroupIdx -= 1;\n }\n localConfig.value[zone][targetGroupIdx].fields.splice(insertAt, 0, field);\n clearDrag();\n onConfigChange();\n};\n\n// 值区域拖放:仅支持从字段池拖入新增值\nconst onDropValues = () => {\n const d = dragData.value;\n if (d && d.kind === 'pool') {\n addToValues(d.field);\n }\n clearDrag();\n};\n\n// 值字段拖拽排序\nconst onValueDragStart = (event: DragEvent, item: PivotValueField) => {\n dragData.value = { kind: 'value', field: item };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', item.id);\n }\n};\n\nconst onValueItemDragOver = (index: number, event: DragEvent) => {\n dragOverZone.value = 'values';\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n if (event.clientY < midpoint) {\n dragInsertIndex.value = index;\n } else {\n dragInsertIndex.value = index + 1;\n }\n};\n\nconst onValueDrop = () => {\n const d = dragData.value;\n if (!d) return;\n\n // 从字段池拖入:创建新值\n if (d.kind === 'pool') {\n const insertIndex =\n dragInsertIndex.value >= 0 ? dragInsertIndex.value : localConfig.value.values.length;\n const valueField: PivotValueField = {\n id: `${d.field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: d.field.field,\n label: d.field.label,\n aggregationType: 'SUM',\n title: d.field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.splice(insertIndex, 0, valueField);\n clearDrag();\n onConfigChange();\n return;\n }\n\n // 值区域内重排\n if (d.kind !== 'value') return;\n const list = localConfig.value.values;\n const fromIndex = list.findIndex(f => f.id === (d as any).field.id);\n if (fromIndex === -1) {\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n return;\n }\n let toIndex = dragInsertIndex.value;\n if (toIndex < 0) toIndex = list.length;\n if (toIndex > fromIndex) toIndex -= 1;\n if (toIndex >= list.length) toIndex = list.length - 1;\n if (toIndex < 0) toIndex = 0;\n const [item] = list.splice(fromIndex, 1);\n list.splice(toIndex, 0, item);\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n onConfigChange();\n};\n\n// 双击字段快速添加到值区域(允许重复添加,不限制是否已在行/列中)\nconst addToValues = (field: PivotFieldOption) => {\n const valueField: PivotValueField = {\n id: `${field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: field.field,\n label: field.label,\n aggregationType: 'SUM',\n title: field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.push(valueField);\n onConfigChange();\n};\n\n// 移除整个维度组\nconst removeGroup = (zone: 'rows' | 'columns', index: number) => {\n localConfig.value[zone].splice(index, 1);\n onConfigChange();\n};\n\n// 从维度组移除单个字段;若组变空则一并移除该组\nconst removeFieldFromGroup = (zone: 'rows' | 'columns', gIndex: number, fIndex: number) => {\n const g = localConfig.value[zone][gIndex];\n g.fields.splice(fIndex, 1);\n if (g.fields.length === 0) {\n localConfig.value[zone].splice(gIndex, 1);\n }\n onConfigChange();\n};\n\n// 移除值字段\nconst removeValue = (index: number) => {\n localConfig.value.values.splice(index, 1);\n onConfigChange();\n};\n\nconst onValueConfigChange = () => {\n onConfigChange();\n};\n\n// ===== 子表达式(可复用表达式片段)管理 =====\nconst subExprModalOpen = ref(false);\nconst editingIndex = ref(-1); // >=0 表示编辑已有项,-1 表示新增\nconst editingSubExpr = ref<PivotSubExpression>({ id: '', name: '', expression: [] });\n\n// 把子表达式映射为一个「伪值字段」,复用 valueFieldDisplay 渲染其表达式预览\nconst subExprAsValueField = (se: PivotSubExpression): PivotValueField => ({\n id: se.id,\n field: '',\n aggregationType: 'NONE',\n useExpression: true,\n expression: se.expression || [],\n filter: [],\n});\n\nconst onAddSubExpr = () => {\n editingIndex.value = -1;\n editingSubExpr.value = { id: genID(), name: '', expression: [] };\n subExprModalOpen.value = true;\n};\nconst onEditSubExpr = (i: number) => {\n editingIndex.value = i;\n editingSubExpr.value = cloneDeep(localConfig.value.subExpressions[i]);\n subExprModalOpen.value = true;\n};\nconst onSubExprOk = () => {\n const se = editingSubExpr.value;\n if (!se.name || !se.name.trim()) return;\n if (editingIndex.value >= 0) {\n localConfig.value.subExpressions[editingIndex.value] = cloneDeep(se);\n } else {\n localConfig.value.subExpressions.push(cloneDeep(se));\n }\n subExprModalOpen.value = false;\n onConfigChange();\n};\nconst removeSubExpr = (i: number) => {\n localConfig.value.subExpressions.splice(i, 1);\n onConfigChange();\n};\n\nconst onConfigChange = () => {\n skipWatch = true; // 标记本次 emit 引起的 modelValue 回灌,watch 中应跳过\n const config = cloneDeep(localConfig.value);\n emit('update:modelValue', config);\n emit('change', config);\n};\nconst onOpenValueConfig = (valueField: PivotValueField) => {\n emit('openValueConfig', valueField);\n};\n\n// 供父级(tableConfig/index.vue)在「高级配置」抽屉内修改值字段后,触发一次完整配置回传\n// getSubExpressions:把当前(含本会话内新增/编辑的)子表达式列表暴露给 index.vue,\n// 使「高级配置」里的表达式编辑器能引用已配置的子表达式(不依赖上层 props 的实时回灌)。\ndefineExpose({\n forceEmit: onConfigChange,\n getSubExpressions: () => localConfig.value.subExpressions,\n});\n</script>\n\n<style lang=\"less\" scoped>\n.pivot-config-wrapper {\n display: flex;\n flex-direction: column;\n gap: 12px;\n height: 100%;\n}\n\n.pivot-config-main {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n}\n\n.pivot-field-list {\n width: 220px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .pivot-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n color: #333;\n }\n\n .pivot-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n\n .pivot-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n\n .pivot-field-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-field-tag {\n flex-shrink: 0;\n font-size: 10px;\n color: #fff;\n background: #1890ff;\n border-radius: 8px;\n padding: 1px 6px;\n line-height: 16px;\n }\n }\n\n .pivot-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n\n .pivot-field-icon {\n color: #bfbfbf;\n }\n }\n\n .pivot-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n }\n\n .pivot-field-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.pivot-drop-zones {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n overflow-y: auto;\n}\n\n.pivot-drop-zone {\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n padding: 8px;\n transition: all 0.2s;\n min-height: 80px;\n flex-shrink: 0;\n\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n .pivot-zone-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n\n .pivot-zone-title {\n font-weight: 600;\n font-size: 13px;\n color: #333;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .pivot-zone-tip {\n color: #999;\n font-size: 12px;\n cursor: help;\n }\n }\n\n .pivot-zone-items {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n min-height: 32px;\n }\n\n .pivot-zone-placeholder {\n color: #bbb;\n font-size: 12px;\n padding: 4px 8px;\n width: 100%;\n text-align: center;\n }\n}\n\n.pivot-zone-item {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n background: #fafafa;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n max-width: 100%;\n box-sizing: border-box;\n position: relative;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-zone-item-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n margin-left: 2px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n}\n\n.pivot-value-item {\n background: #fff7e6;\n border-color: #ffd591;\n flex-wrap: wrap;\n width: 100%;\n max-width: 100%;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n cursor: grab;\n user-select: none;\n\n &:hover {\n border-color: #fa8c16;\n background: #fff7e6;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n &.drag-before {\n box-shadow: 0 -2px 0 0 #1890ff;\n }\n\n &.drag-after {\n box-shadow: 0 2px 0 0 #1890ff;\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n\n :deep(.ant-input) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n}\n\n.pivot-drop-zone-values {\n /* 值区域根据内容自动扩展,不再用 flex: 1 限制高度 */\n min-height: 80px;\n}\n\n.pivot-dim-group {\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n border-radius: 6px;\n padding: 6px 8px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 6px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n position: relative;\n\n &:hover {\n border-color: #52c41a;\n background: #f6ffed;\n }\n\n &.drag-over {\n border-color: #52c41a;\n box-shadow: 0 0 0 2px #52c41a inset;\n }\n\n // 组间插入指示线:在相邻组的上/下边框高亮(蓝),提示将在此处新建/插入维度组;\n // 与「组内 4 边框高亮(绿,添加字段到该组)」区分。\n &.drop-group-before::before,\n &.drop-group-after::after {\n content: '';\n position: absolute;\n left: -2px;\n right: -2px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n z-index: 2;\n }\n\n &.drop-group-before::before {\n top: -4px;\n }\n\n &.drop-group-after::after {\n bottom: -4px;\n }\n\n .pivot-dim-group-header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n\n .pivot-group-label-input {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .pivot-group-remove {\n color: #999;\n cursor: pointer;\n font-size: 11px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n\n .pivot-dim-group-fields {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 4px;\n width: 100%;\n }\n\n .pivot-field-chip {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n background: #fff;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n position: relative;\n\n // 组内字段重排指示线:上/下边框高亮,提示释放后的插入位置\n &.drop-before::before,\n &.drop-after::after {\n content: '';\n position: absolute;\n left: -1px;\n right: -1px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n }\n\n &.drop-before::before {\n top: -3px;\n }\n\n &.drop-after::after {\n bottom: -3px;\n }\n\n &:hover {\n border-color: #52c41a;\n }\n\n .pivot-chip-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-chip-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 80px;\n }\n }\n\n .pivot-sort-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n width: 100%;\n padding-top: 6px;\n border-top: 1px dashed #d9d9d9;\n\n .pivot-sort-label {\n font-size: 11px;\n color: #999;\n flex-shrink: 0;\n }\n\n :deep(.ant-select) {\n flex: 0 1 auto;\n min-width: 0;\n }\n }\n}\n\n.pivot-options {\n flex-shrink: 0;\n padding: 10px 0 4px;\n border-top: 1px solid #f0f0f0;\n display: flex;\n flex-direction: column;\n gap: 10px;\n\n .pivot-options-group {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px 12px;\n\n .pivot-options-label {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n flex-shrink: 0;\n }\n\n .pivot-option-switch {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n color: #333;\n cursor: pointer;\n user-select: none;\n\n span {\n white-space: nowrap;\n }\n }\n }\n}\n.value-item-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n gap: 6px;\n width: 100%;\n}\n.value-item-content {\n display: flex;\n flex-direction: row;\n gap: 6px;\n align-items: center;\n}\n\n.pivot-drop-zone-subexpr {\n background: #fafaff;\n}\n\n.subexpr-items {\n align-items: stretch;\n}\n\n.pivot-subexpr-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 10px;\n background: #fff;\n border: 1px solid #efd6ff;\n border-radius: 6px;\n min-width: 220px;\n max-width: 100%;\n\n .subexpr-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n\n .subexpr-card-name {\n font-weight: 600;\n font-size: 13px;\n color: #722ed1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .subexpr-card-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n }\n }\n}\n</style>\n","<template>\n <div class=\"pivot-charts-config-wrapper\">\n <div class=\"pivot-charts-config-main\">\n <!-- 字段列表区域 -->\n <div class=\"pivot-field-list\">\n <div class=\"pivot-section-title\">{{ $t('字段列表') }}</div>\n <a-input\n v-model:value=\"searchText\"\n :placeholder=\"$t('搜索字段')\"\n allow-clear\n size=\"small\"\n style=\"margin-bottom: 8px\"\n />\n <div class=\"pivot-field-pool\">\n <div\n v-for=\"field in availableFieldRows\"\n :key=\"field.key\"\n :class=\"['pivot-field-item', { 'pivot-field-group': !field.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + field.depth * 16}px` }\"\n :draggable=\"field.isLeaf\"\n @dragstart=\"field.isLeaf ? onDragStart($event, field.option) : undefined\"\n @dragend=\"field.isLeaf ? onDragEnd() : undefined\"\n @dblclick=\"field.isLeaf ? addToValues(field.option) : undefined\"\n >\n <span class=\"pivot-field-icon\">\n <database-outlined v-if=\"field.isLeaf\" />\n <span\n v-else\n class=\"pivot-field-branch-dot\"\n />\n </span>\n <a-tooltip\n :title=\"\n field.isLeaf\n ? field.option.label + '(' + field.option.field + ')'\n : field.option.label\n \"\n >\n <span class=\"pivot-field-label\">\n {{ field.isLeaf ? `${field.option.label}(${field.option.field})` : field.option.label }}\n </span>\n </a-tooltip>\n <span\n v-if=\"field.isLeaf && isFieldInSeriesOrCategory(field.option.field)\"\n class=\"pivot-field-tag\"\n >\n {{ getFieldLocation(field.option.field) }}\n </span>\n </div>\n <div\n v-if=\"availableFieldRows.length === 0\"\n class=\"pivot-field-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 拖放区域 -->\n <div class=\"pivot-drop-zones\">\n <!-- 轴(类别)区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'category' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('category', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('category', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <bars-outlined />\n {{ $t('轴(类别)') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为X轴(类别)维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.category\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'category' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'category' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'category' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'category')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('category', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('category', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('category', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'category' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'category' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'category', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('category', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('category', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('category', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.category.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 图例(系列)区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'series' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('series', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('series', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <swap-outlined />\n {{ $t('图例(系列)') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为图例(系列)维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.series\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'series' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'series' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'series' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'series')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('series', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('series', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('series', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'series' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'series' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'series', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('series', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('series', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('series', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.series.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 值区域 -->\n <div\n class=\"pivot-drop-zone pivot-drop-zone-values\"\n :class=\"{ 'drag-over': dragOverZone === 'values' }\"\n @dragover.prevent=\"onDragOver('values')\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onValueDrop\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <calculator-outlined />\n {{ $t('值') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为值,双击字段可快速添加')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(item, index) in localConfig.values\"\n :key=\"item.id\"\n class=\"pivot-zone-item pivot-value-item\"\n :class=\"{\n 'drag-before': dragOverZone === 'values' && dragInsertIndex === index,\n 'drag-after': dragOverZone === 'values' && dragInsertIndex === index + 1,\n 'value-item-invalid': valueExprInvalid(item),\n }\"\n draggable=\"true\"\n @dragstart=\"onValueDragStart($event, item)\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onValueItemDragOver(index, $event)\"\n >\n <div class=\"value-item-header\">\n <DebouncedInput\n v-model:value=\"item.title\"\n size=\"small\"\n style=\"width: 100px\"\n :placeholder=\"$t('显示名称')\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onOpenValueConfig(item)\"\n >\n 高级配置\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeValue(index)\"\n />\n </div>\n <div class=\"value-item-content\">\n <valueFieldDisplay\n :valueField=\"item\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div class=\"value-item-content\">\n <a-select\n v-model:value=\"item.aggregationType\"\n size=\"small\"\n style=\"width: 90px\"\n :options=\"aggregationOptions\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"item.formatFun\"\n size=\"small\"\n style=\"width: 140px\"\n :options=\"valueFormatOptions\"\n :placeholder=\"$t('显示格式')\"\n :allow-clear=\"true\"\n @change=\"onFormatFunChange(item)\"\n @click.stop\n />\n <!-- 通用数字格式:可选预设格式串,也可自定义输入(numfmt / Excel 标准格式) -->\n <div\n v-if=\"item.formatFun === NUMFMT_FORMAT_KEY\"\n class=\"value-item-content\"\n >\n <a-auto-complete\n v-model:value=\"item.formatPattern\"\n size=\"small\"\n style=\"width: 234px\"\n :options=\"numfmtPatternOptions\"\n :placeholder=\"$t('数据格式,如 #,##0.00')\"\n @change=\"onPatternChange\"\n @click.stop\n />\n </div>\n </div>\n </div>\n <div\n v-if=\"localConfig.values.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 子表达式区域:可复用表达式片段,供任意值字段的表达式/过滤条件引用 -->\n <div class=\"pivot-drop-zone pivot-drop-zone-subexpr\">\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <function-outlined />\n {{ $t('子表达式') }}\n </span>\n <div class=\"header-item\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onAddSubExpr\"\n >\n {{ $t('添加子表达式') }}\n </a-button>\n <a-tooltip\n :title=\"\n $t(\n '子表达式是可复用的表达式片段,可在值字段表达式/过滤条件中以名称引用,后端递归内联展开',\n )\n \"\n >\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n </div>\n <div class=\"pivot-zone-items subexpr-items\">\n <div\n v-for=\"(se, i) in localConfig.subExpressions\"\n :key=\"se.id\"\n class=\"pivot-subexpr-card\"\n >\n <div class=\"subexpr-card-head\">\n <span class=\"subexpr-card-name\">{{ se.name || $t('(未命名)') }}</span>\n <span class=\"subexpr-card-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onEditSubExpr(i)\"\n >\n {{ $t('编辑') }}\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeSubExpr(i)\"\n />\n </span>\n </div>\n <valueFieldDisplay\n :valueField=\"subExprAsValueField(se)\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div\n v-if=\"!localConfig.subExpressions.length\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('暂无子表达式,点击「添加子表达式」创建可复用的表达式片段') }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 图表类型选择 -->\n <div class=\"pivot-options\">\n <a-form\n layout=\"inline\"\n size=\"small\"\n >\n <a-form-item :label=\"$t('图表类型')\">\n <a-radio-group\n v-model:value=\"localConfig.chartType\"\n @change=\"onConfigChange\"\n >\n <a-radio-button value=\"bar\">{{ $t('柱状图') }}</a-radio-button>\n <a-radio-button value=\"line\">{{ $t('折线图') }}</a-radio-button>\n <a-radio-button value=\"area\">{{ $t('面积图') }}</a-radio-button>\n <a-radio-button value=\"pie\">{{ $t('饼图') }}</a-radio-button>\n <a-radio-button value=\"donut\">{{ $t('圆环图') }}</a-radio-button>\n </a-radio-group>\n </a-form-item>\n <a-form-item\n v-if=\"localConfig.chartType === 'pie' || localConfig.chartType === 'donut'\"\n :label=\"$t('显示百分比')\"\n >\n <a-switch\n v-model:checked=\"localConfig.showPercent\"\n @change=\"onConfigChange\"\n />\n </a-form-item>\n </a-form>\n </div>\n\n <!-- 子表达式编辑弹窗 -->\n <a-modal\n v-model:open=\"subExprModalOpen\"\n :title=\"editingIndex >= 0 ? $t('编辑子表达式') : $t('添加子表达式')\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n @ok=\"onSubExprOk\"\n @cancel=\"subExprModalOpen = false\"\n >\n <sub-expression-editor\n v-if=\"subExprModalOpen\"\n v-model=\"editingSubExpr\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n <template #footer>\n <a-button\n type=\"primary\"\n :disabled=\"!editingSubExpr.name || !editingSubExpr.name.trim()\"\n @click=\"onSubExprOk\"\n >\n {{ $t('确定') }}\n </a-button>\n <a-button @click=\"subExprModalOpen = false\">\n {{ $t('取消') }}\n </a-button>\n </template>\n </a-modal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n DatabaseOutlined,\n SwapOutlined,\n BarsOutlined,\n CalculatorOutlined,\n CloseOutlined,\n InfoCircleOutlined,\n FunctionOutlined,\n} from '@ant-design/icons-vue';\nimport { cloneDeep, debounce } from 'lodash';\nimport { computed, reactive, ref, watch } from 'vue';\nimport {\n Column,\n PivotChartsConfig,\n PivotFieldConfig,\n PivotFieldOption,\n PivotValueField,\n PivotSubExpression,\n SettingMode,\n} from '../typing';\nimport {\n VALUE_FORMATS,\n PIVOT_FIELD_PROCESS_FUNCS,\n NUMFMT_FORMAT_KEY,\n NUMFMT_PATTERN_PRESETS,\n DEFAULT_NUMFMT_PATTERN,\n normalizeValueFormat,\n} from '../func';\nimport { genID } from 'liyu-pc-base/utils/function';\nimport { combineLabel, validateValueExpression } from '../utils';\nimport valueFieldDisplay from './valueFieldDisplay.vue';\nimport DebouncedInput from './DebouncedInput.vue';\nimport subExpressionEditor from './subExpressionEditor.vue';\ninterface Props {\n columns: Column[];\n modelValue?: PivotChartsConfig;\n settingMode: SettingMode;\n}\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: PivotChartsConfig): void;\n (e: 'change', value: PivotChartsConfig): void;\n (e: 'openValueConfig', value: PivotValueField): void;\n}>();\n\nconst searchText = ref('');\n\n// 拖拽负载(判别联合)\ntype DragPayload =\n | { kind: 'pool'; field: PivotFieldOption }\n | { kind: 'group'; zone: 'series' | 'category'; groupId: string }\n | { kind: 'field'; zone: 'series' | 'category'; groupId: string; fieldIndex: number }\n | { kind: 'value'; field: PivotValueField };\nconst dragData = ref<DragPayload | null>(null);\nconst dragOverZone = ref<string>('');\nconst dragOverGroupZone = ref<string>('');\nconst dragOverGroupIndex = ref<number>(-1);\nconst dragInsertIndex = ref<number>(-1); // 值区域拖拽插入位置索引\n// 组内字段重排指示:拖拽字段经过某 chip 时,记录插入位置(前/后)\nconst dragFieldTarget = ref<{\n zone: string;\n groupIndex: number;\n fieldIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n// 组间插入指示:拖拽到组外(组间间隙)新建/插入/移动维度组时,在相邻组的上/下边框高亮\nconst dragGroupInsert = ref<{\n zone: string;\n refIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n\n// 维度组「显示名」是否为用户手动自定义:自定义名在字段增删/重排时不被自动覆盖\nconst customLabels = reactive(new Set<string>());\n\n// 重建 custom 标记:外部/旧配置中已带 label 的组视为自定义(保留),不调用\n// defaultGroupLabel 以避免 setup 阶段访问尚未初始化的依赖。\nconst syncCustomFlags = (config: PivotChartsConfig) => {\n customLabels.clear();\n const dims = [...(config.series || []), ...(config.category || [])];\n dims.forEach(g => {\n if (g.id && g.label) customLabels.add(g.id);\n });\n};\n\n// 用户手动输入显示名:标记为自定义,后续字段变化不再自动覆盖\nconst onLabelInput = (group: PivotFieldConfig) => {\n if (group.id) customLabels.add(group.id);\n onConfigChange();\n};\n\nconst defaultConfig: PivotChartsConfig = {\n series: [],\n category: [],\n values: [],\n chartType: 'bar',\n showPercent: false,\n};\n\nconst localConfig = ref<PivotChartsConfig>(cloneDeep(props.modelValue || defaultConfig));\n\n// 兼容旧数据:把「单字段」维度 {field,label,processFunc} 归一为「维度组」{id,fields:[...],linkType}\nconst normalizeDim = (d: any): PivotFieldConfig => {\n if (d && Array.isArray(d.fields)) {\n return { ...d, id: d.id || genID() };\n }\n return {\n id: d?.id || genID(),\n fields: [{ field: d?.field, label: d?.label, processFunc: d?.processFunc }],\n label: d?.label, // 旧单字段配置的显示名提升到组级(兼容旧数据)\n linkType: d?.linkType || '_',\n sortOrder: d?.sortOrder,\n sortBy: d?.sortBy,\n sortProcessFunc: d?.sortProcessFunc,\n };\n};\n// 确保每个值字段都有唯一 id(兼容旧数据)\nconst ensureValueIds = (config: PivotChartsConfig) => {\n config.values.forEach(v => {\n if (!v.id && v.field) {\n v.id = `${v.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;\n }\n // 旧格式 key('0.00' 等)归一为「通用数字格式 + formatPattern」\n normalizeValueFormat(v);\n });\n};\nconst normalizeConfig = (config: PivotChartsConfig) => {\n config.series = (config.series || []).map(normalizeDim);\n config.category = (config.category || []).map(normalizeDim);\n if (!Array.isArray(config.subExpressions)) config.subExpressions = [];\n ensureValueIds(config);\n};\nnormalizeConfig(localConfig.value);\nsyncCustomFlags(localConfig.value);\n\n// 自身 emit 引起的 modelValue 回灌无需再 cloneDeep+重渲染(localConfig 已是最新),\n// 用 skipWatch 跳过本次,避免一次 drop 触发两次整面板重渲染导致的卡顿。\nlet skipWatch = false;\n\nwatch(\n () => props.modelValue,\n val => {\n if (skipWatch) {\n skipWatch = false;\n return;\n }\n if (val) {\n const cloned = cloneDeep(val);\n normalizeConfig(cloned);\n syncCustomFlags(cloned);\n localConfig.value = cloned;\n }\n },\n { deep: true },\n);\n\n// 所有可用字段列表\nconst allFields = computed<PivotFieldOption[]>(() => {\n const buildTree = (cols: Column[]): PivotFieldOption[] => {\n return cols\n .map(col => {\n const label = col.title || col.dataIndex || col.key || '';\n const children = col.children?.length ? buildTree(col.children) : [];\n const isLeaf = children.length === 0;\n if (isLeaf) {\n if (!col.dataIndex || col.dataIndex === 'index' || col.dataIndex === 'action') {\n return null;\n }\n return {\n field: col.dataIndex,\n label,\n };\n }\n return {\n field: col.dataIndex || col.key || label,\n label,\n children,\n };\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n return buildTree(props.columns || []);\n});\n\nconst flatAllFields = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(allFields.value);\n});\n\nconst availableFieldTree = computed<PivotFieldOption[]>(() => {\n const filterTree = (fields: PivotFieldOption[], search: string): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(search) || field.field.toLowerCase().includes(search);\n const children = field.children?.length ? filterTree(field.children, search) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n const search = searchText.value.trim().toLowerCase();\n if (!search) return allFields.value;\n return filterTree(allFields.value, search);\n});\n\nconst availableFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(availableFieldTree.value);\n return list;\n});\n\n// 检查字段是否已在任意系列/类别维度组中\nconst isFieldInSeriesOrCategory = (field: string) => {\n return (\n localConfig.value.series.some(g => g.fields.some(f => f.field === field)) ||\n localConfig.value.category.some(g => g.fields.some(f => f.field === field))\n );\n};\n\nconst getFieldLocation = (field: string) => {\n if (localConfig.value.series.some(g => g.fields.some(f => f.field === field))) return '系列';\n if (localConfig.value.category.some(g => g.fields.some(f => f.field === field))) return '轴';\n return '';\n};\n\nconst aggregationOptions = [\n { label: '求和', value: 'SUM' },\n { label: '计数', value: 'COUNTA' },\n { label: '数值计数', value: 'COUNT' },\n { label: '平均值', value: 'AVG' },\n { label: '最小值', value: 'MIN' },\n { label: '最大值', value: 'MAX' },\n { label: '乘积', value: 'PRODUCT' },\n { label: '标准偏差', value: 'STD' },\n { label: '总体标准偏差', value: 'POPSTD' },\n { label: '方差', value: 'VAR' },\n { label: '总体方差', value: 'POPVAR' },\n { label: '无', value: 'NONE' },\n];\n\nconst valueFormatOptions = computed(() =>\n VALUE_FORMATS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 通用数字格式的常用格式串预设(a-auto-complete 选项,支持自定义输入)\nconst numfmtPatternOptions = NUMFMT_PATTERN_PRESETS.map(p => ({\n value: p.value,\n label: p.label || p.value,\n}));\n\n// 切换显示格式:选中「通用数字格式」且尚无格式串时填入缺省值\nconst onFormatFunChange = (item: PivotValueField) => {\n if (item.formatFun === NUMFMT_FORMAT_KEY && !item.formatPattern) {\n item.formatPattern = DEFAULT_NUMFMT_PATTERN;\n }\n onValueConfigChange();\n};\n\n// 格式串输入防抖:避免逐字符输入触发接口调用\nconst onPatternChange = debounce(() => onValueConfigChange(), 500);\n\n// 系列/类别维度「数据处理函数」下拉选项(来自 func.ts 注册表)\nconst processFuncOptions = computed(() =>\n PIVOT_FIELD_PROCESS_FUNCS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 维度组「连接符」下拉选项:'_'(默认)/ '-' / '()'\nconst linkTypeOptions = [\n { label: '_', value: '_' },\n { label: '-', value: '-' },\n { label: '( )', value: '()' },\n];\n\n// 维度「排序方向」下拉选项\nconst sortOrderOptions = [\n { label: '升序', value: 'asc' },\n { label: '降序', value: 'desc' },\n];\n\n// 维度组「排序依据字段」下拉选项:先列出本组各字段(按原始 field 名,Rust 侧已为组内\n// 每个字段登记单字段解析列,sort_by 指向该 field 即按「组内该字段」排序),再列出其余所有\n// 可用字段(含值字段、其它维度、未参与维度但存在于数据中的字段)。\n// 与 Rust 侧 resolve_sort 对齐:sort_by 现可解析任意存在于数据中的字段。\nconst getSortByOptions = (group: PivotFieldConfig) => {\n const own = group.fields.map(f => ({\n label: `${fieldLabel(f.field)}(${f.field})`,\n value: f.field,\n }));\n const ownSet = new Set(group.fields.map(f => f.field));\n const others = flatAllFields.value\n .filter(f => !ownSet.has(f.field))\n .map(f => ({ label: `${f.label}(${f.field})`, value: f.field }));\n return [...own, ...others];\n};\n\n// 排序依据字段下拉的搜索过滤(按 label 模糊匹配)\nconst filterFieldOption = (input: string, option: { label?: string }) =>\n (option?.label ?? '').toLowerCase().includes(input.toLowerCase());\n\n// 排序依据字段变化:清空后同步清除其处理函数(处理函数依赖排序依据字段存在)\nconst onGroupSortByChange = (group: PivotFieldConfig) => {\n if (!group.sortBy) {\n group.sortProcessFunc = undefined;\n }\n onConfigChange();\n};\n\n// 维度组默认显示名:按字段名以下划线连接(缺省值,用户可在配置中覆盖 group.label)\nconst defaultGroupLabel = (group: PivotFieldConfig): string => {\n if (!group.fields.length) return '';\n const labelList = group.fields.map(f => f.label || fieldLabel(f.field));\n return combineLabel(labelList, group.linkType);\n};\n\n// 字段显示名:优先用字段列表中的 title,缺省回退到字段名\nconst fieldLabel = (field: string): string => {\n const f = flatAllFields.value.find(x => x.field === field);\n return f ? f.label : field;\n};\n\n// ===== 拖拽 =====\nconst onDragStart = (event: DragEvent, field: PivotFieldOption) => {\n dragData.value = { kind: 'pool', field };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', field.field);\n }\n};\n\nconst onGroupDragStart = (\n event: DragEvent,\n group: PivotFieldConfig,\n zone: 'series' | 'category',\n) => {\n dragData.value = { kind: 'group', zone, groupId: group.id! };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onFieldDragStart = (\n event: DragEvent,\n group: PivotFieldConfig,\n zone: 'series' | 'category',\n fieldIndex: number,\n) => {\n dragData.value = { kind: 'field', zone, groupId: group.id!, fieldIndex };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onDragEnd = () => clearDrag();\n\nconst clearDrag = () => {\n dragData.value = null;\n dragOverZone.value = '';\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragInsertIndex.value = -1;\n dragFieldTarget.value = null;\n dragGroupInsert.value = null;\n};\n\nconst onDragOver = (zone: string) => {\n dragOverZone.value = zone;\n dragFieldTarget.value = null;\n};\n\nconst onDragLeave = () => {\n // 只在离开整个区域时清除\n};\n\nconst onGroupDragOver = (zone: 'series' | 'category', index: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n event.stopPropagation();\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';\n\n if (d.kind === 'group') {\n // 拖拽整组进行排序:始终显示单边框插入指示(上/下),不显示 4 边框高亮。\n // 悬停在自身组上:不显示插入指示(原地不动)。\n const groupId = localConfig.value[zone][index]?.id;\n const overSelf = d.zone === zone && d.groupId === groupId;\n let nextIns: { zone: 'series' | 'category'; refIndex: number; pos: 'before' | 'after' } | null =\n null;\n if (!overSelf) {\n // 指针在组的上半区→插入到该组之前(高亮上边框);下半区→之后(高亮下边框)\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n nextIns = { zone, refIndex: index, pos: before ? 'before' : 'after' };\n }\n applyGroupInsert(nextIns);\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n dragOverZone.value = '';\n return;\n }\n\n // 其它情况(字段 / 池字段):悬停整组内部 → 4 边框高亮(把字段添加进该组)\n dragOverGroupZone.value = zone;\n dragOverGroupIndex.value = index;\n dragFieldTarget.value = null; // 悬停整组(非具体字段)时清除字段级指示\n dragGroupInsert.value = null; // 悬停整组(内部)时清除组间插入指示\n dragOverZone.value = ''; // 悬停整组(内部)时清除整区高亮,避免与组内 4 边框叠加\n};\n\nconst findGroupIndex = (zone: 'series' | 'category', groupId: string) =>\n localConfig.value[zone].findIndex(g => g.id === groupId);\n\n// 拖到区域空白:池字段→新建维度组;组→移到该区域末尾;字段→从原组移除\nconst onDropZone = (zone: 'series' | 'category') => {\n const d = dragData.value;\n if (!d) return;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].push(ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n localConfig.value[zone].push(g);\n }\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n localConfig.value[d.zone][srcIdx].fields.splice(d.fieldIndex, 1);\n if (localConfig.value[d.zone][srcIdx].fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖到具体维度组:池字段→追加;字段→跨组移动;组→组内重排 / 跨区移动\nconst onDropOnGroup = (zone: 'series' | 'category', gIndex: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) return;\n\n if (d.kind === 'pool') {\n targetGroup.fields.push({ field: d.field.field, label: d.field.label });\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n targetGroup.fields.push(field);\n }\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n let tIdx = gIndex;\n if (d.zone === zone && srcIdx < gIndex) tIdx -= 1;\n localConfig.value[zone].splice(before ? tIdx : tIdx + 1, 0, g);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 区域级拖拽悬浮:把探测范围提升到 .pivot-drop-zone(覆盖整片区域),\n// 既设置整区高亮(dragOverZone),又按指针相对各组位置计算「组间插入」指示\n// (dragGroupInsert)。关键点:第一组上方与末组下方的留白属于区域 padding/header,\n// 原本不在 .pivot-zone-items 内,导致那两块区域只显示整区边框、且 drop 落到末尾;\n// 现在由本函数统一探测,使「第一组上方」→ 高亮组0上边框并插入到首个位置,\n// 「末组下方」→ 高亮末组下边框并插入到末尾。组内拖拽由 .pivot-dim-group 的\n// .stop 拦截,仍显示 4 边框(添加字段)。\nconst onZoneDragOver = (zone: 'series' | 'category', event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n onDragOver(zone); // 整区高亮\n computeGroupInsert(zone, event);\n};\n\n// 计算「组间插入」指示:第一组上方→before(0);末组下方→after(末);组间间隙→after(i);\n// 命中组内部由 .stop 拦截不会进入本函数,故此处无需处理组内情况。\n// 仅在插入目标(zone/refIndex/pos)真正变化时才更新 dragGroupInsert,\n// 避免拖拽过程中每个 dragover 都触发整面板重渲染(卡顿来源之一)。\nconst applyGroupInsert = (\n next: { zone: 'series' | 'category'; refIndex: number; pos: 'before' | 'after' } | null,\n) => {\n const cur = dragGroupInsert.value;\n if (\n cur &&\n next &&\n cur.zone === next.zone &&\n cur.refIndex === next.refIndex &&\n cur.pos === next.pos\n )\n return;\n if (!cur && !next) return;\n dragGroupInsert.value = next;\n};\n\nconst computeGroupInsert = (zone: 'series' | 'category', event: DragEvent) => {\n const d = dragData.value;\n if (!d) {\n applyGroupInsert(null);\n return;\n }\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n const container = event.currentTarget as HTMLElement;\n const groups = Array.from(container.querySelectorAll('.pivot-dim-group')) as HTMLElement[];\n if (groups.length === 0) {\n applyGroupInsert(null); // 无组(空白区)由区域层级高亮处理\n return;\n }\n const y = event.clientY;\n const firstTop = groups[0].getBoundingClientRect().top;\n if (y < firstTop) {\n applyGroupInsert({ zone, refIndex: 0, pos: 'before' });\n return;\n }\n const lastBottom = groups[groups.length - 1].getBoundingClientRect().bottom;\n if (y > lastBottom) {\n applyGroupInsert({ zone, refIndex: groups.length - 1, pos: 'after' });\n return;\n }\n for (let i = 0; i < groups.length - 1; i++) {\n const top = groups[i].getBoundingClientRect();\n const nxt = groups[i + 1].getBoundingClientRect();\n if (y >= top.bottom && y <= nxt.top) {\n // 落在组 i 与组 i+1 之间:在组 i 之后插入(高亮组 i 下边框)\n applyGroupInsert({ zone, refIndex: i, pos: 'after' });\n return;\n }\n }\n applyGroupInsert(null);\n};\n\n// 在组间间隙释放:新建(池字段)/ 插入(整组重排)/ 移出(字段独立成组)维度组到对应位置。\n// 未命中组间插入目标(如空白区)时回退到区域空白逻辑。\nconst onZoneItemsDrop = (zone: 'series' | 'category', _event: DragEvent) => {\n const d = dragData.value;\n const ins = dragGroupInsert.value;\n if (!d || !ins || ins.zone !== zone) {\n onDropZone(zone);\n return;\n }\n let insIndex = ins.pos === 'before' ? ins.refIndex : ins.refIndex + 1;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].splice(insIndex, 0, ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n if (d.zone === zone && srcIdx < insIndex) insIndex -= 1;\n localConfig.value[zone].splice(insIndex, 0, g);\n }\n } else if (d.kind === 'field') {\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < insIndex) insIndex -= 1;\n }\n const ng: PivotFieldConfig = { id: genID(), fields: [field], linkType: '_' };\n localConfig.value[zone].splice(insIndex, 0, ng);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖拽字段经过组内某个 chip:记录插入位置(上边框=前 / 下边框=后),用于高亮提示\nconst onChipDragOver = (\n zone: 'series' | 'category',\n gIndex: number,\n fIndex: number,\n event: DragEvent,\n) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 仅字段重排时处理,其它拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n dragGroupInsert.value = null; // 悬停具体字段 chip 时清除组间插入指示\n const group = localConfig.value[zone][gIndex];\n if (!group) return;\n // 悬停在被拖拽的源字段自身上时,不显示指示线(避免原地误判)\n if (d.zone === zone && d.groupId === group.id && d.fieldIndex === fIndex) {\n dragFieldTarget.value = null;\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n dragFieldTarget.value = {\n zone,\n groupIndex: gIndex,\n fieldIndex: fIndex,\n pos: before ? 'before' : 'after',\n };\n};\n\n// 在组内某字段的上/下边界释放:把被拖字段插入到对应位置(支持组内重排与跨组定位)\nconst onChipDrop = (\n zone: 'series' | 'category',\n gIndex: number,\n fIndex: number,\n event: DragEvent,\n) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 非字段拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) {\n clearDrag();\n return;\n }\n // 直接落在源字段自身:视为无移动\n if (d.zone === zone && d.groupId === targetGroup.id && d.fieldIndex === fIndex) {\n clearDrag();\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx < 0) {\n clearDrag();\n return;\n }\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n\n const sameGroup = d.zone === zone && srcGroupIdx === gIndex;\n let targetGroupIdx = gIndex;\n let insertAt = before ? fIndex : fIndex + 1;\n // 同组内、源在目标之前时被移除会导致下标左移一位\n if (sameGroup && d.fieldIndex < fIndex) insertAt -= 1;\n\n // 跨组移动且源组已空:移除源组,并按需要修正目标组下标\n if (srcGroup.fields.length === 0 && !sameGroup) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < targetGroupIdx) targetGroupIdx -= 1;\n }\n localConfig.value[zone][targetGroupIdx].fields.splice(insertAt, 0, field);\n clearDrag();\n onConfigChange();\n};\n\n// 值区域拖放:支持从字段池拖入新增值,以及值字段内部重排\nconst onDropValues = () => {\n const d = dragData.value;\n if (d && d.kind === 'pool') {\n addToValues(d.field);\n }\n clearDrag();\n};\n\n// 值字段拖拽排序\nconst onValueDragStart = (event: DragEvent, item: PivotValueField) => {\n dragData.value = { kind: 'value', field: item };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', item.id);\n }\n};\n\nconst onValueItemDragOver = (index: number, event: DragEvent) => {\n dragOverZone.value = 'values';\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n if (event.clientY < midpoint) {\n dragInsertIndex.value = index;\n } else {\n dragInsertIndex.value = index + 1;\n }\n};\n\nconst onValueDrop = () => {\n const d = dragData.value;\n if (!d) return;\n\n // 从字段池拖入:创建新值\n if (d.kind === 'pool') {\n const insertIndex =\n dragInsertIndex.value >= 0 ? dragInsertIndex.value : localConfig.value.values.length;\n const valueField: PivotValueField = {\n id: `${d.field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: d.field.field,\n label: d.field.label,\n aggregationType: 'SUM',\n title: d.field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.splice(insertIndex, 0, valueField);\n clearDrag();\n onConfigChange();\n return;\n }\n\n // 值区域内重排\n if (d.kind !== 'value') return;\n const list = localConfig.value.values;\n const fromIndex = list.findIndex(f => f.id === (d as any).field.id);\n if (fromIndex === -1) {\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n return;\n }\n let toIndex = dragInsertIndex.value;\n if (toIndex < 0) toIndex = list.length;\n if (toIndex > fromIndex) toIndex -= 1;\n if (toIndex >= list.length) toIndex = list.length - 1;\n if (toIndex < 0) toIndex = 0;\n const [item] = list.splice(fromIndex, 1);\n list.splice(toIndex, 0, item);\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n onConfigChange();\n};\n\n// 双击字段快速添加到值区域(允许重复添加,不限制是否已在系列/类别中)\nconst addToValues = (field: PivotFieldOption) => {\n const valueField: PivotValueField = {\n id: `${field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: field.field,\n label: field.label,\n aggregationType: 'SUM',\n title: field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.push(valueField);\n onConfigChange();\n};\n\n// 移除整个维度组\nconst removeGroup = (zone: 'series' | 'category', index: number) => {\n localConfig.value[zone].splice(index, 1);\n onConfigChange();\n};\n\n// 从维度组移除单个字段;若组变空则一并移除该组\nconst removeFieldFromGroup = (zone: 'series' | 'category', gIndex: number, fIndex: number) => {\n const g = localConfig.value[zone][gIndex];\n g.fields.splice(fIndex, 1);\n if (g.fields.length === 0) {\n localConfig.value[zone].splice(gIndex, 1);\n }\n onConfigChange();\n};\n\n// 移除值字段\nconst removeValue = (index: number) => {\n localConfig.value.values.splice(index, 1);\n onConfigChange();\n};\n\nconst onValueConfigChange = () => {\n onConfigChange();\n};\n\n// ===== 子表达式(可复用表达式片段)管理 =====\nconst subExprModalOpen = ref(false);\nconst editingIndex = ref(-1); // >=0 表示编辑已有项,-1 表示新增\nconst editingSubExpr = ref<PivotSubExpression>({ id: '', name: '', expression: [] });\n\n// 把子表达式映射为一个「伪值字段」,复用 valueFieldDisplay 渲染其表达式预览\nconst subExprAsValueField = (se: PivotSubExpression): PivotValueField => ({\n id: se.id,\n field: '',\n aggregationType: 'NONE',\n useExpression: true,\n expression: se.expression || [],\n filter: [],\n});\n\nconst onAddSubExpr = () => {\n editingIndex.value = -1;\n editingSubExpr.value = { id: genID(), name: '', expression: [] };\n subExprModalOpen.value = true;\n};\nconst onEditSubExpr = (i: number) => {\n editingIndex.value = i;\n editingSubExpr.value = cloneDeep(localConfig.value.subExpressions[i]);\n subExprModalOpen.value = true;\n};\nconst onSubExprOk = () => {\n const se = editingSubExpr.value;\n if (!se.name || !se.name.trim()) return;\n if (editingIndex.value >= 0) {\n localConfig.value.subExpressions[editingIndex.value] = cloneDeep(se);\n } else {\n localConfig.value.subExpressions.push(cloneDeep(se));\n }\n subExprModalOpen.value = false;\n onConfigChange();\n};\nconst removeSubExpr = (i: number) => {\n localConfig.value.subExpressions.splice(i, 1);\n onConfigChange();\n};\n\n// 值字段表达式是否非法(useExpression 且表达式非空但校验不通过时高亮标记)\nconst valueExprInvalid = (item: PivotValueField): boolean => {\n if (!item.useExpression || !item.expression || item.expression.length === 0) return false;\n return !validateValueExpression(item.expression).valid;\n};\n\nconst onConfigChange = () => {\n skipWatch = true; // 标记本次 emit 引起的 modelValue 回灌,watch 中应跳过\n const config = cloneDeep(localConfig.value);\n emit('update:modelValue', config);\n emit('change', config);\n};\nconst onOpenValueConfig = (valueField: PivotValueField) => {\n emit('openValueConfig', valueField);\n};\n\n// 供父级(tableConfig/index.vue)在「高级配置」抽屉内修改值字段后,触发一次完整配置回传\n// getSubExpressions:把当前(含本会话内新增/编辑的)子表达式列表暴露给 index.vue,\n// 使「高级配置」里的表达式编辑器能引用已配置的子表达式(不依赖上层 props 的实时回灌)。\ndefineExpose({\n forceEmit: onConfigChange,\n getSubExpressions: () => localConfig.value.subExpressions,\n});\n</script>\n\n<style lang=\"less\" scoped>\n.pivot-charts-config-wrapper {\n display: flex;\n flex-direction: column;\n gap: 12px;\n height: 100%;\n}\n\n.pivot-charts-config-main {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n}\n\n.pivot-field-list {\n width: 220px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .pivot-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n color: #333;\n }\n\n .pivot-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n\n .pivot-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n\n .pivot-field-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-field-tag {\n flex-shrink: 0;\n font-size: 10px;\n color: #fff;\n background: #1890ff;\n border-radius: 8px;\n padding: 1px 6px;\n line-height: 16px;\n }\n }\n\n .pivot-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n\n .pivot-field-icon {\n color: #bfbfbf;\n }\n }\n\n .pivot-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n }\n\n .pivot-field-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.pivot-drop-zones {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n overflow-y: auto;\n}\n\n.pivot-drop-zone {\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n padding: 8px;\n transition: all 0.2s;\n min-height: 80px;\n flex-shrink: 0;\n\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n .pivot-zone-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n\n .pivot-zone-title {\n font-weight: 600;\n font-size: 13px;\n color: #333;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .pivot-zone-tip {\n color: #999;\n font-size: 12px;\n cursor: help;\n }\n }\n\n .pivot-zone-items {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n min-height: 32px;\n }\n\n .pivot-zone-placeholder {\n color: #bbb;\n font-size: 12px;\n padding: 4px 8px;\n width: 100%;\n text-align: center;\n }\n}\n\n.pivot-zone-item {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n background: #fafafa;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n max-width: 100%;\n box-sizing: border-box;\n position: relative;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-zone-item-label {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-zone-item-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n margin-left: 2px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n}\n\n.pivot-value-item {\n background: #fff7e6;\n border-color: #ffd591;\n flex-wrap: wrap;\n width: 100%;\n max-width: 100%;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n cursor: grab;\n user-select: none;\n\n &:hover {\n border-color: #fa8c16;\n background: #fff7e6;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n // 表达式非法:红色高亮提示(与 valueFieldDisplay 内联告警互补)\n &.value-item-invalid {\n border-color: #ff4d4f;\n background: #fff1f0;\n }\n\n &.drag-before {\n box-shadow: 0 -2px 0 0 #1890ff;\n }\n\n &.drag-after {\n box-shadow: 0 2px 0 0 #1890ff;\n }\n\n .pivot-zone-item-label {\n flex-shrink: 0;\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n\n :deep(.ant-input) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n}\n\n.pivot-drop-zone-values {\n min-height: 80px;\n}\n\n.pivot-dim-group {\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n border-radius: 6px;\n padding: 6px 8px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 6px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n position: relative;\n\n &:hover {\n border-color: #52c41a;\n background: #f6ffed;\n }\n\n &.drag-over {\n border-color: #52c41a;\n box-shadow: 0 0 0 2px #52c41a inset;\n }\n\n // 组间插入指示线:在相邻组的上/下边框高亮(蓝),提示将在此处新建/插入维度组;\n // 与「组内 4 边框高亮(绿,添加字段到该组)」区分。\n &.drop-group-before::before,\n &.drop-group-after::after {\n content: '';\n position: absolute;\n left: -2px;\n right: -2px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n z-index: 2;\n }\n\n &.drop-group-before::before {\n top: -4px;\n }\n\n &.drop-group-after::after {\n bottom: -4px;\n }\n\n .pivot-dim-group-header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n\n .pivot-group-label-input {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .pivot-group-remove {\n color: #999;\n cursor: pointer;\n font-size: 11px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n\n .pivot-dim-group-fields {\n display: flex;\n flex-wrap: wrap;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n width: 100%;\n }\n\n .pivot-field-chip {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n background: #fff;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n position: relative;\n\n // 组内字段重排指示线:上/下边框高亮,提示释放后的插入位置\n &.drop-before::before,\n &.drop-after::after {\n content: '';\n position: absolute;\n left: -1px;\n right: -1px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n }\n\n &.drop-before::before {\n top: -3px;\n }\n\n &.drop-after::after {\n bottom: -3px;\n }\n\n &:hover {\n border-color: #52c41a;\n }\n\n .pivot-chip-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-chip-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 80px;\n }\n }\n\n .pivot-sort-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n width: 100%;\n padding-top: 6px;\n border-top: 1px dashed #d9d9d9;\n\n .pivot-sort-label {\n font-size: 11px;\n color: #999;\n flex-shrink: 0;\n }\n\n :deep(.ant-select) {\n flex: 0 1 auto;\n min-width: 0;\n }\n }\n}\n\n.pivot-options {\n flex-shrink: 0;\n padding: 8px 0;\n border-top: 1px solid #f0f0f0;\n}\n.value-item-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n gap: 6px;\n width: 100%;\n}\n.value-item-content {\n display: flex;\n flex-direction: row;\n gap: 6px;\n align-items: center;\n}\n\n.pivot-drop-zone-subexpr {\n background: #fafaff;\n}\n\n.subexpr-items {\n align-items: stretch;\n}\n\n.pivot-subexpr-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 10px;\n background: #fff;\n border: 1px solid #efd6ff;\n border-radius: 6px;\n min-width: 220px;\n max-width: 100%;\n\n .subexpr-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n\n .subexpr-card-name {\n font-weight: 600;\n font-size: 13px;\n color: #722ed1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .subexpr-card-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n }\n }\n}\n</style>\n","<template>\n <div class=\"pivot-charts-config-wrapper\">\n <div class=\"pivot-charts-config-main\">\n <!-- 字段列表区域 -->\n <div class=\"pivot-field-list\">\n <div class=\"pivot-section-title\">{{ $t('字段列表') }}</div>\n <a-input\n v-model:value=\"searchText\"\n :placeholder=\"$t('搜索字段')\"\n allow-clear\n size=\"small\"\n style=\"margin-bottom: 8px\"\n />\n <div class=\"pivot-field-pool\">\n <div\n v-for=\"field in availableFieldRows\"\n :key=\"field.key\"\n :class=\"['pivot-field-item', { 'pivot-field-group': !field.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + field.depth * 16}px` }\"\n :draggable=\"field.isLeaf\"\n @dragstart=\"field.isLeaf ? onDragStart($event, field.option) : undefined\"\n @dragend=\"field.isLeaf ? onDragEnd() : undefined\"\n @dblclick=\"field.isLeaf ? addToValues(field.option) : undefined\"\n >\n <span class=\"pivot-field-icon\">\n <database-outlined v-if=\"field.isLeaf\" />\n <span\n v-else\n class=\"pivot-field-branch-dot\"\n />\n </span>\n <a-tooltip\n :title=\"\n field.isLeaf\n ? field.option.label + '(' + field.option.field + ')'\n : field.option.label\n \"\n >\n <span class=\"pivot-field-label\">\n {{ field.isLeaf ? `${field.option.label}(${field.option.field})` : field.option.label }}\n </span>\n </a-tooltip>\n <span\n v-if=\"field.isLeaf && isFieldInSeriesOrCategory(field.option.field)\"\n class=\"pivot-field-tag\"\n >\n {{ getFieldLocation(field.option.field) }}\n </span>\n </div>\n <div\n v-if=\"availableFieldRows.length === 0\"\n class=\"pivot-field-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 拖放区域 -->\n <div class=\"pivot-drop-zones\">\n <!-- 轴(类别)区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'category' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('category', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('category', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <bars-outlined />\n {{ $t('轴(类别)') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为X轴(类别)维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.category\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'category' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'category' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'category' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'category')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('category', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('category', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('category', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'category' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'category' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'category', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('category', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('category', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('category', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.category.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 图例(系列)区域 -->\n <div\n class=\"pivot-drop-zone\"\n :class=\"{ 'drag-over': dragOverZone === 'series' && !dragGroupInsert }\"\n @dragover.prevent=\"onZoneDragOver('series', $event)\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onZoneItemsDrop('series', $event)\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <swap-outlined />\n {{ $t('图例(系列)') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为图例(系列)维度;拖入已有维度组可追加字段')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(group, gIndex) in localConfig.series\"\n :key=\"group.id\"\n class=\"pivot-dim-group\"\n :class=\"{\n 'drag-over': dragOverGroupZone === 'series' && dragOverGroupIndex === gIndex,\n 'drop-group-before':\n dragGroupInsert &&\n dragGroupInsert.zone === 'series' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'before',\n 'drop-group-after':\n dragGroupInsert &&\n dragGroupInsert.zone === 'series' &&\n dragGroupInsert.refIndex === gIndex &&\n dragGroupInsert.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart=\"onGroupDragStart($event, group, 'series')\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onGroupDragOver('series', gIndex, $event)\"\n @drop.stop=\"onDropOnGroup('series', gIndex, $event)\"\n >\n <div class=\"pivot-dim-group-header\">\n <DebouncedInput\n v-model:value=\"group.label\"\n size=\"small\"\n class=\"pivot-group-label-input\"\n :placeholder=\"defaultGroupLabel(group)\"\n :title=\"$t('显示名称(拖入字段后自动按字段标签下划线连接,可手动覆盖)')\"\n @change=\"onLabelInput(group)\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.linkType\"\n size=\"small\"\n style=\"width: 96px\"\n :options=\"linkTypeOptions\"\n :placeholder=\"$t('连接符')\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-group-remove\"\n @click.stop=\"removeGroup('series', gIndex)\"\n />\n </div>\n <div class=\"pivot-dim-group-fields\">\n <div\n v-for=\"(fld, fIndex) in group.fields\"\n :key=\"fld.field + '_' + fIndex\"\n class=\"pivot-field-chip\"\n :class=\"{\n 'drop-before':\n dragFieldTarget &&\n dragFieldTarget.zone === 'series' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'before',\n 'drop-after':\n dragFieldTarget &&\n dragFieldTarget.zone === 'series' &&\n dragFieldTarget.groupIndex === gIndex &&\n dragFieldTarget.fieldIndex === fIndex &&\n dragFieldTarget.pos === 'after',\n }\"\n draggable=\"true\"\n @dragstart.stop=\"onFieldDragStart($event, group, 'series', fIndex)\"\n @dragend=\"onDragEnd\"\n @dragover=\"onChipDragOver('series', gIndex, fIndex, $event)\"\n @drop=\"onChipDrop('series', gIndex, fIndex, $event)\"\n >\n <span\n class=\"pivot-chip-label\"\n :title=\"`${fld.label || fieldLabel(fld.field)}(${fld.field})`\"\n >\n {{ `${fld.label || fieldLabel(fld.field)}(${fld.field})` }}\n </span>\n <a-select\n v-model:value=\"fld.processFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <close-outlined\n class=\"pivot-chip-remove\"\n @click.stop=\"removeFieldFromGroup('series', gIndex, fIndex)\"\n />\n </div>\n </div>\n <div class=\"pivot-sort-row\">\n <span class=\"pivot-sort-label\">{{ $t('排序') }}</span>\n <a-select\n v-model:value=\"group.sortOrder\"\n size=\"small\"\n style=\"width: 84px\"\n :options=\"sortOrderOptions\"\n :placeholder=\"$t('升序')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"group.sortBy\"\n size=\"small\"\n style=\"width: 170px\"\n show-search\n :filter-option=\"filterFieldOption\"\n :options=\"getSortByOptions(group)\"\n :placeholder=\"$t('排序依据字段')\"\n :allow-clear=\"true\"\n @change=\"onGroupSortByChange(group)\"\n @click.stop\n />\n <a-select\n v-if=\"group.sortBy\"\n v-model:value=\"group.sortProcessFunc\"\n size=\"small\"\n style=\"width: 160px\"\n :options=\"processFuncOptions\"\n :placeholder=\"$t('排序处理')\"\n :allow-clear=\"true\"\n @change=\"onConfigChange\"\n @click.stop\n />\n </div>\n </div>\n <div\n v-if=\"localConfig.series.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 值区域 -->\n <div\n class=\"pivot-drop-zone pivot-drop-zone-values\"\n :class=\"{ 'drag-over': dragOverZone === 'values' }\"\n @dragover.prevent=\"onDragOver('values')\"\n @dragleave=\"onDragLeave\"\n @drop.stop=\"onValueDrop\"\n >\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <calculator-outlined />\n {{ $t('值') }}\n </span>\n <a-tooltip :title=\"$t('拖拽字段到此处作为值,双击字段可快速添加')\">\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n <div class=\"pivot-zone-items\">\n <div\n v-for=\"(item, index) in localConfig.values\"\n :key=\"item.id\"\n class=\"pivot-zone-item pivot-value-item\"\n :class=\"{\n 'drag-before': dragOverZone === 'values' && dragInsertIndex === index,\n 'drag-after': dragOverZone === 'values' && dragInsertIndex === index + 1,\n 'value-item-invalid': valueExprInvalid(item),\n }\"\n draggable=\"true\"\n @dragstart=\"onValueDragStart($event, item)\"\n @dragend=\"onDragEnd\"\n @dragover.prevent.stop=\"onValueItemDragOver(index, $event)\"\n >\n <div class=\"value-item-header\">\n <DebouncedInput\n v-model:value=\"item.title\"\n size=\"small\"\n style=\"width: 100px\"\n :placeholder=\"$t('显示名称')\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onOpenValueConfig(item)\"\n >\n 高级配置\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeValue(index)\"\n />\n </div>\n <div class=\"value-item-content\">\n <valueFieldDisplay\n :valueField=\"item\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div class=\"value-item-content\">\n <a-select\n v-model:value=\"item.aggregationType\"\n size=\"small\"\n style=\"width: 90px\"\n :options=\"aggregationOptions\"\n @change=\"onValueConfigChange\"\n @click.stop\n />\n <a-select\n v-model:value=\"item.formatFun\"\n size=\"small\"\n style=\"width: 140px\"\n :options=\"valueFormatOptions\"\n :placeholder=\"$t('显示格式')\"\n :allow-clear=\"true\"\n @change=\"onFormatFunChange(item)\"\n @click.stop\n />\n <!-- 通用数字格式:可选预设格式串,也可自定义输入(numfmt / Excel 标准格式) -->\n <div\n v-if=\"item.formatFun === NUMFMT_FORMAT_KEY\"\n class=\"value-item-content\"\n >\n <a-auto-complete\n v-model:value=\"item.formatPattern\"\n size=\"small\"\n style=\"width: 234px\"\n :options=\"numfmtPatternOptions\"\n :placeholder=\"$t('数据格式,如 #,##0.00')\"\n @change=\"onPatternChange\"\n @click.stop\n />\n </div>\n </div>\n </div>\n <div\n v-if=\"localConfig.values.length === 0\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('拖拽字段到此处') }}\n </div>\n </div>\n </div>\n\n <!-- 子表达式区域:可复用表达式片段,供任意值字段的表达式/过滤条件引用 -->\n <div class=\"pivot-drop-zone pivot-drop-zone-subexpr\">\n <div class=\"pivot-zone-header\">\n <span class=\"pivot-zone-title\">\n <function-outlined />\n {{ $t('子表达式') }}\n </span>\n <div class=\"header-item\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onAddSubExpr\"\n >\n {{ $t('添加子表达式') }}\n </a-button>\n <a-tooltip\n :title=\"\n $t(\n '子表达式是可复用的表达式片段,可在值字段表达式/过滤条件中以名称引用,后端递归内联展开',\n )\n \"\n >\n <info-circle-outlined class=\"pivot-zone-tip\" />\n </a-tooltip>\n </div>\n </div>\n <div class=\"pivot-zone-items subexpr-items\">\n <div\n v-for=\"(se, i) in localConfig.subExpressions\"\n :key=\"se.id\"\n class=\"pivot-subexpr-card\"\n >\n <div class=\"subexpr-card-head\">\n <span class=\"subexpr-card-name\">{{ se.name || $t('(未命名)') }}</span>\n <span class=\"subexpr-card-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"onEditSubExpr(i)\"\n >\n {{ $t('编辑') }}\n </a-button>\n <close-outlined\n class=\"pivot-zone-item-remove\"\n @click=\"removeSubExpr(i)\"\n />\n </span>\n </div>\n <valueFieldDisplay\n :valueField=\"subExprAsValueField(se)\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n </div>\n <div\n v-if=\"!localConfig.subExpressions.length\"\n class=\"pivot-zone-placeholder\"\n >\n {{ $t('暂无子表达式,点击「添加子表达式」创建可复用的表达式片段') }}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 图表类型选择 -->\n <div class=\"pivot-options\">\n <a-form\n layout=\"inline\"\n size=\"small\"\n >\n <a-form-item :label=\"$t('图表类型')\">\n <a-radio-group\n v-model:value=\"localConfig.chartType\"\n @change=\"onConfigChange\"\n >\n <a-radio-button value=\"bar\">{{ $t('柱状图') }}</a-radio-button>\n <a-radio-button value=\"line\">{{ $t('折线图') }}</a-radio-button>\n <a-radio-button value=\"area\">{{ $t('面积图') }}</a-radio-button>\n <a-radio-button value=\"pie\">{{ $t('饼图') }}</a-radio-button>\n <a-radio-button value=\"donut\">{{ $t('圆环图') }}</a-radio-button>\n </a-radio-group>\n </a-form-item>\n <a-form-item\n v-if=\"localConfig.chartType === 'pie' || localConfig.chartType === 'donut'\"\n :label=\"$t('显示百分比')\"\n >\n <a-switch\n v-model:checked=\"localConfig.showPercent\"\n @change=\"onConfigChange\"\n />\n </a-form-item>\n </a-form>\n </div>\n\n <!-- 子表达式编辑弹窗 -->\n <a-modal\n v-model:open=\"subExprModalOpen\"\n :title=\"editingIndex >= 0 ? $t('编辑子表达式') : $t('添加子表达式')\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n @ok=\"onSubExprOk\"\n @cancel=\"subExprModalOpen = false\"\n >\n <sub-expression-editor\n v-if=\"subExprModalOpen\"\n v-model=\"editingSubExpr\"\n :field-options=\"flatAllFields\"\n :sub-expressions=\"localConfig.subExpressions\"\n />\n <template #footer>\n <a-button\n type=\"primary\"\n :disabled=\"!editingSubExpr.name || !editingSubExpr.name.trim()\"\n @click=\"onSubExprOk\"\n >\n {{ $t('确定') }}\n </a-button>\n <a-button @click=\"subExprModalOpen = false\">\n {{ $t('取消') }}\n </a-button>\n </template>\n </a-modal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n DatabaseOutlined,\n SwapOutlined,\n BarsOutlined,\n CalculatorOutlined,\n CloseOutlined,\n InfoCircleOutlined,\n FunctionOutlined,\n} from '@ant-design/icons-vue';\nimport { cloneDeep, debounce } from 'lodash';\nimport { computed, reactive, ref, watch } from 'vue';\nimport {\n Column,\n PivotChartsConfig,\n PivotFieldConfig,\n PivotFieldOption,\n PivotValueField,\n PivotSubExpression,\n SettingMode,\n} from '../typing';\nimport {\n VALUE_FORMATS,\n PIVOT_FIELD_PROCESS_FUNCS,\n NUMFMT_FORMAT_KEY,\n NUMFMT_PATTERN_PRESETS,\n DEFAULT_NUMFMT_PATTERN,\n normalizeValueFormat,\n} from '../func';\nimport { genID } from 'liyu-pc-base/utils/function';\nimport { combineLabel, validateValueExpression } from '../utils';\nimport valueFieldDisplay from './valueFieldDisplay.vue';\nimport DebouncedInput from './DebouncedInput.vue';\nimport subExpressionEditor from './subExpressionEditor.vue';\ninterface Props {\n columns: Column[];\n modelValue?: PivotChartsConfig;\n settingMode: SettingMode;\n}\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: PivotChartsConfig): void;\n (e: 'change', value: PivotChartsConfig): void;\n (e: 'openValueConfig', value: PivotValueField): void;\n}>();\n\nconst searchText = ref('');\n\n// 拖拽负载(判别联合)\ntype DragPayload =\n | { kind: 'pool'; field: PivotFieldOption }\n | { kind: 'group'; zone: 'series' | 'category'; groupId: string }\n | { kind: 'field'; zone: 'series' | 'category'; groupId: string; fieldIndex: number }\n | { kind: 'value'; field: PivotValueField };\nconst dragData = ref<DragPayload | null>(null);\nconst dragOverZone = ref<string>('');\nconst dragOverGroupZone = ref<string>('');\nconst dragOverGroupIndex = ref<number>(-1);\nconst dragInsertIndex = ref<number>(-1); // 值区域拖拽插入位置索引\n// 组内字段重排指示:拖拽字段经过某 chip 时,记录插入位置(前/后)\nconst dragFieldTarget = ref<{\n zone: string;\n groupIndex: number;\n fieldIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n// 组间插入指示:拖拽到组外(组间间隙)新建/插入/移动维度组时,在相邻组的上/下边框高亮\nconst dragGroupInsert = ref<{\n zone: string;\n refIndex: number;\n pos: 'before' | 'after';\n} | null>(null);\n\n// 维度组「显示名」是否为用户手动自定义:自定义名在字段增删/重排时不被自动覆盖\nconst customLabels = reactive(new Set<string>());\n\n// 重建 custom 标记:外部/旧配置中已带 label 的组视为自定义(保留),不调用\n// defaultGroupLabel 以避免 setup 阶段访问尚未初始化的依赖。\nconst syncCustomFlags = (config: PivotChartsConfig) => {\n customLabels.clear();\n const dims = [...(config.series || []), ...(config.category || [])];\n dims.forEach(g => {\n if (g.id && g.label) customLabels.add(g.id);\n });\n};\n\n// 用户手动输入显示名:标记为自定义,后续字段变化不再自动覆盖\nconst onLabelInput = (group: PivotFieldConfig) => {\n if (group.id) customLabels.add(group.id);\n onConfigChange();\n};\n\nconst defaultConfig: PivotChartsConfig = {\n series: [],\n category: [],\n values: [],\n chartType: 'bar',\n showPercent: false,\n};\n\nconst localConfig = ref<PivotChartsConfig>(cloneDeep(props.modelValue || defaultConfig));\n\n// 兼容旧数据:把「单字段」维度 {field,label,processFunc} 归一为「维度组」{id,fields:[...],linkType}\nconst normalizeDim = (d: any): PivotFieldConfig => {\n if (d && Array.isArray(d.fields)) {\n return { ...d, id: d.id || genID() };\n }\n return {\n id: d?.id || genID(),\n fields: [{ field: d?.field, label: d?.label, processFunc: d?.processFunc }],\n label: d?.label, // 旧单字段配置的显示名提升到组级(兼容旧数据)\n linkType: d?.linkType || '_',\n sortOrder: d?.sortOrder,\n sortBy: d?.sortBy,\n sortProcessFunc: d?.sortProcessFunc,\n };\n};\n// 确保每个值字段都有唯一 id(兼容旧数据)\nconst ensureValueIds = (config: PivotChartsConfig) => {\n config.values.forEach(v => {\n if (!v.id && v.field) {\n v.id = `${v.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;\n }\n // 旧格式 key('0.00' 等)归一为「通用数字格式 + formatPattern」\n normalizeValueFormat(v);\n });\n};\nconst normalizeConfig = (config: PivotChartsConfig) => {\n config.series = (config.series || []).map(normalizeDim);\n config.category = (config.category || []).map(normalizeDim);\n if (!Array.isArray(config.subExpressions)) config.subExpressions = [];\n ensureValueIds(config);\n};\nnormalizeConfig(localConfig.value);\nsyncCustomFlags(localConfig.value);\n\n// 自身 emit 引起的 modelValue 回灌无需再 cloneDeep+重渲染(localConfig 已是最新),\n// 用 skipWatch 跳过本次,避免一次 drop 触发两次整面板重渲染导致的卡顿。\nlet skipWatch = false;\n\nwatch(\n () => props.modelValue,\n val => {\n if (skipWatch) {\n skipWatch = false;\n return;\n }\n if (val) {\n const cloned = cloneDeep(val);\n normalizeConfig(cloned);\n syncCustomFlags(cloned);\n localConfig.value = cloned;\n }\n },\n { deep: true },\n);\n\n// 所有可用字段列表\nconst allFields = computed<PivotFieldOption[]>(() => {\n const buildTree = (cols: Column[]): PivotFieldOption[] => {\n return cols\n .map(col => {\n const label = col.title || col.dataIndex || col.key || '';\n const children = col.children?.length ? buildTree(col.children) : [];\n const isLeaf = children.length === 0;\n if (isLeaf) {\n if (!col.dataIndex || col.dataIndex === 'index' || col.dataIndex === 'action') {\n return null;\n }\n return {\n field: col.dataIndex,\n label,\n };\n }\n return {\n field: col.dataIndex || col.key || label,\n label,\n children,\n };\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n return buildTree(props.columns || []);\n});\n\nconst flatAllFields = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(allFields.value);\n});\n\nconst availableFieldTree = computed<PivotFieldOption[]>(() => {\n const filterTree = (fields: PivotFieldOption[], search: string): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(search) || field.field.toLowerCase().includes(search);\n const children = field.children?.length ? filterTree(field.children, search) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n const search = searchText.value.trim().toLowerCase();\n if (!search) return allFields.value;\n return filterTree(allFields.value, search);\n});\n\nconst availableFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(availableFieldTree.value);\n return list;\n});\n\n// 检查字段是否已在任意系列/类别维度组中\nconst isFieldInSeriesOrCategory = (field: string) => {\n return (\n localConfig.value.series.some(g => g.fields.some(f => f.field === field)) ||\n localConfig.value.category.some(g => g.fields.some(f => f.field === field))\n );\n};\n\nconst getFieldLocation = (field: string) => {\n if (localConfig.value.series.some(g => g.fields.some(f => f.field === field))) return '系列';\n if (localConfig.value.category.some(g => g.fields.some(f => f.field === field))) return '轴';\n return '';\n};\n\nconst aggregationOptions = [\n { label: '求和', value: 'SUM' },\n { label: '计数', value: 'COUNTA' },\n { label: '数值计数', value: 'COUNT' },\n { label: '平均值', value: 'AVG' },\n { label: '最小值', value: 'MIN' },\n { label: '最大值', value: 'MAX' },\n { label: '乘积', value: 'PRODUCT' },\n { label: '标准偏差', value: 'STD' },\n { label: '总体标准偏差', value: 'POPSTD' },\n { label: '方差', value: 'VAR' },\n { label: '总体方差', value: 'POPVAR' },\n { label: '无', value: 'NONE' },\n];\n\nconst valueFormatOptions = computed(() =>\n VALUE_FORMATS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 通用数字格式的常用格式串预设(a-auto-complete 选项,支持自定义输入)\nconst numfmtPatternOptions = NUMFMT_PATTERN_PRESETS.map(p => ({\n value: p.value,\n label: p.label || p.value,\n}));\n\n// 切换显示格式:选中「通用数字格式」且尚无格式串时填入缺省值\nconst onFormatFunChange = (item: PivotValueField) => {\n if (item.formatFun === NUMFMT_FORMAT_KEY && !item.formatPattern) {\n item.formatPattern = DEFAULT_NUMFMT_PATTERN;\n }\n onValueConfigChange();\n};\n\n// 格式串输入防抖:避免逐字符输入触发接口调用\nconst onPatternChange = debounce(() => onValueConfigChange(), 500);\n\n// 系列/类别维度「数据处理函数」下拉选项(来自 func.ts 注册表)\nconst processFuncOptions = computed(() =>\n PIVOT_FIELD_PROCESS_FUNCS.map(f => ({ label: f.label, value: f.key })),\n);\n\n// 维度组「连接符」下拉选项:'_'(默认)/ '-' / '()'\nconst linkTypeOptions = [\n { label: '_', value: '_' },\n { label: '-', value: '-' },\n { label: '( )', value: '()' },\n];\n\n// 维度「排序方向」下拉选项\nconst sortOrderOptions = [\n { label: '升序', value: 'asc' },\n { label: '降序', value: 'desc' },\n];\n\n// 维度组「排序依据字段」下拉选项:先列出本组各字段(按原始 field 名,Rust 侧已为组内\n// 每个字段登记单字段解析列,sort_by 指向该 field 即按「组内该字段」排序),再列出其余所有\n// 可用字段(含值字段、其它维度、未参与维度但存在于数据中的字段)。\n// 与 Rust 侧 resolve_sort 对齐:sort_by 现可解析任意存在于数据中的字段。\nconst getSortByOptions = (group: PivotFieldConfig) => {\n const own = group.fields.map(f => ({\n label: `${fieldLabel(f.field)}(${f.field})`,\n value: f.field,\n }));\n const ownSet = new Set(group.fields.map(f => f.field));\n const others = flatAllFields.value\n .filter(f => !ownSet.has(f.field))\n .map(f => ({ label: `${f.label}(${f.field})`, value: f.field }));\n return [...own, ...others];\n};\n\n// 排序依据字段下拉的搜索过滤(按 label 模糊匹配)\nconst filterFieldOption = (input: string, option: { label?: string }) =>\n (option?.label ?? '').toLowerCase().includes(input.toLowerCase());\n\n// 排序依据字段变化:清空后同步清除其处理函数(处理函数依赖排序依据字段存在)\nconst onGroupSortByChange = (group: PivotFieldConfig) => {\n if (!group.sortBy) {\n group.sortProcessFunc = undefined;\n }\n onConfigChange();\n};\n\n// 维度组默认显示名:按字段名以下划线连接(缺省值,用户可在配置中覆盖 group.label)\nconst defaultGroupLabel = (group: PivotFieldConfig): string => {\n if (!group.fields.length) return '';\n const labelList = group.fields.map(f => f.label || fieldLabel(f.field));\n return combineLabel(labelList, group.linkType);\n};\n\n// 字段显示名:优先用字段列表中的 title,缺省回退到字段名\nconst fieldLabel = (field: string): string => {\n const f = flatAllFields.value.find(x => x.field === field);\n return f ? f.label : field;\n};\n\n// ===== 拖拽 =====\nconst onDragStart = (event: DragEvent, field: PivotFieldOption) => {\n dragData.value = { kind: 'pool', field };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', field.field);\n }\n};\n\nconst onGroupDragStart = (\n event: DragEvent,\n group: PivotFieldConfig,\n zone: 'series' | 'category',\n) => {\n dragData.value = { kind: 'group', zone, groupId: group.id! };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onFieldDragStart = (\n event: DragEvent,\n group: PivotFieldConfig,\n zone: 'series' | 'category',\n fieldIndex: number,\n) => {\n dragData.value = { kind: 'field', zone, groupId: group.id!, fieldIndex };\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n};\n\nconst onDragEnd = () => clearDrag();\n\nconst clearDrag = () => {\n dragData.value = null;\n dragOverZone.value = '';\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragInsertIndex.value = -1;\n dragFieldTarget.value = null;\n dragGroupInsert.value = null;\n};\n\nconst onDragOver = (zone: string) => {\n dragOverZone.value = zone;\n dragFieldTarget.value = null;\n};\n\nconst onDragLeave = () => {\n // 只在离开整个区域时清除\n};\n\nconst onGroupDragOver = (zone: 'series' | 'category', index: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n event.stopPropagation();\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';\n\n if (d.kind === 'group') {\n // 拖拽整组进行排序:始终显示单边框插入指示(上/下),不显示 4 边框高亮。\n // 悬停在自身组上:不显示插入指示(原地不动)。\n const groupId = localConfig.value[zone][index]?.id;\n const overSelf = d.zone === zone && d.groupId === groupId;\n let nextIns: { zone: 'series' | 'category'; refIndex: number; pos: 'before' | 'after' } | null =\n null;\n if (!overSelf) {\n // 指针在组的上半区→插入到该组之前(高亮上边框);下半区→之后(高亮下边框)\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n nextIns = { zone, refIndex: index, pos: before ? 'before' : 'after' };\n }\n applyGroupInsert(nextIns);\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n dragOverZone.value = '';\n return;\n }\n\n // 其它情况(字段 / 池字段):悬停整组内部 → 4 边框高亮(把字段添加进该组)\n dragOverGroupZone.value = zone;\n dragOverGroupIndex.value = index;\n dragFieldTarget.value = null; // 悬停整组(非具体字段)时清除字段级指示\n dragGroupInsert.value = null; // 悬停整组(内部)时清除组间插入指示\n dragOverZone.value = ''; // 悬停整组(内部)时清除整区高亮,避免与组内 4 边框叠加\n};\n\nconst findGroupIndex = (zone: 'series' | 'category', groupId: string) =>\n localConfig.value[zone].findIndex(g => g.id === groupId);\n\n// 拖到区域空白:池字段→新建维度组;组→移到该区域末尾;字段→从原组移除\nconst onDropZone = (zone: 'series' | 'category') => {\n const d = dragData.value;\n if (!d) return;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].push(ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n localConfig.value[zone].push(g);\n }\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n localConfig.value[d.zone][srcIdx].fields.splice(d.fieldIndex, 1);\n if (localConfig.value[d.zone][srcIdx].fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖到具体维度组:池字段→追加;字段→跨组移动;组→组内重排 / 跨区移动\nconst onDropOnGroup = (zone: 'series' | 'category', gIndex: number, event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) return;\n\n if (d.kind === 'pool') {\n targetGroup.fields.push({ field: d.field.field, label: d.field.label });\n } else if (d.kind === 'field') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcIdx, 1);\n }\n targetGroup.fields.push(field);\n }\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n let tIdx = gIndex;\n if (d.zone === zone && srcIdx < gIndex) tIdx -= 1;\n localConfig.value[zone].splice(before ? tIdx : tIdx + 1, 0, g);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 区域级拖拽悬浮:把探测范围提升到 .pivot-drop-zone(覆盖整片区域),\n// 既设置整区高亮(dragOverZone),又按指针相对各组位置计算「组间插入」指示\n// (dragGroupInsert)。关键点:第一组上方与末组下方的留白属于区域 padding/header,\n// 原本不在 .pivot-zone-items 内,导致那两块区域只显示整区边框、且 drop 落到末尾;\n// 现在由本函数统一探测,使「第一组上方」→ 高亮组0上边框并插入到首个位置,\n// 「末组下方」→ 高亮末组下边框并插入到末尾。组内拖拽由 .pivot-dim-group 的\n// .stop 拦截,仍显示 4 边框(添加字段)。\nconst onZoneDragOver = (zone: 'series' | 'category', event: DragEvent) => {\n const d = dragData.value;\n if (!d) return;\n event.preventDefault();\n onDragOver(zone); // 整区高亮\n computeGroupInsert(zone, event);\n};\n\n// 计算「组间插入」指示:第一组上方→before(0);末组下方→after(末);组间间隙→after(i);\n// 命中组内部由 .stop 拦截不会进入本函数,故此处无需处理组内情况。\n// 仅在插入目标(zone/refIndex/pos)真正变化时才更新 dragGroupInsert,\n// 避免拖拽过程中每个 dragover 都触发整面板重渲染(卡顿来源之一)。\nconst applyGroupInsert = (\n next: { zone: 'series' | 'category'; refIndex: number; pos: 'before' | 'after' } | null,\n) => {\n const cur = dragGroupInsert.value;\n if (\n cur &&\n next &&\n cur.zone === next.zone &&\n cur.refIndex === next.refIndex &&\n cur.pos === next.pos\n )\n return;\n if (!cur && !next) return;\n dragGroupInsert.value = next;\n};\n\nconst computeGroupInsert = (zone: 'series' | 'category', event: DragEvent) => {\n const d = dragData.value;\n if (!d) {\n applyGroupInsert(null);\n return;\n }\n dragOverGroupZone.value = '';\n dragOverGroupIndex.value = -1;\n dragFieldTarget.value = null;\n const container = event.currentTarget as HTMLElement;\n const groups = Array.from(container.querySelectorAll('.pivot-dim-group')) as HTMLElement[];\n if (groups.length === 0) {\n applyGroupInsert(null); // 无组(空白区)由区域层级高亮处理\n return;\n }\n const y = event.clientY;\n const firstTop = groups[0].getBoundingClientRect().top;\n if (y < firstTop) {\n applyGroupInsert({ zone, refIndex: 0, pos: 'before' });\n return;\n }\n const lastBottom = groups[groups.length - 1].getBoundingClientRect().bottom;\n if (y > lastBottom) {\n applyGroupInsert({ zone, refIndex: groups.length - 1, pos: 'after' });\n return;\n }\n for (let i = 0; i < groups.length - 1; i++) {\n const top = groups[i].getBoundingClientRect();\n const nxt = groups[i + 1].getBoundingClientRect();\n if (y >= top.bottom && y <= nxt.top) {\n // 落在组 i 与组 i+1 之间:在组 i 之后插入(高亮组 i 下边框)\n applyGroupInsert({ zone, refIndex: i, pos: 'after' });\n return;\n }\n }\n applyGroupInsert(null);\n};\n\n// 在组间间隙释放:新建(池字段)/ 插入(整组重排)/ 移出(字段独立成组)维度组到对应位置。\n// 未命中组间插入目标(如空白区)时回退到区域空白逻辑。\nconst onZoneItemsDrop = (zone: 'series' | 'category', _event: DragEvent) => {\n const d = dragData.value;\n const ins = dragGroupInsert.value;\n if (!d || !ins || ins.zone !== zone) {\n onDropZone(zone);\n return;\n }\n let insIndex = ins.pos === 'before' ? ins.refIndex : ins.refIndex + 1;\n if (d.kind === 'pool') {\n const ng: PivotFieldConfig = {\n id: genID(),\n fields: [{ field: d.field.field, label: d.field.label }],\n linkType: '_',\n };\n localConfig.value[zone].splice(insIndex, 0, ng);\n } else if (d.kind === 'group') {\n const srcIdx = findGroupIndex(d.zone, d.groupId);\n if (srcIdx >= 0) {\n const [g] = localConfig.value[d.zone].splice(srcIdx, 1);\n if (d.zone === zone && srcIdx < insIndex) insIndex -= 1;\n localConfig.value[zone].splice(insIndex, 0, g);\n }\n } else if (d.kind === 'field') {\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx >= 0) {\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n if (srcGroup.fields.length === 0) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < insIndex) insIndex -= 1;\n }\n const ng: PivotFieldConfig = { id: genID(), fields: [field], linkType: '_' };\n localConfig.value[zone].splice(insIndex, 0, ng);\n }\n }\n clearDrag();\n onConfigChange();\n};\n\n// 拖拽字段经过组内某个 chip:记录插入位置(上边框=前 / 下边框=后),用于高亮提示\nconst onChipDragOver = (\n zone: 'series' | 'category',\n gIndex: number,\n fIndex: number,\n event: DragEvent,\n) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 仅字段重排时处理,其它拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n dragGroupInsert.value = null; // 悬停具体字段 chip 时清除组间插入指示\n const group = localConfig.value[zone][gIndex];\n if (!group) return;\n // 悬停在被拖拽的源字段自身上时,不显示指示线(避免原地误判)\n if (d.zone === zone && d.groupId === group.id && d.fieldIndex === fIndex) {\n dragFieldTarget.value = null;\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n dragFieldTarget.value = {\n zone,\n groupIndex: gIndex,\n fieldIndex: fIndex,\n pos: before ? 'before' : 'after',\n };\n};\n\n// 在组内某字段的上/下边界释放:把被拖字段插入到对应位置(支持组内重排与跨组定位)\nconst onChipDrop = (\n zone: 'series' | 'category',\n gIndex: number,\n fIndex: number,\n event: DragEvent,\n) => {\n const d = dragData.value;\n if (!d || d.kind !== 'field') return; // 非字段拖拽交给组/区域处理\n event.preventDefault();\n event.stopPropagation();\n const targetGroup = localConfig.value[zone][gIndex];\n if (!targetGroup) {\n clearDrag();\n return;\n }\n // 直接落在源字段自身:视为无移动\n if (d.zone === zone && d.groupId === targetGroup.id && d.fieldIndex === fIndex) {\n clearDrag();\n return;\n }\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const before = event.clientY < rect.top + rect.height / 2;\n\n const srcGroupIdx = findGroupIndex(d.zone, d.groupId);\n if (srcGroupIdx < 0) {\n clearDrag();\n return;\n }\n const srcGroup = localConfig.value[d.zone][srcGroupIdx];\n const [field] = srcGroup.fields.splice(d.fieldIndex, 1);\n\n const sameGroup = d.zone === zone && srcGroupIdx === gIndex;\n let targetGroupIdx = gIndex;\n let insertAt = before ? fIndex : fIndex + 1;\n // 同组内、源在目标之前时被移除会导致下标左移一位\n if (sameGroup && d.fieldIndex < fIndex) insertAt -= 1;\n\n // 跨组移动且源组已空:移除源组,并按需要修正目标组下标\n if (srcGroup.fields.length === 0 && !sameGroup) {\n localConfig.value[d.zone].splice(srcGroupIdx, 1);\n if (d.zone === zone && srcGroupIdx < targetGroupIdx) targetGroupIdx -= 1;\n }\n localConfig.value[zone][targetGroupIdx].fields.splice(insertAt, 0, field);\n clearDrag();\n onConfigChange();\n};\n\n// 值区域拖放:支持从字段池拖入新增值,以及值字段内部重排\nconst onDropValues = () => {\n const d = dragData.value;\n if (d && d.kind === 'pool') {\n addToValues(d.field);\n }\n clearDrag();\n};\n\n// 值字段拖拽排序\nconst onValueDragStart = (event: DragEvent, item: PivotValueField) => {\n dragData.value = { kind: 'value', field: item };\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', item.id);\n }\n};\n\nconst onValueItemDragOver = (index: number, event: DragEvent) => {\n dragOverZone.value = 'values';\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n if (event.clientY < midpoint) {\n dragInsertIndex.value = index;\n } else {\n dragInsertIndex.value = index + 1;\n }\n};\n\nconst onValueDrop = () => {\n const d = dragData.value;\n if (!d) return;\n\n // 从字段池拖入:创建新值\n if (d.kind === 'pool') {\n const insertIndex =\n dragInsertIndex.value >= 0 ? dragInsertIndex.value : localConfig.value.values.length;\n const valueField: PivotValueField = {\n id: `${d.field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: d.field.field,\n label: d.field.label,\n aggregationType: 'SUM',\n title: d.field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.splice(insertIndex, 0, valueField);\n clearDrag();\n onConfigChange();\n return;\n }\n\n // 值区域内重排\n if (d.kind !== 'value') return;\n const list = localConfig.value.values;\n const fromIndex = list.findIndex(f => f.id === (d as any).field.id);\n if (fromIndex === -1) {\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n return;\n }\n let toIndex = dragInsertIndex.value;\n if (toIndex < 0) toIndex = list.length;\n if (toIndex > fromIndex) toIndex -= 1;\n if (toIndex >= list.length) toIndex = list.length - 1;\n if (toIndex < 0) toIndex = 0;\n const [item] = list.splice(fromIndex, 1);\n list.splice(toIndex, 0, item);\n dragOverZone.value = '';\n dragInsertIndex.value = -1;\n onConfigChange();\n};\n\n// 双击字段快速添加到值区域(允许重复添加,不限制是否已在系列/类别中)\nconst addToValues = (field: PivotFieldOption) => {\n const valueField: PivotValueField = {\n id: `${field.field}_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n field: field.field,\n label: field.label,\n aggregationType: 'SUM',\n title: field.label,\n useExpression: false,\n expression: [],\n filter: [],\n };\n localConfig.value.values.push(valueField);\n onConfigChange();\n};\n\n// 移除整个维度组\nconst removeGroup = (zone: 'series' | 'category', index: number) => {\n localConfig.value[zone].splice(index, 1);\n onConfigChange();\n};\n\n// 从维度组移除单个字段;若组变空则一并移除该组\nconst removeFieldFromGroup = (zone: 'series' | 'category', gIndex: number, fIndex: number) => {\n const g = localConfig.value[zone][gIndex];\n g.fields.splice(fIndex, 1);\n if (g.fields.length === 0) {\n localConfig.value[zone].splice(gIndex, 1);\n }\n onConfigChange();\n};\n\n// 移除值字段\nconst removeValue = (index: number) => {\n localConfig.value.values.splice(index, 1);\n onConfigChange();\n};\n\nconst onValueConfigChange = () => {\n onConfigChange();\n};\n\n// ===== 子表达式(可复用表达式片段)管理 =====\nconst subExprModalOpen = ref(false);\nconst editingIndex = ref(-1); // >=0 表示编辑已有项,-1 表示新增\nconst editingSubExpr = ref<PivotSubExpression>({ id: '', name: '', expression: [] });\n\n// 把子表达式映射为一个「伪值字段」,复用 valueFieldDisplay 渲染其表达式预览\nconst subExprAsValueField = (se: PivotSubExpression): PivotValueField => ({\n id: se.id,\n field: '',\n aggregationType: 'NONE',\n useExpression: true,\n expression: se.expression || [],\n filter: [],\n});\n\nconst onAddSubExpr = () => {\n editingIndex.value = -1;\n editingSubExpr.value = { id: genID(), name: '', expression: [] };\n subExprModalOpen.value = true;\n};\nconst onEditSubExpr = (i: number) => {\n editingIndex.value = i;\n editingSubExpr.value = cloneDeep(localConfig.value.subExpressions[i]);\n subExprModalOpen.value = true;\n};\nconst onSubExprOk = () => {\n const se = editingSubExpr.value;\n if (!se.name || !se.name.trim()) return;\n if (editingIndex.value >= 0) {\n localConfig.value.subExpressions[editingIndex.value] = cloneDeep(se);\n } else {\n localConfig.value.subExpressions.push(cloneDeep(se));\n }\n subExprModalOpen.value = false;\n onConfigChange();\n};\nconst removeSubExpr = (i: number) => {\n localConfig.value.subExpressions.splice(i, 1);\n onConfigChange();\n};\n\n// 值字段表达式是否非法(useExpression 且表达式非空但校验不通过时高亮标记)\nconst valueExprInvalid = (item: PivotValueField): boolean => {\n if (!item.useExpression || !item.expression || item.expression.length === 0) return false;\n return !validateValueExpression(item.expression).valid;\n};\n\nconst onConfigChange = () => {\n skipWatch = true; // 标记本次 emit 引起的 modelValue 回灌,watch 中应跳过\n const config = cloneDeep(localConfig.value);\n emit('update:modelValue', config);\n emit('change', config);\n};\nconst onOpenValueConfig = (valueField: PivotValueField) => {\n emit('openValueConfig', valueField);\n};\n\n// 供父级(tableConfig/index.vue)在「高级配置」抽屉内修改值字段后,触发一次完整配置回传\n// getSubExpressions:把当前(含本会话内新增/编辑的)子表达式列表暴露给 index.vue,\n// 使「高级配置」里的表达式编辑器能引用已配置的子表达式(不依赖上层 props 的实时回灌)。\ndefineExpose({\n forceEmit: onConfigChange,\n getSubExpressions: () => localConfig.value.subExpressions,\n});\n</script>\n\n<style lang=\"less\" scoped>\n.pivot-charts-config-wrapper {\n display: flex;\n flex-direction: column;\n gap: 12px;\n height: 100%;\n}\n\n.pivot-charts-config-main {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n}\n\n.pivot-field-list {\n width: 220px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .pivot-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n color: #333;\n }\n\n .pivot-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n\n .pivot-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n\n .pivot-field-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-field-tag {\n flex-shrink: 0;\n font-size: 10px;\n color: #fff;\n background: #1890ff;\n border-radius: 8px;\n padding: 1px 6px;\n line-height: 16px;\n }\n }\n\n .pivot-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n\n .pivot-field-icon {\n color: #bfbfbf;\n }\n }\n\n .pivot-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n }\n\n .pivot-field-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.pivot-drop-zones {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n overflow-y: auto;\n}\n\n.pivot-drop-zone {\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n padding: 8px;\n transition: all 0.2s;\n min-height: 80px;\n flex-shrink: 0;\n\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n .pivot-zone-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n\n .pivot-zone-title {\n font-weight: 600;\n font-size: 13px;\n color: #333;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .pivot-zone-tip {\n color: #999;\n font-size: 12px;\n cursor: help;\n }\n }\n\n .pivot-zone-items {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n min-height: 32px;\n }\n\n .pivot-zone-placeholder {\n color: #bbb;\n font-size: 12px;\n padding: 4px 8px;\n width: 100%;\n text-align: center;\n }\n}\n\n.pivot-zone-item {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n background: #fafafa;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n max-width: 100%;\n box-sizing: border-box;\n position: relative;\n\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n .pivot-zone-item-label {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-zone-item-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n margin-left: 2px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n}\n\n.pivot-value-item {\n background: #fff7e6;\n border-color: #ffd591;\n flex-wrap: wrap;\n width: 100%;\n max-width: 100%;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n cursor: grab;\n user-select: none;\n\n &:hover {\n border-color: #fa8c16;\n background: #fff7e6;\n }\n\n &:active {\n cursor: grabbing;\n }\n\n // 表达式非法:红色高亮提示(与 valueFieldDisplay 内联告警互补)\n &.value-item-invalid {\n border-color: #ff4d4f;\n background: #fff1f0;\n }\n\n &.drag-before {\n box-shadow: 0 -2px 0 0 #1890ff;\n }\n\n &.drag-after {\n box-shadow: 0 2px 0 0 #1890ff;\n }\n\n .pivot-zone-item-label {\n flex-shrink: 0;\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n\n :deep(.ant-input) {\n flex: 1 1 auto;\n min-width: 70px;\n }\n}\n\n.pivot-drop-zone-values {\n min-height: 80px;\n}\n\n.pivot-dim-group {\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n border-radius: 6px;\n padding: 6px 8px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 6px;\n cursor: grab;\n user-select: none;\n transition: all 0.2s;\n position: relative;\n\n &:hover {\n border-color: #52c41a;\n background: #f6ffed;\n }\n\n &.drag-over {\n border-color: #52c41a;\n box-shadow: 0 0 0 2px #52c41a inset;\n }\n\n // 组间插入指示线:在相邻组的上/下边框高亮(蓝),提示将在此处新建/插入维度组;\n // 与「组内 4 边框高亮(绿,添加字段到该组)」区分。\n &.drop-group-before::before,\n &.drop-group-after::after {\n content: '';\n position: absolute;\n left: -2px;\n right: -2px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n z-index: 2;\n }\n\n &.drop-group-before::before {\n top: -4px;\n }\n\n &.drop-group-after::after {\n bottom: -4px;\n }\n\n .pivot-dim-group-header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n\n .pivot-group-label-input {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .pivot-group-remove {\n color: #999;\n cursor: pointer;\n font-size: 11px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n\n .pivot-dim-group-fields {\n display: flex;\n flex-wrap: wrap;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n width: 100%;\n }\n\n .pivot-field-chip {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n background: #fff;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font-size: 12px;\n cursor: grab;\n user-select: none;\n position: relative;\n\n // 组内字段重排指示线:上/下边框高亮,提示释放后的插入位置\n &.drop-before::before,\n &.drop-after::after {\n content: '';\n position: absolute;\n left: -1px;\n right: -1px;\n height: 0;\n border-top: 2px solid #1890ff;\n box-shadow: 0 0 4px rgba(24, 144, 255, 0.7);\n pointer-events: none;\n }\n\n &.drop-before::before {\n top: -3px;\n }\n\n &.drop-after::after {\n bottom: -3px;\n }\n\n &:hover {\n border-color: #52c41a;\n }\n\n .pivot-chip-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pivot-chip-remove {\n color: #999;\n cursor: pointer;\n font-size: 10px;\n flex-shrink: 0;\n\n &:hover {\n color: #ff4d4f;\n }\n }\n\n :deep(.ant-select) {\n flex: 1 1 auto;\n min-width: 80px;\n }\n }\n\n .pivot-sort-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n width: 100%;\n padding-top: 6px;\n border-top: 1px dashed #d9d9d9;\n\n .pivot-sort-label {\n font-size: 11px;\n color: #999;\n flex-shrink: 0;\n }\n\n :deep(.ant-select) {\n flex: 0 1 auto;\n min-width: 0;\n }\n }\n}\n\n.pivot-options {\n flex-shrink: 0;\n padding: 8px 0;\n border-top: 1px solid #f0f0f0;\n}\n.value-item-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n gap: 6px;\n width: 100%;\n}\n.value-item-content {\n display: flex;\n flex-direction: row;\n gap: 6px;\n align-items: center;\n}\n\n.pivot-drop-zone-subexpr {\n background: #fafaff;\n}\n\n.subexpr-items {\n align-items: stretch;\n}\n\n.pivot-subexpr-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 10px;\n background: #fff;\n border: 1px solid #efd6ff;\n border-radius: 6px;\n min-width: 220px;\n max-width: 100%;\n\n .subexpr-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n\n .subexpr-card-name {\n font-weight: 600;\n font-size: 13px;\n color: #722ed1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .subexpr-card-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n }\n }\n}\n</style>\n","import { ExclamationCircleOutlined } from '@ant-design/icons-vue';\nimport { message, Modal } from 'ant-design-vue';\nimport { h, reactive, Ref } from 'vue';\nimport { create_view_settings, update_view_settings } from 'liyu-pc-base/api/view-model';\nimport modalBox from 'liyu-pc-base/components/form-modal/modal-tools';\nexport interface ViewHook {\n saveView: () => void;\n addView: (view_type: string | null) => void;\n}\nexport interface ViewHookProps {\n currentView: Ref<any>;\n getViewData: () => any;\n isViewChange: Ref<boolean>;\n userid: number;\n view_project__flag: string;\n view_menu__flag: string;\n view_menu__name: string;\n fetchViews: () => void;\n is_auth: boolean;\n t: (key: string) => string;\n}\n\nexport const useView = (props: ViewHookProps): ViewHook => {\n const {\n currentView,\n getViewData,\n isViewChange,\n userid,\n view_project__flag,\n view_menu__flag,\n view_menu__name,\n fetchViews,\n is_auth,\n t,\n } = props;\n const saveView = () => {\n Modal.confirm({\n title: () => '确定覆盖视图吗?',\n content: '当前视图原有的数据会被覆盖',\n icon: () => h(ExclamationCircleOutlined),\n okText: '确定',\n async onOk() {\n currentView.value.data = getViewData();\n await update_view_settings({\n view_settings_id: currentView.value.id,\n data: currentView.value.data,\n });\n isViewChange.value = false;\n message.success('修改成功');\n },\n onCancel() {},\n });\n };\n const addView = (view_type: string | null) => {\n const tempModal = reactive({\n open: true,\n loading: false,\n title: t('新建视图'),\n form: {\n settings: { labelAlign: 'right', layout: 'vertical', col: [] },\n fields: [\n {\n type: 'input',\n name: 'name',\n label: t('视图名称'),\n disabled: false,\n allowClear: true,\n inputType: 'text',\n defaultValue: '',\n placeholder: t('请输入视图名称'),\n },\n ],\n rules: reactive({\n name: [{ required: true, message: t('请输入视图名称') }],\n }),\n model: reactive({ name: '', is_system: [false], view_type: 'personal' }),\n },\n options: {\n view_type: [\n { name: t('个人视图(仅自己可见)'), value: 'personal' },\n { name: t('公共视图(项目成员均可见)'), value: 'public' },\n ],\n is_system: [{ name: t('设为系统视图'), value: true }],\n },\n ok: async (data: any) => {\n const type = view_type || data.view_type || 'personal';\n const res = await create_view_settings({\n user_id: type === 'personal' ? userid : null,\n name: data.name,\n view_project__flag: view_project__flag,\n view_menu__flag: view_menu__flag,\n view_menu__name: view_menu__name,\n is_system: data.is_system && data.is_system[0],\n data: getViewData(),\n });\n await fetchViews();\n currentView.value = res;\n message.success(t('创建成功'));\n isViewChange.value = false;\n },\n });\n if (!view_type) {\n tempModal.form.fields.push({\n type: 'radio',\n name: 'view_type',\n label: t('视图可见范围'),\n disabled: false,\n allowClear: true,\n labelKey: 'name',\n valueKey: 'value',\n defaultValue: '',\n placeholder: '',\n } as any);\n }\n if (is_auth) {\n tempModal.form.fields.push({\n type: 'checkbox',\n name: 'is_system',\n label: '',\n disabled: false,\n allowClear: true,\n labelKey: 'name',\n valueKey: 'value',\n defaultValue: [false],\n } as any);\n }\n modalBox(tempModal);\n };\n return {\n saveView,\n addView,\n };\n};\n","<template>\n <div class=\"view-manage-container\">\n <div class=\"view-section\">\n <div class=\"section-header\">\n <span class=\"title\">{{ $t('个人视图') }}</span>\n <div class=\"header-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"handleManageView('personal')\"\n >\n <template #icon><setting-outlined /></template>\n </a-button>\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"addView('personal')\"\n >\n <template #icon><plus-outlined /></template>\n </a-button>\n </div>\n </div>\n <s-table\n :selectedRowKeys=\"selectedRowKeys\"\n :columns=\"viewTableColumns\"\n :dataSource=\"personData\"\n :pagination=\"false\"\n size=\"small\"\n :showHeader=\"false\"\n row-key=\"id\"\n :range-selection=\"false\"\n >\n <template #bodyCell=\"{ column, record }\">\n <template v-if=\"column.dataIndex === 'name'\">\n <div class=\"view-item-row\">\n <span\n class=\"view-name\"\n @click=\"handleApplyView(record)\"\n >\n {{ record.name }}\n <a-tag\n v-if=\"record.is_default\"\n color=\"blue\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('默认') }}\n </a-tag>\n <a-tag\n v-if=\"record.is_system\"\n color=\"orange\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('系统') }}\n </a-tag>\n </span>\n <div\n class=\"view-actions\"\n v-if=\"!record.is_system || is_auth\"\n >\n <a-tooltip :title=\"$t('设为默认')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleSetDefault(record)\"\n >\n <template #icon>\n <star-filled\n v-if=\"record.is_default\"\n style=\"color: #fadb14\"\n />\n <star-outlined v-else />\n </template>\n </a-button>\n </a-tooltip>\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleEditView(record)\"\n >\n <template #icon><edit-outlined /></template>\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n danger\n @click=\"handleDeleteView(record)\"\n >\n <template #icon><delete-outlined /></template>\n </a-button>\n </div>\n <span\n class=\"view-num\"\n style=\"float: right\"\n :style=\"\n currentView?.id === record.id ? { color: '#1890ff', fontWeight: 'bold' } : {}\n \"\n v-if=\"record.num\"\n :title=\"record.num\"\n >\n {{ record.num }}\n </span>\n </div>\n </template>\n </template>\n </s-table>\n </div>\n <div class=\"view-section\">\n <div class=\"section-header\">\n <span class=\"title\">{{ $t('公共视图') }}</span>\n <div class=\"header-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"handleManageView('public')\"\n >\n <template #icon><setting-outlined /></template>\n </a-button>\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"addView('public')\"\n >\n <template #icon><plus-outlined /></template>\n </a-button>\n </div>\n </div>\n <s-table\n :selectedRowKeys=\"selectedRowKeys\"\n :columns=\"viewTableColumns\"\n :dataSource=\"publicData\"\n :pagination=\"false\"\n size=\"small\"\n :showHeader=\"false\"\n row-key=\"id\"\n :range-selection=\"false\"\n >\n <template #bodyCell=\"{ column, record }\">\n <template v-if=\"column.dataIndex === 'name'\">\n <div class=\"view-item-row\">\n <span\n class=\"view-name\"\n @click=\"handleApplyView(record)\"\n >\n {{ record.name }}\n <a-tag\n v-if=\"record.is_default\"\n color=\"blue\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('默认') }}\n </a-tag>\n <a-tag\n v-if=\"record.is_system\"\n color=\"orange\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('系统') }}\n </a-tag>\n </span>\n <div\n class=\"view-actions\"\n v-if=\"!record.is_system || is_auth\"\n >\n <a-tooltip :title=\"$t('设为默认')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleSetDefault(record)\"\n >\n <template #icon>\n <star-filled\n v-if=\"record.is_default\"\n style=\"color: #fadb14\"\n />\n <star-outlined v-else />\n </template>\n </a-button>\n </a-tooltip>\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleEditView(record)\"\n >\n <template #icon><edit-outlined /></template>\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n danger\n @click=\"handleDeleteView(record)\"\n >\n <template #icon><delete-outlined /></template>\n </a-button>\n </div>\n <span\n class=\"view-num\"\n style=\"float: right\"\n :style=\"\n currentView?.id === record.id ? { color: '#1890ff', fontWeight: 'bold' } : {}\n \"\n v-if=\"record.num\"\n :title=\"record.num\"\n >\n {{ record.num }}\n </span>\n </div>\n </template>\n </template>\n </s-table>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, ref, onMounted, reactive, createVNode } from 'vue';\nimport {\n SettingOutlined,\n PlusOutlined,\n DeleteOutlined,\n EditOutlined,\n StarOutlined,\n StarFilled,\n ExclamationCircleOutlined,\n} from '@ant-design/icons-vue';\nimport { useI18n } from 'vue-i18n';\nimport { useUserStore, useEnv } from 'liyu-pc-base/init-plugin';\nimport {\n delete_view_settings,\n update_view_settings,\n set_view_settings_default,\n refresh_view_settings_cache,\n change_view_settings_sort,\n} from 'liyu-pc-base/api/view-model';\nimport { message, Modal } from 'ant-design-vue';\nimport modalBox from 'liyu-pc-base/components/form-modal/modal-tools';\nimport { useTableProState } from '../utils';\nimport { Column } from '../typing';\nimport { useView } from '../hooks/view';\n\nexport default defineComponent({\n name: 'ViewsConfig',\n components: {\n SettingOutlined,\n PlusOutlined,\n DeleteOutlined,\n EditOutlined,\n StarOutlined,\n StarFilled,\n },\n props: {\n columns: {\n type: Array as () => Array<Column>,\n default: () => [],\n },\n view_key: {\n type: String,\n default: '',\n },\n view_flag_name: {\n type: String,\n default: '',\n },\n is_view: {\n type: Boolean,\n default: false,\n },\n },\n setup(props) {\n const { t } = useI18n();\n const userStore = useUserStore();\n const env = useEnv();\n const current_org = userStore.currentOrg;\n const userid = userStore.userid;\n const VIEW_SYSTEM_EDIT = 'view_system_edit';\n const is_auth = ref(userStore.CHECK_AUTH(VIEW_SYSTEM_EDIT));\n\n const { setViewData, getViewData, viewDataList, fetchViews, currentView, isViewChange } =\n useTableProState();\n\n const viewTableColumns = [{ dataIndex: 'name', key: 'name' }];\n const selectedRowKeys = computed(() => {\n return [currentView.value?.id];\n });\n\n const personData = computed(() => {\n const list = viewDataList.value\n .filter((item: any) => !!item.user_id && item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n const list1 = viewDataList.value\n .filter((item: any) => !!item.user_id && !item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n return list.concat(list1);\n });\n\n const publicData = computed(() => {\n const list = viewDataList.value\n .filter((item: any) => !item.user_id && item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n const list1 = viewDataList.value\n .filter((item: any) => !item.user_id && !item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n return list.concat(list1);\n });\n\n const handleApplyView = (view: any) => {\n if (currentView.value?.id === view.id) {\n currentView.value = { id: 'no-view' };\n setViewData(null);\n message.warning(t('视图已取消'));\n } else {\n currentView.value = view;\n setViewData(view.data);\n message.success(t('已应用视图:') + view.name);\n }\n };\n\n const handleSetDefault = async (view: any) => {\n try {\n await set_view_settings_default({\n org_id: current_org.id,\n view_settings_id: view.id,\n });\n await refresh_view_settings_cache({\n org_id: current_org.id,\n view_settings_id: view.id,\n });\n message.success(t('设置默认成功'));\n fetchViews();\n } catch (e) {\n message.error(t('设置默认失败'));\n }\n };\n\n const handleDeleteView = (view: any) => {\n Modal.confirm({\n title: t('确认删除视图?'),\n content: t('视图删除后不可恢复,是否确认删除?'),\n icon: createVNode(ExclamationCircleOutlined),\n onOk: async () => {\n await delete_view_settings({\n org_id: current_org.id,\n view_settings_id: view.id,\n });\n message.success(t('删除成功'));\n fetchViews();\n },\n });\n };\n\n const handleEditView = (record: any) => {\n const tempModal = reactive({\n open: true,\n loading: false,\n title: t('编辑视图'),\n form: {\n settings: { labelAlign: 'right', layout: 'vertical', col: [] },\n fields: [\n {\n type: 'input',\n name: 'name',\n label: t('视图名称'),\n disabled: false,\n allowClear: true,\n inputType: 'text',\n defaultValue: record.name,\n placeholder: t('请输入视图名称'),\n },\n ],\n rules: reactive({\n name: [{ required: true, message: t('请输入视图名称') }],\n }),\n model: reactive({\n name: record.name,\n is_system: [record.is_system || false],\n }),\n },\n options: {\n is_system: [{ name: t('设为系统视图'), value: true }],\n },\n ok: async (data: any) => {\n await update_view_settings({\n org_id: current_org.id,\n view_settings_id: record.id,\n name: data.name,\n is_system: data.is_system && data.is_system[0],\n });\n message.success(t('修改成功'));\n fetchViews();\n },\n });\n\n if (is_auth.value) {\n tempModal.form.fields.push({\n type: 'checkbox',\n name: 'is_system',\n label: '',\n disabled: false,\n allowClear: true,\n labelKey: 'name',\n valueKey: 'value',\n defaultValue: [false],\n } as any);\n }\n modalBox(tempModal);\n };\n const { addView } = useView({\n currentView,\n getViewData,\n isViewChange,\n userid,\n view_project__flag: env.VITE_APP_VIEW_PROJECT_FLAG,\n view_menu__flag: props.view_key,\n view_menu__name: props.view_flag_name,\n fetchViews,\n is_auth,\n t,\n });\n\n const handleManageView = (view_type: string) => {\n const tempModal = reactive({\n open: true,\n component: 'ViewEditModal',\n compObj: {\n title: view_type == 'personal' ? t('个人视图管理') : t('公共视图管理'),\n data: view_type == 'personal' ? personData.value : publicData.value,\n },\n ok: (ids: any) => {\n if (ids && ids.length > 0) {\n change_view_settings_sort({\n org_id: current_org.id,\n view_settings_ids: ids,\n }).then(() => {\n message.success(t('保存成功'));\n fetchViews();\n });\n } else {\n message.success(t('保存成功'));\n fetchViews();\n }\n },\n cancel: (isEdit: any) => {\n if (isEdit) {\n fetchViews();\n }\n },\n });\n modalBox(tempModal);\n };\n\n return {\n viewTableColumns,\n personData,\n publicData,\n handleApplyView,\n handleSetDefault,\n handleDeleteView,\n handleEditView,\n addView,\n handleManageView,\n is_auth,\n fetchViews,\n selectedRowKeys,\n currentView,\n };\n },\n});\n</script>\n\n<style lang=\"less\" scoped>\n.view-manage-container {\n display: flex;\n gap: 16px;\n height: 400px;\n overflow-y: auto;\n\n .view-section {\n flex: 1;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n padding: 8px;\n\n .section-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n padding-bottom: 4px;\n border-bottom: 1px solid #f0f0f0;\n\n .title {\n font-weight: bold;\n }\n\n .header-actions {\n display: flex;\n align-items: center;\n }\n }\n }\n}\n\n.view-item-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n\n .view-name {\n cursor: pointer;\n flex: 1;\n padding: 4px 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 0 1 auto;\n &:hover {\n color: #1890ff;\n }\n }\n\n .view-actions {\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 0.2s;\n }\n\n &:hover .view-actions {\n opacity: 1;\n }\n}\n.view-num {\n display: inline-block;\n white-space: nowrap;\n}\n</style>\n","<template>\n <div class=\"view-manage-container\">\n <div class=\"view-section\">\n <div class=\"section-header\">\n <span class=\"title\">{{ $t('个人视图') }}</span>\n <div class=\"header-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"handleManageView('personal')\"\n >\n <template #icon><setting-outlined /></template>\n </a-button>\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"addView('personal')\"\n >\n <template #icon><plus-outlined /></template>\n </a-button>\n </div>\n </div>\n <s-table\n :selectedRowKeys=\"selectedRowKeys\"\n :columns=\"viewTableColumns\"\n :dataSource=\"personData\"\n :pagination=\"false\"\n size=\"small\"\n :showHeader=\"false\"\n row-key=\"id\"\n :range-selection=\"false\"\n >\n <template #bodyCell=\"{ column, record }\">\n <template v-if=\"column.dataIndex === 'name'\">\n <div class=\"view-item-row\">\n <span\n class=\"view-name\"\n @click=\"handleApplyView(record)\"\n >\n {{ record.name }}\n <a-tag\n v-if=\"record.is_default\"\n color=\"blue\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('默认') }}\n </a-tag>\n <a-tag\n v-if=\"record.is_system\"\n color=\"orange\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('系统') }}\n </a-tag>\n </span>\n <div\n class=\"view-actions\"\n v-if=\"!record.is_system || is_auth\"\n >\n <a-tooltip :title=\"$t('设为默认')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleSetDefault(record)\"\n >\n <template #icon>\n <star-filled\n v-if=\"record.is_default\"\n style=\"color: #fadb14\"\n />\n <star-outlined v-else />\n </template>\n </a-button>\n </a-tooltip>\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleEditView(record)\"\n >\n <template #icon><edit-outlined /></template>\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n danger\n @click=\"handleDeleteView(record)\"\n >\n <template #icon><delete-outlined /></template>\n </a-button>\n </div>\n <span\n class=\"view-num\"\n style=\"float: right\"\n :style=\"\n currentView?.id === record.id ? { color: '#1890ff', fontWeight: 'bold' } : {}\n \"\n v-if=\"record.num\"\n :title=\"record.num\"\n >\n {{ record.num }}\n </span>\n </div>\n </template>\n </template>\n </s-table>\n </div>\n <div class=\"view-section\">\n <div class=\"section-header\">\n <span class=\"title\">{{ $t('公共视图') }}</span>\n <div class=\"header-actions\">\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"handleManageView('public')\"\n >\n <template #icon><setting-outlined /></template>\n </a-button>\n <a-button\n type=\"link\"\n size=\"small\"\n @click=\"addView('public')\"\n >\n <template #icon><plus-outlined /></template>\n </a-button>\n </div>\n </div>\n <s-table\n :selectedRowKeys=\"selectedRowKeys\"\n :columns=\"viewTableColumns\"\n :dataSource=\"publicData\"\n :pagination=\"false\"\n size=\"small\"\n :showHeader=\"false\"\n row-key=\"id\"\n :range-selection=\"false\"\n >\n <template #bodyCell=\"{ column, record }\">\n <template v-if=\"column.dataIndex === 'name'\">\n <div class=\"view-item-row\">\n <span\n class=\"view-name\"\n @click=\"handleApplyView(record)\"\n >\n {{ record.name }}\n <a-tag\n v-if=\"record.is_default\"\n color=\"blue\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('默认') }}\n </a-tag>\n <a-tag\n v-if=\"record.is_system\"\n color=\"orange\"\n size=\"small\"\n style=\"margin-left: 4px\"\n >\n {{ $t('系统') }}\n </a-tag>\n </span>\n <div\n class=\"view-actions\"\n v-if=\"!record.is_system || is_auth\"\n >\n <a-tooltip :title=\"$t('设为默认')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleSetDefault(record)\"\n >\n <template #icon>\n <star-filled\n v-if=\"record.is_default\"\n style=\"color: #fadb14\"\n />\n <star-outlined v-else />\n </template>\n </a-button>\n </a-tooltip>\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"handleEditView(record)\"\n >\n <template #icon><edit-outlined /></template>\n </a-button>\n <a-button\n type=\"text\"\n size=\"small\"\n danger\n @click=\"handleDeleteView(record)\"\n >\n <template #icon><delete-outlined /></template>\n </a-button>\n </div>\n <span\n class=\"view-num\"\n style=\"float: right\"\n :style=\"\n currentView?.id === record.id ? { color: '#1890ff', fontWeight: 'bold' } : {}\n \"\n v-if=\"record.num\"\n :title=\"record.num\"\n >\n {{ record.num }}\n </span>\n </div>\n </template>\n </template>\n </s-table>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, ref, onMounted, reactive, createVNode } from 'vue';\nimport {\n SettingOutlined,\n PlusOutlined,\n DeleteOutlined,\n EditOutlined,\n StarOutlined,\n StarFilled,\n ExclamationCircleOutlined,\n} from '@ant-design/icons-vue';\nimport { useI18n } from 'vue-i18n';\nimport { useUserStore, useEnv } from 'liyu-pc-base/init-plugin';\nimport {\n delete_view_settings,\n update_view_settings,\n set_view_settings_default,\n refresh_view_settings_cache,\n change_view_settings_sort,\n} from 'liyu-pc-base/api/view-model';\nimport { message, Modal } from 'ant-design-vue';\nimport modalBox from 'liyu-pc-base/components/form-modal/modal-tools';\nimport { useTableProState } from '../utils';\nimport { Column } from '../typing';\nimport { useView } from '../hooks/view';\n\nexport default defineComponent({\n name: 'ViewsConfig',\n components: {\n SettingOutlined,\n PlusOutlined,\n DeleteOutlined,\n EditOutlined,\n StarOutlined,\n StarFilled,\n },\n props: {\n columns: {\n type: Array as () => Array<Column>,\n default: () => [],\n },\n view_key: {\n type: String,\n default: '',\n },\n view_flag_name: {\n type: String,\n default: '',\n },\n is_view: {\n type: Boolean,\n default: false,\n },\n },\n setup(props) {\n const { t } = useI18n();\n const userStore = useUserStore();\n const env = useEnv();\n const current_org = userStore.currentOrg;\n const userid = userStore.userid;\n const VIEW_SYSTEM_EDIT = 'view_system_edit';\n const is_auth = ref(userStore.CHECK_AUTH(VIEW_SYSTEM_EDIT));\n\n const { setViewData, getViewData, viewDataList, fetchViews, currentView, isViewChange } =\n useTableProState();\n\n const viewTableColumns = [{ dataIndex: 'name', key: 'name' }];\n const selectedRowKeys = computed(() => {\n return [currentView.value?.id];\n });\n\n const personData = computed(() => {\n const list = viewDataList.value\n .filter((item: any) => !!item.user_id && item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n const list1 = viewDataList.value\n .filter((item: any) => !!item.user_id && !item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n return list.concat(list1);\n });\n\n const publicData = computed(() => {\n const list = viewDataList.value\n .filter((item: any) => !item.user_id && item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n const list1 = viewDataList.value\n .filter((item: any) => !item.user_id && !item.is_system)\n .sort((a, b) => (a.sort == b.sort ? a.id - b.id : a.sort - b.sort));\n return list.concat(list1);\n });\n\n const handleApplyView = (view: any) => {\n if (currentView.value?.id === view.id) {\n currentView.value = { id: 'no-view' };\n setViewData(null);\n message.warning(t('视图已取消'));\n } else {\n currentView.value = view;\n setViewData(view.data);\n message.success(t('已应用视图:') + view.name);\n }\n };\n\n const handleSetDefault = async (view: any) => {\n try {\n await set_view_settings_default({\n org_id: current_org.id,\n view_settings_id: view.id,\n });\n await refresh_view_settings_cache({\n org_id: current_org.id,\n view_settings_id: view.id,\n });\n message.success(t('设置默认成功'));\n fetchViews();\n } catch (e) {\n message.error(t('设置默认失败'));\n }\n };\n\n const handleDeleteView = (view: any) => {\n Modal.confirm({\n title: t('确认删除视图?'),\n content: t('视图删除后不可恢复,是否确认删除?'),\n icon: createVNode(ExclamationCircleOutlined),\n onOk: async () => {\n await delete_view_settings({\n org_id: current_org.id,\n view_settings_id: view.id,\n });\n message.success(t('删除成功'));\n fetchViews();\n },\n });\n };\n\n const handleEditView = (record: any) => {\n const tempModal = reactive({\n open: true,\n loading: false,\n title: t('编辑视图'),\n form: {\n settings: { labelAlign: 'right', layout: 'vertical', col: [] },\n fields: [\n {\n type: 'input',\n name: 'name',\n label: t('视图名称'),\n disabled: false,\n allowClear: true,\n inputType: 'text',\n defaultValue: record.name,\n placeholder: t('请输入视图名称'),\n },\n ],\n rules: reactive({\n name: [{ required: true, message: t('请输入视图名称') }],\n }),\n model: reactive({\n name: record.name,\n is_system: [record.is_system || false],\n }),\n },\n options: {\n is_system: [{ name: t('设为系统视图'), value: true }],\n },\n ok: async (data: any) => {\n await update_view_settings({\n org_id: current_org.id,\n view_settings_id: record.id,\n name: data.name,\n is_system: data.is_system && data.is_system[0],\n });\n message.success(t('修改成功'));\n fetchViews();\n },\n });\n\n if (is_auth.value) {\n tempModal.form.fields.push({\n type: 'checkbox',\n name: 'is_system',\n label: '',\n disabled: false,\n allowClear: true,\n labelKey: 'name',\n valueKey: 'value',\n defaultValue: [false],\n } as any);\n }\n modalBox(tempModal);\n };\n const { addView } = useView({\n currentView,\n getViewData,\n isViewChange,\n userid,\n view_project__flag: env.VITE_APP_VIEW_PROJECT_FLAG,\n view_menu__flag: props.view_key,\n view_menu__name: props.view_flag_name,\n fetchViews,\n is_auth,\n t,\n });\n\n const handleManageView = (view_type: string) => {\n const tempModal = reactive({\n open: true,\n component: 'ViewEditModal',\n compObj: {\n title: view_type == 'personal' ? t('个人视图管理') : t('公共视图管理'),\n data: view_type == 'personal' ? personData.value : publicData.value,\n },\n ok: (ids: any) => {\n if (ids && ids.length > 0) {\n change_view_settings_sort({\n org_id: current_org.id,\n view_settings_ids: ids,\n }).then(() => {\n message.success(t('保存成功'));\n fetchViews();\n });\n } else {\n message.success(t('保存成功'));\n fetchViews();\n }\n },\n cancel: (isEdit: any) => {\n if (isEdit) {\n fetchViews();\n }\n },\n });\n modalBox(tempModal);\n };\n\n return {\n viewTableColumns,\n personData,\n publicData,\n handleApplyView,\n handleSetDefault,\n handleDeleteView,\n handleEditView,\n addView,\n handleManageView,\n is_auth,\n fetchViews,\n selectedRowKeys,\n currentView,\n };\n },\n});\n</script>\n\n<style lang=\"less\" scoped>\n.view-manage-container {\n display: flex;\n gap: 16px;\n height: 400px;\n overflow-y: auto;\n\n .view-section {\n flex: 1;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n padding: 8px;\n\n .section-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n padding-bottom: 4px;\n border-bottom: 1px solid #f0f0f0;\n\n .title {\n font-weight: bold;\n }\n\n .header-actions {\n display: flex;\n align-items: center;\n }\n }\n }\n}\n\n.view-item-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n\n .view-name {\n cursor: pointer;\n flex: 1;\n padding: 4px 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 0 1 auto;\n &:hover {\n color: #1890ff;\n }\n }\n\n .view-actions {\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 0.2s;\n }\n\n &:hover .view-actions {\n opacity: 1;\n }\n}\n.view-num {\n display: inline-block;\n white-space: nowrap;\n}\n</style>\n","<template>\n <div class=\"vf-wrap\">\n <!-- 左侧:所有字段(拖拽源) -->\n <div class=\"vf-left\">\n <div class=\"vf-section-title\">{{ $t('字段') }}</div>\n <a-input\n class=\"vf-field-search\"\n v-model:value=\"fieldSearch\"\n size=\"small\"\n allow-clear\n :placeholder=\"$t('搜索字段')\"\n >\n <template #prefix>\n <search-outlined />\n </template>\n </a-input>\n <div class=\"vf-field-pool\">\n <div\n v-for=\"f in displayFieldRows\"\n :key=\"f.key\"\n :class=\"['vf-field-item', { 'vf-field-group': !f.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + f.depth * 16}px` }\"\n :draggable=\"f.isLeaf\"\n @dragstart=\"f.isLeaf ? onFieldDragStart(f.option) : undefined\"\n @dragend=\"f.isLeaf ? onDragEnd() : undefined\"\n :title=\"f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label\"\n >\n <database-outlined\n v-if=\"f.isLeaf\"\n class=\"vf-field-icon\"\n />\n <span\n v-else\n class=\"vf-field-branch-dot\"\n />\n <span class=\"vf-field-label\">\n {{ f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label }}\n </span>\n </div>\n <div\n v-if=\"displayFieldRows.length === 0\"\n class=\"vf-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 右侧:当前值字段配置区 -->\n <div class=\"vf-right\">\n <div class=\"vf-switch-row\">\n <a-switch\n v-model:checked=\"useExpr\"\n :disabled=\"!hasFieldOptions\"\n />\n <span class=\"vf-switch-label\">{{ $t('启用高级表达式(字段间运算/比较/逻辑/三目)') }}</span>\n </div>\n\n <!-- 未启用表达式:单字段配置 -->\n <div\n v-if=\"!useExpr\"\n class=\"vf-single\"\n >\n <div\n class=\"vf-single-drop\"\n :class=\"{ 'drag-over': dragOverSingle }\"\n @dragover.prevent=\"onSingleDragOver\"\n @dragleave=\"dragOverSingle = false\"\n @drop.stop=\"onSingleDrop\"\n >\n <template v-if=\"valueField.field\">\n <span class=\"vf-single-field\">{{ fieldLabel(valueField.field) }}</span>\n <close-outlined\n class=\"vf-single-remove\"\n @click.stop=\"clearField\"\n />\n </template>\n <span\n v-else\n class=\"vf-single-placeholder\"\n >\n {{ $t('从左侧拖拽字段到此处') }}\n </span>\n </div>\n <a-select\n class=\"vf-single-select\"\n v-model:value=\"valueField.field\"\n size=\"small\"\n show-search\n :filter-option=\"filterOption\"\n :placeholder=\"$t('选择字段')\"\n :options=\"\n flatFieldOptions.map(f => ({ label: `${f.label}(${f.field})`, value: f.field }))\n \"\n @change=\"onFieldSelect\"\n @click.stop\n />\n <div class=\"vf-tip\">{{ $t('关闭表达式时,从左侧拖拽字段即可更改当前值字段') }}</div>\n </div>\n\n <!-- 计算表达式 + 过滤条件:折叠面板,默认两个面板全展开 -->\n <a-collapse\n v-model:activeKey=\"activeKeys\"\n :bordered=\"false\"\n class=\"vf-collapse\"\n >\n <!-- 启用表达式时显示「计算表达式」面板 -->\n <a-collapse-panel\n v-if=\"useExpr\"\n key=\"expr\"\n :header=\"$t('计算表达式')\"\n >\n <valueExprEditor\n v-model=\"valueField.expression\"\n :field-options=\"flatFieldOptions\"\n :sub-expressions=\"subExpressions\"\n :placeholder=\"$t('从左侧拖拽字段,或拖拽/点击运算符、数字、字符串到此处构建表达式')\"\n @change=\"emitChange\"\n />\n </a-collapse-panel>\n\n <!-- 过滤条件:始终可用(仅聚合满足条件的行) -->\n <a-collapse-panel\n key=\"filter\"\n :header=\"$t('过滤条件(仅聚合满足条件的行)')\"\n >\n <valueExprEditor\n v-model=\"valueField.filter\"\n :field-options=\"flatFieldOptions\"\n :sub-expressions=\"subExpressions\"\n :placeholder=\"$t('从左侧拖拽字段,构建如 销量 > 50 的过滤条件')\"\n @change=\"emitChange\"\n />\n </a-collapse-panel>\n </a-collapse>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, provide, ref } from 'vue';\nimport { DatabaseOutlined, CloseOutlined, SearchOutlined } from '@ant-design/icons-vue';\nimport { PivotValueField, PivotFieldOption, PivotSubExpression, ExprLocalDrag } from '../typing';\nimport ValueExprEditor from './valueExprEditor.vue';\n\ninterface Props {\n valueField: PivotValueField;\n fieldOptions: PivotFieldOption[];\n // 当前配置中全部子表达式(供表达式/过滤条件编辑器引用)\n subExpressions?: PivotSubExpression[];\n}\nconst props = defineProps<Props>();\nconst emit = defineEmits<{ (e: 'change'): void }>();\n\n// 跨子组件共享的拖拽负载(字段从左侧池拖出,落到编辑器画布)\nconst localDrag = ref<ExprLocalDrag | null>(null);\nprovide('vfLocalDrag', localDrag);\n\nconst flatFieldOptions = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(props.fieldOptions || []);\n});\n\nconst hasFieldOptions = computed(() => flatFieldOptions.value.length > 0);\n\n// 折叠面板展开状态:默认「计算表达式」与「过滤条件」两个面板全展开\nconst activeKeys = ref<string[]>(['expr', 'filter']);\n\n// 左侧字段搜索\nconst fieldSearch = ref('');\nconst filteredFieldTree = computed<PivotFieldOption[]>(() => {\n const q = fieldSearch.value.trim().toLowerCase();\n if (!q) return props.fieldOptions;\n\n const filterTree = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(q) || field.field.toLowerCase().includes(q);\n const children = field.children?.length ? filterTree(field.children) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n\n return filterTree(props.fieldOptions);\n});\n\nconst displayFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(filteredFieldTree.value);\n return list;\n});\n\nconst fieldLabel = (field: string): string => {\n const f = flatFieldOptions.value.find(x => x.field === field);\n return f ? f.label : field;\n};\n\nconst useExpr = computed<boolean>({\n get: () => !!props.valueField.useExpression,\n set: val => {\n props.valueField.useExpression = val;\n if (val) {\n // 启用时:若表达式为空且当前有单字段,以其为种子\n if ((props.valueField.expression || []).length === 0 && props.valueField.field) {\n props.valueField.expression = [{ type: 'field', value: props.valueField.field }];\n } else if (!props.valueField.expression) {\n props.valueField.expression = [];\n }\n } else {\n // 关闭时:若单字段为空,用表达式中首个字段兜底,避免值字段悬空\n if (!props.valueField.field) {\n const firstField = (props.valueField.expression || []).find(t => t.type === 'field');\n if (firstField) props.valueField.field = firstField.value;\n }\n }\n emitChange();\n },\n});\n\nconst filterOption = (input: string, option: { label?: string }) =>\n (option?.label ?? '').toLowerCase().includes(input.toLowerCase());\n\nconst emitChange = () => emit('change');\n\n// ===== 拖拽起始(字段)=====\nconst onFieldDragStart = (f: PivotFieldOption) => {\n localDrag.value = { kind: 'field', field: f.field, label: f.label };\n};\nconst onDragEnd = () => {\n localDrag.value = null;\n dragOverSingle.value = false;\n};\n\n// ===== 单字段(未启用表达式)=====\nconst dragOverSingle = ref(false);\nconst onSingleDragOver = () => {\n dragOverSingle.value = true;\n};\nconst onSingleDrop = () => {\n const d = localDrag.value;\n dragOverSingle.value = false;\n if (d && d.kind === 'field') {\n props.valueField.field = d.field;\n props.valueField.label = d.label;\n emitChange();\n }\n};\nconst onFieldSelect = () => {\n emitChange();\n};\nconst clearField = () => {\n props.valueField.field = '';\n props.valueField.label = undefined;\n emitChange();\n};\n</script>\n\n<style lang=\"less\" scoped>\n.vf-wrap {\n display: flex;\n gap: 16px;\n height: 100%;\n min-height: 360px;\n}\n\n.vf-left {\n width: 240px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .vf-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n }\n .vf-field-search {\n margin-bottom: 8px;\n flex-shrink: 0;\n }\n .vf-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n .vf-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n &:active {\n cursor: grabbing;\n }\n .vf-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n .vf-field-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n .vf-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n }\n .vf-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n flex-shrink: 0;\n }\n .vf-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.vf-right {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n overflow-y: auto;\n}\n\n.vf-switch-row {\n display: flex;\n align-items: center;\n gap: 8px;\n .vf-switch-label {\n font-size: 13px;\n color: #333;\n }\n}\n\n.vf-single {\n display: flex;\n flex-direction: column;\n gap: 8px;\n .vf-single-drop {\n min-height: 40px;\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 10px;\n transition: all 0.2s;\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n .vf-single-field {\n font-size: 13px;\n font-weight: 600;\n color: #1890ff;\n }\n .vf-single-placeholder {\n color: #bbb;\n font-size: 12px;\n }\n .vf-single-remove {\n color: #999;\n cursor: pointer;\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n .vf-single-select {\n max-width: 320px;\n }\n .vf-tip {\n font-size: 12px;\n color: #999;\n }\n}\n\n.vf-collapse {\n background: transparent;\n :deep(.ant-collapse-item) {\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n margin-bottom: 12px;\n overflow: hidden;\n background: #fff;\n }\n :deep(.ant-collapse-header) {\n font-weight: 600;\n font-size: 13px;\n }\n}\n</style>\n","<template>\n <div class=\"vf-wrap\">\n <!-- 左侧:所有字段(拖拽源) -->\n <div class=\"vf-left\">\n <div class=\"vf-section-title\">{{ $t('字段') }}</div>\n <a-input\n class=\"vf-field-search\"\n v-model:value=\"fieldSearch\"\n size=\"small\"\n allow-clear\n :placeholder=\"$t('搜索字段')\"\n >\n <template #prefix>\n <search-outlined />\n </template>\n </a-input>\n <div class=\"vf-field-pool\">\n <div\n v-for=\"f in displayFieldRows\"\n :key=\"f.key\"\n :class=\"['vf-field-item', { 'vf-field-group': !f.isLeaf }]\"\n :style=\"{ paddingLeft: `${8 + f.depth * 16}px` }\"\n :draggable=\"f.isLeaf\"\n @dragstart=\"f.isLeaf ? onFieldDragStart(f.option) : undefined\"\n @dragend=\"f.isLeaf ? onDragEnd() : undefined\"\n :title=\"f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label\"\n >\n <database-outlined\n v-if=\"f.isLeaf\"\n class=\"vf-field-icon\"\n />\n <span\n v-else\n class=\"vf-field-branch-dot\"\n />\n <span class=\"vf-field-label\">\n {{ f.isLeaf ? `${f.option.label}(${f.option.field})` : f.option.label }}\n </span>\n </div>\n <div\n v-if=\"displayFieldRows.length === 0\"\n class=\"vf-empty\"\n >\n {{ $t('未找到匹配字段') }}\n </div>\n </div>\n </div>\n\n <!-- 右侧:当前值字段配置区 -->\n <div class=\"vf-right\">\n <div class=\"vf-switch-row\">\n <a-switch\n v-model:checked=\"useExpr\"\n :disabled=\"!hasFieldOptions\"\n />\n <span class=\"vf-switch-label\">{{ $t('启用高级表达式(字段间运算/比较/逻辑/三目)') }}</span>\n </div>\n\n <!-- 未启用表达式:单字段配置 -->\n <div\n v-if=\"!useExpr\"\n class=\"vf-single\"\n >\n <div\n class=\"vf-single-drop\"\n :class=\"{ 'drag-over': dragOverSingle }\"\n @dragover.prevent=\"onSingleDragOver\"\n @dragleave=\"dragOverSingle = false\"\n @drop.stop=\"onSingleDrop\"\n >\n <template v-if=\"valueField.field\">\n <span class=\"vf-single-field\">{{ fieldLabel(valueField.field) }}</span>\n <close-outlined\n class=\"vf-single-remove\"\n @click.stop=\"clearField\"\n />\n </template>\n <span\n v-else\n class=\"vf-single-placeholder\"\n >\n {{ $t('从左侧拖拽字段到此处') }}\n </span>\n </div>\n <a-select\n class=\"vf-single-select\"\n v-model:value=\"valueField.field\"\n size=\"small\"\n show-search\n :filter-option=\"filterOption\"\n :placeholder=\"$t('选择字段')\"\n :options=\"\n flatFieldOptions.map(f => ({ label: `${f.label}(${f.field})`, value: f.field }))\n \"\n @change=\"onFieldSelect\"\n @click.stop\n />\n <div class=\"vf-tip\">{{ $t('关闭表达式时,从左侧拖拽字段即可更改当前值字段') }}</div>\n </div>\n\n <!-- 计算表达式 + 过滤条件:折叠面板,默认两个面板全展开 -->\n <a-collapse\n v-model:activeKey=\"activeKeys\"\n :bordered=\"false\"\n class=\"vf-collapse\"\n >\n <!-- 启用表达式时显示「计算表达式」面板 -->\n <a-collapse-panel\n v-if=\"useExpr\"\n key=\"expr\"\n :header=\"$t('计算表达式')\"\n >\n <valueExprEditor\n v-model=\"valueField.expression\"\n :field-options=\"flatFieldOptions\"\n :sub-expressions=\"subExpressions\"\n :placeholder=\"$t('从左侧拖拽字段,或拖拽/点击运算符、数字、字符串到此处构建表达式')\"\n @change=\"emitChange\"\n />\n </a-collapse-panel>\n\n <!-- 过滤条件:始终可用(仅聚合满足条件的行) -->\n <a-collapse-panel\n key=\"filter\"\n :header=\"$t('过滤条件(仅聚合满足条件的行)')\"\n >\n <valueExprEditor\n v-model=\"valueField.filter\"\n :field-options=\"flatFieldOptions\"\n :sub-expressions=\"subExpressions\"\n :placeholder=\"$t('从左侧拖拽字段,构建如 销量 > 50 的过滤条件')\"\n @change=\"emitChange\"\n />\n </a-collapse-panel>\n </a-collapse>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, provide, ref } from 'vue';\nimport { DatabaseOutlined, CloseOutlined, SearchOutlined } from '@ant-design/icons-vue';\nimport { PivotValueField, PivotFieldOption, PivotSubExpression, ExprLocalDrag } from '../typing';\nimport ValueExprEditor from './valueExprEditor.vue';\n\ninterface Props {\n valueField: PivotValueField;\n fieldOptions: PivotFieldOption[];\n // 当前配置中全部子表达式(供表达式/过滤条件编辑器引用)\n subExpressions?: PivotSubExpression[];\n}\nconst props = defineProps<Props>();\nconst emit = defineEmits<{ (e: 'change'): void }>();\n\n// 跨子组件共享的拖拽负载(字段从左侧池拖出,落到编辑器画布)\nconst localDrag = ref<ExprLocalDrag | null>(null);\nprovide('vfLocalDrag', localDrag);\n\nconst flatFieldOptions = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(props.fieldOptions || []);\n});\n\nconst hasFieldOptions = computed(() => flatFieldOptions.value.length > 0);\n\n// 折叠面板展开状态:默认「计算表达式」与「过滤条件」两个面板全展开\nconst activeKeys = ref<string[]>(['expr', 'filter']);\n\n// 左侧字段搜索\nconst fieldSearch = ref('');\nconst filteredFieldTree = computed<PivotFieldOption[]>(() => {\n const q = fieldSearch.value.trim().toLowerCase();\n if (!q) return props.fieldOptions;\n\n const filterTree = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n return fields\n .map(field => {\n const selfMatch =\n field.label.toLowerCase().includes(q) || field.field.toLowerCase().includes(q);\n const children = field.children?.length ? filterTree(field.children) : [];\n if (selfMatch) {\n return {\n ...field,\n children: field.children?.length ? field.children : undefined,\n };\n }\n if (children.length) {\n return {\n ...field,\n children,\n };\n }\n if (!field.children?.length && selfMatch) {\n return { ...field };\n }\n return null;\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n\n return filterTree(props.fieldOptions);\n});\n\nconst displayFieldRows = computed<\n Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }>\n>(() => {\n const list: Array<{ key: string; depth: number; isLeaf: boolean; option: PivotFieldOption }> = [];\n const walk = (fields: PivotFieldOption[], depth = 0) => {\n fields.forEach(field => {\n const isLeaf = !field.children?.length;\n list.push({\n key: `${depth}_${field.field}_${field.label}`,\n depth,\n isLeaf,\n option: field,\n });\n if (field.children?.length) {\n walk(field.children, depth + 1);\n }\n });\n };\n walk(filteredFieldTree.value);\n return list;\n});\n\nconst fieldLabel = (field: string): string => {\n const f = flatFieldOptions.value.find(x => x.field === field);\n return f ? f.label : field;\n};\n\nconst useExpr = computed<boolean>({\n get: () => !!props.valueField.useExpression,\n set: val => {\n props.valueField.useExpression = val;\n if (val) {\n // 启用时:若表达式为空且当前有单字段,以其为种子\n if ((props.valueField.expression || []).length === 0 && props.valueField.field) {\n props.valueField.expression = [{ type: 'field', value: props.valueField.field }];\n } else if (!props.valueField.expression) {\n props.valueField.expression = [];\n }\n } else {\n // 关闭时:若单字段为空,用表达式中首个字段兜底,避免值字段悬空\n if (!props.valueField.field) {\n const firstField = (props.valueField.expression || []).find(t => t.type === 'field');\n if (firstField) props.valueField.field = firstField.value;\n }\n }\n emitChange();\n },\n});\n\nconst filterOption = (input: string, option: { label?: string }) =>\n (option?.label ?? '').toLowerCase().includes(input.toLowerCase());\n\nconst emitChange = () => emit('change');\n\n// ===== 拖拽起始(字段)=====\nconst onFieldDragStart = (f: PivotFieldOption) => {\n localDrag.value = { kind: 'field', field: f.field, label: f.label };\n};\nconst onDragEnd = () => {\n localDrag.value = null;\n dragOverSingle.value = false;\n};\n\n// ===== 单字段(未启用表达式)=====\nconst dragOverSingle = ref(false);\nconst onSingleDragOver = () => {\n dragOverSingle.value = true;\n};\nconst onSingleDrop = () => {\n const d = localDrag.value;\n dragOverSingle.value = false;\n if (d && d.kind === 'field') {\n props.valueField.field = d.field;\n props.valueField.label = d.label;\n emitChange();\n }\n};\nconst onFieldSelect = () => {\n emitChange();\n};\nconst clearField = () => {\n props.valueField.field = '';\n props.valueField.label = undefined;\n emitChange();\n};\n</script>\n\n<style lang=\"less\" scoped>\n.vf-wrap {\n display: flex;\n gap: 16px;\n height: 100%;\n min-height: 360px;\n}\n\n.vf-left {\n width: 240px;\n flex-shrink: 0;\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n .vf-section-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-size: 13px;\n }\n .vf-field-search {\n margin-bottom: 8px;\n flex-shrink: 0;\n }\n .vf-field-pool {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n .vf-field-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n margin-bottom: 4px;\n border: 1px solid #f0f0f0;\n border-radius: 4px;\n cursor: grab;\n font-size: 13px;\n transition: all 0.2s;\n user-select: none;\n &:hover {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n &:active {\n cursor: grabbing;\n }\n .vf-field-icon {\n color: #1890ff;\n font-size: 12px;\n }\n .vf-field-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n .vf-field-group {\n cursor: default;\n background: #fafafa;\n font-weight: 600;\n\n &:hover {\n border-color: #f0f0f0;\n background: #fafafa;\n }\n }\n .vf-field-branch-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #d9d9d9;\n display: inline-block;\n flex-shrink: 0;\n }\n .vf-empty {\n text-align: center;\n color: #999;\n font-size: 12px;\n padding: 20px 0;\n }\n}\n\n.vf-right {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n overflow-y: auto;\n}\n\n.vf-switch-row {\n display: flex;\n align-items: center;\n gap: 8px;\n .vf-switch-label {\n font-size: 13px;\n color: #333;\n }\n}\n\n.vf-single {\n display: flex;\n flex-direction: column;\n gap: 8px;\n .vf-single-drop {\n min-height: 40px;\n border: 2px dashed #d9d9d9;\n border-radius: 6px;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 10px;\n transition: all 0.2s;\n &.drag-over {\n border-color: #1890ff;\n background: #f0f7ff;\n }\n .vf-single-field {\n font-size: 13px;\n font-weight: 600;\n color: #1890ff;\n }\n .vf-single-placeholder {\n color: #bbb;\n font-size: 12px;\n }\n .vf-single-remove {\n color: #999;\n cursor: pointer;\n &:hover {\n color: #ff4d4f;\n }\n }\n }\n .vf-single-select {\n max-width: 320px;\n }\n .vf-tip {\n font-size: 12px;\n color: #999;\n }\n}\n\n.vf-collapse {\n background: transparent;\n :deep(.ant-collapse-item) {\n border: 1px solid #f0f0f0;\n border-radius: 6px;\n margin-bottom: 12px;\n overflow: hidden;\n background: #fff;\n }\n :deep(.ant-collapse-header) {\n font-weight: 600;\n font-size: 13px;\n }\n}\n</style>\n","<template>\n <div class=\"column-edit-view-wrapper\">\n <a-tabs v-model:activeKey=\"activeTab\">\n <a-tab-pane\n key=\"columns\"\n :tab=\"component_type === 'normal' ? $t('列设置') : $t('字段设置')\"\n >\n <div class=\"tab-content-scroll\">\n <pivot-config-comp\n v-if=\"component_type === 'pivot_table'\"\n ref=\"pivotConfigRef\"\n :columns=\"columns\"\n :modelValue=\"pivotConfig\"\n :settingMode=\"settingMode\"\n @change=\"onPivotConfigChange\"\n @openValueConfig=\"onOpenValueConfig\"\n />\n <pivot-charts-config-comp\n v-else-if=\"component_type === 'pivot_charts'\"\n ref=\"pivotChartsConfigRef\"\n :columns=\"columns\"\n :modelValue=\"pivotChartsConfig\"\n :settingMode=\"settingMode\"\n @change=\"onPivotChartsConfigChange\"\n @openValueConfig=\"onOpenValueConfig\"\n />\n <columns-config\n v-else-if=\"component_type === 'normal'\"\n :columns=\"columns\"\n @change=\"onColumnChange\"\n />\n </div>\n </a-tab-pane>\n <a-tab-pane\n key=\"views\"\n :tab=\"$t('视图管理')\"\n v-if=\"is_view\"\n >\n <div class=\"tab-content-scroll\">\n <views-config\n :columns=\"columns\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :is_view=\"is_view\"\n />\n </div>\n </a-tab-pane>\n </a-tabs>\n </div>\n <a-drawer\n v-if=\"settingMode === 'drawer'\"\n :title=\"valueFieldTitle\"\n :width=\"settingDrawerWidth\"\n :placement=\"settingDrawerPlacement\"\n :push=\"false\"\n :mask=\"false\"\n v-model:open=\"valueSettingOpen\"\n >\n <value-field-config\n :valueField=\"currentValueField\"\n :field-options=\"fieldOptions\"\n :sub-expressions=\"currentSubExpressions\"\n @change=\"onValueFieldChange\"\n />\n </a-drawer>\n <a-modal\n v-else\n :title=\"valueFieldTitle\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n v-model:open=\"valueSettingOpen\"\n >\n <value-field-config\n :valueField=\"currentValueField\"\n :field-options=\"fieldOptions\"\n :sub-expressions=\"currentSubExpressions\"\n @change=\"onValueFieldChange\"\n />\n <template #footer>\n <a-button @click=\"valueSettingOpen = false\">\n {{ $t('关闭') }}\n </a-button>\n </template>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch, computed } from 'vue';\nimport {\n Column,\n PivotConfig,\n PivotChartsConfig,\n SettingMode,\n PivotValueField,\n PivotFieldOption,\n PivotSubExpression,\n SettingDrawerPlacement,\n} from '../typing';\nimport ColumnsConfig from './columnsConfig.vue';\nimport PivotConfigComp from './pivotConfig.vue';\nimport PivotChartsConfigComp from './pivotChartsConfig.vue';\nimport ViewsConfig from './viewsConfig.vue';\nimport valueFieldConfig from './valueFieldConfig.vue';\nimport { useTableProState } from '../utils';\n\ninterface Props {\n columns: Array<Column>;\n view_key: string;\n view_flag_name: string;\n is_view: boolean;\n component_type: string;\n pivotConfig: PivotConfig | null;\n pivotChartsConfig: PivotChartsConfig | null;\n settingMode: SettingMode;\n settingDrawerWidth: string | number;\n settingDrawerPlacement: SettingDrawerPlacement;\n}\n\nconst props = defineProps<Props>();\nconst emit = defineEmits(['change', 'pivotConfigChange', 'pivotChartsConfigChange']);\n\nconst activeTab = ref('columns');\nconst valueSettingOpen = ref(false);\nconst currentValueField = ref<PivotValueField | null>(null);\n// 高级配置抽屉关联的源组件(用于把抽屉内的修改回写到对应配置并向上 emit)\nconst currentValueFieldSource = ref<'pivot' | 'charts' | null>(null);\nconst pivotConfigRef = ref<InstanceType<typeof PivotConfigComp> | null>(null);\nconst pivotChartsConfigRef = ref<InstanceType<typeof PivotChartsConfigComp> | null>(null);\n\n// 左侧字段列表(叶子列)映射为 {field, label}\nconst fieldOptions = computed<PivotFieldOption[]>(() => {\n const buildTree = (cols: Column[]): PivotFieldOption[] => {\n return cols\n .map(col => {\n const label = col.title || col.dataIndex || col.key || '';\n const children = col.children?.length ? buildTree(col.children) : [];\n const isLeaf = children.length === 0;\n if (isLeaf) {\n if (!col.dataIndex || col.dataIndex === 'index' || col.dataIndex === 'action') {\n return null;\n }\n return {\n field: col.dataIndex,\n label,\n };\n }\n return {\n field: col.dataIndex || col.key || label,\n label,\n children,\n };\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n return buildTree(props.columns || []);\n});\n\nconst flatFieldOptions = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(fieldOptions.value);\n});\n\n// 抽屉标题:表达式型显示「表达式 + 预览片段」,否则显示字段名\nconst valueFieldTitle = computed(() => {\n const v = currentValueField.value;\n if (!v) return '';\n if (v.useExpression && (v.expression || []).length) {\n const preview = (v.expression || [])\n .map(t => {\n if (t.type === 'field') {\n const f = flatFieldOptions.value.find(x => x.field === t.value);\n return f ? f.label : t.value;\n }\n if (t.type === 'operator') return t.value;\n if (t.type === 'number') return t.value;\n if (t.type === 'lparen') return '(';\n if (t.type === 'rparen') return ')';\n return '';\n })\n .join(' ');\n return `${\n props.component_type === 'pivot_charts' ? '透视图' : '透视表'\n } · ${'表达式'}: ${preview}`;\n }\n return `${'字段设置'}: ${v.label || v.field || ''}`;\n});\n\nconst onColumnChange = (data: any) => {\n emit('change', data);\n};\nconst onPivotConfigChange = (data: PivotConfig) => {\n emit('pivotConfigChange', data);\n};\nconst onPivotChartsConfigChange = (data: PivotChartsConfig) => {\n emit('pivotChartsConfigChange', data);\n};\nconst { fetchViews } = useTableProState();\nwatch(\n () => activeTab.value,\n val => {\n if (val === 'views') {\n fetchViews();\n }\n },\n);\n\n// 高级配置抽屉内的修改(表达式开关 / token 增删)由 valueFieldConfig 直接 mutate 共享对象,\n// 这里通过源组件暴露的 forceEmit 触发一次完整配置回传,确保后端重算与持久化拿到最新值。\nconst onValueFieldChange = () => {\n if (currentValueFieldSource.value === 'charts') {\n pivotChartsConfigRef.value?.forceEmit?.();\n } else {\n pivotConfigRef.value?.forceEmit?.();\n }\n};\n\nconst onOpenValueConfig = (field: PivotValueField) => {\n currentValueField.value = field;\n currentValueFieldSource.value = props.component_type === 'pivot_charts' ? 'charts' : 'pivot';\n valueSettingOpen.value = true;\n};\n\n// 当前配置中可引用的子表达式列表。\n// 注意:直接读 props.pivotConfig/pivotChartsConfig 只能拿到上层回灌的快照,\n// 会话内新增/编辑的子表达式(localConfig)不会实时同步到 props,导致「高级配置」里\n// 的表达式编辑器看不到已配置的子表达式。因此改为直接读取对应子组件暴露的实时列表。\nconst currentSubExpressions = computed<PivotSubExpression[]>(() => {\n const compRef =\n props.component_type === 'pivot_charts' ? pivotChartsConfigRef.value : pivotConfigRef.value;\n return compRef?.getSubExpressions?.() || [];\n});\n</script>\n\n<style lang=\"less\" scoped>\n.column-edit-view-wrapper {\n padding: 0 8px;\n height: 100%;\n}\n.tab-content-scroll {\n overflow-y: auto;\n height: 100%;\n}\n:deep(.ant-tabs) {\n height: 100%;\n}\n:deep(.ant-tabs-content) {\n height: 100%;\n}\n</style>\n","<template>\n <div class=\"column-edit-view-wrapper\">\n <a-tabs v-model:activeKey=\"activeTab\">\n <a-tab-pane\n key=\"columns\"\n :tab=\"component_type === 'normal' ? $t('列设置') : $t('字段设置')\"\n >\n <div class=\"tab-content-scroll\">\n <pivot-config-comp\n v-if=\"component_type === 'pivot_table'\"\n ref=\"pivotConfigRef\"\n :columns=\"columns\"\n :modelValue=\"pivotConfig\"\n :settingMode=\"settingMode\"\n @change=\"onPivotConfigChange\"\n @openValueConfig=\"onOpenValueConfig\"\n />\n <pivot-charts-config-comp\n v-else-if=\"component_type === 'pivot_charts'\"\n ref=\"pivotChartsConfigRef\"\n :columns=\"columns\"\n :modelValue=\"pivotChartsConfig\"\n :settingMode=\"settingMode\"\n @change=\"onPivotChartsConfigChange\"\n @openValueConfig=\"onOpenValueConfig\"\n />\n <columns-config\n v-else-if=\"component_type === 'normal'\"\n :columns=\"columns\"\n @change=\"onColumnChange\"\n />\n </div>\n </a-tab-pane>\n <a-tab-pane\n key=\"views\"\n :tab=\"$t('视图管理')\"\n v-if=\"is_view\"\n >\n <div class=\"tab-content-scroll\">\n <views-config\n :columns=\"columns\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :is_view=\"is_view\"\n />\n </div>\n </a-tab-pane>\n </a-tabs>\n </div>\n <a-drawer\n v-if=\"settingMode === 'drawer'\"\n :title=\"valueFieldTitle\"\n :width=\"settingDrawerWidth\"\n :placement=\"settingDrawerPlacement\"\n :push=\"false\"\n :mask=\"false\"\n v-model:open=\"valueSettingOpen\"\n >\n <value-field-config\n :valueField=\"currentValueField\"\n :field-options=\"fieldOptions\"\n :sub-expressions=\"currentSubExpressions\"\n @change=\"onValueFieldChange\"\n />\n </a-drawer>\n <a-modal\n v-else\n :title=\"valueFieldTitle\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n v-model:open=\"valueSettingOpen\"\n >\n <value-field-config\n :valueField=\"currentValueField\"\n :field-options=\"fieldOptions\"\n :sub-expressions=\"currentSubExpressions\"\n @change=\"onValueFieldChange\"\n />\n <template #footer>\n <a-button @click=\"valueSettingOpen = false\">\n {{ $t('关闭') }}\n </a-button>\n </template>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch, computed } from 'vue';\nimport {\n Column,\n PivotConfig,\n PivotChartsConfig,\n SettingMode,\n PivotValueField,\n PivotFieldOption,\n PivotSubExpression,\n SettingDrawerPlacement,\n} from '../typing';\nimport ColumnsConfig from './columnsConfig.vue';\nimport PivotConfigComp from './pivotConfig.vue';\nimport PivotChartsConfigComp from './pivotChartsConfig.vue';\nimport ViewsConfig from './viewsConfig.vue';\nimport valueFieldConfig from './valueFieldConfig.vue';\nimport { useTableProState } from '../utils';\n\ninterface Props {\n columns: Array<Column>;\n view_key: string;\n view_flag_name: string;\n is_view: boolean;\n component_type: string;\n pivotConfig: PivotConfig | null;\n pivotChartsConfig: PivotChartsConfig | null;\n settingMode: SettingMode;\n settingDrawerWidth: string | number;\n settingDrawerPlacement: SettingDrawerPlacement;\n}\n\nconst props = defineProps<Props>();\nconst emit = defineEmits(['change', 'pivotConfigChange', 'pivotChartsConfigChange']);\n\nconst activeTab = ref('columns');\nconst valueSettingOpen = ref(false);\nconst currentValueField = ref<PivotValueField | null>(null);\n// 高级配置抽屉关联的源组件(用于把抽屉内的修改回写到对应配置并向上 emit)\nconst currentValueFieldSource = ref<'pivot' | 'charts' | null>(null);\nconst pivotConfigRef = ref<InstanceType<typeof PivotConfigComp> | null>(null);\nconst pivotChartsConfigRef = ref<InstanceType<typeof PivotChartsConfigComp> | null>(null);\n\n// 左侧字段列表(叶子列)映射为 {field, label}\nconst fieldOptions = computed<PivotFieldOption[]>(() => {\n const buildTree = (cols: Column[]): PivotFieldOption[] => {\n return cols\n .map(col => {\n const label = col.title || col.dataIndex || col.key || '';\n const children = col.children?.length ? buildTree(col.children) : [];\n const isLeaf = children.length === 0;\n if (isLeaf) {\n if (!col.dataIndex || col.dataIndex === 'index' || col.dataIndex === 'action') {\n return null;\n }\n return {\n field: col.dataIndex,\n label,\n };\n }\n return {\n field: col.dataIndex || col.key || label,\n label,\n children,\n };\n })\n .filter(Boolean) as PivotFieldOption[];\n };\n return buildTree(props.columns || []);\n});\n\nconst flatFieldOptions = computed<PivotFieldOption[]>(() => {\n const flatten = (fields: PivotFieldOption[]): PivotFieldOption[] => {\n const list: PivotFieldOption[] = [];\n fields.forEach(field => {\n if (field.children?.length) {\n list.push(...flatten(field.children));\n } else {\n list.push({ field: field.field, label: field.label });\n }\n });\n return list;\n };\n return flatten(fieldOptions.value);\n});\n\n// 抽屉标题:表达式型显示「表达式 + 预览片段」,否则显示字段名\nconst valueFieldTitle = computed(() => {\n const v = currentValueField.value;\n if (!v) return '';\n if (v.useExpression && (v.expression || []).length) {\n const preview = (v.expression || [])\n .map(t => {\n if (t.type === 'field') {\n const f = flatFieldOptions.value.find(x => x.field === t.value);\n return f ? f.label : t.value;\n }\n if (t.type === 'operator') return t.value;\n if (t.type === 'number') return t.value;\n if (t.type === 'lparen') return '(';\n if (t.type === 'rparen') return ')';\n return '';\n })\n .join(' ');\n return `${\n props.component_type === 'pivot_charts' ? '透视图' : '透视表'\n } · ${'表达式'}: ${preview}`;\n }\n return `${'字段设置'}: ${v.label || v.field || ''}`;\n});\n\nconst onColumnChange = (data: any) => {\n emit('change', data);\n};\nconst onPivotConfigChange = (data: PivotConfig) => {\n emit('pivotConfigChange', data);\n};\nconst onPivotChartsConfigChange = (data: PivotChartsConfig) => {\n emit('pivotChartsConfigChange', data);\n};\nconst { fetchViews } = useTableProState();\nwatch(\n () => activeTab.value,\n val => {\n if (val === 'views') {\n fetchViews();\n }\n },\n);\n\n// 高级配置抽屉内的修改(表达式开关 / token 增删)由 valueFieldConfig 直接 mutate 共享对象,\n// 这里通过源组件暴露的 forceEmit 触发一次完整配置回传,确保后端重算与持久化拿到最新值。\nconst onValueFieldChange = () => {\n if (currentValueFieldSource.value === 'charts') {\n pivotChartsConfigRef.value?.forceEmit?.();\n } else {\n pivotConfigRef.value?.forceEmit?.();\n }\n};\n\nconst onOpenValueConfig = (field: PivotValueField) => {\n currentValueField.value = field;\n currentValueFieldSource.value = props.component_type === 'pivot_charts' ? 'charts' : 'pivot';\n valueSettingOpen.value = true;\n};\n\n// 当前配置中可引用的子表达式列表。\n// 注意:直接读 props.pivotConfig/pivotChartsConfig 只能拿到上层回灌的快照,\n// 会话内新增/编辑的子表达式(localConfig)不会实时同步到 props,导致「高级配置」里\n// 的表达式编辑器看不到已配置的子表达式。因此改为直接读取对应子组件暴露的实时列表。\nconst currentSubExpressions = computed<PivotSubExpression[]>(() => {\n const compRef =\n props.component_type === 'pivot_charts' ? pivotChartsConfigRef.value : pivotConfigRef.value;\n return compRef?.getSubExpressions?.() || [];\n});\n</script>\n\n<style lang=\"less\" scoped>\n.column-edit-view-wrapper {\n padding: 0 8px;\n height: 100%;\n}\n.tab-content-scroll {\n overflow-y: auto;\n height: 100%;\n}\n:deep(.ant-tabs) {\n height: 100%;\n}\n:deep(.ant-tabs-content) {\n height: 100%;\n}\n</style>\n","import { ConfirmModelConfig } from 'liyu-pc-base/api/typing';\n\nimport { useUserStore } from 'liyu-pc-base/init-plugin';\n\nimport { Modal } from 'ant-design-vue';\nimport Handlebars from 'handlebars';\nimport { HeadBtnConfig } from 'liyu-pc-base/api/typing';\nimport { useRequest } from 'liyu-pc-base/init-plugin';\nimport { getObjListBySelectedRowKeys } from 'liyu-pc-base/utils/function';\nimport { unref } from 'vue';\n\nexport const head_btn_confirm = ({\n dataSource,\n button,\n selectedRowKeys,\n event_obj,\n}: {\n dataSource: any[];\n button: HeadBtnConfig;\n selectedRowKeys: any[];\n event_obj: any;\n}) => {\n const request = useRequest();\n selectedRowKeys = unref(selectedRowKeys);\n const preProcessData = button.preProcessData;\n const model_config = button.model_config as ConfirmModelConfig;\n const init_data = model_config.init_data;\n const batch_api_fields = model_config.batch_api_fields;\n const title = model_config.title;\n const content = model_config.content;\n const ok_url = model_config.ok_url;\n const cancel_url = model_config.cancel_url;\n const method = model_config.method || 'post';\n const userStore = useUserStore();\n const current_org = userStore.currentOrg; // 当前组织org\n const org_id = current_org?.id;\n let api_data = dataSource as any;\n if (preProcessData) {\n api_data = preProcessData({ dataSource, selectedRowKeys });\n }\n if (batch_api_fields) {\n api_data = getObjListBySelectedRowKeys(batch_api_fields, dataSource, selectedRowKeys);\n }\n if (init_data) {\n api_data = { ...api_data, ...init_data };\n }\n api_data = { ...api_data, org_id };\n\n const titleTemplate = Handlebars.compile(title || '');\n const contentTemplate = Handlebars.compile(content || '');\n\n Modal.confirm({\n title: titleTemplate({ record: api_data }),\n content: contentTemplate({ record: api_data }),\n onOk() {\n if (ok_url) {\n request[method](ok_url, api_data).then(() => {\n event_obj.emit('refresh');\n });\n }\n },\n onCancel() {\n if (cancel_url) {\n request[method](cancel_url, api_data).then(() => {\n event_obj.emit('refresh');\n });\n }\n },\n });\n};\n","import { ComponentModelConfig, HeadBtnConfig } from 'liyu-pc-base/api/typing';\n\nimport { useUserStore } from 'liyu-pc-base/init-plugin';\n\nimport Handlebars from 'handlebars';\nimport modalBox from 'liyu-pc-base/components/form-modal/modal-tools';\nimport { useRequest } from 'liyu-pc-base/init-plugin';\nimport { getObjListBySelectedRowKeys } from 'liyu-pc-base/utils/function';\nimport { reactive, unref } from 'vue';\n\nexport const head_btn_custom_component = ({\n dataSource,\n button,\n selectedRowKeys,\n event_obj,\n}: {\n dataSource: any[];\n button: HeadBtnConfig;\n selectedRowKeys: any[];\n event_obj: any;\n}) => {\n const request = useRequest();\n const preProcessData = button.preProcessData;\n const model_config = button.model_config as ComponentModelConfig;\n const title = model_config.title;\n const component = model_config.component;\n const ok_url = model_config.ok_url;\n const cancel_url = model_config.cancel_url;\n const method = model_config.method || 'post';\n const init_data = model_config.init_data;\n const batch_api_fields = model_config.batch_api_fields;\n const userStore = useUserStore();\n const current_org = userStore.currentOrg; // 当前组织org\n const org_id = current_org?.id;\n let api_data = dataSource;\n if (preProcessData) {\n api_data = preProcessData({ dataSource, selectedRowKeys });\n }\n if (init_data) {\n api_data = { ...api_data, ...init_data };\n }\n const tempModal = reactive({\n open: true,\n loading: false,\n component: component,\n record: unref(api_data),\n dataSource: dataSource,\n button: button,\n title: Handlebars.compile(title || '')({ record: api_data }),\n ok: (data: any) => {\n data = { ...data, org_id };\n if (batch_api_fields) {\n data = {\n ...data,\n ...getObjListBySelectedRowKeys(batch_api_fields, dataSource, selectedRowKeys),\n };\n }\n if (ok_url) {\n request[method](ok_url, data).then(() => {\n event_obj.emit('refresh');\n });\n }\n },\n cancel: (data: any) => {\n data = { ...data, org_id };\n if (batch_api_fields) {\n data = {\n ...data,\n ...getObjListBySelectedRowKeys(batch_api_fields, dataSource, selectedRowKeys),\n };\n }\n if (cancel_url) {\n request[method](cancel_url, data).then(() => {\n event_obj.emit('refresh');\n });\n }\n },\n });\n modalBox(tempModal);\n};\n","import { FormModelConfig, HeadBtnConfig } from 'liyu-pc-base/api/typing';\n\nimport { useUserStore } from 'liyu-pc-base/init-plugin';\n\nimport { message } from 'ant-design-vue';\nimport Handlebars from 'handlebars';\nimport modalBox from 'liyu-pc-base/components/form-modal/modal-tools';\nimport { useRequest } from 'liyu-pc-base/init-plugin';\nimport { getObjList, getObjListBySelectedRowKeys } from 'liyu-pc-base/utils/function';\nimport { reactive } from 'vue';\nexport const head_btn_form = ({\n dataSource,\n button,\n selectedRowKeys,\n event_obj,\n}: {\n dataSource: any[];\n button: HeadBtnConfig;\n selectedRowKeys: any[];\n event_obj: any;\n}) => {\n const request = useRequest();\n const preProcessData = button.preProcessData;\n const model_config = button.model_config as FormModelConfig;\n const title = model_config.title;\n const ok_url = model_config.ok_url;\n const cancel_url = model_config.cancel_url;\n const method = model_config.method || 'post';\n const init_data = model_config.init_data;\n const api_fields = model_config.api_fields;\n const batch_api_fields = model_config.batch_api_fields;\n const form_config = model_config.form;\n const userStore = useUserStore();\n const current_org = userStore.currentOrg; // 当前组织org\n const org_id = current_org?.id;\n\n let record = null;\n if (preProcessData) {\n record = preProcessData({ dataSource, selectedRowKeys });\n } else {\n record = dataSource.find(item => selectedRowKeys.includes(item.id));\n }\n\n let model_data = {\n ...form_config.model,\n ...record,\n };\n if (api_fields) {\n model_data = {\n ...model_data,\n ...getObjList(api_fields, record),\n };\n }\n if (init_data) {\n model_data = {\n ...model_data,\n ...init_data,\n };\n }\n // 创建表单模态框配置\n const form_modal_config = reactive({\n settings: form_config.settings || {\n labelAlign: 'right',\n layout: 'vertical',\n col: [],\n },\n dynamicFieldList: form_config.dynamicFieldList || [],\n model: reactive(model_data),\n rules: form_config.rules || {},\n options: form_config.options || {},\n });\n\n // 创建模态框\n const tempModal = reactive({\n open: true,\n loading: false,\n title: Handlebars.compile(title || '')({ record: model_data }),\n form: form_modal_config,\n ok: (data: any) => {\n return new Promise(function (resolve, reject) {\n if (!ok_url) {\n resolve(null);\n return;\n }\n tempModal.loading = true;\n let submit_data = data;\n // if (api_fields) {\n // submit_data = {\n // ...submit_data,\n // ...getObjList(api_fields, data),\n // }\n // }\n if (batch_api_fields) {\n submit_data = {\n ...submit_data,\n ...getObjListBySelectedRowKeys(batch_api_fields, dataSource, selectedRowKeys),\n };\n }\n if (button.beforeCommitProcessData) {\n submit_data = button.beforeCommitProcessData({\n dataSource,\n selectedRowKeys,\n data: submit_data,\n });\n }\n submit_data.org_id = org_id;\n\n request({\n url: ok_url,\n method: method,\n data: submit_data,\n })\n .then(() => {\n message.success('操作成功');\n event_obj.emit('refresh');\n resolve(null);\n })\n .catch(error => {\n reject(error);\n })\n .finally(() => {\n tempModal.loading = false;\n });\n });\n },\n cancel: (data: any) => {\n return new Promise(function (resolve, reject) {\n if (!cancel_url) {\n resolve(null);\n return;\n }\n tempModal.loading = true;\n\n let submit_data = data;\n // if (api_fields) {\n // submit_data = {\n // ...submit_data,\n // ...getObjList(api_fields, data),\n // }\n // }\n if (batch_api_fields) {\n submit_data = {\n ...submit_data,\n ...getObjListBySelectedRowKeys(batch_api_fields, dataSource, selectedRowKeys),\n };\n }\n if (button.beforeCommitProcessData) {\n submit_data = button.beforeCommitProcessData({\n dataSource,\n selectedRowKeys,\n data: submit_data,\n });\n }\n submit_data.org_id = org_id;\n\n request({\n url: cancel_url,\n method: method,\n data: submit_data,\n })\n .then(() => {\n event_obj.emit('refresh');\n resolve(null);\n })\n .catch(error => {\n reject(error);\n })\n .finally(() => {\n tempModal.loading = false;\n });\n });\n },\n });\n\n // 显示模态框\n modalBox(tempModal);\n};\n","<template>\n <template\n v-for=\"(item, index) in children_list\"\n :key=\"index\"\n >\n <template v-if=\"item?.children && item.children.length > 0\">\n <a-sub-menu\n :key=\"index\"\n :title=\"item.name\"\n :disabled=\"item.disabled\"\n >\n <head-btn-children\n :children_list=\"item.children\"\n :selected-row-keys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :current_org=\"current_org\"\n :handleBtnClick=\"handleBtnClick\"\n />\n </a-sub-menu>\n </template>\n <template v-else>\n <a-menu-item\n :key=\"index\"\n :disabled=\"item.disabled\"\n @click=\"\n () => {\n handleBtnClick(item, {\n searchModel: searchConfig.model,\n dataSource: dataSource,\n selectedRowKeys: selectedRowKeys,\n searchConfigOptions: searchConfig.options,\n current_org: current_org,\n });\n }\n \"\n >\n {{ item.name }}\n </a-menu-item>\n </template>\n </template>\n</template>\n<script lang=\"ts\">\nimport { HeadBtnListConfig } from 'liyu-pc-base/api/typing';\nimport { defineComponent, PropType } from 'vue';\n\nexport default defineComponent({\n name: 'HeadBtnChildren',\n props: {\n children_list: {\n type: Array as PropType<HeadBtnListConfig>,\n required: true,\n },\n selectedRowKeys: {\n type: Array as PropType<any[]>,\n required: true,\n },\n dataSource: {\n type: Array as PropType<any[]>,\n required: true,\n },\n current_org: {\n type: Object as PropType<any>,\n required: true,\n },\n handleBtnClick: {\n type: Function as PropType<any>,\n required: true,\n },\n },\n setup(props) {\n return {};\n },\n});\n</script>\n","<template>\n <template\n v-for=\"(item, index) in children_list\"\n :key=\"index\"\n >\n <template v-if=\"item?.children && item.children.length > 0\">\n <a-sub-menu\n :key=\"index\"\n :title=\"item.name\"\n :disabled=\"item.disabled\"\n >\n <head-btn-children\n :children_list=\"item.children\"\n :selected-row-keys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :current_org=\"current_org\"\n :handleBtnClick=\"handleBtnClick\"\n />\n </a-sub-menu>\n </template>\n <template v-else>\n <a-menu-item\n :key=\"index\"\n :disabled=\"item.disabled\"\n @click=\"\n () => {\n handleBtnClick(item, {\n searchModel: searchConfig.model,\n dataSource: dataSource,\n selectedRowKeys: selectedRowKeys,\n searchConfigOptions: searchConfig.options,\n current_org: current_org,\n });\n }\n \"\n >\n {{ item.name }}\n </a-menu-item>\n </template>\n </template>\n</template>\n<script lang=\"ts\">\nimport { HeadBtnListConfig } from 'liyu-pc-base/api/typing';\nimport { defineComponent, PropType } from 'vue';\n\nexport default defineComponent({\n name: 'HeadBtnChildren',\n props: {\n children_list: {\n type: Array as PropType<HeadBtnListConfig>,\n required: true,\n },\n selectedRowKeys: {\n type: Array as PropType<any[]>,\n required: true,\n },\n dataSource: {\n type: Array as PropType<any[]>,\n required: true,\n },\n current_org: {\n type: Object as PropType<any>,\n required: true,\n },\n handleBtnClick: {\n type: Function as PropType<any>,\n required: true,\n },\n },\n setup(props) {\n return {};\n },\n});\n</script>\n","<template>\n <template\n v-for=\"(btn, index) in filter_head_btn_list\"\n :key=\"index\"\n >\n <a-button\n v-if=\"!btn.children || btn.children?.length == 0\"\n :type=\"btn.clazz\"\n size=\"small\"\n style=\"margin-right: 5px\"\n :disabled=\"btn.disabled\"\n @click=\"\n handleBtnClick(btn, {\n dataSource: dataSource,\n selectedRowKeys: selectedRowKeys,\n current_org: current_org,\n router: router,\n })\n \"\n >\n <plus-outlined v-if=\"btn.icon === 'plus-outlined'\" />\n {{ btn.name }}\n </a-button>\n <a-dropdown\n v-else\n v-model:open=\"isMenuOpen[index]\"\n trigger=\"click\"\n >\n <a-button\n :bordered=\"false\"\n size=\"small\"\n >\n {{ btn.name }}\n <down-outlined\n v-if=\"!isMenuOpen[index]\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n <up-outlined\n v-if=\"isMenuOpen[index]\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n </a-button>\n <template #overlay>\n <a-menu>\n <head-btn-children\n :children_list=\"btn.children\"\n :selected-row-keys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :current_org=\"current_org\"\n :handleBtnClick=\"handleBtnClick\"\n />\n </a-menu>\n </template>\n </a-dropdown>\n </template>\n</template>\n<script lang=\"ts\">\nimport { defineComponent, computed, PropType, ref } from 'vue';\nimport { checkCondition, nested_sort_list } from 'liyu-pc-base/utils/function';\nimport { HeadBtnListConfig } from 'liyu-pc-base/api/typing';\nimport { head_btn_confirm } from './head_btn_confirm';\nimport { head_btn_custom_component } from './head_btn_custom_component';\nimport { head_btn_form } from './head_btn_form';\nimport { useI18n } from 'vue-i18n';\nimport { cloneDeep } from 'lodash';\nimport { useRoute } from 'vue-router';\nimport HeadBtnChildren from './headBtnChildren.vue';\n\nexport default defineComponent({\n name: 'HeadBtn',\n props: {\n auth_batch_update_list: {\n type: Array as PropType<HeadBtnListConfig>,\n required: true,\n },\n auth_head_btn_list: {\n type: Array as PropType<HeadBtnListConfig>,\n required: true,\n },\n selectedRowKeys: {\n type: Array as PropType<any[]>,\n required: true,\n },\n dataSource: {\n type: Array as PropType<any[]>,\n required: true,\n },\n current_org: {\n type: Object as PropType<any>,\n required: true,\n },\n router: {\n type: Object as PropType<any>,\n required: true,\n },\n event_obj: {\n type: Object as PropType<any>,\n required: true,\n },\n },\n components: {\n HeadBtnChildren,\n },\n setup(props) {\n const { t } = useI18n();\n const isMenuOpen = ref<Record<string, boolean>>({});\n const route = useRoute();\n const process_list = (\n list: any,\n {\n dataSource,\n selectedRowKeys,\n batchList,\n }: { dataSource: any[]; selectedRowKeys: any[]; batchList: any[] | null },\n ) => {\n if (batchList?.length > 0) {\n const batchGroup = list.find((item: any) => item.name === '批量操作');\n if (batchGroup) {\n if (!batchGroup.children || batchGroup.children.length == 0) {\n batchGroup.children = batchList;\n } else {\n batchGroup.children = [...batchGroup.children, ...batchList];\n }\n } else {\n list.push({\n name: '批量操作',\n order: 1000,\n children: batchList,\n });\n }\n }\n const result = list.filter((button: any) => {\n let data = dataSource;\n // if (button.preProcessData) {\n // data = button.preProcessData({ dataSource: data, selectedRowKeys });\n // }\n let isDisabled = false;\n if (typeof button.is_disabled === 'function') {\n isDisabled = button.is_disabled({ dataSource: data, selectedRowKeys: selectedRowKeys });\n }\n if (typeof button.is_disabled === 'boolean') {\n isDisabled = button.is_disabled;\n }\n if (button.disabled_condition && button.disabled_condition.length > 0) {\n isDisabled = checkCondition({\n conditions: button.disabled_condition,\n dataSource: data,\n selectedRowKeys: selectedRowKeys,\n });\n }\n button.disabled = isDisabled;\n\n if (button.show_condition && button.show_condition.length > 0) {\n return checkCondition({\n conditions: button.show_condition,\n dataSource: data,\n selectedRowKeys: selectedRowKeys,\n });\n }\n\n if (typeof button.is_show === 'function') {\n return button.is_show({ dataSource: data, selectedRowKeys: selectedRowKeys });\n }\n if (typeof button.is_show === 'boolean') {\n return button.is_show;\n }\n return true;\n });\n result.forEach((item: any) => {\n if (item.children) {\n item.children = process_list(item.children, {\n dataSource: dataSource,\n selectedRowKeys: selectedRowKeys,\n batchList: null,\n });\n }\n });\n return result;\n };\n const filter_head_btn_list = computed(() => {\n if (!props.auth_head_btn_list) return [];\n const list = process_list(cloneDeep(props.auth_head_btn_list), {\n dataSource: props.dataSource,\n selectedRowKeys: props.selectedRowKeys,\n batchList: props.auth_batch_update_list,\n });\n nested_sort_list(list);\n return list;\n });\n // 处理按钮点击事件\n const handleBtnClick = (btn: any, params: any) => {\n let action_func = null;\n let query_json = {};\n if (route.query?.query_json) {\n query_json = JSON.parse(route.query?.query_json as any);\n }\n const routeQueryParams = JSON.parse(JSON.stringify({ ...route.query, ...query_json }));\n if (!btn.model_kind) {\n action_func = btn.cb;\n } else if (btn.model_kind === 'confirm') {\n action_func = head_btn_confirm;\n } else if (btn.model_kind === 'custom_component') {\n action_func = head_btn_custom_component;\n } else if (btn.model_kind === 'form') {\n action_func = head_btn_form;\n }\n\n if (!action_func) return;\n\n action_func({\n ...params,\n button: btn,\n t: t,\n current_org: props.current_org,\n routeQueryParams: routeQueryParams,\n event_obj: props.event_obj,\n });\n };\n return {\n filter_head_btn_list,\n handleBtnClick,\n isMenuOpen,\n };\n },\n});\n</script>\n","<template>\n <template\n v-for=\"(btn, index) in filter_head_btn_list\"\n :key=\"index\"\n >\n <a-button\n v-if=\"!btn.children || btn.children?.length == 0\"\n :type=\"btn.clazz\"\n size=\"small\"\n style=\"margin-right: 5px\"\n :disabled=\"btn.disabled\"\n @click=\"\n handleBtnClick(btn, {\n dataSource: dataSource,\n selectedRowKeys: selectedRowKeys,\n current_org: current_org,\n router: router,\n })\n \"\n >\n <plus-outlined v-if=\"btn.icon === 'plus-outlined'\" />\n {{ btn.name }}\n </a-button>\n <a-dropdown\n v-else\n v-model:open=\"isMenuOpen[index]\"\n trigger=\"click\"\n >\n <a-button\n :bordered=\"false\"\n size=\"small\"\n >\n {{ btn.name }}\n <down-outlined\n v-if=\"!isMenuOpen[index]\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n <up-outlined\n v-if=\"isMenuOpen[index]\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n </a-button>\n <template #overlay>\n <a-menu>\n <head-btn-children\n :children_list=\"btn.children\"\n :selected-row-keys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :current_org=\"current_org\"\n :handleBtnClick=\"handleBtnClick\"\n />\n </a-menu>\n </template>\n </a-dropdown>\n </template>\n</template>\n<script lang=\"ts\">\nimport { defineComponent, computed, PropType, ref } from 'vue';\nimport { checkCondition, nested_sort_list } from 'liyu-pc-base/utils/function';\nimport { HeadBtnListConfig } from 'liyu-pc-base/api/typing';\nimport { head_btn_confirm } from './head_btn_confirm';\nimport { head_btn_custom_component } from './head_btn_custom_component';\nimport { head_btn_form } from './head_btn_form';\nimport { useI18n } from 'vue-i18n';\nimport { cloneDeep } from 'lodash';\nimport { useRoute } from 'vue-router';\nimport HeadBtnChildren from './headBtnChildren.vue';\n\nexport default defineComponent({\n name: 'HeadBtn',\n props: {\n auth_batch_update_list: {\n type: Array as PropType<HeadBtnListConfig>,\n required: true,\n },\n auth_head_btn_list: {\n type: Array as PropType<HeadBtnListConfig>,\n required: true,\n },\n selectedRowKeys: {\n type: Array as PropType<any[]>,\n required: true,\n },\n dataSource: {\n type: Array as PropType<any[]>,\n required: true,\n },\n current_org: {\n type: Object as PropType<any>,\n required: true,\n },\n router: {\n type: Object as PropType<any>,\n required: true,\n },\n event_obj: {\n type: Object as PropType<any>,\n required: true,\n },\n },\n components: {\n HeadBtnChildren,\n },\n setup(props) {\n const { t } = useI18n();\n const isMenuOpen = ref<Record<string, boolean>>({});\n const route = useRoute();\n const process_list = (\n list: any,\n {\n dataSource,\n selectedRowKeys,\n batchList,\n }: { dataSource: any[]; selectedRowKeys: any[]; batchList: any[] | null },\n ) => {\n if (batchList?.length > 0) {\n const batchGroup = list.find((item: any) => item.name === '批量操作');\n if (batchGroup) {\n if (!batchGroup.children || batchGroup.children.length == 0) {\n batchGroup.children = batchList;\n } else {\n batchGroup.children = [...batchGroup.children, ...batchList];\n }\n } else {\n list.push({\n name: '批量操作',\n order: 1000,\n children: batchList,\n });\n }\n }\n const result = list.filter((button: any) => {\n let data = dataSource;\n // if (button.preProcessData) {\n // data = button.preProcessData({ dataSource: data, selectedRowKeys });\n // }\n let isDisabled = false;\n if (typeof button.is_disabled === 'function') {\n isDisabled = button.is_disabled({ dataSource: data, selectedRowKeys: selectedRowKeys });\n }\n if (typeof button.is_disabled === 'boolean') {\n isDisabled = button.is_disabled;\n }\n if (button.disabled_condition && button.disabled_condition.length > 0) {\n isDisabled = checkCondition({\n conditions: button.disabled_condition,\n dataSource: data,\n selectedRowKeys: selectedRowKeys,\n });\n }\n button.disabled = isDisabled;\n\n if (button.show_condition && button.show_condition.length > 0) {\n return checkCondition({\n conditions: button.show_condition,\n dataSource: data,\n selectedRowKeys: selectedRowKeys,\n });\n }\n\n if (typeof button.is_show === 'function') {\n return button.is_show({ dataSource: data, selectedRowKeys: selectedRowKeys });\n }\n if (typeof button.is_show === 'boolean') {\n return button.is_show;\n }\n return true;\n });\n result.forEach((item: any) => {\n if (item.children) {\n item.children = process_list(item.children, {\n dataSource: dataSource,\n selectedRowKeys: selectedRowKeys,\n batchList: null,\n });\n }\n });\n return result;\n };\n const filter_head_btn_list = computed(() => {\n if (!props.auth_head_btn_list) return [];\n const list = process_list(cloneDeep(props.auth_head_btn_list), {\n dataSource: props.dataSource,\n selectedRowKeys: props.selectedRowKeys,\n batchList: props.auth_batch_update_list,\n });\n nested_sort_list(list);\n return list;\n });\n // 处理按钮点击事件\n const handleBtnClick = (btn: any, params: any) => {\n let action_func = null;\n let query_json = {};\n if (route.query?.query_json) {\n query_json = JSON.parse(route.query?.query_json as any);\n }\n const routeQueryParams = JSON.parse(JSON.stringify({ ...route.query, ...query_json }));\n if (!btn.model_kind) {\n action_func = btn.cb;\n } else if (btn.model_kind === 'confirm') {\n action_func = head_btn_confirm;\n } else if (btn.model_kind === 'custom_component') {\n action_func = head_btn_custom_component;\n } else if (btn.model_kind === 'form') {\n action_func = head_btn_form;\n }\n\n if (!action_func) return;\n\n action_func({\n ...params,\n button: btn,\n t: t,\n current_org: props.current_org,\n routeQueryParams: routeQueryParams,\n event_obj: props.event_obj,\n });\n };\n return {\n filter_head_btn_list,\n handleBtnClick,\n isMenuOpen,\n };\n },\n});\n</script>\n","<template>\n <a-popover>\n <template #content>\n <a-button\n size=\"small\"\n style=\"margin: 10px 5px\"\n :type=\"refreshType === '手动' ? 'primary' : 'default'\"\n @click=\"selectRefreshType('手动')\"\n >\n {{ $t('手动刷新') }}\n </a-button>\n <a-dropdown class=\"dropdown\">\n <a-button\n size=\"small\"\n style=\"margin: 10px 5px\"\n :type=\"refreshType === '手动' ? 'default' : 'primary'\"\n >\n {{ $t('自动刷新') }}\n <span v-if=\"refreshType !== '手动'\">({{ refreshType }})</span>\n </a-button>\n <template #overlay>\n <a-menu>\n <a-menu-item>\n <a\n href=\"javascript:;\"\n @click=\"selectRefreshType('5s')\"\n >\n 5s\n </a>\n </a-menu-item>\n <a-menu-item>\n <a\n href=\"javascript:;\"\n @click=\"selectRefreshType('10s')\"\n >\n 10s\n </a>\n </a-menu-item>\n <a-menu-item>\n <a\n href=\"javascript:;\"\n @click=\"selectRefreshType('30s')\"\n >\n 30s\n </a>\n </a-menu-item>\n </a-menu>\n </template>\n </a-dropdown>\n </template>\n <reload-outlined\n v-if=\"refreshType === '手动'\"\n @click=\"refresh\"\n />\n <loading-outlined v-if=\"(countdown == -1 || loading) && refreshType !== '手动'\" />\n <span v-if=\"countdown >= 0 && !loading\">{{ countdown + 1 }}s</span>\n </a-popover>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref, onActivated, onDeactivated, onUnmounted, watch } from 'vue';\nimport { LoadingOutlined, ReloadOutlined } from '@ant-design/icons-vue';\nexport default defineComponent({\n name: 'refreshTypeModal',\n props: {\n refreshType: {\n type: String,\n default: () => '手动',\n },\n loading: {\n type: Boolean,\n default: () => false,\n },\n },\n components: {\n LoadingOutlined,\n ReloadOutlined,\n },\n emits: ['refresh', 'typeChange'],\n setup(props, { emit }) {\n let init = false;\n const countdown: any = ref(0);\n const current_page: any = ref(true);\n\n // 刷新模式切换\n const selectRefreshType = (type: any) => {\n emit('typeChange', type);\n countdown.value = -1;\n if (props.refreshType == '手动') {\n emit('refresh');\n }\n };\n const timer_countdown = setInterval(() => {\n if (countdown.value < 0) {\n switch (props.refreshType) {\n case '手动':\n countdown.value = -1;\n break;\n case '5s':\n countdown.value = 4;\n break;\n case '10s':\n countdown.value = 9;\n break;\n case '30s':\n countdown.value = 29;\n }\n } else if (countdown.value == 0) {\n if (current_page.value && !props.loading && init) {\n emit('refresh', true);\n }\n countdown.value = -1;\n } else {\n countdown.value--;\n }\n }, 1000);\n\n watch(\n () => props.loading,\n () => {\n if (!props.loading) {\n countdown.value = -1;\n init = true;\n }\n },\n );\n\n onActivated(() => {\n if (!current_page.value) {\n current_page.value = true;\n }\n });\n onDeactivated(() => {\n current_page.value = false;\n });\n\n onUnmounted(() => {\n clearInterval(timer_countdown);\n });\n\n const refresh = () => {\n emit('refresh');\n };\n return {\n props,\n countdown,\n refresh,\n selectRefreshType,\n };\n },\n});\n</script>\n","<template>\n <a-popover>\n <template #content>\n <a-button\n size=\"small\"\n style=\"margin: 10px 5px\"\n :type=\"refreshType === '手动' ? 'primary' : 'default'\"\n @click=\"selectRefreshType('手动')\"\n >\n {{ $t('手动刷新') }}\n </a-button>\n <a-dropdown class=\"dropdown\">\n <a-button\n size=\"small\"\n style=\"margin: 10px 5px\"\n :type=\"refreshType === '手动' ? 'default' : 'primary'\"\n >\n {{ $t('自动刷新') }}\n <span v-if=\"refreshType !== '手动'\">({{ refreshType }})</span>\n </a-button>\n <template #overlay>\n <a-menu>\n <a-menu-item>\n <a\n href=\"javascript:;\"\n @click=\"selectRefreshType('5s')\"\n >\n 5s\n </a>\n </a-menu-item>\n <a-menu-item>\n <a\n href=\"javascript:;\"\n @click=\"selectRefreshType('10s')\"\n >\n 10s\n </a>\n </a-menu-item>\n <a-menu-item>\n <a\n href=\"javascript:;\"\n @click=\"selectRefreshType('30s')\"\n >\n 30s\n </a>\n </a-menu-item>\n </a-menu>\n </template>\n </a-dropdown>\n </template>\n <reload-outlined\n v-if=\"refreshType === '手动'\"\n @click=\"refresh\"\n />\n <loading-outlined v-if=\"(countdown == -1 || loading) && refreshType !== '手动'\" />\n <span v-if=\"countdown >= 0 && !loading\">{{ countdown + 1 }}s</span>\n </a-popover>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref, onActivated, onDeactivated, onUnmounted, watch } from 'vue';\nimport { LoadingOutlined, ReloadOutlined } from '@ant-design/icons-vue';\nexport default defineComponent({\n name: 'refreshTypeModal',\n props: {\n refreshType: {\n type: String,\n default: () => '手动',\n },\n loading: {\n type: Boolean,\n default: () => false,\n },\n },\n components: {\n LoadingOutlined,\n ReloadOutlined,\n },\n emits: ['refresh', 'typeChange'],\n setup(props, { emit }) {\n let init = false;\n const countdown: any = ref(0);\n const current_page: any = ref(true);\n\n // 刷新模式切换\n const selectRefreshType = (type: any) => {\n emit('typeChange', type);\n countdown.value = -1;\n if (props.refreshType == '手动') {\n emit('refresh');\n }\n };\n const timer_countdown = setInterval(() => {\n if (countdown.value < 0) {\n switch (props.refreshType) {\n case '手动':\n countdown.value = -1;\n break;\n case '5s':\n countdown.value = 4;\n break;\n case '10s':\n countdown.value = 9;\n break;\n case '30s':\n countdown.value = 29;\n }\n } else if (countdown.value == 0) {\n if (current_page.value && !props.loading && init) {\n emit('refresh', true);\n }\n countdown.value = -1;\n } else {\n countdown.value--;\n }\n }, 1000);\n\n watch(\n () => props.loading,\n () => {\n if (!props.loading) {\n countdown.value = -1;\n init = true;\n }\n },\n );\n\n onActivated(() => {\n if (!current_page.value) {\n current_page.value = true;\n }\n });\n onDeactivated(() => {\n current_page.value = false;\n });\n\n onUnmounted(() => {\n clearInterval(timer_countdown);\n });\n\n const refresh = () => {\n emit('refresh');\n };\n return {\n props,\n countdown,\n refresh,\n selectRefreshType,\n };\n },\n});\n</script>\n","<template>\n <a-tooltip :title=\"$t('分组')\">\n <a-popover\n title=\"\"\n trigger=\"click\"\n placement=\"bottom\"\n >\n <template #content>\n <div v-if=\"selectedConditionGroup\">\n <a-menu>\n <a-menu-item @click=\"handleClearConditionGroup\">\n {{ $t('取消分组') }}\n </a-menu-item>\n </a-menu>\n </div>\n <a-input\n v-model:value=\"searchGroupKeyword\"\n :allowClear=\"true\"\n :placeholder=\"$t('搜索...')\"\n style=\"width: 240px; margin-right: 25px\"\n >\n <template #prefix>\n <search-outlined />\n </template>\n </a-input>\n <div>\n <a-menu :selectedKeys=\"selectedKeys\">\n <template\n v-for=\"item in condition_group_config_list_filtered\"\n :key=\"item.field_name\"\n >\n <a-menu-item @click=\"handleSetSelectedConditionGroup(item)\">\n {{ $t('按{label}分组', { label: item.label }) }}\n </a-menu-item>\n </template>\n </a-menu>\n </div>\n </template>\n <group-outlined :style=\"selectedConditionGroup ? 'color: #1890ff' : ''\" />\n </a-popover>\n </a-tooltip>\n</template>\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue';\nimport { ConditionGroupConfigItem, Table } from './typing';\n\ninterface Props {\n config: Table;\n selfEventBus: any;\n}\nconst props = withDefaults(defineProps<Props>(), {\n config: null,\n});\nconst emit = defineEmits(['change']);\nconst selectedConditionGroup = defineModel('selectedConditionGroup', {\n default: null,\n});\nconst groupConditionValue = defineModel('groupConditionValue', {\n default: null,\n});\nconst searchGroupKeyword = ref('');\nconst condition_group_config_list_filtered = computed(() => {\n return props.config?.condition_group_config_list?.filter(item => {\n return item.label.includes(searchGroupKeyword.value);\n });\n});\nconst selectedKeys = computed(() => {\n return selectedConditionGroup.value?.field_name ? [selectedConditionGroup.value.field_name] : [];\n});\n\nconst handleClearConditionGroup = () => {\n groupConditionValue.value = null;\n selectedConditionGroup.value = null;\n emit('change', selectedConditionGroup.value);\n props.selfEventBus.emit('refresh');\n};\nconst handleSetSelectedConditionGroup = (item: ConditionGroupConfigItem) => {\n groupConditionValue.value = null;\n selectedConditionGroup.value = item;\n emit('change', selectedConditionGroup.value);\n props.selfEventBus.emit('refresh');\n};\n</script>\n","<template>\n <a-tooltip :title=\"$t('分组')\">\n <a-popover\n title=\"\"\n trigger=\"click\"\n placement=\"bottom\"\n >\n <template #content>\n <div v-if=\"selectedConditionGroup\">\n <a-menu>\n <a-menu-item @click=\"handleClearConditionGroup\">\n {{ $t('取消分组') }}\n </a-menu-item>\n </a-menu>\n </div>\n <a-input\n v-model:value=\"searchGroupKeyword\"\n :allowClear=\"true\"\n :placeholder=\"$t('搜索...')\"\n style=\"width: 240px; margin-right: 25px\"\n >\n <template #prefix>\n <search-outlined />\n </template>\n </a-input>\n <div>\n <a-menu :selectedKeys=\"selectedKeys\">\n <template\n v-for=\"item in condition_group_config_list_filtered\"\n :key=\"item.field_name\"\n >\n <a-menu-item @click=\"handleSetSelectedConditionGroup(item)\">\n {{ $t('按{label}分组', { label: item.label }) }}\n </a-menu-item>\n </template>\n </a-menu>\n </div>\n </template>\n <group-outlined :style=\"selectedConditionGroup ? 'color: #1890ff' : ''\" />\n </a-popover>\n </a-tooltip>\n</template>\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue';\nimport { ConditionGroupConfigItem, Table } from './typing';\n\ninterface Props {\n config: Table;\n selfEventBus: any;\n}\nconst props = withDefaults(defineProps<Props>(), {\n config: null,\n});\nconst emit = defineEmits(['change']);\nconst selectedConditionGroup = defineModel('selectedConditionGroup', {\n default: null,\n});\nconst groupConditionValue = defineModel('groupConditionValue', {\n default: null,\n});\nconst searchGroupKeyword = ref('');\nconst condition_group_config_list_filtered = computed(() => {\n return props.config?.condition_group_config_list?.filter(item => {\n return item.label.includes(searchGroupKeyword.value);\n });\n});\nconst selectedKeys = computed(() => {\n return selectedConditionGroup.value?.field_name ? [selectedConditionGroup.value.field_name] : [];\n});\n\nconst handleClearConditionGroup = () => {\n groupConditionValue.value = null;\n selectedConditionGroup.value = null;\n emit('change', selectedConditionGroup.value);\n props.selfEventBus.emit('refresh');\n};\nconst handleSetSelectedConditionGroup = (item: ConditionGroupConfigItem) => {\n groupConditionValue.value = null;\n selectedConditionGroup.value = item;\n emit('change', selectedConditionGroup.value);\n props.selfEventBus.emit('refresh');\n};\n</script>\n","<template>\n <div style=\"max-height: 156px; overflow-y: auto\">\n <a-flex\n v-if=\"selectedConditionGroup && conditionGroupDataList?.length > 0\"\n style=\"padding-left: 24px; padding-bottom: 10px\"\n >\n <a-space\n wrap\n style=\"margin-bottom: 0\"\n >\n <span>{{ $t('按{label}分组', { label: selectedConditionGroup.label }) }}:</span>\n <a-tooltip\n placement=\"bottom\"\n v-for=\"item in conditionGroupDataList\"\n :key=\"item.group_info.value\"\n :title=\"`${item.group_info.display_name ? item.group_info.display_name : $t('空')}`\"\n >\n <a-button\n style=\"padding: 0\"\n :class=\"currentGroupConditionValue == item.group_info.value ? 'link-button' : ''\"\n :type=\"currentGroupConditionValue == item.group_info.value ? 'link' : 'text'\"\n :value=\"item.group_info.value\"\n @click=\"handleChangeConditionGroupValue(item.group_info.value)\"\n >\n <a-space :size=\"3\">\n <span\n :class=\"\n currentGroupConditionValue == item.group_info.value ? 'active-text' : 'text'\n \"\n >\n {{ item.group_info.display_name ? item.group_info.display_name : $t('空') }}\n </span>\n <a-badge\n :count=\"item.count\"\n :overflowCount=\"9999999999\"\n :number-style=\"\n currentGroupConditionValue == item.group_info.value\n ? { backgroundColor: '#f0f9ff', color: '#1b9aee', fontWeight: 600 }\n : { backgroundColor: '#f0f9ff', color: '#1b9aee' }\n \"\n />\n </a-space>\n </a-button>\n </a-tooltip>\n <span>\n <a-button\n type=\"link\"\n @click=\"handleClearConditionGroup\"\n >\n {{ $t('取消分组') }}\n </a-button>\n </span>\n </a-space>\n </a-flex>\n </div>\n</template>\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { Table } from './typing';\nimport { getCurrentGroupConditionValue } from './utils';\n\ninterface Props {\n config: Table;\n selfEventBus: any;\n}\nconst props = withDefaults(defineProps<Props>(), {\n config: null,\n});\nconst emit = defineEmits(['change']);\nconst selectedConditionGroup = defineModel('selectedConditionGroup', {\n default: null,\n});\nconst conditionGroupDataList = defineModel<any[]>('conditionGroupDataList', {\n default: null,\n});\nconst groupConditionValue = defineModel('groupConditionValue', {\n default: null,\n});\nconst handleClearConditionGroup = () => {\n conditionGroupDataList.value = [];\n selectedConditionGroup.value = null;\n groupConditionValue.value = null;\n emit('change', groupConditionValue.value);\n props.selfEventBus.emit('refresh');\n};\nconst handleChangeConditionGroupValue = (value: string) => {\n groupConditionValue.value = value;\n emit('change', value);\n props.selfEventBus.emit('refresh');\n};\nconst currentGroupConditionValue = computed(() => {\n return getCurrentGroupConditionValue(conditionGroupDataList.value, groupConditionValue.value)\n ?.value;\n});\n</script>\n","<template>\n <div style=\"max-height: 156px; overflow-y: auto\">\n <a-flex\n v-if=\"selectedConditionGroup && conditionGroupDataList?.length > 0\"\n style=\"padding-left: 24px; padding-bottom: 10px\"\n >\n <a-space\n wrap\n style=\"margin-bottom: 0\"\n >\n <span>{{ $t('按{label}分组', { label: selectedConditionGroup.label }) }}:</span>\n <a-tooltip\n placement=\"bottom\"\n v-for=\"item in conditionGroupDataList\"\n :key=\"item.group_info.value\"\n :title=\"`${item.group_info.display_name ? item.group_info.display_name : $t('空')}`\"\n >\n <a-button\n style=\"padding: 0\"\n :class=\"currentGroupConditionValue == item.group_info.value ? 'link-button' : ''\"\n :type=\"currentGroupConditionValue == item.group_info.value ? 'link' : 'text'\"\n :value=\"item.group_info.value\"\n @click=\"handleChangeConditionGroupValue(item.group_info.value)\"\n >\n <a-space :size=\"3\">\n <span\n :class=\"\n currentGroupConditionValue == item.group_info.value ? 'active-text' : 'text'\n \"\n >\n {{ item.group_info.display_name ? item.group_info.display_name : $t('空') }}\n </span>\n <a-badge\n :count=\"item.count\"\n :overflowCount=\"9999999999\"\n :number-style=\"\n currentGroupConditionValue == item.group_info.value\n ? { backgroundColor: '#f0f9ff', color: '#1b9aee', fontWeight: 600 }\n : { backgroundColor: '#f0f9ff', color: '#1b9aee' }\n \"\n />\n </a-space>\n </a-button>\n </a-tooltip>\n <span>\n <a-button\n type=\"link\"\n @click=\"handleClearConditionGroup\"\n >\n {{ $t('取消分组') }}\n </a-button>\n </span>\n </a-space>\n </a-flex>\n </div>\n</template>\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { Table } from './typing';\nimport { getCurrentGroupConditionValue } from './utils';\n\ninterface Props {\n config: Table;\n selfEventBus: any;\n}\nconst props = withDefaults(defineProps<Props>(), {\n config: null,\n});\nconst emit = defineEmits(['change']);\nconst selectedConditionGroup = defineModel('selectedConditionGroup', {\n default: null,\n});\nconst conditionGroupDataList = defineModel<any[]>('conditionGroupDataList', {\n default: null,\n});\nconst groupConditionValue = defineModel('groupConditionValue', {\n default: null,\n});\nconst handleClearConditionGroup = () => {\n conditionGroupDataList.value = [];\n selectedConditionGroup.value = null;\n groupConditionValue.value = null;\n emit('change', groupConditionValue.value);\n props.selfEventBus.emit('refresh');\n};\nconst handleChangeConditionGroupValue = (value: string) => {\n groupConditionValue.value = value;\n emit('change', value);\n props.selfEventBus.emit('refresh');\n};\nconst currentGroupConditionValue = computed(() => {\n return getCurrentGroupConditionValue(conditionGroupDataList.value, groupConditionValue.value)\n ?.value;\n});\n</script>\n","<template>\n <div\n class=\"ant-pro-table-list-toolbar\"\n v-if=\"!config?.hide_header\"\n ref=\"headerRef\"\n >\n <div class=\"ant-pro-table-list-toolbar-container\">\n <div class=\"ant-pro-table-list-toolbar-left\">\n <slot name=\"header_bar_before_title\"></slot>\n <div class=\"ant-pro-table-list-toolbar-title\">\n <span v-if=\"config?.show_title\">{{ config?.title }}</span>\n <div\n v-if=\"config?.show_current_view\"\n class=\"current-view-container\"\n >\n <a-tooltip\n v-if=\"currentView?.name\"\n :title=\"'视图:' + currentView?.name\"\n >\n <span class=\"current-view-name\">\n {{ currentView?.name }}\n </span>\n </a-tooltip>\n <template v-if=\"enableEditCurrentView\">\n <span style=\"font-size: 12px; margin-right: 5px; display: inline-block\">\n (已编辑)\n </span>\n <a-dropdown\n v-model:open=\"isDropdown\"\n trigger=\"click\"\n >\n <span class=\"view-menu\">\n {{ $t('保存') }}\n <down-outlined\n v-if=\"!isDropdown\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n <up-outlined\n v-if=\"isDropdown\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n </span>\n <template #overlay>\n <a-menu>\n <a-menu-item>\n <span\n @click=\"\n () => {\n isDropdown = false;\n selfEventBus.emit('saveView');\n }\n \"\n >\n {{ $t('保存视图') }}\n </span>\n </a-menu-item>\n <a-menu-item>\n <span\n @click=\"\n () => {\n isDropdown = false;\n selfEventBus.emit('saveNewView');\n }\n \"\n >\n {{ $t('另存为新视图') }}\n </span>\n </a-menu-item>\n </a-menu>\n </template>\n </a-dropdown>\n <span\n :bordered=\"false\"\n class=\"view-menu\"\n @click=\"\n () => {\n selfEventBus.emit('reinit');\n }\n \"\n >\n {{ $t('重置') }}\n </span>\n </template>\n </div>\n </div>\n <slot name=\"header_bar_after_title\"></slot>\n </div>\n <div class=\"ant-pro-table-list-toolbar-right\">\n <a-space align=\"center\">\n <slot name=\"header_bar_before_btn\"></slot>\n <head-btn\n :auth_batch_update_list=\"auth_batch_update_list\"\n :auth_head_btn_list=\"auth_head_btn_list\"\n :selectedRowKeys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :searchConfig=\"config.query_params || {}\"\n :current_org=\"current_org\"\n :router=\"router\"\n :event_obj=\"selfEventBus\"\n />\n <slot name=\"header_bar_after_btn\"></slot>\n </a-space>\n\n <div class=\"ant-pro-table-list-toolbar-divider\">\n <a-divider type=\"vertical\" />\n </div>\n\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"component_type === 'normal' && config?.condition_group_config_list?.length > 0\"\n >\n <ConditionGroupSelecter\n :config=\"config\"\n :selfEventBus=\"selfEventBus\"\n v-model:selectedConditionGroup=\"selectedConditionGroup\"\n v-model:groupConditionValue=\"groupConditionValue\"\n @change=\"handleSelectedConditionGroupChange\"\n />\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"config?.sys_btn?.refresh !== false\"\n >\n <refresh-type-modal\n :refreshType=\"refreshType\"\n :loading=\"loading\"\n @refresh=\"$emit('refresh')\"\n @typeChange=\"$emit('refreshTypeChange', $event)\"\n />\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"\n (component_type === 'normal' || component_type === 'pivot_table') &&\n config?.sys_btn?.export !== false\n \"\n >\n <a-tooltip :title=\"$t('导出Excel')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"$emit('export')\"\n >\n <template #icon>\n <download-outlined></download-outlined>\n </template>\n </a-button>\n </a-tooltip>\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"component_type === 'normal' && config?.sys_btn?.table_size !== false\"\n >\n <a-tooltip :title=\"$t('密度')\">\n <a-dropdown\n :trigger=\"['click']\"\n placement=\"bottomRight\"\n >\n <column-height-outlined />\n <template #overlay>\n <a-menu\n :selected-keys=\"[tableSize]\"\n style=\"width: 80px\"\n @click=\"\n ({ key }) => {\n $emit('update:tableSize', key as string);\n $emit('tableSizeChange', key as string);\n }\n \"\n >\n <a-menu-item key=\"default\">\n <a href=\"javascript:\">{{ $t('默认') }}</a>\n </a-menu-item>\n <a-menu-item key=\"middle\">\n <a href=\"javascript:\">{{ $t('中等') }}</a>\n </a-menu-item>\n <a-menu-item key=\"small\">\n <a href=\"javascript:\">{{ $t('紧凑') }}</a>\n </a-menu-item>\n </a-menu>\n </template>\n </a-dropdown>\n </a-tooltip>\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"config?.sys_btn?.column_config !== false\"\n >\n <a-tooltip :title=\"$t('设置')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"settingOpen = true\"\n >\n <template #icon>\n <setting-outlined />\n </template>\n </a-button>\n </a-tooltip>\n </div>\n\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"config?.sys_btn?.full_screen !== false\"\n >\n <a-tooltip :title=\"screenState ? $t('退出全屏') : $t('全屏')\">\n <fullscreen-outlined\n v-if=\"!screenState\"\n @click=\"$emit('setFull')\"\n />\n <fullscreen-exit-outlined\n v-else\n @click=\"$emit('exitFull')\"\n />\n </a-tooltip>\n </div>\n </div>\n </div>\n </div>\n <ConditionGroupView\n :config=\"config\"\n v-model:conditionGroupDataList=\"conditionGroupDataList\"\n v-model:selectedConditionGroup=\"selectedConditionGroup\"\n :selfEventBus=\"selfEventBus\"\n v-model:groupConditionValue=\"groupConditionValue\"\n @change=\"handleGroupConditionValueChange\"\n />\n <template v-if=\"settingMode === 'drawer'\">\n <a-drawer\n :title=\"$t('设置')\"\n :width=\"settingDrawerWidth\"\n :placement=\"settingDrawerPlacement\"\n v-model:open=\"settingOpen\"\n :mask=\"false\"\n :push=\"false\"\n >\n <column-edit-view\n :columns=\"columns\"\n :is_view=\"is_view\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :component_type=\"component_type\"\n :pivotConfig=\"localPivotConfig\"\n :pivotChartsConfig=\"localPivotChartsConfig\"\n :settingMode=\"settingMode\"\n :settingDrawerWidth=\"settingDrawerWidth\"\n :settingDrawerPlacement=\"settingDrawerPlacement\"\n @change=\"$emit('columnsChange', $event)\"\n @pivotConfigChange=\"onPivotConfigChange\"\n @pivotChartsConfigChange=\"onPivotChartsConfigChange\"\n ></column-edit-view>\n </a-drawer>\n </template>\n <a-modal\n v-else\n :title=\"$t('设置')\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n v-model:open=\"settingOpen\"\n >\n <column-edit-view\n :columns=\"columns\"\n :is_view=\"is_view\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :component_type=\"component_type\"\n :settingMode=\"settingMode\"\n :pivotConfig=\"localPivotConfig\"\n :pivotChartsConfig=\"localPivotChartsConfig\"\n :settingDrawerWidth=\"settingDrawerWidth\"\n :settingDrawerPlacement=\"settingDrawerPlacement\"\n @change=\"$emit('columnsChange', $event)\"\n @pivotConfigChange=\"onPivotConfigChange\"\n @pivotChartsConfigChange=\"onPivotChartsConfigChange\"\n ></column-edit-view>\n <template #footer>\n <a-button @click=\"settingOpen = false\">\n {{ $t('关闭') }}\n </a-button>\n </template>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n ColumnHeightOutlined,\n FullscreenExitOutlined,\n FullscreenOutlined,\n DownloadOutlined,\n SettingOutlined,\n} from '@ant-design/icons-vue';\nimport { useResizeObserver } from '@vueuse/core';\nimport { computed, ref, watch } from 'vue';\nimport columnEditView from './tableConfig/index.vue';\nimport HeadBtn from './headBtn.vue';\nimport RefreshTypeModal from './refresh-type-modal/index.vue';\nimport {\n Column,\n ConditionGroupConfigItem,\n Table,\n PivotConfig,\n PivotChartsConfig,\n SettingMode,\n SettingDrawerPlacement,\n} from './typing';\nimport { useTableProState } from './utils';\nimport ConditionGroupSelecter from './ConditionGroupSelecter.vue';\nimport ConditionGroupView from './ConditionGroupView.vue';\nimport { useUserStore } from 'liyu-pc-base/init-plugin';\nimport { cloneDeep } from 'lodash';\n\ninterface Props {\n config?: Table;\n auth_batch_update_list?: any[];\n auth_head_btn_list?: any[];\n selectedRowKeys?: any[];\n dataSource?: any[];\n current_org?: any;\n router?: any;\n selfEventBus?: any;\n loading?: boolean;\n screenState?: boolean;\n tableSize?: string;\n columns?: Column[];\n view_key?: string;\n view_flag_name?: string;\n is_view?: boolean;\n refreshType?: string;\n conditionsParams?: any;\n pivotConfig?: PivotConfig | null;\n pivotChartsConfig?: PivotChartsConfig | null;\n component_type?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n config: () => ({} as Table),\n auth_batch_update_list: () => [],\n auth_head_btn_list: () => [],\n selectedRowKeys: () => [],\n dataSource: () => [],\n loading: false,\n screenState: false,\n tableSize: 'small',\n columns: () => [],\n view_key: '',\n view_flag_name: '',\n is_view: true,\n refreshType: '手动',\n conditionsParams: () => null,\n pivotConfig: () => null,\n pivotChartsConfig: () => null,\n component_type: 'normal',\n});\n\nconst emit = defineEmits([\n 'refresh',\n 'export',\n 'setFull',\n 'exitFull',\n 'columnsChange',\n 'pivotConfigChange',\n 'pivotChartsConfigChange',\n 'viewSelected',\n 'update:tableSize',\n 'heightChange',\n 'refreshTypeChange',\n 'tableSizeChange',\n 'selectedConditionGroupChange',\n 'groupConditionValueChange',\n]);\nconst settingOpen = defineModel('settingOpen', {\n default: false,\n});\nconst userStore = useUserStore();\nconst current_org = userStore.currentOrg;\nconst VIEW_SYSTEM_EDIT = 'view_system_edit';\nconst is_view_system_edit_auth = ref(userStore.CHECK_AUTH(VIEW_SYSTEM_EDIT));\nconst enableEditCurrentView = computed(() => {\n if (currentView.value?.is_other_user_view) {\n return false;\n }\n if (currentView.value?.is_system && !is_view_system_edit_auth.value) {\n return false;\n }\n return isViewChange.value && currentView.value && currentView.value.id !== 'no-view';\n});\nconst selectedConditionGroup = defineModel('selectedConditionGroup', {\n default: null,\n});\nconst conditionGroupDataList = defineModel<any[]>('conditionGroupDataList', {\n default: null,\n});\nconst groupConditionValue = defineModel('groupConditionValue', {\n default: null,\n});\n// 「设置」弹窗交互方式:modal(模态框,默认)或 drawer(抽屉)\nconst settingMode = computed<SettingMode>(() => props.config?.setting_mode || 'modal');\nconst settingDrawerWidth = computed<string | number>(\n () => props.config?.setting_drawer_width ?? '700px',\n);\nconst settingDrawerPlacement = computed<SettingDrawerPlacement>(\n () => props.config?.setting_drawer_placement || 'left',\n);\nconst headerRef = ref(null);\nuseResizeObserver(headerRef, entries => {\n emit('heightChange', entries[0].contentRect.height);\n});\nconst { currentView, isViewChange } = useTableProState();\nconst isDropdown = ref(false);\nconst handleSelectedConditionGroupChange = (item: ConditionGroupConfigItem) => {\n emit('selectedConditionGroupChange', item);\n};\nconst handleGroupConditionValueChange = (value: any) => {\n emit('groupConditionValueChange', value);\n};\n\n// ============ 数据透视表相关 ============\nconst localPivotConfig = ref<PivotConfig>(\n props.pivotConfig\n ? cloneDeep(props.pivotConfig)\n : { rows: [], columns: [], values: [], indicatorsAsCol: true, rowHierarchyType: 'grid' },\n);\n\nwatch(\n () => props.pivotConfig,\n val => {\n if (val) {\n localPivotConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotConfigChange = (config: PivotConfig) => {\n emit('pivotConfigChange', config);\n};\n\n// ============ 数据透视图相关 ============\nconst localPivotChartsConfig = ref<PivotChartsConfig>(\n props.pivotChartsConfig\n ? cloneDeep(props.pivotChartsConfig)\n : { series: [], category: [], values: [], chartType: 'bar' },\n);\n\nwatch(\n () => props.pivotChartsConfig,\n val => {\n if (val) {\n localPivotChartsConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotChartsConfigChange = (config: PivotChartsConfig) => {\n emit('pivotChartsConfigChange', config);\n};\n</script>\n\n<style lang=\"less\" scoped>\n.ant-pro-table-list-toolbar-container {\n height: 30px;\n line-height: 30px;\n padding: 0 8px;\n}\n.ant-pro-table-list-toolbar-setting-item {\n font-size: 14px;\n}\n.current-view-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n display: inline-block;\n max-width: 150px;\n}\n.current-view-container {\n display: flex;\n height: 100%;\n padding: 2px;\n gap: 4px;\n}\n.view-menu {\n font-size: 12px;\n font-weight: normal;\n cursor: pointer;\n padding: 0 4px;\n background: #fafafa;\n border-radius: 4px;\n border: 1px solid #f0f0f0;\n line-height: 26px;\n}\n</style>\n","<template>\n <div\n class=\"ant-pro-table-list-toolbar\"\n v-if=\"!config?.hide_header\"\n ref=\"headerRef\"\n >\n <div class=\"ant-pro-table-list-toolbar-container\">\n <div class=\"ant-pro-table-list-toolbar-left\">\n <slot name=\"header_bar_before_title\"></slot>\n <div class=\"ant-pro-table-list-toolbar-title\">\n <span v-if=\"config?.show_title\">{{ config?.title }}</span>\n <div\n v-if=\"config?.show_current_view\"\n class=\"current-view-container\"\n >\n <a-tooltip\n v-if=\"currentView?.name\"\n :title=\"'视图:' + currentView?.name\"\n >\n <span class=\"current-view-name\">\n {{ currentView?.name }}\n </span>\n </a-tooltip>\n <template v-if=\"enableEditCurrentView\">\n <span style=\"font-size: 12px; margin-right: 5px; display: inline-block\">\n (已编辑)\n </span>\n <a-dropdown\n v-model:open=\"isDropdown\"\n trigger=\"click\"\n >\n <span class=\"view-menu\">\n {{ $t('保存') }}\n <down-outlined\n v-if=\"!isDropdown\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n <up-outlined\n v-if=\"isDropdown\"\n style=\"font-size: 10px; cursor: pointer\"\n />\n </span>\n <template #overlay>\n <a-menu>\n <a-menu-item>\n <span\n @click=\"\n () => {\n isDropdown = false;\n selfEventBus.emit('saveView');\n }\n \"\n >\n {{ $t('保存视图') }}\n </span>\n </a-menu-item>\n <a-menu-item>\n <span\n @click=\"\n () => {\n isDropdown = false;\n selfEventBus.emit('saveNewView');\n }\n \"\n >\n {{ $t('另存为新视图') }}\n </span>\n </a-menu-item>\n </a-menu>\n </template>\n </a-dropdown>\n <span\n :bordered=\"false\"\n class=\"view-menu\"\n @click=\"\n () => {\n selfEventBus.emit('reinit');\n }\n \"\n >\n {{ $t('重置') }}\n </span>\n </template>\n </div>\n </div>\n <slot name=\"header_bar_after_title\"></slot>\n </div>\n <div class=\"ant-pro-table-list-toolbar-right\">\n <a-space align=\"center\">\n <slot name=\"header_bar_before_btn\"></slot>\n <head-btn\n :auth_batch_update_list=\"auth_batch_update_list\"\n :auth_head_btn_list=\"auth_head_btn_list\"\n :selectedRowKeys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :searchConfig=\"config.query_params || {}\"\n :current_org=\"current_org\"\n :router=\"router\"\n :event_obj=\"selfEventBus\"\n />\n <slot name=\"header_bar_after_btn\"></slot>\n </a-space>\n\n <div class=\"ant-pro-table-list-toolbar-divider\">\n <a-divider type=\"vertical\" />\n </div>\n\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"component_type === 'normal' && config?.condition_group_config_list?.length > 0\"\n >\n <ConditionGroupSelecter\n :config=\"config\"\n :selfEventBus=\"selfEventBus\"\n v-model:selectedConditionGroup=\"selectedConditionGroup\"\n v-model:groupConditionValue=\"groupConditionValue\"\n @change=\"handleSelectedConditionGroupChange\"\n />\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"config?.sys_btn?.refresh !== false\"\n >\n <refresh-type-modal\n :refreshType=\"refreshType\"\n :loading=\"loading\"\n @refresh=\"$emit('refresh')\"\n @typeChange=\"$emit('refreshTypeChange', $event)\"\n />\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"\n (component_type === 'normal' || component_type === 'pivot_table') &&\n config?.sys_btn?.export !== false\n \"\n >\n <a-tooltip :title=\"$t('导出Excel')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"$emit('export')\"\n >\n <template #icon>\n <download-outlined></download-outlined>\n </template>\n </a-button>\n </a-tooltip>\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"component_type === 'normal' && config?.sys_btn?.table_size !== false\"\n >\n <a-tooltip :title=\"$t('密度')\">\n <a-dropdown\n :trigger=\"['click']\"\n placement=\"bottomRight\"\n >\n <column-height-outlined />\n <template #overlay>\n <a-menu\n :selected-keys=\"[tableSize]\"\n style=\"width: 80px\"\n @click=\"\n ({ key }) => {\n $emit('update:tableSize', key as string);\n $emit('tableSizeChange', key as string);\n }\n \"\n >\n <a-menu-item key=\"default\">\n <a href=\"javascript:\">{{ $t('默认') }}</a>\n </a-menu-item>\n <a-menu-item key=\"middle\">\n <a href=\"javascript:\">{{ $t('中等') }}</a>\n </a-menu-item>\n <a-menu-item key=\"small\">\n <a href=\"javascript:\">{{ $t('紧凑') }}</a>\n </a-menu-item>\n </a-menu>\n </template>\n </a-dropdown>\n </a-tooltip>\n </div>\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"config?.sys_btn?.column_config !== false\"\n >\n <a-tooltip :title=\"$t('设置')\">\n <a-button\n type=\"text\"\n size=\"small\"\n @click=\"settingOpen = true\"\n >\n <template #icon>\n <setting-outlined />\n </template>\n </a-button>\n </a-tooltip>\n </div>\n\n <div\n class=\"ant-pro-table-list-toolbar-setting-item\"\n v-if=\"config?.sys_btn?.full_screen !== false\"\n >\n <a-tooltip :title=\"screenState ? $t('退出全屏') : $t('全屏')\">\n <fullscreen-outlined\n v-if=\"!screenState\"\n @click=\"$emit('setFull')\"\n />\n <fullscreen-exit-outlined\n v-else\n @click=\"$emit('exitFull')\"\n />\n </a-tooltip>\n </div>\n </div>\n </div>\n </div>\n <ConditionGroupView\n :config=\"config\"\n v-model:conditionGroupDataList=\"conditionGroupDataList\"\n v-model:selectedConditionGroup=\"selectedConditionGroup\"\n :selfEventBus=\"selfEventBus\"\n v-model:groupConditionValue=\"groupConditionValue\"\n @change=\"handleGroupConditionValueChange\"\n />\n <template v-if=\"settingMode === 'drawer'\">\n <a-drawer\n :title=\"$t('设置')\"\n :width=\"settingDrawerWidth\"\n :placement=\"settingDrawerPlacement\"\n v-model:open=\"settingOpen\"\n :mask=\"false\"\n :push=\"false\"\n >\n <column-edit-view\n :columns=\"columns\"\n :is_view=\"is_view\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :component_type=\"component_type\"\n :pivotConfig=\"localPivotConfig\"\n :pivotChartsConfig=\"localPivotChartsConfig\"\n :settingMode=\"settingMode\"\n :settingDrawerWidth=\"settingDrawerWidth\"\n :settingDrawerPlacement=\"settingDrawerPlacement\"\n @change=\"$emit('columnsChange', $event)\"\n @pivotConfigChange=\"onPivotConfigChange\"\n @pivotChartsConfigChange=\"onPivotChartsConfigChange\"\n ></column-edit-view>\n </a-drawer>\n </template>\n <a-modal\n v-else\n :title=\"$t('设置')\"\n width=\"900px\"\n :body-style=\"{ height: 'calc(100vh - 250px)' }\"\n v-model:open=\"settingOpen\"\n >\n <column-edit-view\n :columns=\"columns\"\n :is_view=\"is_view\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :component_type=\"component_type\"\n :settingMode=\"settingMode\"\n :pivotConfig=\"localPivotConfig\"\n :pivotChartsConfig=\"localPivotChartsConfig\"\n :settingDrawerWidth=\"settingDrawerWidth\"\n :settingDrawerPlacement=\"settingDrawerPlacement\"\n @change=\"$emit('columnsChange', $event)\"\n @pivotConfigChange=\"onPivotConfigChange\"\n @pivotChartsConfigChange=\"onPivotChartsConfigChange\"\n ></column-edit-view>\n <template #footer>\n <a-button @click=\"settingOpen = false\">\n {{ $t('关闭') }}\n </a-button>\n </template>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n ColumnHeightOutlined,\n FullscreenExitOutlined,\n FullscreenOutlined,\n DownloadOutlined,\n SettingOutlined,\n} from '@ant-design/icons-vue';\nimport { useResizeObserver } from '@vueuse/core';\nimport { computed, ref, watch } from 'vue';\nimport columnEditView from './tableConfig/index.vue';\nimport HeadBtn from './headBtn.vue';\nimport RefreshTypeModal from './refresh-type-modal/index.vue';\nimport {\n Column,\n ConditionGroupConfigItem,\n Table,\n PivotConfig,\n PivotChartsConfig,\n SettingMode,\n SettingDrawerPlacement,\n} from './typing';\nimport { useTableProState } from './utils';\nimport ConditionGroupSelecter from './ConditionGroupSelecter.vue';\nimport ConditionGroupView from './ConditionGroupView.vue';\nimport { useUserStore } from 'liyu-pc-base/init-plugin';\nimport { cloneDeep } from 'lodash';\n\ninterface Props {\n config?: Table;\n auth_batch_update_list?: any[];\n auth_head_btn_list?: any[];\n selectedRowKeys?: any[];\n dataSource?: any[];\n current_org?: any;\n router?: any;\n selfEventBus?: any;\n loading?: boolean;\n screenState?: boolean;\n tableSize?: string;\n columns?: Column[];\n view_key?: string;\n view_flag_name?: string;\n is_view?: boolean;\n refreshType?: string;\n conditionsParams?: any;\n pivotConfig?: PivotConfig | null;\n pivotChartsConfig?: PivotChartsConfig | null;\n component_type?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n config: () => ({} as Table),\n auth_batch_update_list: () => [],\n auth_head_btn_list: () => [],\n selectedRowKeys: () => [],\n dataSource: () => [],\n loading: false,\n screenState: false,\n tableSize: 'small',\n columns: () => [],\n view_key: '',\n view_flag_name: '',\n is_view: true,\n refreshType: '手动',\n conditionsParams: () => null,\n pivotConfig: () => null,\n pivotChartsConfig: () => null,\n component_type: 'normal',\n});\n\nconst emit = defineEmits([\n 'refresh',\n 'export',\n 'setFull',\n 'exitFull',\n 'columnsChange',\n 'pivotConfigChange',\n 'pivotChartsConfigChange',\n 'viewSelected',\n 'update:tableSize',\n 'heightChange',\n 'refreshTypeChange',\n 'tableSizeChange',\n 'selectedConditionGroupChange',\n 'groupConditionValueChange',\n]);\nconst settingOpen = defineModel('settingOpen', {\n default: false,\n});\nconst userStore = useUserStore();\nconst current_org = userStore.currentOrg;\nconst VIEW_SYSTEM_EDIT = 'view_system_edit';\nconst is_view_system_edit_auth = ref(userStore.CHECK_AUTH(VIEW_SYSTEM_EDIT));\nconst enableEditCurrentView = computed(() => {\n if (currentView.value?.is_other_user_view) {\n return false;\n }\n if (currentView.value?.is_system && !is_view_system_edit_auth.value) {\n return false;\n }\n return isViewChange.value && currentView.value && currentView.value.id !== 'no-view';\n});\nconst selectedConditionGroup = defineModel('selectedConditionGroup', {\n default: null,\n});\nconst conditionGroupDataList = defineModel<any[]>('conditionGroupDataList', {\n default: null,\n});\nconst groupConditionValue = defineModel('groupConditionValue', {\n default: null,\n});\n// 「设置」弹窗交互方式:modal(模态框,默认)或 drawer(抽屉)\nconst settingMode = computed<SettingMode>(() => props.config?.setting_mode || 'modal');\nconst settingDrawerWidth = computed<string | number>(\n () => props.config?.setting_drawer_width ?? '700px',\n);\nconst settingDrawerPlacement = computed<SettingDrawerPlacement>(\n () => props.config?.setting_drawer_placement || 'left',\n);\nconst headerRef = ref(null);\nuseResizeObserver(headerRef, entries => {\n emit('heightChange', entries[0].contentRect.height);\n});\nconst { currentView, isViewChange } = useTableProState();\nconst isDropdown = ref(false);\nconst handleSelectedConditionGroupChange = (item: ConditionGroupConfigItem) => {\n emit('selectedConditionGroupChange', item);\n};\nconst handleGroupConditionValueChange = (value: any) => {\n emit('groupConditionValueChange', value);\n};\n\n// ============ 数据透视表相关 ============\nconst localPivotConfig = ref<PivotConfig>(\n props.pivotConfig\n ? cloneDeep(props.pivotConfig)\n : { rows: [], columns: [], values: [], indicatorsAsCol: true, rowHierarchyType: 'grid' },\n);\n\nwatch(\n () => props.pivotConfig,\n val => {\n if (val) {\n localPivotConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotConfigChange = (config: PivotConfig) => {\n emit('pivotConfigChange', config);\n};\n\n// ============ 数据透视图相关 ============\nconst localPivotChartsConfig = ref<PivotChartsConfig>(\n props.pivotChartsConfig\n ? cloneDeep(props.pivotChartsConfig)\n : { series: [], category: [], values: [], chartType: 'bar' },\n);\n\nwatch(\n () => props.pivotChartsConfig,\n val => {\n if (val) {\n localPivotChartsConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotChartsConfigChange = (config: PivotChartsConfig) => {\n emit('pivotChartsConfigChange', config);\n};\n</script>\n\n<style lang=\"less\" scoped>\n.ant-pro-table-list-toolbar-container {\n height: 30px;\n line-height: 30px;\n padding: 0 8px;\n}\n.ant-pro-table-list-toolbar-setting-item {\n font-size: 14px;\n}\n.current-view-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n display: inline-block;\n max-width: 150px;\n}\n.current-view-container {\n display: flex;\n height: 100%;\n padding: 2px;\n gap: 4px;\n}\n.view-menu {\n font-size: 12px;\n font-weight: normal;\n cursor: pointer;\n padding: 0 4px;\n background: #fafafa;\n border-radius: 4px;\n border: 1px solid #f0f0f0;\n line-height: 26px;\n}\n</style>\n","<template>\n <component\n :is=\"'CellSummary_' + item.cellSummaryComponent\"\n v-if=\"item.cellSummaryComponent\"\n :cmpObj=\"{\n item: item,\n total: total,\n }\"\n ></component>\n <span\n v-else-if=\"item !== undefined\"\n :style=\"item.total_color ? item.total_color(item) : null\"\n >\n {{ item.total_content ? item.total_content(item) : total }}\n </span>\n\n <span\n v-else\n :style=\"item.total_color ? item.total_color(total) : null\"\n >\n {{ item.total_content ? item.total_content(total) : total }}\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\nconst props = defineProps({\n item: {\n type: Object,\n required: true,\n },\n summaryParams: {\n type: Object,\n required: true,\n },\n});\nconst total = computed(() => {\n let totla: any = 0;\n for (const item of props.summaryParams.pageData) {\n let itemValue = item[props.summaryParams.column.dataIndex];\n itemValue = getValue(itemValue);\n\n if (isNaN(itemValue) || itemValue == null) {\n itemValue = 0;\n }\n totla = add(totla, itemValue);\n }\n\n return totla;\n});\nfunction add(a, b) {\n const precision = Math.max(\n (a.toString().split('.')[1] || '').length,\n (b.toString().split('.')[1] || '').length,\n );\n return parseFloat((a + b).toFixed(precision));\n}\nconst getValue = (value: any) => {\n if (typeof value === 'string') {\n value = parseFloat(value);\n }\n return value;\n};\n</script>\n<style lang=\"less\" scoped></style>\n","<template>\n <component\n :is=\"'CellSummary_' + item.cellSummaryComponent\"\n v-if=\"item.cellSummaryComponent\"\n :cmpObj=\"{\n item: item,\n total: total,\n }\"\n ></component>\n <span\n v-else-if=\"item !== undefined\"\n :style=\"item.total_color ? item.total_color(item) : null\"\n >\n {{ item.total_content ? item.total_content(item) : total }}\n </span>\n\n <span\n v-else\n :style=\"item.total_color ? item.total_color(total) : null\"\n >\n {{ item.total_content ? item.total_content(total) : total }}\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\nconst props = defineProps({\n item: {\n type: Object,\n required: true,\n },\n summaryParams: {\n type: Object,\n required: true,\n },\n});\nconst total = computed(() => {\n let totla: any = 0;\n for (const item of props.summaryParams.pageData) {\n let itemValue = item[props.summaryParams.column.dataIndex];\n itemValue = getValue(itemValue);\n\n if (isNaN(itemValue) || itemValue == null) {\n itemValue = 0;\n }\n totla = add(totla, itemValue);\n }\n\n return totla;\n});\nfunction add(a, b) {\n const precision = Math.max(\n (a.toString().split('.')[1] || '').length,\n (b.toString().split('.')[1] || '').length,\n );\n return parseFloat((a + b).toFixed(precision));\n}\nconst getValue = (value: any) => {\n if (typeof value === 'string') {\n value = parseFloat(value);\n }\n return value;\n};\n</script>\n<style lang=\"less\" scoped></style>\n","<template>\n <a-menu\n :items=\"menuItems\"\n size=\"small\"\n mode=\"vertical\"\n @click=\"handleClick\"\n ></a-menu>\n <a-modal\n :title=\"$t('设置')\"\n width=\"1000px\"\n v-model:open=\"settingOpen\"\n >\n <column-edit-view\n :columns=\"columns\"\n :is_view=\"is_view\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :component_type=\"component_type\"\n :pivotConfig=\"localPivotConfig\"\n :pivotChartsConfig=\"localPivotChartsConfig\"\n :settingMode=\"settingMode\"\n :settingDrawerWidth=\"settingDrawerWidth\"\n :settingDrawerPlacement=\"settingDrawerPlacement\"\n @change=\"$emit('columnsChange', $event)\"\n @pivotConfigChange=\"onPivotConfigChange\"\n @pivotChartsConfigChange=\"onPivotChartsConfigChange\"\n ></column-edit-view>\n <template #footer>\n <a-button @click=\"settingOpen = false\">\n {{ $t('关闭') }}\n </a-button>\n </template>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n CopyOutlined,\n ReloadOutlined,\n DownloadOutlined,\n SettingOutlined,\n SaveOutlined,\n AlignLeftOutlined,\n} from '@ant-design/icons-vue';\n\nimport { copyValue, isNotEmpty } from 'liyu-pc-base/utils/function';\nimport { computed, h, ref, watch } from 'vue';\nimport { cloneDeep } from 'lodash';\nimport columnEditView from './tableConfig/index.vue';\nimport {\n PivotConfig,\n PivotChartsConfig,\n SettingMode,\n SettingDrawerPlacement,\n ContextMenuProps,\n MenuItem,\n} from './typing';\nimport { useTableProState } from './utils';\nimport { MenuProps } from 'ant-design-vue';\n\nconst props = withDefaults(defineProps<ContextMenuProps>(), {\n dataSource: () => [],\n columns: () => [],\n view_key: '',\n view_flag_name: '',\n is_view: false,\n selfEventBus: null,\n component_type: 'normal',\n pivotConfig: () => null,\n pivotChartsConfig: () => null,\n});\n\nconst emit = defineEmits([\n 'refresh',\n 'export',\n 'columnsChange',\n 'pivotConfigChange',\n 'pivotChartsConfigChange',\n]);\n\nconst settingOpen = ref(false);\nconst { currentView, isViewChange, config } = useTableProState();\n\n// 「设置」弹窗交互方式:modal(模态框,默认)或 drawer(抽屉)\nconst settingMode = computed<SettingMode>(() => config?.setting_mode || 'modal');\nconst settingDrawerWidth = computed<string | number>(() => config?.setting_drawer_width ?? '700px');\nconst settingDrawerPlacement = computed<SettingDrawerPlacement>(\n () => config?.setting_drawer_placement || 'left',\n);\n\n// ============ 数据透视表相关 ============\nconst localPivotConfig = ref<PivotConfig>(\n props.pivotConfig\n ? cloneDeep(props.pivotConfig)\n : { rows: [], columns: [], values: [], indicatorsAsCol: true, rowHierarchyType: 'grid' },\n);\n\nwatch(\n () => props.pivotConfig,\n val => {\n if (val) {\n localPivotConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotConfigChange = (cfg: PivotConfig) => {\n emit('pivotConfigChange', cfg);\n};\n\n// ============ 数据透视图相关 ============\nconst localPivotChartsConfig = ref<PivotChartsConfig>(\n props.pivotChartsConfig\n ? cloneDeep(props.pivotChartsConfig)\n : { series: [], category: [], values: [], chartType: 'bar' },\n);\n\nwatch(\n () => props.pivotChartsConfig,\n val => {\n if (val) {\n localPivotChartsConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotChartsConfigChange = (cfg: PivotChartsConfig) => {\n emit('pivotChartsConfigChange', cfg);\n};\nconst filterMenuItemByDisabledKey = (list: MenuItem[], keys: string[]) => {\n return list.filter(item => {\n if (item.children?.length) {\n filterMenuItemByDisabledKey(item.children, keys);\n if (!item.children?.length) {\n delete item.children;\n }\n }\n return !keys.includes(item.key);\n });\n};\nconst menuItems = computed<any[]>(() => {\n let res: MenuItem[] = [\n {\n key: 'copy',\n icon: () => h(CopyOutlined),\n label: '复制',\n children: [\n {\n key: 'copy_cell',\n icon: () => h(CopyOutlined),\n label: '复制单元格',\n func: () => copyClick('cell'),\n },\n {\n key: 'copy_record',\n icon: () => h(CopyOutlined),\n label: '复制行',\n func: () => copyClick('record'),\n },\n {\n key: 'copy_column',\n icon: () => h(CopyOutlined),\n label: '复制列',\n func: () => copyClick('column'),\n },\n ],\n },\n {\n key: 'refresh',\n icon: () => h(ReloadOutlined),\n label: '刷新',\n func: () => emit('refresh'),\n },\n {\n key: 'export',\n icon: () => h(DownloadOutlined),\n label: '导出',\n func: () => emit('export'),\n },\n {\n key: 'table_config',\n icon: () => h(SettingOutlined),\n label: '设置',\n func: () => (settingOpen.value = true),\n },\n ];\n if (\n props.is_view &&\n isViewChange.value &&\n currentView.value &&\n currentView.value.id !== 'no-view'\n ) {\n res = [\n ...res,\n {\n key: 'view',\n icon: () => h(AlignLeftOutlined),\n label: '视图',\n children: [\n {\n key: 'save_view',\n icon: () => h(SaveOutlined),\n label: '保存视图',\n },\n {\n key: 'save_new_view',\n icon: () => h(SaveOutlined),\n label: '另存视图',\n },\n {\n key: 'reinit',\n icon: () => h(ReloadOutlined),\n label: '重置',\n },\n ],\n },\n ];\n }\n if (config?.extraTableMenuItems?.length) {\n res = [...res, ...config.extraTableMenuItems];\n }\n if (config?.disabledTableMenuItemKeys?.length) {\n res = filterMenuItemByDisabledKey(res, config?.disabledTableMenuItemKeys);\n }\n return res;\n});\n\nconst formatCopyValue = (value: any) => {\n return typeof value === 'object' ? JSON.stringify(value) : value;\n};\n\nconst copyClick = (type: 'cell' | 'column' | 'record') => {\n const { contextmenuPopup, dataSource, columns } = props;\n if (type === 'cell') {\n if (contextmenuPopup.column.key !== 'operation') {\n copyValue(formatCopyValue(contextmenuPopup.text));\n }\n } else if (type === 'column') {\n if (contextmenuPopup.column.key !== 'operation') {\n const { dataIndex } = contextmenuPopup.column;\n const value = dataSource.map((d: any) => formatCopyValue(d[dataIndex])).join('\\r\\n');\n copyValue(value);\n }\n } else if (type === 'record') {\n const record = contextmenuPopup.record;\n const value = columns\n .map((c: any) => (c.dataIndex ? formatCopyValue(record[c.dataIndex]) : ''))\n .filter(c => isNotEmpty(c))\n .join(' ');\n copyValue(value);\n }\n};\nconst findMenuItemByKey = (list: MenuItem[], key: string) => {\n for (let item of list) {\n if (item.key === key) {\n return item;\n }\n if (item.children) {\n const child = findMenuItemByKey(item.children, key);\n if (child) {\n return child;\n }\n }\n }\n};\nconst handleClick: MenuProps['onClick'] = (menuInfo: any) => {\n const menuItem = findMenuItemByKey(menuItems.value, menuInfo.key);\n if (menuItem.func) {\n menuItem.func(props);\n }\n props.contextmenuPopup.hidePopup();\n};\n</script>\n\n<style lang=\"less\" scoped>\n.popup-divider {\n :deep(.ant-divider-horizontal) {\n margin-top: 1px;\n margin-bottom: 1px;\n }\n}\n</style>\n","<template>\n <a-menu\n :items=\"menuItems\"\n size=\"small\"\n mode=\"vertical\"\n @click=\"handleClick\"\n ></a-menu>\n <a-modal\n :title=\"$t('设置')\"\n width=\"1000px\"\n v-model:open=\"settingOpen\"\n >\n <column-edit-view\n :columns=\"columns\"\n :is_view=\"is_view\"\n :view_key=\"view_key\"\n :view_flag_name=\"view_flag_name\"\n :component_type=\"component_type\"\n :pivotConfig=\"localPivotConfig\"\n :pivotChartsConfig=\"localPivotChartsConfig\"\n :settingMode=\"settingMode\"\n :settingDrawerWidth=\"settingDrawerWidth\"\n :settingDrawerPlacement=\"settingDrawerPlacement\"\n @change=\"$emit('columnsChange', $event)\"\n @pivotConfigChange=\"onPivotConfigChange\"\n @pivotChartsConfigChange=\"onPivotChartsConfigChange\"\n ></column-edit-view>\n <template #footer>\n <a-button @click=\"settingOpen = false\">\n {{ $t('关闭') }}\n </a-button>\n </template>\n </a-modal>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n CopyOutlined,\n ReloadOutlined,\n DownloadOutlined,\n SettingOutlined,\n SaveOutlined,\n AlignLeftOutlined,\n} from '@ant-design/icons-vue';\n\nimport { copyValue, isNotEmpty } from 'liyu-pc-base/utils/function';\nimport { computed, h, ref, watch } from 'vue';\nimport { cloneDeep } from 'lodash';\nimport columnEditView from './tableConfig/index.vue';\nimport {\n PivotConfig,\n PivotChartsConfig,\n SettingMode,\n SettingDrawerPlacement,\n ContextMenuProps,\n MenuItem,\n} from './typing';\nimport { useTableProState } from './utils';\nimport { MenuProps } from 'ant-design-vue';\n\nconst props = withDefaults(defineProps<ContextMenuProps>(), {\n dataSource: () => [],\n columns: () => [],\n view_key: '',\n view_flag_name: '',\n is_view: false,\n selfEventBus: null,\n component_type: 'normal',\n pivotConfig: () => null,\n pivotChartsConfig: () => null,\n});\n\nconst emit = defineEmits([\n 'refresh',\n 'export',\n 'columnsChange',\n 'pivotConfigChange',\n 'pivotChartsConfigChange',\n]);\n\nconst settingOpen = ref(false);\nconst { currentView, isViewChange, config } = useTableProState();\n\n// 「设置」弹窗交互方式:modal(模态框,默认)或 drawer(抽屉)\nconst settingMode = computed<SettingMode>(() => config?.setting_mode || 'modal');\nconst settingDrawerWidth = computed<string | number>(() => config?.setting_drawer_width ?? '700px');\nconst settingDrawerPlacement = computed<SettingDrawerPlacement>(\n () => config?.setting_drawer_placement || 'left',\n);\n\n// ============ 数据透视表相关 ============\nconst localPivotConfig = ref<PivotConfig>(\n props.pivotConfig\n ? cloneDeep(props.pivotConfig)\n : { rows: [], columns: [], values: [], indicatorsAsCol: true, rowHierarchyType: 'grid' },\n);\n\nwatch(\n () => props.pivotConfig,\n val => {\n if (val) {\n localPivotConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotConfigChange = (cfg: PivotConfig) => {\n emit('pivotConfigChange', cfg);\n};\n\n// ============ 数据透视图相关 ============\nconst localPivotChartsConfig = ref<PivotChartsConfig>(\n props.pivotChartsConfig\n ? cloneDeep(props.pivotChartsConfig)\n : { series: [], category: [], values: [], chartType: 'bar' },\n);\n\nwatch(\n () => props.pivotChartsConfig,\n val => {\n if (val) {\n localPivotChartsConfig.value = cloneDeep(val);\n }\n },\n { deep: true },\n);\n\nconst onPivotChartsConfigChange = (cfg: PivotChartsConfig) => {\n emit('pivotChartsConfigChange', cfg);\n};\nconst filterMenuItemByDisabledKey = (list: MenuItem[], keys: string[]) => {\n return list.filter(item => {\n if (item.children?.length) {\n filterMenuItemByDisabledKey(item.children, keys);\n if (!item.children?.length) {\n delete item.children;\n }\n }\n return !keys.includes(item.key);\n });\n};\nconst menuItems = computed<any[]>(() => {\n let res: MenuItem[] = [\n {\n key: 'copy',\n icon: () => h(CopyOutlined),\n label: '复制',\n children: [\n {\n key: 'copy_cell',\n icon: () => h(CopyOutlined),\n label: '复制单元格',\n func: () => copyClick('cell'),\n },\n {\n key: 'copy_record',\n icon: () => h(CopyOutlined),\n label: '复制行',\n func: () => copyClick('record'),\n },\n {\n key: 'copy_column',\n icon: () => h(CopyOutlined),\n label: '复制列',\n func: () => copyClick('column'),\n },\n ],\n },\n {\n key: 'refresh',\n icon: () => h(ReloadOutlined),\n label: '刷新',\n func: () => emit('refresh'),\n },\n {\n key: 'export',\n icon: () => h(DownloadOutlined),\n label: '导出',\n func: () => emit('export'),\n },\n {\n key: 'table_config',\n icon: () => h(SettingOutlined),\n label: '设置',\n func: () => (settingOpen.value = true),\n },\n ];\n if (\n props.is_view &&\n isViewChange.value &&\n currentView.value &&\n currentView.value.id !== 'no-view'\n ) {\n res = [\n ...res,\n {\n key: 'view',\n icon: () => h(AlignLeftOutlined),\n label: '视图',\n children: [\n {\n key: 'save_view',\n icon: () => h(SaveOutlined),\n label: '保存视图',\n },\n {\n key: 'save_new_view',\n icon: () => h(SaveOutlined),\n label: '另存视图',\n },\n {\n key: 'reinit',\n icon: () => h(ReloadOutlined),\n label: '重置',\n },\n ],\n },\n ];\n }\n if (config?.extraTableMenuItems?.length) {\n res = [...res, ...config.extraTableMenuItems];\n }\n if (config?.disabledTableMenuItemKeys?.length) {\n res = filterMenuItemByDisabledKey(res, config?.disabledTableMenuItemKeys);\n }\n return res;\n});\n\nconst formatCopyValue = (value: any) => {\n return typeof value === 'object' ? JSON.stringify(value) : value;\n};\n\nconst copyClick = (type: 'cell' | 'column' | 'record') => {\n const { contextmenuPopup, dataSource, columns } = props;\n if (type === 'cell') {\n if (contextmenuPopup.column.key !== 'operation') {\n copyValue(formatCopyValue(contextmenuPopup.text));\n }\n } else if (type === 'column') {\n if (contextmenuPopup.column.key !== 'operation') {\n const { dataIndex } = contextmenuPopup.column;\n const value = dataSource.map((d: any) => formatCopyValue(d[dataIndex])).join('\\r\\n');\n copyValue(value);\n }\n } else if (type === 'record') {\n const record = contextmenuPopup.record;\n const value = columns\n .map((c: any) => (c.dataIndex ? formatCopyValue(record[c.dataIndex]) : ''))\n .filter(c => isNotEmpty(c))\n .join(' ');\n copyValue(value);\n }\n};\nconst findMenuItemByKey = (list: MenuItem[], key: string) => {\n for (let item of list) {\n if (item.key === key) {\n return item;\n }\n if (item.children) {\n const child = findMenuItemByKey(item.children, key);\n if (child) {\n return child;\n }\n }\n }\n};\nconst handleClick: MenuProps['onClick'] = (menuInfo: any) => {\n const menuItem = findMenuItemByKey(menuItems.value, menuInfo.key);\n if (menuItem.func) {\n menuItem.func(props);\n }\n props.contextmenuPopup.hidePopup();\n};\n</script>\n\n<style lang=\"less\" scoped>\n.popup-divider {\n :deep(.ant-divider-horizontal) {\n margin-top: 1px;\n margin-bottom: 1px;\n }\n}\n</style>\n","<template>\n <div\n class=\"table-wraper\"\n ref=\"tableWraperRef\"\n >\n <div ref=\"searchRef\">\n <Search\n v-if=\"\n config?.search_config &&\n config.search_config.length > 0 &&\n !enable_advanced_search_multigroups\n \"\n :searchConfig=\"config?.search_config || []\"\n :autoRefreshOnSearchChange=\"config?.auto_refresh_on_search_change\"\n v-model:searchConditions=\"searchConditions\"\n :event-bus=\"selfEventBus\"\n :searchType=\"search_type\"\n :searchDisplayType=\"config?.search_display_type\"\n :searchColumnCount=\"config?.search_column_count\"\n :viewSearchConfig=\"viewSearchConfig\"\n :disabledShowSearchGroupName=\"config?.disabledShowSearchGroupName\"\n @viewSearchConfigChange=\"viewSearchConfigChange\"\n @change=\"handleSearchChange\"\n ></Search>\n <SearchFormGroup\n v-if=\"\n config?.search_config &&\n config.search_config.length > 0 &&\n enable_advanced_search_multigroups\n \"\n :searchConfig=\"config?.search_config || []\"\n :autoRefreshOnSearchChange=\"config?.auto_refresh_on_search_change\"\n v-model:searchGroups=\"searchGroups\"\n :event-bus=\"selfEventBus\"\n :searchType=\"search_type\"\n :searchDisplayType=\"config?.search_display_type\"\n :searchColumnCount=\"config?.search_column_count\"\n :disabledShowSearchGroupName=\"config?.disabledShowSearchGroupName\"\n @change=\"handleSearchChange\"\n ></SearchFormGroup>\n </div>\n <a-card\n ref=\"elRef\"\n :body-style=\"{ padding: 0 }\"\n >\n <TableHeader\n :config=\"config\"\n :auth_batch_update_list=\"auth_batch_update_list\"\n :auth_head_btn_list=\"auth_head_btn_list\"\n :selectedRowKeys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :current_org=\"current_org\"\n :router=\"router\"\n :selfEventBus=\"selfEventBus\"\n :loading=\"loading\"\n :screenState=\"screenState\"\n v-model:tableSize=\"state.tableSize\"\n :columns=\"columns\"\n :view_key=\"view_key\"\n :view_flag_name=\"config.title\"\n :is_view=\"is_view\"\n :refreshType=\"refreshType\"\n v-model:selectedConditionGroup=\"selectedConditionGroup\"\n v-model:conditionGroupDataList=\"conditionGroupDataList\"\n v-model:groupConditionValue=\"groupConditionValue\"\n v-model:settingOpen=\"settingOpen\"\n @refresh=\"refresh\"\n @export=\"handleExport\"\n @setFull=\"setFull\"\n @exitFull=\"exitFull\"\n @columnsChange=\"onColumnsChange\"\n @heightChange=\"h => (headerHeight = h)\"\n @refreshTypeChange=\"handleRefreshTypeChange\"\n @tableSizeChange=\"handleTableSizeChange\"\n @selectedConditionGroupChange=\"handleSelectedConditionGroupChange\"\n @groupConditionValueChange=\"handleGroupConditionValueChange\"\n >\n <template #header_bar_before_title>\n <slot name=\"header_bar_before_title\"></slot>\n </template>\n <template #header_bar_after_title>\n <slot name=\"header_bar_after_title\"></slot>\n </template>\n <template #header_bar_before_btn>\n <slot name=\"header_bar_before_btn\"></slot>\n </template>\n <template #header_bar_after_btn>\n <slot name=\"header_bar_after_btn\"></slot>\n </template>\n </TableHeader>\n <s-table\n v-if=\"columns?.length > 0\"\n :columns=\"filtedColumns\"\n v-model:selectedRowKeys=\"selectedRowKeys\"\n :data-source=\"dataSource\"\n :bordered=\"true\"\n :size=\"state.tableSize\"\n :height=\"tableHeight\"\n :rowHeight=\"config.rowHeightFunc || config.rowHeight || undefined\"\n :scroll=\"{\n x: tableContentWidth,\n }\"\n :loading=\"loading && !config?.disabled_table_loading\"\n :pagination=\"\n config?.is_pagination\n ? {\n current: state.current,\n pageSize: state.pageSize,\n total: state.total,\n onChange: handlePageChange,\n showSizeChanger: true,\n pageSizeOptions: pageSizeOptions,\n }\n : false\n \"\n :rowKey=\"rowKey\"\n :rowSelection=\"config?.is_selection ? row_selection : null\"\n :custom-cell=\"config?.customCell\"\n :custom-header-cell=\"config?.customHeaderCell\"\n :custom-row=\"config?.customRow\"\n :virtual=\"true\"\n sticky\n summary-fixed\n :preserveRow=\"preserveRow\"\n :style=\"{\n '--row-heigth': `${config.rowHeight}px`,\n }\"\n :class=\"{ 'modify-row-height': !!config.rowHeight }\"\n @change=\"handleChange\"\n @cellClick=\"handleCellClick\"\n @resizeColumn=\"resizeColumn\"\n >\n <template #customFilterDropdown=\"{ confirm, clearFilters, column }\">\n <TableFilterDropdown\n :column=\"column\"\n :searchConfig=\"config.search_config || []\"\n v-model:searchConditions=\"headerSearchConditions\"\n :searchType=\"search_type\"\n :confirm=\"confirm\"\n :clearFilters=\"clearFilters\"\n @search=\"handleSearch\"\n @reset=\"handleReset\"\n />\n </template>\n <template\n #customFilterIcon=\"{ filtered, column }\"\n v-if=\"config?.enable_custom_filter\"\n >\n <FilterFilled\n :style=\"{\n color:\n filtered ||\n filterConditions(headerSearchConditions).some(\n s => s.field_name === column.dataIndex,\n )\n ? '#108ee9'\n : undefined,\n }\"\n />\n </template>\n <template #bodyCell=\"{ column, index, text, record }\">\n <template v-if=\"column.dataIndex == 'index'\">\n {{ index + 1 + state.pageSize * (state.current - 1) }}\n </template>\n <component\n :is=\"'Cell_' + column.cellComponent\"\n v-else-if=\"column.cellComponent\"\n :cmpObj=\"{\n column: column ? column : {},\n index: index,\n text: text,\n dataSource: dataSource,\n record: record ? record : {},\n pageSize: state.pageSize ? state.pageSize : 500,\n current: state.current ? state.current : 1,\n eventBus: eventBus,\n tableHeightY: tableHeight,\n requestParams: config.query_params,\n auth_btn_list: auth_btn_list,\n event_obj: selfEventBus,\n ...(config.cellComponentExt || {}),\n }\"\n ></component>\n <template v-else>\n <span :style=\"column.color ? column.color({ column, index, text, record }) : null\">\n {{ column.content ? column.content({ column, index, text, record }) : text }}\n </span>\n </template>\n </template>\n <template\n #summary\n v-if=\"summaryColumns.length > 0\"\n >\n <s-table-summary-row>\n <s-table-summary-cell :index=\"0\">合计</s-table-summary-cell>\n <s-table-summary-cell\n v-for=\"item in summaryColumns\"\n :key=\"item.key\"\n :columnKey=\"item.key\"\n >\n <template #default=\"vars\">\n <TableSummary\n :item=\"item\"\n :summaryParams=\"vars\"\n ></TableSummary>\n </template>\n </s-table-summary-cell>\n </s-table-summary-row>\n </template>\n <template #contextmenuPopup=\"contextmenuPopup\">\n <TableContextMenu\n :contextmenuPopup=\"contextmenuPopup\"\n :dataSource=\"dataSource\"\n :columns=\"columns\"\n :view_key=\"view_key\"\n :view_flag_name=\"config.title\"\n :is_view=\"is_view\"\n :self-event-bus=\"selfEventBus\"\n @refresh=\"refresh\"\n @export=\"handleExport\"\n @columnsChange=\"onColumnsChange\"\n ></TableContextMenu>\n </template>\n </s-table>\n </a-card>\n </div>\n <export-excel-modal-view\n v-if=\"exportOpen\"\n :column-list=\"exportExcelColumnList\"\n :columns=\"exportExcelColumn\"\n :source-columns=\"config.columns\"\n :current=\"state.current\"\n :datasource=\"dataSource\"\n :filename=\"filename\"\n :pageSize=\"state.pageSize\"\n :total=\"state.total\"\n :requestParams=\"getQueryParams({ type: 'list' })\"\n :queryData=\"config.list_url\"\n v-model:open=\"exportOpen\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { FilterFilled } from '@ant-design/icons-vue';\nimport { useResizeObserver } from '@vueuse/core';\nimport { TableProps } from 'ant-design-vue';\nimport dayjs from 'dayjs';\nimport { getApiData } from 'liyu-pc-base/api/api-model';\nimport { filterConditions, genID } from 'liyu-pc-base/utils/function';\nimport { useFullscreen } from 'liyu-pc-base/utils/hooks/useFullscreen';\nimport { debounce, isArray, cloneDeep } from 'lodash';\nimport mitt from 'mitt';\nimport { computed, h, onMounted, onUnmounted, provide, reactive, ref, watch } from 'vue';\nimport { useRouter } from 'vue-router';\nimport Search from './search.vue';\nimport SearchFormGroup from './SearchFormGroup.vue';\nimport TableFilterDropdown from './TableFilterDropdown.vue';\nimport TableHeader from './tableHeader.vue';\nimport TableSummary from './TableSummary.vue';\nimport { useRequiredConditionStore, required_condition_event_obj } from 'liyu-pc-base/init-plugin';\nimport { useI18n } from 'vue-i18n';\nimport { useEnv, useUserStore } from 'liyu-pc-base/init-plugin';\nimport {\n Column,\n SearchConditionItem,\n SearchGroupItem,\n Table,\n TableProState,\n ViewColumnItem,\n ViewData,\n Response,\n ViewSearchItem,\n ConditionGroupConfigItem,\n} from './typing';\nimport {\n fetchViewsData,\n getParamsCount,\n tableProStateSymbol,\n getCurrentGroupConditionValue,\n} from './utils';\nimport TableContextMenu from './TableContextMenu.vue';\nimport { useView } from './hooks/view';\nimport { query_view_settings_list } from 'liyu-pc-base/api/view-model';\nconst DEFAULT_PAGE_SIZE = 50;\nconst props = defineProps({\n config: {\n type: Object as () => Table,\n required: true,\n default: () => ({\n id: '',\n title: '',\n is_normal_model: true,\n modelType: null,\n list_url: '',\n columns: [],\n }),\n },\n eventBus: {\n type: Object,\n required: false,\n },\n initSelectValue: {\n type: Array,\n required: false,\n },\n initValueKey: {\n type: String,\n required: false,\n },\n});\ninterface Emits {\n (e: 'selectChange', selectdRecord: any[], unSelectdRecord: any[]): void;\n (e: 'dataChange', params: { data: any[]; current: number; total: number }): void;\n}\nconst emit = defineEmits<Emits>();\nconst { t } = useI18n();\nconst env = useEnv();\nconst requiredConditionStore = useRequiredConditionStore();\nconst userStore = useUserStore();\nconst userid = userStore.userid;\nconst current_org = userStore.currentOrg;\nconst VIEW_SYSTEM_EDIT = 'view_system_edit';\nconst is_view_system_edit_auth = ref(userStore.CHECK_AUTH(VIEW_SYSTEM_EDIT));\nconst router = useRouter();\nconst dataSource = ref([]);\nconst selectedRowKeys = ref([]);\nconst loading = ref(false);\nconst selfEventBus = mitt();\nconst searchConditions = ref<SearchConditionItem[]>([]);\nconst searchGroups = ref<SearchGroupItem[]>([]);\nconst settingOpen = ref(false);\nconst enable_advanced_search_multigroups = computed(\n () => !!props.config.enable_advanced_search_multigroups,\n);\n/**\n * 表头搜索条件桥接 computed\n * - 未开启 enable_advanced_search_multigroups 时,直接读写 searchConditions\n * - 开启 enable_advanced_search_multigroups 时,读写 searchGroups[0].conditions\n * 使表头筛选(TableFilterDropdown / filter action)与多分组搜索第一个分组联动\n */\nconst headerSearchConditions = computed<SearchConditionItem[]>({\n get() {\n if (enable_advanced_search_multigroups.value) {\n return searchGroups.value[0]?.conditions || [];\n }\n return searchConditions.value;\n },\n set(val) {\n if (enable_advanced_search_multigroups.value) {\n if (searchGroups.value.length > 0) {\n searchGroups.value = searchGroups.value.map((g, i) =>\n i === 0 ? { ...g, conditions: val } : g,\n );\n } else {\n searchGroups.value = [\n {\n id: genID(),\n conditions: val,\n viewSearchConfig: {},\n },\n ];\n }\n } else {\n searchConditions.value = val;\n }\n },\n});\nlet loadDataId = null;\nconst tableWraperRef = ref(null);\nconst tableWraperHeight = ref(null);\nconst searchRef = ref(null);\nconst searchHeight = ref(null);\nconst headerHeight = ref(0);\nconst rowActionWidth = ref(30);\nconst sortedMap = ref({});\nconst sortedList = ref([]);\nconst auth_batch_update_list = ref([]);\nconst auth_head_btn_list = ref<any[]>([]);\nconst auth_btn_list = ref<any[]>([]);\nconst exportOpen = ref(false);\nconst searchInput = ref();\nconst refreshType = ref(props.config.refreshType);\nconst selectedConditionGroup = ref<ConditionGroupConfigItem | null>(null);\nconst conditionGroupDataList = ref<any[]>([]);\nconst groupConditionValue = ref<string | null>(null);\nconst rowKey = computed(() => props.config?.rowKey || 'id');\nconst getAuthBtn = () => {\n auth_btn_list.value = userStore.CHECK_AUTH_BY_BTN_LIST(props.config?.btn_list || []) || [];\n auth_head_btn_list.value =\n userStore.CHECK_AUTH_BY_BTN_LIST(props.config?.head_btn_list || []) || [];\n auth_batch_update_list.value =\n userStore.CHECK_AUTH_BY_BTN_LIST(props.config?.batch_update_list || []) || [];\n};\ngetAuthBtn();\nconst pageSizeOptions = ref(\n props.config?.pageSizeOptions || [\n '10',\n '20',\n '50',\n '100',\n '200',\n '500',\n '1000',\n '3000',\n '10000',\n '50000',\n '60000',\n '70000',\n ],\n);\nconst view_key = computed(() => {\n return `table_pro_${props.config.key}`;\n});\nconst state = reactive({\n current: 1,\n pageSize: props.config?.pageSize || props.config?.query_params?.pageSize || DEFAULT_PAGE_SIZE,\n total: 0,\n tableSize: 'small',\n tableHead: null,\n sum_data: null,\n});\nuseResizeObserver(tableWraperRef, entries => {\n tableWraperHeight.value = entries[0].contentRect.height;\n});\nuseResizeObserver(searchRef, entries => {\n searchHeight.value = entries[0].contentRect.height;\n});\n\nconst preserveRow = () => {\n return false;\n};\nconst getQueryParams = ({ type = 'list', viewData = null, groupDataList = null } = {}) => {\n const getColumnFields = (list: Column[]) => {\n const fields = [];\n list.forEach(item => {\n if (\n item.dataIndex &&\n item.dataIndex !== 'index' &&\n item.dataIndex !== 'action' &&\n item.checked !== false\n ) {\n fields.push(item.dataIndex);\n }\n if (item.children && item.children.length > 0) {\n fields.push(...getColumnFields(item.children));\n }\n });\n return fields;\n };\n\n const sort_fields = [];\n const used_fields = new Set();\n for (const item of sortedList.value) {\n if (item.order == 'descend') {\n sort_fields.push('-' + item.key);\n } else {\n sort_fields.push(item.key);\n }\n used_fields.add(item.key);\n }\n if (props.config.base_sort_fields) {\n for (const item of props.config.base_sort_fields) {\n const field_name = item.startsWith('-') ? item.slice(1) : item;\n if (!used_fields.has(field_name)) {\n sort_fields.push(item);\n }\n }\n }\n const params = {\n ...(props.config.query_params || {}),\n\n sort_fields,\n };\n if (type === 'list') {\n const column_fields = getColumnFields(columns.value || []);\n if (props.config.base_column_fields) {\n column_fields.push(...props.config.base_column_fields);\n }\n params['current'] = state.current;\n params['pageSize'] = state.pageSize;\n params['column_fields'] = column_fields;\n }\n if (search_type.value === 'advanced') {\n if (enable_advanced_search_multigroups.value) {\n const searchGroupConds = (viewData ? viewData.searchGroups || [] : searchGroups.value)\n .map(group => filterConditions(group.conditions))\n .filter(group => group.length > 0);\n const baseConds = filterConditions(props.config.base_conditions);\n const conditionsList = [];\n if (searchGroupConds.length > 0) {\n conditionsList.push({ op: 'and', children: searchGroupConds });\n }\n if (baseConds.length > 0) {\n conditionsList.push({ op: 'and', children: baseConds });\n }\n params['conditions'] = conditionsList;\n } else {\n const conditions = [\n ...filterConditions(props.config.base_conditions),\n ...filterConditions(viewData ? viewData.searchConditions || [] : searchConditions.value),\n ];\n params['conditions'] = [conditions];\n }\n if (type === 'list' || type === 'count') {\n const groupCondition = getCurrentGroupConditionValue(\n groupDataList || conditionGroupDataList.value,\n viewData ? viewData.groupConditionValue : groupConditionValue.value,\n );\n if (groupCondition) {\n params['conditions'] = [\n {\n op: 'and',\n children: params['conditions'],\n },\n {\n op: 'and',\n children: [\n {\n format: selectedConditionGroup.value?.format || 'input',\n class_name: selectedConditionGroup.value?.class_name || 'str',\n field_name: groupCondition.field_name,\n operator: 'eq',\n value: [groupCondition.value],\n },\n ],\n },\n ];\n }\n }\n } else if (search_type.value === 'simple') {\n for (const item of filterConditions(\n viewData ? viewData.searchConditions || [] : searchConditions.value,\n )) {\n const v_count = getParamsCount(item.operator, item);\n let value = item.value;\n if (v_count === 1) {\n value = value[0];\n }\n params[item.field_name] = value;\n }\n }\n if (props.config.get_query_params_func) {\n const newParams = props.config.get_query_params_func({ params, type });\n if (newParams) {\n Object.assign(params, newParams);\n }\n }\n const requiredConditionObj = getRequiredConditionObj();\n if (requiredConditionObj) {\n params['required_condition'] = requiredConditionObj;\n }\n if (props.config.get_db_alias) {\n params['db_alias'] = props.config.get_db_alias();\n }\n\n return params;\n};\nconst [elRef, screenState, { setFull, exitFull }] = useFullscreen();\nconst getGroupDataList = async () => {\n if (!selectedConditionGroup.value) {\n if (conditionGroupDataList.value?.length > 0) {\n conditionGroupDataList.value = [];\n }\n return;\n }\n if (!props.config.query_group_url) {\n console.error('query_group_url 未配置');\n if (conditionGroupDataList.value?.length > 0) {\n conditionGroupDataList.value = [];\n }\n return;\n }\n if (!conditionGroupDataList.value?.length) {\n const params = getQueryParams({ type: 'group' });\n params['group_condition'] = {\n field_name: selectedConditionGroup.value.field_name,\n display_name: selectedConditionGroup.value.display_name,\n order_name: selectedConditionGroup.value.order_name || [],\n };\n const res = await getApiData(params, props.config.query_group_url || '', false, {\n api_log: `tablePro ${props.config.key} ${props.config.title}`,\n });\n conditionGroupDataList.value = res || [];\n }\n};\nconst getData = async () => {\n if (!props.config.list_url) {\n return;\n }\n const currentLoadId = Symbol(); // 唯一值\n loadDataId = currentLoadId;\n try {\n loading.value = true;\n await getGroupDataList();\n const params = getQueryParams({ type: 'list' });\n let response: Response;\n if (props.config.query_data_func) {\n response = await props.config.query_data_func(params);\n } else {\n response = await getApiData(\n params,\n props.config.list_url.replace('__tree', ''),\n props.config.list_url.includes('__tree'),\n {\n api_log: `tablePro ${props.config.key} ${props.config.title}`,\n },\n );\n }\n if (loadDataId !== currentLoadId) {\n return;\n }\n if (props.config.process_data_func) {\n dataSource.value = props.config.process_data_func(response.data);\n } else {\n dataSource.value = response.data || [];\n }\n state.current = response.current;\n state.total = response.total;\n state.tableHead = response.table_head ? response.table_head : null;\n state.sum_data = response.sum_data ?? null;\n emit('dataChange', {\n data: dataSource.value,\n current: response.current,\n total: response.total,\n });\n syncSelectedRowKeys();\n } finally {\n if (loadDataId !== currentLoadId) {\n return;\n }\n loading.value = false;\n }\n};\nconst delayGetDate = debounce(getData, 500);\nconst handlePageChange = (page: any, pageSize: any) => {\n if (state.current == page && state.pageSize == pageSize) {\n return;\n }\n state.current = page ? page : 1;\n state.pageSize = pageSize ? pageSize : DEFAULT_PAGE_SIZE;\n getData();\n};\nconst search_type = computed(() => {\n if (props.config.search_type === 'simple') {\n return props.config.search_type;\n } else {\n return 'advanced';\n }\n});\nconst is_view = computed(() => {\n return props.config?.is_view !== false;\n});\nconst row_selection: any = computed(() => {\n const rowSelectionType = props.config.rowSelectionType || 'checkbox';\n const rowSelectionCheckStrictly = props.config.rowSelectionCheckStrictly || false;\n const onChange = (selectedRowKeys, selectdRecord) => {\n const unSelectdRecord = dataSource.value.filter(item => {\n return !selectedRowKeys.includes(item.id);\n });\n emit('selectChange', selectdRecord, unSelectdRecord);\n };\n return { type: rowSelectionType, checkStrictly: rowSelectionCheckStrictly, onChange };\n});\nconst viewColumnsConfig = ref<Record<string, ViewColumnItem>>({});\nconst viewSearchConfig = ref<Record<string, ViewSearchItem>>({});\nconst filtedColumns = computed(() => {\n const filterRecursive = (list: Column[]) => {\n return list\n .filter(item => item.checked !== false)\n .map(item => {\n if (item.children && item.children.length > 0) {\n const children = filterRecursive(item.children);\n if (children.length > 0) {\n return {\n ...item,\n children,\n };\n }\n return null;\n }\n return item;\n })\n .filter(Boolean);\n };\n return filterRecursive(columns.value);\n});\nconst columns = computed<Column[]>(() => {\n const filterValueMap = {};\n for (const item of headerSearchConditions.value) {\n if (['in', 'eq'].includes(item.operator)) {\n filterValueMap[item.field_name] = item.value;\n }\n }\n const sorted = sortedMap.value || {};\n const baseColumns = [...(props.config.columns || [])];\n\n if (props.config.btn_list && props.config.btn_list.length > 0) {\n const hasAction = baseColumns.some(item => item.dataIndex === 'action');\n if (!hasAction) {\n baseColumns.push({\n title: '操作',\n dataIndex: 'action',\n cellComponent: 'action',\n width: rowActionWidth.value + 20,\n resizable: true,\n fixed: 'right',\n checked: true,\n });\n }\n }\n\n // 合并视图配置,递归合并\n const mergeRecursive = (list: Column[], isChildren = false): Column[] => {\n const merged = list\n .map(item => {\n const key = item.dataIndex || item.key || item.title;\n const viewCol = viewColumnsConfig.value[key] || {};\n const newItem = {\n ...item,\n ...viewCol,\n is_children: isChildren,\n key: key,\n };\n if (newItem.children && newItem.children.length > 0) {\n newItem.children = mergeRecursive(newItem.children, true);\n }\n return newItem;\n })\n .sort((a, b) => {\n const orderA = a.order ?? 999;\n const orderB = b.order ?? 999;\n return orderA - orderB;\n });\n\n return merged.map(item => {\n const key = item.dataIndex || item.key || item.title;\n let ellipsis = item.ellipsis;\n if (item.resizable && item.ellipsis == undefined) {\n ellipsis = true;\n }\n if (item.autoWidth) {\n return {\n ...item,\n minWidth: item.width,\n width: undefined,\n filteredValue: filterValueMap[key] || null,\n sortOrder: sorted[key] || false,\n key: key,\n ellipsis,\n customFilterDropdown:\n props.config.enable_custom_filter &&\n !!props.config.search_config?.find(s => s.field_name === key),\n onFilterDropdownOpenChange: open => {\n if (open) {\n setTimeout(() => {\n searchInput.value?.focus();\n }, 100);\n }\n },\n };\n } else {\n return {\n ...item,\n width: item.width,\n minWidth: undefined,\n filteredValue: filterValueMap[key] || null,\n sortOrder: sorted[key] || false,\n key: key,\n ellipsis,\n customFilterDropdown:\n props.config.enable_custom_filter &&\n !!props.config.search_config?.find(s => s.field_name === key),\n onFilterDropdownOpenChange: open => {\n if (open) {\n setTimeout(() => {\n searchInput.value?.focus();\n }, 100);\n }\n },\n };\n }\n });\n };\n\n const res: Column[] = mergeRecursive(baseColumns);\n // 序号列处理\n const hasIndex = res.some(item => item.dataIndex === 'index');\n if (!hasIndex && !props.config.hide_index_column) {\n res.unshift({\n title: '#',\n dataIndex: 'index',\n key: 'index',\n maxWidth: 50,\n fixed: 'left',\n checked: true,\n autoHeight: true,\n });\n }\n return res;\n});\nconst tableContentWidth = computed(() => {\n let width = 0;\n const processColumns = datas => {\n for (const item of datas) {\n if (item.children && item.children.length > 0) {\n processColumns(item.children);\n } else {\n width += item.width || item.minWidth || 120;\n }\n }\n };\n processColumns(columns.value);\n});\nconst filename = computed(() => {\n const title = props.config.title || 'data';\n const datetime = dayjs().format('YYYYMMDD_HHmmss');\n return `${title}_${datetime}`;\n});\nconst exportExcelColumnList = computed(() => {\n const list: any = [];\n const getLeaves = (cols: Column[]) => {\n cols.forEach(item => {\n if (item.children && item.children.length > 0) {\n getLeaves(item.children);\n } else {\n if (item.dataIndex === 'action' || item.dataIndex === 'index') {\n return;\n }\n list.push({\n checked: item.checked,\n fixed: item.fixed ? item.fixed : false,\n key: item.dataIndex,\n label: item.title,\n width: item.width,\n });\n }\n });\n };\n getLeaves(columns.value);\n return list;\n});\nconst exportExcelColumn = computed(() => {\n const list: any = [];\n const getLeaves = (cols: Column[]) => {\n cols.forEach(item => {\n if (item.children && item.children.length > 0) {\n getLeaves(item.children);\n } else {\n if (item.dataIndex === 'action' || item.dataIndex === 'index') {\n return;\n }\n list.push(item);\n }\n });\n };\n getLeaves(columns.value);\n return list;\n});\nconst handleSearch = ({ confirm, dataIndex, value }) => {\n confirm();\n getData();\n isViewChange.value = true;\n};\n\nconst handleReset = ({ clearFilters, dataIndex }) => {\n clearFilters();\n const conditions = cloneDeep(headerSearchConditions.value);\n const conditionIndex = conditions.findIndex(item => item.field_name === dataIndex);\n if (conditionIndex > -1) {\n conditions.splice(conditionIndex, 1);\n }\n headerSearchConditions.value = conditions;\n getData();\n isViewChange.value = true;\n};\n\nconst refresh = async () => {\n await getData();\n};\nconst local_refresh = async () => {\n dataSource.value = [...dataSource.value];\n};\nconst handleRefreshTypeChange = type => {\n refreshType.value = type;\n isViewChange.value = true;\n};\nconst paginationHeight = ref(64);\nconst tableHeight = computed(() => {\n if (!tableWraperHeight.value) {\n return 0;\n }\n let value = tableWraperHeight.value - searchHeight.value - headerHeight.value - 4;\n if (props.config.is_pagination) {\n value -= paginationHeight.value;\n }\n console.log('tableWraperHeight', tableWraperHeight.value);\n console.log('searchHeight', searchHeight.value);\n console.log('headerHeight', headerHeight.value);\n console.log('paginationHeight', paginationHeight.value);\n console.log('tableHeight', value);\n return value;\n});\nconst handleTableSizeChange = () => {\n isViewChange.value = true;\n};\nwatch(\n () => state.tableSize,\n () => {\n // 此处设置paginationHeight两次,为了解决切换表格密度后,高度剧烈变化问题。\n paginationHeight.value = 64;\n setTimeout(() => {\n if (state.tableSize === 'small') {\n paginationHeight.value = 56;\n } else if (state.tableSize === 'middle') {\n paginationHeight.value = 56;\n } else if (state.tableSize === 'default') {\n paginationHeight.value = 64;\n }\n }, 200);\n },\n);\nconst syncSelectedRowKeys = () => {\n if (dataSource.value?.length > 0 && props.initValueKey) {\n const valueMap = {};\n selectedRowKeys.value = dataSource.value\n .filter(item => {\n const value = item[props.initValueKey];\n if (props.initSelectValue?.includes(value) && !valueMap[value]) {\n valueMap[value] = true;\n return true;\n }\n })\n .map(item => {\n return item.id;\n });\n }\n};\nconst init = async () => {\n conditionGroupDataList.value = [];\n const defaultSortField = props.config.columns.filter(\n column => column.sorter && column.defaultSortOrder,\n );\n defaultSortField.sort((a, b) => (a.defaultSortIndex || 0) - (b.defaultSortIndex || 0));\n for (const column of defaultSortField) {\n sortedMap.value[column.dataIndex] = column.defaultSortOrder;\n sortedList.value.push({ key: column.dataIndex, order: column.defaultSortOrder });\n }\n const tmpSearchConditions = [];\n if (props.config?.search_config) {\n props.config.search_config.forEach(configItem => {\n if (configItem.default_value) {\n let value = configItem.default_value;\n if (!isArray(value)) {\n value = [value];\n }\n tmpSearchConditions.push({\n field_name: configItem.field_name,\n value: value,\n operator: configItem.default_operator || 'eq',\n format: configItem.format || 'input',\n class_name: configItem.class_name || 'str',\n });\n }\n });\n }\n\n searchConditions.value = tmpSearchConditions;\n if (enable_advanced_search_multigroups.value) {\n if (props.config.default_advanced_search_multigroups) {\n searchGroups.value = props.config.default_advanced_search_multigroups;\n } else {\n searchGroups.value = [\n {\n id: genID(),\n conditions: [...tmpSearchConditions],\n viewSearchConfig: {},\n },\n ];\n }\n\n searchConditions.value = [];\n }\n if (is_view) {\n if (currentView.value?.id !== 'no-view') {\n currentView.value = null;\n await fetchViews();\n if (props.config?.view_uid) {\n const findView = viewDataList.value.find((item: any) => item.uid == props.config?.view_uid);\n if (findView) {\n setViewData(findView.data);\n currentView.value = findView;\n } else {\n const res: any = await query_view_settings_list({\n org_id: current_org.id,\n is_active: true,\n uid: props.config?.view_uid,\n });\n if (res?.length) {\n currentView.value = { ...res[0], is_other_user_view: true };\n }\n }\n }\n if (!currentView.value && viewDataList.value?.length) {\n const tmpData = [...viewDataList.value];\n // 优先级:当前选中视图,默认视图,用户视图,同级按 sort 正序\n const getSortValue = item => {\n let value = item.sort || 0;\n if (item.id !== currentView.value?.id) {\n value *= 1000000;\n }\n if (!item.is_default) {\n value *= 10000;\n }\n if (!item.user_id) {\n value *= 1000;\n }\n return value;\n };\n tmpData.sort((a, b) => getSortValue(a) - getSortValue(b));\n setViewData(tmpData[0].data);\n currentView.value = tmpData[0];\n }\n }\n }\n await getData();\n};\nconst eventReinit = async () => {\n isViewChange.value = false;\n await init();\n};\nconst eventReload = async () => {\n await getData();\n};\nconst flattenedColumns = computed(() => {\n const list = [];\n const getLeaves = (cols: Column[]) => {\n cols.forEach(col => {\n if (col.children && col.children.length > 0) {\n getLeaves(col.children);\n } else {\n list.push(col);\n }\n });\n };\n getLeaves(filtedColumns.value);\n return list;\n});\nconst summaryColumns = computed(() => {\n const list: any = [];\n let index = 0;\n for (const item of flattenedColumns.value) {\n let _index = index;\n if (props.config?.is_selection) {\n _index = index + 1;\n }\n if (item.summary) {\n // if (!props.config.is_selection && props.config.hide_index_column) {\n // console.error('未开启选择(is_selection)且隐藏了序号列(hide_index_column),无法展示\"合计\"');\n // return [];\n // }\n list.push({\n index: _index,\n key: item.key,\n columnKey: item.dataIndex,\n total_color: item.total_color,\n total_content: item.total_content,\n cellSummaryComponent: item.cellSummaryComponent,\n column: item,\n sum_data: state.sum_data,\n });\n }\n index++;\n }\n return list;\n});\nconst getRequiredConditionObj = () => {\n const required_condition = props.config.required_condition;\n if (required_condition) {\n const required_condition_obj = {};\n required_condition.forEach((item: any) => {\n const value = requiredConditionStore.value_map[item.key];\n if (value !== undefined && value !== null) {\n required_condition_obj[item.field_name] = value;\n }\n });\n return required_condition_obj;\n } else {\n return null;\n }\n};\nconst handleChange: TableProps['onChange'] = (pagination, filters, sorter, action) => {\n if (action.action === 'paginate') {\n return;\n } else if (action.action === 'filter') {\n const searchMap = {};\n for (const item of headerSearchConditions.value) {\n searchMap[item.field_name] = item;\n }\n for (const key of Object.keys(filters)) {\n const value = filters[key];\n const item = searchMap[key];\n if (item) {\n if (value?.length > 1) {\n item.operator = 'in';\n } else {\n if (!['in', 'eq'].includes(item.operator)) {\n item.operator = 'in';\n }\n }\n item.value = value;\n } else {\n searchMap[key] = {\n field_name: key,\n operator: 'eq',\n value: value,\n format: 'input',\n class_name: 'str',\n };\n }\n }\n headerSearchConditions.value = Object.values(searchMap);\n getData();\n isViewChange.value = true;\n } else if (action.action === 'sort') {\n const tmpMap = {};\n const tmpList = [];\n if (!isArray(sorter)) {\n tmpMap[sorter.columnKey] = sorter.order;\n tmpList.push({ key: sorter.columnKey, order: sorter.order });\n } else {\n for (const item of sorter) {\n tmpMap[item.columnKey] = item.order;\n tmpList.push({ key: item.columnKey, order: item.order });\n }\n }\n sortedMap.value = tmpMap;\n sortedList.value = tmpList;\n getData();\n isViewChange.value = true;\n }\n};\nconst handleCellClick = (e, cell) => {\n if (!props.config?.is_click_selection) {\n return;\n }\n const rowSelectionType = props.config.rowSelectionType || 'checkbox';\n if (rowSelectionType === 'radio') {\n selectedRowKeys.value = [cell.record.id];\n } else {\n const isSelected = selectedRowKeys.value && selectedRowKeys.value.includes(cell.record.id);\n if (isSelected) {\n selectedRowKeys.value = selectedRowKeys.value.filter(id => {\n return id !== cell.record.id;\n });\n } else {\n selectedRowKeys.value.push(cell.record.id);\n }\n }\n\n const selectdRecord = dataSource.value.filter(item => {\n return selectedRowKeys.value.includes(item.id);\n });\n const unSelectdRecord = dataSource.value.filter(item => {\n return !selectedRowKeys.value.includes(item.id);\n });\n emit('selectChange', selectdRecord, unSelectdRecord);\n};\nconst handleRequiredConditionChange = (key: string) => {\n if (props.config.required_condition) {\n const item = props.config.required_condition.find((item: any) => item.key === key);\n if (item) {\n conditionGroupDataList.value = [];\n getData();\n }\n }\n};\nonMounted(async () => {\n if (!props.config.key) {\n console.error('tablePro config 未配置 key');\n }\n if (!props.config.title) {\n console.error('tablePro config 未配置 title');\n }\n required_condition_event_obj().on('change', handleRequiredConditionChange);\n selfEventBus.on('refresh', refresh);\n selfEventBus.on('local_refresh', local_refresh);\n selfEventBus.on('reinit', eventReinit);\n selfEventBus.on('saveView', saveView);\n selfEventBus.on('saveNewView', addView);\n if (props.eventBus) {\n props.eventBus.on('reinit', eventReinit);\n props.eventBus.on('refresh', refresh);\n props.eventBus.on('local_refresh', local_refresh);\n props.eventBus.on('reload_table', eventReload);\n }\n if (props.config.tableSize) {\n state.tableSize = props.config.tableSize;\n }\n await init();\n});\nconst handleExport = () => {\n exportOpen.value = true;\n};\nconst onColumnsChange = (data: any[]) => {\n if (!props.config || !props.config.columns) return;\n const configObj: Record<string, any> = {};\n\n const processRecursive = (list: any[]) => {\n list.forEach((item, index) => {\n const dataIndex = item.dataIndex || item.key || item.title;\n if (dataIndex === 'index' || dataIndex === 'action') return;\n configObj[dataIndex] = {\n checked: item.checked,\n fixed: item.fixed,\n autoHeight: item.autoHeight,\n autoWidth: item.autoWidth,\n ellipsis: item.ellipsis,\n width: item.width,\n order: index,\n };\n if (item.children && item.children.length > 0) {\n processRecursive(item.children);\n }\n });\n };\n\n processRecursive(data);\n viewColumnsConfig.value = configObj;\n isViewChange.value = true;\n};\nconst setViewData = async (viewData: ViewData) => {\n if (viewData === null) {\n await init();\n isViewChange.value = false;\n return;\n }\n if (viewData.columns) {\n viewColumnsConfig.value = viewData.columns;\n }\n if (enable_advanced_search_multigroups.value) {\n if (viewData.searchGroups && viewData.searchGroups.length > 0) {\n searchGroups.value = viewData.searchGroups;\n } else if (viewData.searchConditions) {\n searchGroups.value = [\n {\n id: genID(),\n conditions: viewData.searchConditions,\n viewSearchConfig: viewData.searchConfig || {},\n },\n ];\n }\n // 保证至少存在一个分组\n if (searchGroups.value.length === 0) {\n searchGroups.value = [\n {\n id: genID(),\n conditions: [],\n viewSearchConfig: {},\n },\n ];\n }\n searchConditions.value = [];\n } else {\n if (viewData.searchConfig) {\n viewSearchConfig.value = viewData.searchConfig;\n }\n if (viewData.searchConditions) {\n searchConditions.value = viewData.searchConditions;\n }\n }\n if (viewData.tableSize) {\n state.tableSize = viewData.tableSize;\n }\n if (viewData.refreshType) {\n refreshType.value = viewData.refreshType;\n }\n if (viewData.sortedList) {\n sortedList.value = viewData.sortedList;\n sortedMap.value = {};\n for (const item of sortedList.value) {\n sortedMap.value[item.key] = item.order;\n }\n }\n if (viewData.groupConditionFieldName && props.config.condition_group_config_list?.length > 0) {\n selectedConditionGroup.value = props.config.condition_group_config_list.find(\n item => item.field_name === viewData.groupConditionFieldName,\n );\n } else {\n selectedConditionGroup.value = null;\n }\n\n groupConditionValue.value = viewData.groupConditionValue;\n\n if (props.config.set_view_callback) {\n props.config.set_view_callback({ viewData });\n }\n await refresh();\n isViewChange.value = false;\n};\nconst resizeColumn = (width, column, action: 'start' | 'move' | 'end') => {\n if (action === 'end') {\n let item = viewColumnsConfig.value[column.dataIndex];\n if (!item) {\n item = {};\n viewColumnsConfig.value[column.dataIndex] = item;\n }\n item.width = width;\n isViewChange.value = true;\n }\n};\nconst viewSearchConfigChange = (config: Record<string, ViewSearchItem>) => {\n viewSearchConfig.value = config;\n isViewChange.value = true;\n};\nconst getViewData = (): ViewData => {\n const viewData: any = {\n columns: viewColumnsConfig.value,\n searchConditions: searchConditions.value,\n tableSize: state.tableSize,\n refreshType: refreshType.value,\n searchConfig: viewSearchConfig.value,\n sortedList: sortedList.value,\n groupConditionFieldName: selectedConditionGroup.value?.field_name,\n groupConditionValue: groupConditionValue.value,\n };\n if (enable_advanced_search_multigroups.value) {\n viewData.searchGroups = searchGroups.value;\n // 将第一个分组的字段配置作为全局 searchConfig 保存,便于单分组模式兼容\n if (searchGroups.value.length > 0) {\n viewData.searchConfig = searchGroups.value[0].viewSearchConfig;\n }\n }\n if (props.config.get_view_data_func) {\n const newViewData = props.config.get_view_data_func({ viewData });\n if (newViewData) {\n Object.assign(viewData, newViewData);\n }\n }\n return viewData;\n};\nconst viewDataList = ref([]);\nconst currentView = ref<any>(null);\nconst isViewChange = ref(false);\nconst fetchViews = async () => {\n const data = await fetchViewsData(\n view_key.value,\n getQueryParams,\n props.config.count_url,\n props.config,\n );\n viewDataList.value = data;\n};\nconst handleSearchChange = () => {\n isViewChange.value = true;\n};\nconst handleGroupConditionValueChange = (value: any) => {\n isViewChange.value = true;\n};\nconst handleSelectedConditionGroupChange = (item: ConditionGroupConfigItem) => {\n conditionGroupDataList.value = [];\n isViewChange.value = true;\n};\nconst { saveView, addView } = useView({\n currentView,\n getViewData,\n isViewChange,\n userid,\n view_project__flag: env.VITE_APP_VIEW_PROJECT_FLAG,\n view_menu__flag: view_key.value,\n view_menu__name: props.config.title,\n fetchViews,\n is_auth: is_view_system_edit_auth,\n t,\n});\n\nprovide<TableProState>(tableProStateSymbol, {\n setViewData,\n getViewData,\n fetchViews,\n viewDataList,\n currentView,\n isViewChange,\n config: props.config,\n});\n\nonUnmounted(() => {\n selfEventBus.off('refresh', refresh);\n selfEventBus.off('local_refresh', local_refresh);\n selfEventBus.off('reinit', eventReinit);\n selfEventBus.off('saveView', saveView);\n selfEventBus.off('saveNewView', addView);\n if (props.eventBus) {\n props.eventBus.off('reinit', eventReinit);\n props.eventBus.off('refresh', refresh);\n props.eventBus.off('local_refresh', local_refresh);\n props.eventBus.off('reload_table', eventReload);\n }\n});\nwatch(\n () => props.initSelectValue,\n () => {\n syncSelectedRowKeys();\n },\n);\nwatch(\n () => props.config.query_params,\n () => {\n delayGetDate();\n },\n {\n deep: true,\n },\n);\nwatch(\n () => props.config.base_conditions,\n () => {\n delayGetDate();\n },\n {\n deep: true,\n },\n);\n\ndefineExpose({\n getViewData,\n setViewData,\n config: props.config,\n columns,\n fetchViews,\n viewDataList,\n currentView,\n isViewChange,\n viewColumnsConfig,\n searchGroups,\n refresh,\n init,\n dataSource,\n selectedRowKeys,\n settingOpen,\n getQueryParams,\n});\n</script>\n<style lang=\"less\" scoped>\n.btn-wraper {\n display: flex;\n justify-content: flex-end;\n}\n.table-wraper {\n height: 100%;\n overflow: hidden;\n}\n.modify-row-height {\n :deep(.surely-table-header) {\n height: var(--row-heigth) !important;\n }\n :deep(.surely-table-header-cell) {\n height: var(--row-heigth) !important;\n }\n :deep(.surely-table-summary) {\n height: var(--row-heigth) !important;\n }\n}\n.popup-divider {\n :deep(.ant-divider-horizontal) {\n margin-top: 1px;\n margin-bottom: 1px;\n }\n}\n.highlight {\n background-color: rgb(255, 192, 105);\n padding: 0px;\n}\n</style>\n","<template>\n <div\n class=\"table-wraper\"\n ref=\"tableWraperRef\"\n >\n <div ref=\"searchRef\">\n <Search\n v-if=\"\n config?.search_config &&\n config.search_config.length > 0 &&\n !enable_advanced_search_multigroups\n \"\n :searchConfig=\"config?.search_config || []\"\n :autoRefreshOnSearchChange=\"config?.auto_refresh_on_search_change\"\n v-model:searchConditions=\"searchConditions\"\n :event-bus=\"selfEventBus\"\n :searchType=\"search_type\"\n :searchDisplayType=\"config?.search_display_type\"\n :searchColumnCount=\"config?.search_column_count\"\n :viewSearchConfig=\"viewSearchConfig\"\n :disabledShowSearchGroupName=\"config?.disabledShowSearchGroupName\"\n @viewSearchConfigChange=\"viewSearchConfigChange\"\n @change=\"handleSearchChange\"\n ></Search>\n <SearchFormGroup\n v-if=\"\n config?.search_config &&\n config.search_config.length > 0 &&\n enable_advanced_search_multigroups\n \"\n :searchConfig=\"config?.search_config || []\"\n :autoRefreshOnSearchChange=\"config?.auto_refresh_on_search_change\"\n v-model:searchGroups=\"searchGroups\"\n :event-bus=\"selfEventBus\"\n :searchType=\"search_type\"\n :searchDisplayType=\"config?.search_display_type\"\n :searchColumnCount=\"config?.search_column_count\"\n :disabledShowSearchGroupName=\"config?.disabledShowSearchGroupName\"\n @change=\"handleSearchChange\"\n ></SearchFormGroup>\n </div>\n <a-card\n ref=\"elRef\"\n :body-style=\"{ padding: 0 }\"\n >\n <TableHeader\n :config=\"config\"\n :auth_batch_update_list=\"auth_batch_update_list\"\n :auth_head_btn_list=\"auth_head_btn_list\"\n :selectedRowKeys=\"selectedRowKeys\"\n :dataSource=\"dataSource\"\n :current_org=\"current_org\"\n :router=\"router\"\n :selfEventBus=\"selfEventBus\"\n :loading=\"loading\"\n :screenState=\"screenState\"\n v-model:tableSize=\"state.tableSize\"\n :columns=\"columns\"\n :view_key=\"view_key\"\n :view_flag_name=\"config.title\"\n :is_view=\"is_view\"\n :refreshType=\"refreshType\"\n v-model:selectedConditionGroup=\"selectedConditionGroup\"\n v-model:conditionGroupDataList=\"conditionGroupDataList\"\n v-model:groupConditionValue=\"groupConditionValue\"\n v-model:settingOpen=\"settingOpen\"\n @refresh=\"refresh\"\n @export=\"handleExport\"\n @setFull=\"setFull\"\n @exitFull=\"exitFull\"\n @columnsChange=\"onColumnsChange\"\n @heightChange=\"h => (headerHeight = h)\"\n @refreshTypeChange=\"handleRefreshTypeChange\"\n @tableSizeChange=\"handleTableSizeChange\"\n @selectedConditionGroupChange=\"handleSelectedConditionGroupChange\"\n @groupConditionValueChange=\"handleGroupConditionValueChange\"\n >\n <template #header_bar_before_title>\n <slot name=\"header_bar_before_title\"></slot>\n </template>\n <template #header_bar_after_title>\n <slot name=\"header_bar_after_title\"></slot>\n </template>\n <template #header_bar_before_btn>\n <slot name=\"header_bar_before_btn\"></slot>\n </template>\n <template #header_bar_after_btn>\n <slot name=\"header_bar_after_btn\"></slot>\n </template>\n </TableHeader>\n <s-table\n v-if=\"columns?.length > 0\"\n :columns=\"filtedColumns\"\n v-model:selectedRowKeys=\"selectedRowKeys\"\n :data-source=\"dataSource\"\n :bordered=\"true\"\n :size=\"state.tableSize\"\n :height=\"tableHeight\"\n :rowHeight=\"config.rowHeightFunc || config.rowHeight || undefined\"\n :scroll=\"{\n x: tableContentWidth,\n }\"\n :loading=\"loading && !config?.disabled_table_loading\"\n :pagination=\"\n config?.is_pagination\n ? {\n current: state.current,\n pageSize: state.pageSize,\n total: state.total,\n onChange: handlePageChange,\n showSizeChanger: true,\n pageSizeOptions: pageSizeOptions,\n }\n : false\n \"\n :rowKey=\"rowKey\"\n :rowSelection=\"config?.is_selection ? row_selection : null\"\n :custom-cell=\"config?.customCell\"\n :custom-header-cell=\"config?.customHeaderCell\"\n :custom-row=\"config?.customRow\"\n :virtual=\"true\"\n sticky\n summary-fixed\n :preserveRow=\"preserveRow\"\n :style=\"{\n '--row-heigth': `${config.rowHeight}px`,\n }\"\n :class=\"{ 'modify-row-height': !!config.rowHeight }\"\n @change=\"handleChange\"\n @cellClick=\"handleCellClick\"\n @resizeColumn=\"resizeColumn\"\n >\n <template #customFilterDropdown=\"{ confirm, clearFilters, column }\">\n <TableFilterDropdown\n :column=\"column\"\n :searchConfig=\"config.search_config || []\"\n v-model:searchConditions=\"headerSearchConditions\"\n :searchType=\"search_type\"\n :confirm=\"confirm\"\n :clearFilters=\"clearFilters\"\n @search=\"handleSearch\"\n @reset=\"handleReset\"\n />\n </template>\n <template\n #customFilterIcon=\"{ filtered, column }\"\n v-if=\"config?.enable_custom_filter\"\n >\n <FilterFilled\n :style=\"{\n color:\n filtered ||\n filterConditions(headerSearchConditions).some(\n s => s.field_name === column.dataIndex,\n )\n ? '#108ee9'\n : undefined,\n }\"\n />\n </template>\n <template #bodyCell=\"{ column, index, text, record }\">\n <template v-if=\"column.dataIndex == 'index'\">\n {{ index + 1 + state.pageSize * (state.current - 1) }}\n </template>\n <component\n :is=\"'Cell_' + column.cellComponent\"\n v-else-if=\"column.cellComponent\"\n :cmpObj=\"{\n column: column ? column : {},\n index: index,\n text: text,\n dataSource: dataSource,\n record: record ? record : {},\n pageSize: state.pageSize ? state.pageSize : 500,\n current: state.current ? state.current : 1,\n eventBus: eventBus,\n tableHeightY: tableHeight,\n requestParams: config.query_params,\n auth_btn_list: auth_btn_list,\n event_obj: selfEventBus,\n ...(config.cellComponentExt || {}),\n }\"\n ></component>\n <template v-else>\n <span :style=\"column.color ? column.color({ column, index, text, record }) : null\">\n {{ column.content ? column.content({ column, index, text, record }) : text }}\n </span>\n </template>\n </template>\n <template\n #summary\n v-if=\"summaryColumns.length > 0\"\n >\n <s-table-summary-row>\n <s-table-summary-cell :index=\"0\">合计</s-table-summary-cell>\n <s-table-summary-cell\n v-for=\"item in summaryColumns\"\n :key=\"item.key\"\n :columnKey=\"item.key\"\n >\n <template #default=\"vars\">\n <TableSummary\n :item=\"item\"\n :summaryParams=\"vars\"\n ></TableSummary>\n </template>\n </s-table-summary-cell>\n </s-table-summary-row>\n </template>\n <template #contextmenuPopup=\"contextmenuPopup\">\n <TableContextMenu\n :contextmenuPopup=\"contextmenuPopup\"\n :dataSource=\"dataSource\"\n :columns=\"columns\"\n :view_key=\"view_key\"\n :view_flag_name=\"config.title\"\n :is_view=\"is_view\"\n :self-event-bus=\"selfEventBus\"\n @refresh=\"refresh\"\n @export=\"handleExport\"\n @columnsChange=\"onColumnsChange\"\n ></TableContextMenu>\n </template>\n </s-table>\n </a-card>\n </div>\n <export-excel-modal-view\n v-if=\"exportOpen\"\n :column-list=\"exportExcelColumnList\"\n :columns=\"exportExcelColumn\"\n :source-columns=\"config.columns\"\n :current=\"state.current\"\n :datasource=\"dataSource\"\n :filename=\"filename\"\n :pageSize=\"state.pageSize\"\n :total=\"state.total\"\n :requestParams=\"getQueryParams({ type: 'list' })\"\n :queryData=\"config.list_url\"\n v-model:open=\"exportOpen\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { FilterFilled } from '@ant-design/icons-vue';\nimport { useResizeObserver } from '@vueuse/core';\nimport { TableProps } from 'ant-design-vue';\nimport dayjs from 'dayjs';\nimport { getApiData } from 'liyu-pc-base/api/api-model';\nimport { filterConditions, genID } from 'liyu-pc-base/utils/function';\nimport { useFullscreen } from 'liyu-pc-base/utils/hooks/useFullscreen';\nimport { debounce, isArray, cloneDeep } from 'lodash';\nimport mitt from 'mitt';\nimport { computed, h, onMounted, onUnmounted, provide, reactive, ref, watch } from 'vue';\nimport { useRouter } from 'vue-router';\nimport Search from './search.vue';\nimport SearchFormGroup from './SearchFormGroup.vue';\nimport TableFilterDropdown from './TableFilterDropdown.vue';\nimport TableHeader from './tableHeader.vue';\nimport TableSummary from './TableSummary.vue';\nimport { useRequiredConditionStore, required_condition_event_obj } from 'liyu-pc-base/init-plugin';\nimport { useI18n } from 'vue-i18n';\nimport { useEnv, useUserStore } from 'liyu-pc-base/init-plugin';\nimport {\n Column,\n SearchConditionItem,\n SearchGroupItem,\n Table,\n TableProState,\n ViewColumnItem,\n ViewData,\n Response,\n ViewSearchItem,\n ConditionGroupConfigItem,\n} from './typing';\nimport {\n fetchViewsData,\n getParamsCount,\n tableProStateSymbol,\n getCurrentGroupConditionValue,\n} from './utils';\nimport TableContextMenu from './TableContextMenu.vue';\nimport { useView } from './hooks/view';\nimport { query_view_settings_list } from 'liyu-pc-base/api/view-model';\nconst DEFAULT_PAGE_SIZE = 50;\nconst props = defineProps({\n config: {\n type: Object as () => Table,\n required: true,\n default: () => ({\n id: '',\n title: '',\n is_normal_model: true,\n modelType: null,\n list_url: '',\n columns: [],\n }),\n },\n eventBus: {\n type: Object,\n required: false,\n },\n initSelectValue: {\n type: Array,\n required: false,\n },\n initValueKey: {\n type: String,\n required: false,\n },\n});\ninterface Emits {\n (e: 'selectChange', selectdRecord: any[], unSelectdRecord: any[]): void;\n (e: 'dataChange', params: { data: any[]; current: number; total: number }): void;\n}\nconst emit = defineEmits<Emits>();\nconst { t } = useI18n();\nconst env = useEnv();\nconst requiredConditionStore = useRequiredConditionStore();\nconst userStore = useUserStore();\nconst userid = userStore.userid;\nconst current_org = userStore.currentOrg;\nconst VIEW_SYSTEM_EDIT = 'view_system_edit';\nconst is_view_system_edit_auth = ref(userStore.CHECK_AUTH(VIEW_SYSTEM_EDIT));\nconst router = useRouter();\nconst dataSource = ref([]);\nconst selectedRowKeys = ref([]);\nconst loading = ref(false);\nconst selfEventBus = mitt();\nconst searchConditions = ref<SearchConditionItem[]>([]);\nconst searchGroups = ref<SearchGroupItem[]>([]);\nconst settingOpen = ref(false);\nconst enable_advanced_search_multigroups = computed(\n () => !!props.config.enable_advanced_search_multigroups,\n);\n/**\n * 表头搜索条件桥接 computed\n * - 未开启 enable_advanced_search_multigroups 时,直接读写 searchConditions\n * - 开启 enable_advanced_search_multigroups 时,读写 searchGroups[0].conditions\n * 使表头筛选(TableFilterDropdown / filter action)与多分组搜索第一个分组联动\n */\nconst headerSearchConditions = computed<SearchConditionItem[]>({\n get() {\n if (enable_advanced_search_multigroups.value) {\n return searchGroups.value[0]?.conditions || [];\n }\n return searchConditions.value;\n },\n set(val) {\n if (enable_advanced_search_multigroups.value) {\n if (searchGroups.value.length > 0) {\n searchGroups.value = searchGroups.value.map((g, i) =>\n i === 0 ? { ...g, conditions: val } : g,\n );\n } else {\n searchGroups.value = [\n {\n id: genID(),\n conditions: val,\n viewSearchConfig: {},\n },\n ];\n }\n } else {\n searchConditions.value = val;\n }\n },\n});\nlet loadDataId = null;\nconst tableWraperRef = ref(null);\nconst tableWraperHeight = ref(null);\nconst searchRef = ref(null);\nconst searchHeight = ref(null);\nconst headerHeight = ref(0);\nconst rowActionWidth = ref(30);\nconst sortedMap = ref({});\nconst sortedList = ref([]);\nconst auth_batch_update_list = ref([]);\nconst auth_head_btn_list = ref<any[]>([]);\nconst auth_btn_list = ref<any[]>([]);\nconst exportOpen = ref(false);\nconst searchInput = ref();\nconst refreshType = ref(props.config.refreshType);\nconst selectedConditionGroup = ref<ConditionGroupConfigItem | null>(null);\nconst conditionGroupDataList = ref<any[]>([]);\nconst groupConditionValue = ref<string | null>(null);\nconst rowKey = computed(() => props.config?.rowKey || 'id');\nconst getAuthBtn = () => {\n auth_btn_list.value = userStore.CHECK_AUTH_BY_BTN_LIST(props.config?.btn_list || []) || [];\n auth_head_btn_list.value =\n userStore.CHECK_AUTH_BY_BTN_LIST(props.config?.head_btn_list || []) || [];\n auth_batch_update_list.value =\n userStore.CHECK_AUTH_BY_BTN_LIST(props.config?.batch_update_list || []) || [];\n};\ngetAuthBtn();\nconst pageSizeOptions = ref(\n props.config?.pageSizeOptions || [\n '10',\n '20',\n '50',\n '100',\n '200',\n '500',\n '1000',\n '3000',\n '10000',\n '50000',\n '60000',\n '70000',\n ],\n);\nconst view_key = computed(() => {\n return `table_pro_${props.config.key}`;\n});\nconst state = reactive({\n current: 1,\n pageSize: props.config?.pageSize || props.config?.query_params?.pageSize || DEFAULT_PAGE_SIZE,\n total: 0,\n tableSize: 'small',\n tableHead: null,\n sum_data: null,\n});\nuseResizeObserver(tableWraperRef, entries => {\n tableWraperHeight.value = entries[0].contentRect.height;\n});\nuseResizeObserver(searchRef, entries => {\n searchHeight.value = entries[0].contentRect.height;\n});\n\nconst preserveRow = () => {\n return false;\n};\nconst getQueryParams = ({ type = 'list', viewData = null, groupDataList = null } = {}) => {\n const getColumnFields = (list: Column[]) => {\n const fields = [];\n list.forEach(item => {\n if (\n item.dataIndex &&\n item.dataIndex !== 'index' &&\n item.dataIndex !== 'action' &&\n item.checked !== false\n ) {\n fields.push(item.dataIndex);\n }\n if (item.children && item.children.length > 0) {\n fields.push(...getColumnFields(item.children));\n }\n });\n return fields;\n };\n\n const sort_fields = [];\n const used_fields = new Set();\n for (const item of sortedList.value) {\n if (item.order == 'descend') {\n sort_fields.push('-' + item.key);\n } else {\n sort_fields.push(item.key);\n }\n used_fields.add(item.key);\n }\n if (props.config.base_sort_fields) {\n for (const item of props.config.base_sort_fields) {\n const field_name = item.startsWith('-') ? item.slice(1) : item;\n if (!used_fields.has(field_name)) {\n sort_fields.push(item);\n }\n }\n }\n const params = {\n ...(props.config.query_params || {}),\n\n sort_fields,\n };\n if (type === 'list') {\n const column_fields = getColumnFields(columns.value || []);\n if (props.config.base_column_fields) {\n column_fields.push(...props.config.base_column_fields);\n }\n params['current'] = state.current;\n params['pageSize'] = state.pageSize;\n params['column_fields'] = column_fields;\n }\n if (search_type.value === 'advanced') {\n if (enable_advanced_search_multigroups.value) {\n const searchGroupConds = (viewData ? viewData.searchGroups || [] : searchGroups.value)\n .map(group => filterConditions(group.conditions))\n .filter(group => group.length > 0);\n const baseConds = filterConditions(props.config.base_conditions);\n const conditionsList = [];\n if (searchGroupConds.length > 0) {\n conditionsList.push({ op: 'and', children: searchGroupConds });\n }\n if (baseConds.length > 0) {\n conditionsList.push({ op: 'and', children: baseConds });\n }\n params['conditions'] = conditionsList;\n } else {\n const conditions = [\n ...filterConditions(props.config.base_conditions),\n ...filterConditions(viewData ? viewData.searchConditions || [] : searchConditions.value),\n ];\n params['conditions'] = [conditions];\n }\n if (type === 'list' || type === 'count') {\n const groupCondition = getCurrentGroupConditionValue(\n groupDataList || conditionGroupDataList.value,\n viewData ? viewData.groupConditionValue : groupConditionValue.value,\n );\n if (groupCondition) {\n params['conditions'] = [\n {\n op: 'and',\n children: params['conditions'],\n },\n {\n op: 'and',\n children: [\n {\n format: selectedConditionGroup.value?.format || 'input',\n class_name: selectedConditionGroup.value?.class_name || 'str',\n field_name: groupCondition.field_name,\n operator: 'eq',\n value: [groupCondition.value],\n },\n ],\n },\n ];\n }\n }\n } else if (search_type.value === 'simple') {\n for (const item of filterConditions(\n viewData ? viewData.searchConditions || [] : searchConditions.value,\n )) {\n const v_count = getParamsCount(item.operator, item);\n let value = item.value;\n if (v_count === 1) {\n value = value[0];\n }\n params[item.field_name] = value;\n }\n }\n if (props.config.get_query_params_func) {\n const newParams = props.config.get_query_params_func({ params, type });\n if (newParams) {\n Object.assign(params, newParams);\n }\n }\n const requiredConditionObj = getRequiredConditionObj();\n if (requiredConditionObj) {\n params['required_condition'] = requiredConditionObj;\n }\n if (props.config.get_db_alias) {\n params['db_alias'] = props.config.get_db_alias();\n }\n\n return params;\n};\nconst [elRef, screenState, { setFull, exitFull }] = useFullscreen();\nconst getGroupDataList = async () => {\n if (!selectedConditionGroup.value) {\n if (conditionGroupDataList.value?.length > 0) {\n conditionGroupDataList.value = [];\n }\n return;\n }\n if (!props.config.query_group_url) {\n console.error('query_group_url 未配置');\n if (conditionGroupDataList.value?.length > 0) {\n conditionGroupDataList.value = [];\n }\n return;\n }\n if (!conditionGroupDataList.value?.length) {\n const params = getQueryParams({ type: 'group' });\n params['group_condition'] = {\n field_name: selectedConditionGroup.value.field_name,\n display_name: selectedConditionGroup.value.display_name,\n order_name: selectedConditionGroup.value.order_name || [],\n };\n const res = await getApiData(params, props.config.query_group_url || '', false, {\n api_log: `tablePro ${props.config.key} ${props.config.title}`,\n });\n conditionGroupDataList.value = res || [];\n }\n};\nconst getData = async () => {\n if (!props.config.list_url) {\n return;\n }\n const currentLoadId = Symbol(); // 唯一值\n loadDataId = currentLoadId;\n try {\n loading.value = true;\n await getGroupDataList();\n const params = getQueryParams({ type: 'list' });\n let response: Response;\n if (props.config.query_data_func) {\n response = await props.config.query_data_func(params);\n } else {\n response = await getApiData(\n params,\n props.config.list_url.replace('__tree', ''),\n props.config.list_url.includes('__tree'),\n {\n api_log: `tablePro ${props.config.key} ${props.config.title}`,\n },\n );\n }\n if (loadDataId !== currentLoadId) {\n return;\n }\n if (props.config.process_data_func) {\n dataSource.value = props.config.process_data_func(response.data);\n } else {\n dataSource.value = response.data || [];\n }\n state.current = response.current;\n state.total = response.total;\n state.tableHead = response.table_head ? response.table_head : null;\n state.sum_data = response.sum_data ?? null;\n emit('dataChange', {\n data: dataSource.value,\n current: response.current,\n total: response.total,\n });\n syncSelectedRowKeys();\n } finally {\n if (loadDataId !== currentLoadId) {\n return;\n }\n loading.value = false;\n }\n};\nconst delayGetDate = debounce(getData, 500);\nconst handlePageChange = (page: any, pageSize: any) => {\n if (state.current == page && state.pageSize == pageSize) {\n return;\n }\n state.current = page ? page : 1;\n state.pageSize = pageSize ? pageSize : DEFAULT_PAGE_SIZE;\n getData();\n};\nconst search_type = computed(() => {\n if (props.config.search_type === 'simple') {\n return props.config.search_type;\n } else {\n return 'advanced';\n }\n});\nconst is_view = computed(() => {\n return props.config?.is_view !== false;\n});\nconst row_selection: any = computed(() => {\n const rowSelectionType = props.config.rowSelectionType || 'checkbox';\n const rowSelectionCheckStrictly = props.config.rowSelectionCheckStrictly || false;\n const onChange = (selectedRowKeys, selectdRecord) => {\n const unSelectdRecord = dataSource.value.filter(item => {\n return !selectedRowKeys.includes(item.id);\n });\n emit('selectChange', selectdRecord, unSelectdRecord);\n };\n return { type: rowSelectionType, checkStrictly: rowSelectionCheckStrictly, onChange };\n});\nconst viewColumnsConfig = ref<Record<string, ViewColumnItem>>({});\nconst viewSearchConfig = ref<Record<string, ViewSearchItem>>({});\nconst filtedColumns = computed(() => {\n const filterRecursive = (list: Column[]) => {\n return list\n .filter(item => item.checked !== false)\n .map(item => {\n if (item.children && item.children.length > 0) {\n const children = filterRecursive(item.children);\n if (children.length > 0) {\n return {\n ...item,\n children,\n };\n }\n return null;\n }\n return item;\n })\n .filter(Boolean);\n };\n return filterRecursive(columns.value);\n});\nconst columns = computed<Column[]>(() => {\n const filterValueMap = {};\n for (const item of headerSearchConditions.value) {\n if (['in', 'eq'].includes(item.operator)) {\n filterValueMap[item.field_name] = item.value;\n }\n }\n const sorted = sortedMap.value || {};\n const baseColumns = [...(props.config.columns || [])];\n\n if (props.config.btn_list && props.config.btn_list.length > 0) {\n const hasAction = baseColumns.some(item => item.dataIndex === 'action');\n if (!hasAction) {\n baseColumns.push({\n title: '操作',\n dataIndex: 'action',\n cellComponent: 'action',\n width: rowActionWidth.value + 20,\n resizable: true,\n fixed: 'right',\n checked: true,\n });\n }\n }\n\n // 合并视图配置,递归合并\n const mergeRecursive = (list: Column[], isChildren = false): Column[] => {\n const merged = list\n .map(item => {\n const key = item.dataIndex || item.key || item.title;\n const viewCol = viewColumnsConfig.value[key] || {};\n const newItem = {\n ...item,\n ...viewCol,\n is_children: isChildren,\n key: key,\n };\n if (newItem.children && newItem.children.length > 0) {\n newItem.children = mergeRecursive(newItem.children, true);\n }\n return newItem;\n })\n .sort((a, b) => {\n const orderA = a.order ?? 999;\n const orderB = b.order ?? 999;\n return orderA - orderB;\n });\n\n return merged.map(item => {\n const key = item.dataIndex || item.key || item.title;\n let ellipsis = item.ellipsis;\n if (item.resizable && item.ellipsis == undefined) {\n ellipsis = true;\n }\n if (item.autoWidth) {\n return {\n ...item,\n minWidth: item.width,\n width: undefined,\n filteredValue: filterValueMap[key] || null,\n sortOrder: sorted[key] || false,\n key: key,\n ellipsis,\n customFilterDropdown:\n props.config.enable_custom_filter &&\n !!props.config.search_config?.find(s => s.field_name === key),\n onFilterDropdownOpenChange: open => {\n if (open) {\n setTimeout(() => {\n searchInput.value?.focus();\n }, 100);\n }\n },\n };\n } else {\n return {\n ...item,\n width: item.width,\n minWidth: undefined,\n filteredValue: filterValueMap[key] || null,\n sortOrder: sorted[key] || false,\n key: key,\n ellipsis,\n customFilterDropdown:\n props.config.enable_custom_filter &&\n !!props.config.search_config?.find(s => s.field_name === key),\n onFilterDropdownOpenChange: open => {\n if (open) {\n setTimeout(() => {\n searchInput.value?.focus();\n }, 100);\n }\n },\n };\n }\n });\n };\n\n const res: Column[] = mergeRecursive(baseColumns);\n // 序号列处理\n const hasIndex = res.some(item => item.dataIndex === 'index');\n if (!hasIndex && !props.config.hide_index_column) {\n res.unshift({\n title: '#',\n dataIndex: 'index',\n key: 'index',\n maxWidth: 50,\n fixed: 'left',\n checked: true,\n autoHeight: true,\n });\n }\n return res;\n});\nconst tableContentWidth = computed(() => {\n let width = 0;\n const processColumns = datas => {\n for (const item of datas) {\n if (item.children && item.children.length > 0) {\n processColumns(item.children);\n } else {\n width += item.width || item.minWidth || 120;\n }\n }\n };\n processColumns(columns.value);\n});\nconst filename = computed(() => {\n const title = props.config.title || 'data';\n const datetime = dayjs().format('YYYYMMDD_HHmmss');\n return `${title}_${datetime}`;\n});\nconst exportExcelColumnList = computed(() => {\n const list: any = [];\n const getLeaves = (cols: Column[]) => {\n cols.forEach(item => {\n if (item.children && item.children.length > 0) {\n getLeaves(item.children);\n } else {\n if (item.dataIndex === 'action' || item.dataIndex === 'index') {\n return;\n }\n list.push({\n checked: item.checked,\n fixed: item.fixed ? item.fixed : false,\n key: item.dataIndex,\n label: item.title,\n width: item.width,\n });\n }\n });\n };\n getLeaves(columns.value);\n return list;\n});\nconst exportExcelColumn = computed(() => {\n const list: any = [];\n const getLeaves = (cols: Column[]) => {\n cols.forEach(item => {\n if (item.children && item.children.length > 0) {\n getLeaves(item.children);\n } else {\n if (item.dataIndex === 'action' || item.dataIndex === 'index') {\n return;\n }\n list.push(item);\n }\n });\n };\n getLeaves(columns.value);\n return list;\n});\nconst handleSearch = ({ confirm, dataIndex, value }) => {\n confirm();\n getData();\n isViewChange.value = true;\n};\n\nconst handleReset = ({ clearFilters, dataIndex }) => {\n clearFilters();\n const conditions = cloneDeep(headerSearchConditions.value);\n const conditionIndex = conditions.findIndex(item => item.field_name === dataIndex);\n if (conditionIndex > -1) {\n conditions.splice(conditionIndex, 1);\n }\n headerSearchConditions.value = conditions;\n getData();\n isViewChange.value = true;\n};\n\nconst refresh = async () => {\n await getData();\n};\nconst local_refresh = async () => {\n dataSource.value = [...dataSource.value];\n};\nconst handleRefreshTypeChange = type => {\n refreshType.value = type;\n isViewChange.value = true;\n};\nconst paginationHeight = ref(64);\nconst tableHeight = computed(() => {\n if (!tableWraperHeight.value) {\n return 0;\n }\n let value = tableWraperHeight.value - searchHeight.value - headerHeight.value - 4;\n if (props.config.is_pagination) {\n value -= paginationHeight.value;\n }\n console.log('tableWraperHeight', tableWraperHeight.value);\n console.log('searchHeight', searchHeight.value);\n console.log('headerHeight', headerHeight.value);\n console.log('paginationHeight', paginationHeight.value);\n console.log('tableHeight', value);\n return value;\n});\nconst handleTableSizeChange = () => {\n isViewChange.value = true;\n};\nwatch(\n () => state.tableSize,\n () => {\n // 此处设置paginationHeight两次,为了解决切换表格密度后,高度剧烈变化问题。\n paginationHeight.value = 64;\n setTimeout(() => {\n if (state.tableSize === 'small') {\n paginationHeight.value = 56;\n } else if (state.tableSize === 'middle') {\n paginationHeight.value = 56;\n } else if (state.tableSize === 'default') {\n paginationHeight.value = 64;\n }\n }, 200);\n },\n);\nconst syncSelectedRowKeys = () => {\n if (dataSource.value?.length > 0 && props.initValueKey) {\n const valueMap = {};\n selectedRowKeys.value = dataSource.value\n .filter(item => {\n const value = item[props.initValueKey];\n if (props.initSelectValue?.includes(value) && !valueMap[value]) {\n valueMap[value] = true;\n return true;\n }\n })\n .map(item => {\n return item.id;\n });\n }\n};\nconst init = async () => {\n conditionGroupDataList.value = [];\n const defaultSortField = props.config.columns.filter(\n column => column.sorter && column.defaultSortOrder,\n );\n defaultSortField.sort((a, b) => (a.defaultSortIndex || 0) - (b.defaultSortIndex || 0));\n for (const column of defaultSortField) {\n sortedMap.value[column.dataIndex] = column.defaultSortOrder;\n sortedList.value.push({ key: column.dataIndex, order: column.defaultSortOrder });\n }\n const tmpSearchConditions = [];\n if (props.config?.search_config) {\n props.config.search_config.forEach(configItem => {\n if (configItem.default_value) {\n let value = configItem.default_value;\n if (!isArray(value)) {\n value = [value];\n }\n tmpSearchConditions.push({\n field_name: configItem.field_name,\n value: value,\n operator: configItem.default_operator || 'eq',\n format: configItem.format || 'input',\n class_name: configItem.class_name || 'str',\n });\n }\n });\n }\n\n searchConditions.value = tmpSearchConditions;\n if (enable_advanced_search_multigroups.value) {\n if (props.config.default_advanced_search_multigroups) {\n searchGroups.value = props.config.default_advanced_search_multigroups;\n } else {\n searchGroups.value = [\n {\n id: genID(),\n conditions: [...tmpSearchConditions],\n viewSearchConfig: {},\n },\n ];\n }\n\n searchConditions.value = [];\n }\n if (is_view) {\n if (currentView.value?.id !== 'no-view') {\n currentView.value = null;\n await fetchViews();\n if (props.config?.view_uid) {\n const findView = viewDataList.value.find((item: any) => item.uid == props.config?.view_uid);\n if (findView) {\n setViewData(findView.data);\n currentView.value = findView;\n } else {\n const res: any = await query_view_settings_list({\n org_id: current_org.id,\n is_active: true,\n uid: props.config?.view_uid,\n });\n if (res?.length) {\n currentView.value = { ...res[0], is_other_user_view: true };\n }\n }\n }\n if (!currentView.value && viewDataList.value?.length) {\n const tmpData = [...viewDataList.value];\n // 优先级:当前选中视图,默认视图,用户视图,同级按 sort 正序\n const getSortValue = item => {\n let value = item.sort || 0;\n if (item.id !== currentView.value?.id) {\n value *= 1000000;\n }\n if (!item.is_default) {\n value *= 10000;\n }\n if (!item.user_id) {\n value *= 1000;\n }\n return value;\n };\n tmpData.sort((a, b) => getSortValue(a) - getSortValue(b));\n setViewData(tmpData[0].data);\n currentView.value = tmpData[0];\n }\n }\n }\n await getData();\n};\nconst eventReinit = async () => {\n isViewChange.value = false;\n await init();\n};\nconst eventReload = async () => {\n await getData();\n};\nconst flattenedColumns = computed(() => {\n const list = [];\n const getLeaves = (cols: Column[]) => {\n cols.forEach(col => {\n if (col.children && col.children.length > 0) {\n getLeaves(col.children);\n } else {\n list.push(col);\n }\n });\n };\n getLeaves(filtedColumns.value);\n return list;\n});\nconst summaryColumns = computed(() => {\n const list: any = [];\n let index = 0;\n for (const item of flattenedColumns.value) {\n let _index = index;\n if (props.config?.is_selection) {\n _index = index + 1;\n }\n if (item.summary) {\n // if (!props.config.is_selection && props.config.hide_index_column) {\n // console.error('未开启选择(is_selection)且隐藏了序号列(hide_index_column),无法展示\"合计\"');\n // return [];\n // }\n list.push({\n index: _index,\n key: item.key,\n columnKey: item.dataIndex,\n total_color: item.total_color,\n total_content: item.total_content,\n cellSummaryComponent: item.cellSummaryComponent,\n column: item,\n sum_data: state.sum_data,\n });\n }\n index++;\n }\n return list;\n});\nconst getRequiredConditionObj = () => {\n const required_condition = props.config.required_condition;\n if (required_condition) {\n const required_condition_obj = {};\n required_condition.forEach((item: any) => {\n const value = requiredConditionStore.value_map[item.key];\n if (value !== undefined && value !== null) {\n required_condition_obj[item.field_name] = value;\n }\n });\n return required_condition_obj;\n } else {\n return null;\n }\n};\nconst handleChange: TableProps['onChange'] = (pagination, filters, sorter, action) => {\n if (action.action === 'paginate') {\n return;\n } else if (action.action === 'filter') {\n const searchMap = {};\n for (const item of headerSearchConditions.value) {\n searchMap[item.field_name] = item;\n }\n for (const key of Object.keys(filters)) {\n const value = filters[key];\n const item = searchMap[key];\n if (item) {\n if (value?.length > 1) {\n item.operator = 'in';\n } else {\n if (!['in', 'eq'].includes(item.operator)) {\n item.operator = 'in';\n }\n }\n item.value = value;\n } else {\n searchMap[key] = {\n field_name: key,\n operator: 'eq',\n value: value,\n format: 'input',\n class_name: 'str',\n };\n }\n }\n headerSearchConditions.value = Object.values(searchMap);\n getData();\n isViewChange.value = true;\n } else if (action.action === 'sort') {\n const tmpMap = {};\n const tmpList = [];\n if (!isArray(sorter)) {\n tmpMap[sorter.columnKey] = sorter.order;\n tmpList.push({ key: sorter.columnKey, order: sorter.order });\n } else {\n for (const item of sorter) {\n tmpMap[item.columnKey] = item.order;\n tmpList.push({ key: item.columnKey, order: item.order });\n }\n }\n sortedMap.value = tmpMap;\n sortedList.value = tmpList;\n getData();\n isViewChange.value = true;\n }\n};\nconst handleCellClick = (e, cell) => {\n if (!props.config?.is_click_selection) {\n return;\n }\n const rowSelectionType = props.config.rowSelectionType || 'checkbox';\n if (rowSelectionType === 'radio') {\n selectedRowKeys.value = [cell.record.id];\n } else {\n const isSelected = selectedRowKeys.value && selectedRowKeys.value.includes(cell.record.id);\n if (isSelected) {\n selectedRowKeys.value = selectedRowKeys.value.filter(id => {\n return id !== cell.record.id;\n });\n } else {\n selectedRowKeys.value.push(cell.record.id);\n }\n }\n\n const selectdRecord = dataSource.value.filter(item => {\n return selectedRowKeys.value.includes(item.id);\n });\n const unSelectdRecord = dataSource.value.filter(item => {\n return !selectedRowKeys.value.includes(item.id);\n });\n emit('selectChange', selectdRecord, unSelectdRecord);\n};\nconst handleRequiredConditionChange = (key: string) => {\n if (props.config.required_condition) {\n const item = props.config.required_condition.find((item: any) => item.key === key);\n if (item) {\n conditionGroupDataList.value = [];\n getData();\n }\n }\n};\nonMounted(async () => {\n if (!props.config.key) {\n console.error('tablePro config 未配置 key');\n }\n if (!props.config.title) {\n console.error('tablePro config 未配置 title');\n }\n required_condition_event_obj().on('change', handleRequiredConditionChange);\n selfEventBus.on('refresh', refresh);\n selfEventBus.on('local_refresh', local_refresh);\n selfEventBus.on('reinit', eventReinit);\n selfEventBus.on('saveView', saveView);\n selfEventBus.on('saveNewView', addView);\n if (props.eventBus) {\n props.eventBus.on('reinit', eventReinit);\n props.eventBus.on('refresh', refresh);\n props.eventBus.on('local_refresh', local_refresh);\n props.eventBus.on('reload_table', eventReload);\n }\n if (props.config.tableSize) {\n state.tableSize = props.config.tableSize;\n }\n await init();\n});\nconst handleExport = () => {\n exportOpen.value = true;\n};\nconst onColumnsChange = (data: any[]) => {\n if (!props.config || !props.config.columns) return;\n const configObj: Record<string, any> = {};\n\n const processRecursive = (list: any[]) => {\n list.forEach((item, index) => {\n const dataIndex = item.dataIndex || item.key || item.title;\n if (dataIndex === 'index' || dataIndex === 'action') return;\n configObj[dataIndex] = {\n checked: item.checked,\n fixed: item.fixed,\n autoHeight: item.autoHeight,\n autoWidth: item.autoWidth,\n ellipsis: item.ellipsis,\n width: item.width,\n order: index,\n };\n if (item.children && item.children.length > 0) {\n processRecursive(item.children);\n }\n });\n };\n\n processRecursive(data);\n viewColumnsConfig.value = configObj;\n isViewChange.value = true;\n};\nconst setViewData = async (viewData: ViewData) => {\n if (viewData === null) {\n await init();\n isViewChange.value = false;\n return;\n }\n if (viewData.columns) {\n viewColumnsConfig.value = viewData.columns;\n }\n if (enable_advanced_search_multigroups.value) {\n if (viewData.searchGroups && viewData.searchGroups.length > 0) {\n searchGroups.value = viewData.searchGroups;\n } else if (viewData.searchConditions) {\n searchGroups.value = [\n {\n id: genID(),\n conditions: viewData.searchConditions,\n viewSearchConfig: viewData.searchConfig || {},\n },\n ];\n }\n // 保证至少存在一个分组\n if (searchGroups.value.length === 0) {\n searchGroups.value = [\n {\n id: genID(),\n conditions: [],\n viewSearchConfig: {},\n },\n ];\n }\n searchConditions.value = [];\n } else {\n if (viewData.searchConfig) {\n viewSearchConfig.value = viewData.searchConfig;\n }\n if (viewData.searchConditions) {\n searchConditions.value = viewData.searchConditions;\n }\n }\n if (viewData.tableSize) {\n state.tableSize = viewData.tableSize;\n }\n if (viewData.refreshType) {\n refreshType.value = viewData.refreshType;\n }\n if (viewData.sortedList) {\n sortedList.value = viewData.sortedList;\n sortedMap.value = {};\n for (const item of sortedList.value) {\n sortedMap.value[item.key] = item.order;\n }\n }\n if (viewData.groupConditionFieldName && props.config.condition_group_config_list?.length > 0) {\n selectedConditionGroup.value = props.config.condition_group_config_list.find(\n item => item.field_name === viewData.groupConditionFieldName,\n );\n } else {\n selectedConditionGroup.value = null;\n }\n\n groupConditionValue.value = viewData.groupConditionValue;\n\n if (props.config.set_view_callback) {\n props.config.set_view_callback({ viewData });\n }\n await refresh();\n isViewChange.value = false;\n};\nconst resizeColumn = (width, column, action: 'start' | 'move' | 'end') => {\n if (action === 'end') {\n let item = viewColumnsConfig.value[column.dataIndex];\n if (!item) {\n item = {};\n viewColumnsConfig.value[column.dataIndex] = item;\n }\n item.width = width;\n isViewChange.value = true;\n }\n};\nconst viewSearchConfigChange = (config: Record<string, ViewSearchItem>) => {\n viewSearchConfig.value = config;\n isViewChange.value = true;\n};\nconst getViewData = (): ViewData => {\n const viewData: any = {\n columns: viewColumnsConfig.value,\n searchConditions: searchConditions.value,\n tableSize: state.tableSize,\n refreshType: refreshType.value,\n searchConfig: viewSearchConfig.value,\n sortedList: sortedList.value,\n groupConditionFieldName: selectedConditionGroup.value?.field_name,\n groupConditionValue: groupConditionValue.value,\n };\n if (enable_advanced_search_multigroups.value) {\n viewData.searchGroups = searchGroups.value;\n // 将第一个分组的字段配置作为全局 searchConfig 保存,便于单分组模式兼容\n if (searchGroups.value.length > 0) {\n viewData.searchConfig = searchGroups.value[0].viewSearchConfig;\n }\n }\n if (props.config.get_view_data_func) {\n const newViewData = props.config.get_view_data_func({ viewData });\n if (newViewData) {\n Object.assign(viewData, newViewData);\n }\n }\n return viewData;\n};\nconst viewDataList = ref([]);\nconst currentView = ref<any>(null);\nconst isViewChange = ref(false);\nconst fetchViews = async () => {\n const data = await fetchViewsData(\n view_key.value,\n getQueryParams,\n props.config.count_url,\n props.config,\n );\n viewDataList.value = data;\n};\nconst handleSearchChange = () => {\n isViewChange.value = true;\n};\nconst handleGroupConditionValueChange = (value: any) => {\n isViewChange.value = true;\n};\nconst handleSelectedConditionGroupChange = (item: ConditionGroupConfigItem) => {\n conditionGroupDataList.value = [];\n isViewChange.value = true;\n};\nconst { saveView, addView } = useView({\n currentView,\n getViewData,\n isViewChange,\n userid,\n view_project__flag: env.VITE_APP_VIEW_PROJECT_FLAG,\n view_menu__flag: view_key.value,\n view_menu__name: props.config.title,\n fetchViews,\n is_auth: is_view_system_edit_auth,\n t,\n});\n\nprovide<TableProState>(tableProStateSymbol, {\n setViewData,\n getViewData,\n fetchViews,\n viewDataList,\n currentView,\n isViewChange,\n config: props.config,\n});\n\nonUnmounted(() => {\n selfEventBus.off('refresh', refresh);\n selfEventBus.off('local_refresh', local_refresh);\n selfEventBus.off('reinit', eventReinit);\n selfEventBus.off('saveView', saveView);\n selfEventBus.off('saveNewView', addView);\n if (props.eventBus) {\n props.eventBus.off('reinit', eventReinit);\n props.eventBus.off('refresh', refresh);\n props.eventBus.off('local_refresh', local_refresh);\n props.eventBus.off('reload_table', eventReload);\n }\n});\nwatch(\n () => props.initSelectValue,\n () => {\n syncSelectedRowKeys();\n },\n);\nwatch(\n () => props.config.query_params,\n () => {\n delayGetDate();\n },\n {\n deep: true,\n },\n);\nwatch(\n () => props.config.base_conditions,\n () => {\n delayGetDate();\n },\n {\n deep: true,\n },\n);\n\ndefineExpose({\n getViewData,\n setViewData,\n config: props.config,\n columns,\n fetchViews,\n viewDataList,\n currentView,\n isViewChange,\n viewColumnsConfig,\n searchGroups,\n refresh,\n init,\n dataSource,\n selectedRowKeys,\n settingOpen,\n getQueryParams,\n});\n</script>\n<style lang=\"less\" scoped>\n.btn-wraper {\n display: flex;\n justify-content: flex-end;\n}\n.table-wraper {\n height: 100%;\n overflow: hidden;\n}\n.modify-row-height {\n :deep(.surely-table-header) {\n height: var(--row-heigth) !important;\n }\n :deep(.surely-table-header-cell) {\n height: var(--row-heigth) !important;\n }\n :deep(.surely-table-summary) {\n height: var(--row-heigth) !important;\n }\n}\n.popup-divider {\n :deep(.ant-divider-horizontal) {\n margin-top: 1px;\n margin-bottom: 1px;\n }\n}\n.highlight {\n background-color: rgb(255, 192, 105);\n padding: 0px;\n}\n</style>\n"],"x_google_ignoreList":[20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39],"mappings":"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OAAO;CACN,MAAM;CACP,OAAO;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACP,OAAO;CACN,MAAM;CACN,MAAM;CACP,OAAO;CACN,MAAM;CACN,MAAM;CACP,OAAO;CACN,MAAM;CACP,OAAO;CACP,OAAO;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACP,OAAO;CACP,OAAO;CACN,MAAM;CACP,OAAO;CACN,MAAM;CACP,OAAO;CACN,MAAM;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACN,MAAM;CACP,OAAO;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,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"}
|