@mybricks/to-code-taro 1.0.5 → 1.0.7

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.
Files changed (57) hide show
  1. package/dist/cjs/core/utils/ComContext.js +11 -2
  2. package/dist/cjs/core/utils/hooks.js +13 -4
  3. package/dist/cjs/core/utils/index.js +9 -2
  4. package/dist/cjs/core/utils/slots.js +124 -0
  5. package/dist/cjs/core/utils/useContext.js +10 -6
  6. package/dist/cjs/core/utils/with.js +32 -5
  7. package/dist/cjs/generate/generateTaroProjectJson.js +2 -2
  8. package/dist/cjs/handleCom.d.ts +7 -30
  9. package/dist/cjs/handleCom.js +171 -61
  10. package/dist/cjs/handleSlot.d.ts +3 -0
  11. package/dist/cjs/handleSlot.js +27 -10
  12. package/dist/cjs/processors/processScene.js +34 -1
  13. package/dist/cjs/processors/processSceneLogic.js +1 -1
  14. package/dist/cjs/taro-template.json +16 -12
  15. package/dist/cjs/toCodeTaro.d.ts +2 -0
  16. package/dist/cjs/utils/logic/handleProcess.js +8 -13
  17. package/dist/cjs/utils/logic/processChildren.d.ts +1 -0
  18. package/dist/cjs/utils/logic/processChildren.js +16 -1
  19. package/dist/cjs/utils/style/converter.js +43 -15
  20. package/dist/cjs/utils/style/pxtransform.d.ts +2 -26
  21. package/dist/cjs/utils/style/pxtransform.js +27 -67
  22. package/dist/cjs/utils/templates/component.js +4 -3
  23. package/dist/cjs/utils/templates/index.d.ts +10 -0
  24. package/dist/cjs/utils/templates/index.js +31 -7
  25. package/dist/cjs/utils/templates/renderManager.d.ts +3 -11
  26. package/dist/cjs/utils/templates/renderManager.js +86 -21
  27. package/dist/cjs/utils/templates/scene.d.ts +2 -1
  28. package/dist/cjs/utils/templates/scene.js +4 -1
  29. package/dist/esm/core/utils/ComContext.js +5 -0
  30. package/dist/esm/core/utils/hooks.js +14 -5
  31. package/dist/esm/core/utils/index.js +4 -2
  32. package/dist/esm/core/utils/slots.js +108 -0
  33. package/dist/esm/core/utils/useContext.js +21 -11
  34. package/dist/esm/core/utils/with.js +50 -7
  35. package/dist/esm/generate/generateTaroProjectJson.js +2 -2
  36. package/dist/esm/handleCom.d.ts +7 -30
  37. package/dist/esm/handleCom.js +224 -81
  38. package/dist/esm/handleSlot.d.ts +3 -0
  39. package/dist/esm/handleSlot.js +34 -11
  40. package/dist/esm/processors/processScene.js +36 -1
  41. package/dist/esm/processors/processSceneLogic.js +3 -1
  42. package/dist/esm/taro-template.json +16 -12
  43. package/dist/esm/toCodeTaro.d.ts +2 -0
  44. package/dist/esm/utils/logic/handleProcess.js +12 -16
  45. package/dist/esm/utils/logic/processChildren.d.ts +1 -0
  46. package/dist/esm/utils/logic/processChildren.js +17 -1
  47. package/dist/esm/utils/style/converter.js +66 -31
  48. package/dist/esm/utils/style/pxtransform.d.ts +2 -26
  49. package/dist/esm/utils/style/pxtransform.js +31 -98
  50. package/dist/esm/utils/templates/component.js +3 -2
  51. package/dist/esm/utils/templates/index.d.ts +10 -0
  52. package/dist/esm/utils/templates/index.js +33 -9
  53. package/dist/esm/utils/templates/renderManager.d.ts +3 -11
  54. package/dist/esm/utils/templates/renderManager.js +92 -23
  55. package/dist/esm/utils/templates/scene.d.ts +2 -1
  56. package/dist/esm/utils/templates/scene.js +4 -2
  57. package/package.json +1 -1
@@ -377,7 +377,7 @@
377
377
  "children": [
378
378
  {
379
379
  "path": "src/components/checkList/runtime/item.less",
380
- "content": ".item {\n width: 100%;\n overflow: hidden;\n padding: 6px;\n border: 1px solid rgb(200, 201, 204);\n border-radius: 3px;\n\n &.horizontal {\n display: flex;\n align-items: center;\n justify-content: center;\n .icon {\n margin-right: 6px;\n }\n\n .text {\n flex: 1;\n }\n }\n\n &.vertical {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n .icon {\n margin-bottom: 6px;\n }\n .text {\n width: 100%;\n }\n }\n\n .icon {\n display: block;\n width: 24px;\n height: 24px;\n }\n\n .text {\n font-size: 14px;\n line-height: 18px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n}\n\n.itemSelected {\n width: 100%;\n overflow: hidden;\n padding: 6px;\n border-radius: 3px;\n background: #fa6400;\n color: #ffffff;\n font-weight: 500;\n border: 1px solid #fa6400;\n}"
380
+ "content": ".item {\n width: 100%;\n overflow: hidden;\n padding: 6px;\n border: 1px solid rgb(200, 201, 204);\n border-radius: 3px;\n\n &.horizontal {\n display: flex;\n align-items: center;\n justify-content: center;\n .icon {\n margin-right: 6px;\n }\n\n .text {\n flex: 1;\n }\n }\n\n &.vertical {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n .icon {\n margin-bottom: 6px;\n }\n .text {\n width: 100%;\n }\n }\n\n .icon {\n display: block;\n width: 24px;\n height: 24px;\n }\n}\n\n.itemSelected {\n width: 100%;\n overflow: hidden;\n padding: 6px;\n border-radius: 3px;\n background: #fa6400;\n color: #ffffff;\n font-weight: 500;\n border: 1px solid #fa6400;\n}\n\n.item,\n.itemSelected {\n .text {\n font-size: 14px;\n line-height: 18px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n}"
381
381
  },
382
382
  {
383
383
  "path": "src/components/checkList/runtime/Item.tsx",
@@ -1943,7 +1943,7 @@
1943
1943
  },
1944
1944
  {
1945
1945
  "path": "src/components/filter-coms/sort/runtime.tsx",
1946
- "content": "import React, {\n useState,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { View, Image, Button } from \"@tarojs/components\";\nimport Taro from \"@tarojs/taro\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport { isEmpty, isString } from \"./../../utils/core\";\nimport { useDiffValue, useFilterItemValue } from \"./../common\";\n\nconst UpSvg = ({ style }) => {\n return (\n <svg\n t=\"1717557925134\"\n class=\"icon\"\n viewBox=\"0 0 1024 1024\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n p-id=\"10247\"\n style={style}\n >\n <path\n d=\"M512 352L232 672h560L512 352z\"\n fill=\"currentColor\"\n p-id=\"10248\"\n ></path>\n </svg>\n );\n};\n\nconst UpSvg = ({ style }) => {\n const base64Svg = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzE3NTU3OTI1MTM0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEwMjQ3Ij4KICA8cGF0aCBkPSJNNTEyIDM1MkwyMzIgNjcyaDU2MEw1MTIgMzUyeiIgZmlsbD0iY3VycmVudENvbG9yIiBwLWlkPSIxMDI0OCI+PC9wYXRoPgo8L3N2Zz4=`;\n\n return <Image src={base64Svg} svg={true} style={style} />;\n};\n\nconst DownSvg = ({ style }) => {\n return <UpSvg style={{ ...style, transform: \"rotate(180deg)\" }} />;\n};\n\nconst UNSET_VALUE = \"_UNSET_\";\n\nexport default (props) => {\n const { env, data, inputs, outputs, slots } = props;\n const { filterValue: selectMode, setFilterValue: setSelectMode } =\n useFilterItemValue(\n {\n defaultValue: UNSET_VALUE,\n onReceiveValue: (value) => {\n switch (true) {\n case isEmpty(value): {\n setSelectMode(UNSET_VALUE);\n break;\n }\n case isString(value):\n setSelectMode(value);\n break;\n default:\n break;\n }\n },\n onChangeValue: (value) => {\n return value === UNSET_VALUE ? \"\" : value;\n },\n },\n props\n );\n\n const _optionMap = useMemo(() => {\n return data.optionMap ? data.optionMap : {};\n }, [data.optionMap]);\n\n const status = useMemo(() => {\n if (_optionMap.asc?.value == selectMode) {\n return \"asc\";\n }\n if (_optionMap.desc?.value == selectMode) {\n return \"desc\";\n }\n return \"none\";\n }, [_optionMap, selectMode]);\n\n const handleClick = useCallback(() => {\n if (env.edit) {\n return;\n }\n\n // 点击按顺序选择下一个\n const valueQueue = [\n UNSET_VALUE,\n _optionMap.asc.value,\n _optionMap.desc.value,\n ];\n\n const done = valueQueue.some((v, index) => {\n if (selectMode == v) {\n setSelectMode(\n index + 1 === valueQueue.length\n ? valueQueue[0]\n : valueQueue[index + 1]\n );\n }\n return selectMode === v;\n });\n\n // 兼容数据不属于options的情况\n if (!done) {\n setSelectMode(valueQueue[1]);\n }\n }, [status, selectMode, _optionMap]);\n\n const actived = useMemo(() => {\n return status !== \"none\";\n }, [status]);\n\n return (\n <View\n className={`${css.sort} ${\n actived ? \"mbs-filter_sort--active\" : \"mbs-filter_sort\"\n }`}\n onClick={handleClick}\n data-id={_optionMap.none?.label}\n >\n {env.edit ? _optionMap.none?.label ?? \"暂未配置\" : _optionMap.none?.label}\n <View className={css.icons}>\n {status === \"none\" && (\n <>\n {data.modes?.length === 1 && data.modes?.includes(\"asc\") && (\n <UpSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n {data.modes?.length === 1 && data.modes?.includes(\"desc\") && (\n <DownSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n {data.modes?.length === 2 && (\n <>\n <UpSvg\n style={{\n width: \"11px\",\n height: \"11px\",\n marginBottom: \"-2px\",\n }}\n />\n <DownSvg\n style={{ width: \"11px\", height: \"11px\", marginTop: \"-2px\" }}\n />\n </>\n )}\n </>\n )}\n {status === \"asc\" && (\n <UpSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n {status === \"desc\" && (\n <DownSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n </View>\n </View>\n );\n};\n"
1946
+ "content": "import React, {\n useState,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { View, Image, Button } from \"@tarojs/components\";\nimport Taro from \"@tarojs/taro\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport { isEmpty, isString } from \"./../../utils/core\";\nimport { useDiffValue, useFilterItemValue } from \"./../common\";\n\n// const UpSvg = ({ style }) => {\n// return (\n// <svg\n// t=\"1717557925134\"\n// class=\"icon\"\n// viewBox=\"0 0 1024 1024\"\n// version=\"1.1\"\n// xmlns=\"http://www.w3.org/2000/svg\"\n// p-id=\"10247\"\n// style={style}\n// >\n// <path\n// d=\"M512 352L232 672h560L512 352z\"\n// fill=\"currentColor\"\n// p-id=\"10248\"\n// ></path>\n// </svg>\n// );\n// };\n\nconst UpSvg = ({ style }) => {\n const base64Svg = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzE3NTU3OTI1MTM0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEwMjQ3Ij4KICA8cGF0aCBkPSJNNTEyIDM1MkwyMzIgNjcyaDU2MEw1MTIgMzUyeiIgZmlsbD0iY3VycmVudENvbG9yIiBwLWlkPSIxMDI0OCI+PC9wYXRoPgo8L3N2Zz4=`;\n\n return <Image src={base64Svg} svg={true} style={style} />;\n};\n\nconst DownSvg = ({ style }) => {\n return <UpSvg style={{ ...style, transform: \"rotate(180deg)\" }} />;\n};\n\nconst UNSET_VALUE = \"_UNSET_\";\n\nexport default (props) => {\n const { env, data, inputs, outputs, slots } = props;\n const { filterValue: selectMode, setFilterValue: setSelectMode } =\n useFilterItemValue(\n {\n defaultValue: UNSET_VALUE,\n onReceiveValue: (value) => {\n switch (true) {\n case isEmpty(value): {\n setSelectMode(UNSET_VALUE);\n break;\n }\n case isString(value):\n setSelectMode(value);\n break;\n default:\n break;\n }\n },\n onChangeValue: (value) => {\n return value === UNSET_VALUE ? \"\" : value;\n },\n },\n props\n );\n\n const _optionMap = useMemo(() => {\n return data.optionMap ? data.optionMap : {};\n }, [data.optionMap]);\n\n const status = useMemo(() => {\n if (_optionMap.asc?.value == selectMode) {\n return \"asc\";\n }\n if (_optionMap.desc?.value == selectMode) {\n return \"desc\";\n }\n return \"none\";\n }, [_optionMap, selectMode]);\n\n const handleClick = useCallback(() => {\n if (env.edit) {\n return;\n }\n\n // 点击按顺序选择下一个\n const valueQueue = [\n UNSET_VALUE,\n _optionMap.asc.value,\n _optionMap.desc.value,\n ];\n\n const done = valueQueue.some((v, index) => {\n if (selectMode == v) {\n setSelectMode(\n index + 1 === valueQueue.length\n ? valueQueue[0]\n : valueQueue[index + 1]\n );\n }\n return selectMode === v;\n });\n\n // 兼容数据不属于options的情况\n if (!done) {\n setSelectMode(valueQueue[1]);\n }\n }, [status, selectMode, _optionMap]);\n\n const actived = useMemo(() => {\n return status !== \"none\";\n }, [status]);\n\n return (\n <View\n className={`${css.sort} ${\n actived ? \"mbs-filter_sort--active\" : \"mbs-filter_sort\"\n }`}\n onClick={handleClick}\n data-id={_optionMap.none?.label}\n >\n {env.edit ? _optionMap.none?.label ?? \"暂未配置\" : _optionMap.none?.label}\n <View className={css.icons}>\n {status === \"none\" && (\n <>\n {data.modes?.length === 1 && data.modes?.includes(\"asc\") && (\n <UpSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n {data.modes?.length === 1 && data.modes?.includes(\"desc\") && (\n <DownSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n {data.modes?.length === 2 && (\n <>\n <UpSvg\n style={{\n width: \"11px\",\n height: \"11px\",\n marginBottom: \"-2px\",\n }}\n />\n <DownSvg\n style={{ width: \"11px\", height: \"11px\", marginTop: \"-2px\" }}\n />\n </>\n )}\n </>\n )}\n {status === \"asc\" && (\n <UpSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n {status === \"desc\" && (\n <DownSvg style={{ width: \"11px\", height: \"11px\" }} />\n )}\n </View>\n </View>\n );\n};\n"
1947
1947
  },
1948
1948
  {
1949
1949
  "path": "src/components/filter-coms/sort/style.less",
@@ -2081,7 +2081,7 @@
2081
2081
  },
2082
2082
  {
2083
2083
  "path": "src/components/formContainer/runtime.tsx",
2084
- "content": "import React, {\n useState,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useLayoutEffect,\n} from \"react\";\nimport { View, Button, Image } from \"@tarojs/components\";\nimport Taro from \"@tarojs/taro\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport { isEmpty, isObject } from \"./../utils/core\";\nimport { Cell, Form, Field } from \"brickd-mobile\";\nimport { getFormItem, findFormItemIndex } from \"./utils\";\nimport { FormItems, RuleKeys } from \"./types\";\n\n// @ts-ignore\nconst isH5 = Taro.getEnv() === Taro.ENV_TYPE.WEB || Taro.getEnv() === \"Unknown\";\n\nconst formatRulesFromItem = (item) => {\n const rules = item?.rules ?? [];\n\n return rules\n .filter((rule) => rule.status)\n .map((rule) => {\n if (rule.key === RuleKeys.REQUIRED) {\n return {\n required: true,\n message: rule.message,\n };\n }\n /** TODO */\n return {\n required: true,\n message: rule.message,\n };\n });\n};\n\n/** 去除value为undefined场景的对象 */\nconst omitUndefinedKeys = (obj) => {\n obj = obj || {};\n\n const resWithoutUndefined = {};\n Object.keys(obj).forEach((key) => {\n if (obj[key] !== undefined) {\n resWithoutUndefined[key] = obj[key];\n }\n });\n return resWithoutUndefined;\n};\n\n/** 代理fomrRef,因为组件库的setValues居然是增量的,不合理,这里代理成全量的 */\nconst useForm = ({ items, childrenInputs }) => {\n const formRef: any = useRef(null);\n\n const ref = useMemo(() => {\n return {\n getValues: () => {\n const res = formRef?.current?.getValues();\n return omitUndefinedKeys(res);\n },\n setFieldValue: (name, value) => {\n const res = formRef?.current?.getValues();\n\n // 如果值相同,不广播更新\n if (res[name] === value) {\n return;\n }\n\n res[name] = value;\n formRef?.current?.setValues?.(res);\n },\n setValues: (val) => {\n const valuesWithUndefined = {};\n /** 设置表单项的value */\n items.forEach((item) => {\n const itemValue = { ...(val ?? {}) }[item.name ?? item.id];\n childrenInputs.current[item.comName ?? item.id]?.[\"setValue\"]?.(\n itemValue\n );\n valuesWithUndefined[item.name ?? item.id] =\n itemValue !== undefined ? itemValue : undefined;\n });\n\n formRef?.cxurrent?.setValues?.(valuesWithUndefined);\n },\n validate: () =>\n new Promise((resolve, reject) => {\n formRef?.current\n ?.validate()\n ?.then((res) => {\n resolve(omitUndefinedKeys(res));\n })\n .catch((e) => reject(e));\n }),\n };\n }, []);\n\n return [ref, formRef];\n};\n\nexport default function ({ env, data, inputs, outputs, slots }) {\n const childrenInputs = useRef({});\n const [form, formRef] = useForm({ items: data.items, childrenInputs });\n const [loading, setLoading] = useState(false);\n\n useLayoutEffect(() => {\n /** 下发表单项的onChange函数,用来收集表单项数据 */\n slots[\"content\"]._inputs[\"onChange\"](({ id, name, value }) => {\n const item = getFormItem(data.items, { id, name });\n if (item) {\n form.setFieldValue(item.name || item.label, value);\n }\n });\n\n /** 动态修改标题等属性 */\n slots[\"content\"]._inputs[\"setProps\"](({ id, name, value }) => {\n const item = getFormItem(data.items, { id, name });\n if (item) {\n Object.keys(value).forEach((key) => {\n item[key] = value[key];\n });\n }\n });\n\n // 提交表单\n inputs[\"submit\"]((val, outputRels) => {\n form\n ?.validate()\n .then((res) => {\n outputRels[\"onSubmit\"](res);\n })\n .catch((err) => {\n console.error(\"validate\", err);\n });\n });\n\n inputs[\"submitAndMerge\"]((val, outputRels) => {\n let _val = isObject(val) ? val : {};\n\n form\n ?.validate()\n .then((res) => {\n outputRels[\"onMergeSubmit\"]({\n ...res,\n ..._val,\n });\n })\n .catch((err) => {\n console.error(\"validate\", err);\n });\n });\n\n // 重置表单\n inputs[\"resetFields\"]((val, outputRels) => {\n form.setValues({});\n outputRels[\"onReset\"]();\n });\n\n // 异步提交完成\n inputs[\"finishLoading\"]?.(() => {\n setLoading(false);\n });\n\n inputs[\"setFieldsValue\"]((val) => {\n if (isEmpty(val) || !isObject(val)) {\n return;\n }\n\n form.setValues(val);\n // 触发「表单数据输入」\n slots[\"content\"].inputs[\"setFieldsValue\"](val);\n });\n\n inputs[\"getFieldsValue\"]((val, outputRels) => {\n const values = form.getValues();\n outputRels[\"returnValues\"](values);\n });\n }, []);\n\n const content = useMemo(() => {\n return (\n <View>\n {slots[\"content\"].render({\n itemWrap(com: { id; jsx; name }) {\n // todo name\n const idx = findFormItemIndex(data.items, com);\n const item = data.items[idx] ?? ({} as FormItems);\n let rules = formatRulesFromItem(item);\n\n if (data.skipValidation === \"hidden\" && item.visible === false) {\n rules = [];\n }\n\n const showRequired = rules.findIndex((rule) => rule.required) > -1;\n\n // const isLast = data.items.length - 1 === idx\n const isLast = data.items.length - 1 === item.index;\n\n const icon = item.icon ? (\n <Image mode=\"scaleToFill\" src={item.icon} />\n ) : null;\n\n const itemLayout =\n !item.itemLayout || item.itemLayout === \"unset\"\n ? data.itemLayout\n : item.itemLayout;\n\n return (\n <Field\n className={cx(\n \"mybricks-field\",\n { [\"border-bottom\"]: !isLast },\n { [\"border-none\"]: isLast },\n { [css.hidden]: !env?.edit && item.hidden },\n { [\"vertical\"]: itemLayout === \"vertical\" }\n )}\n required={showRequired}\n rules={rules}\n label={!item.hideLabel && item.label}\n name={item.name}\n icon={icon}\n >\n {com.jsx}\n </Field>\n );\n },\n wrap(comAray: { id; name; jsx; def; inputs; outputs; style }[]) {\n const items = data.items ?? [];\n\n const jsx = comAray?.map((com, idx) => {\n if (com) {\n let item = getFormItem(data.items, com);\n item.index = idx;\n item.visible = com.style.display !== \"none\";\n\n if (!item) {\n if (items.length === comAray.length) {\n console.warn(`formItem comId ${com.id} formItem not found`);\n }\n return;\n }\n\n if (item.comName) {\n childrenInputs.current[com.name] = com.inputs;\n } else {\n childrenInputs.current[com.id] = com.inputs;\n }\n\n return com.jsx;\n }\n });\n\n return jsx;\n },\n })}\n </View>\n );\n }, []);\n\n const onCustomSubmit = useCallback(() => {\n if (env.runtime) {\n // 异步提交中\n if (data.useLoading && loading) {\n return;\n }\n\n if (data.useLoading) {\n setLoading(true);\n }\n\n switch (data.skipValidation) {\n case false:\n case \"all\":\n // 校验所有表单项\n form\n ?.validate()\n .then((res) => {\n outputs[\"onSubmit\"](res);\n })\n .catch((err) => {\n // 遇到异常,自动回滚 loading 状态\n setLoading(false);\n console.error(\"validate\", err);\n });\n break;\n\n case \"hidden\":\n // 不校验隐藏的表单项\n form\n ?.validate()\n .then((res) => {\n outputs[\"onSubmit\"](res);\n })\n .catch((err) => {\n // 遇到异常,自动回滚 loading 状态\n setLoading(false);\n console.error(\"validate\", err);\n });\n break;\n\n case true:\n case \"none\":\n // 不校验所有的表单项\n outputs[\"onSubmit\"](form?.getValues());\n break;\n }\n\n // if (data.skipValidation) {\n // outputs[\"onSubmit\"](form?.getValues());\n // } else {\n // form\n // ?.validate()\n // .then((res) => {\n // outputs[\"onSubmit\"](res);\n // })\n // .catch((err) => {\n // // 遇到异常,自动回滚 loading 状态\n // setLoading(false);\n // console.error(\"validate\", err);\n // });\n // }\n }\n }, [data.useLoading, loading, data.skipValidation]);\n\n return (\n <Form className={cx(css.form, { [css.h5]: isH5 })} ref={formRef}>\n <Cell.Group bordered={false}>{content}</Cell.Group>\n {data.useSubmitButton ? (\n <View className={cx(css.foot, \"mybricks-submit\")}>\n <Button className=\"taroify-button\" onClick={onCustomSubmit}>\n {loading ? (\n // ... 的动画\n <View className={css.loading}>\n <View className={css.dot1}></View>\n <View className={css.dot2}></View>\n <View className={css.dot3}></View>\n </View>\n ) : (\n data.submitButtonText\n )}\n </Button>\n </View>\n ) : null}\n </Form>\n );\n}\n"
2084
+ "content": "import React, {\n useState,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useLayoutEffect,\n} from \"react\";\nimport { View, Button, Image } from \"@tarojs/components\";\nimport Taro from \"@tarojs/taro\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport { isEmpty, isObject } from \"./../utils/core\";\nimport { Cell, Form, Field } from \"brickd-mobile\";\nimport { getFormItem, findFormItemIndex } from \"./utils\";\nimport { FormItems, RuleKeys } from \"./types\";\n\n// @ts-ignore\nconst isH5 = Taro.getEnv() === Taro.ENV_TYPE.WEB || Taro.getEnv() === \"Unknown\";\n\nconst formatRulesFromItem = (item) => {\n const rules = item?.rules ?? [];\n\n return rules\n .filter((rule) => rule.status)\n .map((rule) => {\n if (rule.key === RuleKeys.REQUIRED) {\n return {\n required: true,\n message: rule.message,\n };\n }\n /** TODO */\n return {\n required: true,\n message: rule.message,\n };\n });\n};\n\n/** 去除value为undefined场景的对象 */\nconst omitUndefinedKeys = (obj) => {\n obj = obj || {};\n\n const resWithoutUndefined = {};\n Object.keys(obj).forEach((key) => {\n if (obj[key] !== undefined) {\n resWithoutUndefined[key] = obj[key];\n }\n });\n return resWithoutUndefined;\n};\n\n/** 代理fomrRef,因为组件库的setValues居然是增量的,不合理,这里代理成全量的 */\nconst useForm = ({ items, childrenInputs }) => {\n const formRef: any = useRef(null);\n\n const ref = useMemo(() => {\n return {\n getValues: () => {\n const res = formRef?.current?.getValues();\n return omitUndefinedKeys(res);\n },\n setFieldValue: (name, value) => {\n const res = formRef?.current?.getValues();\n\n // 如果值相同,不广播更新\n if (res[name] === value) {\n return;\n }\n\n res[name] = value;\n formRef?.current?.setValues?.(res);\n },\n setValues: (val) => {\n const valuesWithUndefined = {};\n /** 设置表单项的value */\n items.forEach((item) => {\n const itemValue = { ...(val ?? {}) }[item.name ?? item.id];\n childrenInputs.current[item.comName ?? item.id]?.[\"setValue\"]?.(\n itemValue\n );\n valuesWithUndefined[item.name ?? item.id] =\n itemValue !== undefined ? itemValue : undefined;\n });\n\n formRef?.cxurrent?.setValues?.(valuesWithUndefined);\n },\n validate: () =>\n new Promise((resolve, reject) => {\n formRef?.current\n ?.validate()\n ?.then((res) => {\n resolve(omitUndefinedKeys(res));\n })\n .catch((e) => reject(e));\n }),\n };\n }, []);\n\n return [ref, formRef];\n};\n\nexport default function ({ env, data, inputs, outputs, slots }) {\n const childrenInputs = useRef({});\n const [form, formRef] = useForm({ items: data.items, childrenInputs });\n const [loading, setLoading] = useState(false);\n\n useLayoutEffect(() => {\n /** 下发表单项的onChange函数,用来收集表单项数据 */\n slots[\"content\"]._inputs[\"onChange\"](({ id, name, value }) => {\n const item = getFormItem(data.items, { id, name });\n if (item) {\n form.setFieldValue(item.name || item.label, value);\n }\n });\n\n /** 动态修改标题等属性 */\n slots[\"content\"]._inputs[\"setProps\"](({ id, name, value }) => {\n const item = getFormItem(data.items, { id, name });\n if (item) {\n Object.keys(value).forEach((key) => {\n item[key] = value[key];\n });\n }\n });\n\n // 提交表单\n inputs[\"submit\"]((val, outputRels) => {\n form\n ?.validate()\n .then((res) => {\n outputRels[\"onSubmit\"](res);\n })\n .catch((err) => {\n console.error(\"validate\", err);\n });\n });\n\n inputs[\"submitAndMerge\"]((val, outputRels) => {\n let _val = isObject(val) ? val : {};\n\n form\n ?.validate()\n .then((res) => {\n outputRels[\"onMergeSubmit\"]({\n ...res,\n ..._val,\n });\n })\n .catch((err) => {\n console.error(\"validate\", err);\n });\n });\n\n // 重置表单\n inputs[\"resetFields\"]((val, outputRels) => {\n form.setValues({});\n outputRels[\"onReset\"]();\n });\n\n // 异步提交完成\n inputs[\"finishLoading\"]?.(() => {\n setLoading(false);\n });\n\n inputs[\"setFieldsValue\"]((val) => {\n if (isEmpty(val) || !isObject(val)) {\n return;\n }\n\n form.setValues(val);\n // 触发「表单数据输入」\n slots[\"content\"].inputs[\"setFieldsValue\"](val);\n });\n\n inputs[\"getFieldsValue\"]((val, outputRels) => {\n const values = form.getValues();\n outputRels[\"returnValues\"](values);\n });\n }, []);\n\n const content = useMemo(() => {\n return (\n <View>\n {slots[\"content\"].render({\n itemWrap(com: { id; jsx; name }) {\n // todo name\n const idx = findFormItemIndex(data.items, com);\n const item = data.items[idx] ?? ({} as FormItems);\n let rules = formatRulesFromItem(item);\n\n if (data.skipValidation === \"hidden\" && item.visible === false) {\n rules = [];\n }\n\n const showRequired = rules.findIndex((rule) => rule.required) > -1;\n\n // const isLast = data.items.length - 1 === idx\n const isLast = data.items.length - 1 === item.index;\n\n const icon = item.icon ? (\n <Image mode=\"scaleToFill\" src={item.icon} />\n ) : null;\n\n const itemLayout =\n !item.itemLayout || item.itemLayout === \"unset\"\n ? data.itemLayout\n : item.itemLayout;\n\n return (\n <Field\n className={cx(\n \"mybricks-field\",\n com.id,\n { [\"border-bottom\"]: !isLast },\n { [\"border-none\"]: isLast },\n { [css.hidden]: !env?.edit && item.hidden },\n { [\"vertical\"]: itemLayout === \"vertical\" }\n )}\n required={showRequired}\n rules={rules}\n label={!item.hideLabel && item.label}\n name={item.name}\n icon={icon}\n >\n {com.jsx}\n </Field>\n );\n },\n wrap(comAray: { id; name; jsx; def; inputs; outputs; style }[]) {\n const items = data.items ?? [];\n\n const jsx = comAray?.map((com, idx) => {\n if (com) {\n let item = getFormItem(data.items, com);\n item.index = idx;\n item.visible = com.style.display !== \"none\";\n\n if (!item) {\n if (items.length === comAray.length) {\n console.warn(`formItem comId ${com.id} formItem not found`);\n }\n return;\n }\n\n if (item.comName) {\n childrenInputs.current[com.name] = com.inputs;\n } else {\n childrenInputs.current[com.id] = com.inputs;\n }\n\n return com.jsx;\n }\n });\n\n return jsx;\n },\n })}\n </View>\n );\n }, []);\n\n const onCustomSubmit = useCallback(() => {\n if (env.runtime) {\n // 异步提交中\n if (data.useLoading && loading) {\n return;\n }\n\n if (data.useLoading) {\n setLoading(true);\n }\n\n switch (data.skipValidation) {\n case false:\n case \"all\":\n // 校验所有表单项\n form\n ?.validate()\n .then((res) => {\n outputs[\"onSubmit\"](res);\n })\n .catch((err) => {\n // 遇到异常,自动回滚 loading 状态\n setLoading(false);\n console.error(\"validate\", err);\n });\n break;\n\n case \"hidden\":\n // 不校验隐藏的表单项\n form\n ?.validate()\n .then((res) => {\n outputs[\"onSubmit\"](res);\n })\n .catch((err) => {\n // 遇到异常,自动回滚 loading 状态\n setLoading(false);\n console.error(\"validate\", err);\n });\n break;\n\n case true:\n case \"none\":\n // 不校验所有的表单项\n outputs[\"onSubmit\"](form?.getValues());\n break;\n }\n\n // if (data.skipValidation) {\n // outputs[\"onSubmit\"](form?.getValues());\n // } else {\n // form\n // ?.validate()\n // .then((res) => {\n // outputs[\"onSubmit\"](res);\n // })\n // .catch((err) => {\n // // 遇到异常,自动回滚 loading 状态\n // setLoading(false);\n // console.error(\"validate\", err);\n // });\n // }\n }\n }, [data.useLoading, loading, data.skipValidation]);\n\n return (\n <Form className={cx(css.form, { [css.h5]: isH5 })} ref={formRef}>\n <Cell.Group bordered={false}>{content}</Cell.Group>\n {data.useSubmitButton ? (\n <View className={cx(css.foot, \"mybricks-submit\")}>\n <Button className=\"taroify-button\" onClick={onCustomSubmit}>\n {loading ? (\n // ... 的动画\n <View className={css.loading}>\n <View className={css.dot1}></View>\n <View className={css.dot2}></View>\n <View className={css.dot3}></View>\n </View>\n ) : (\n data.submitButtonText\n )}\n </Button>\n </View>\n ) : null}\n </Form>\n );\n}\n"
2085
2085
  },
2086
2086
  {
2087
2087
  "path": "src/components/formContainer/style.less",
@@ -2469,7 +2469,7 @@
2469
2469
  },
2470
2470
  {
2471
2471
  "path": "src/components/formPassword/runtime.tsx",
2472
- "content": "import React, { useCallback, useMemo, useState, useEffect } from \"react\";\nimport { isNumber, isObject, isString, isEmpty } from \"./../utils/core/type\";\nimport { View } from \"@tarojs/components\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport { Input } from \"brickd-mobile\";\nimport { isH5 } from \"../utils/env\";\nimport cx from \"classnames\";\n\nexport default function (props) {\n const { env, data, inputs, outputs, slots, parentSlot } = props;\n\n const [value, setValue] = useState(data.value);\n const [paasword, setPassword] = useState(true);\n\n useEffect(() => {\n parentSlot?._inputs[\"setProps\"]?.({\n id: props.id,\n name: props.name,\n value: {\n visible: props.style.display !== \"none\",\n },\n });\n }, [props.style.display]);\n\n useEffect(() => {\n inputs[\"getValue\"]((val, outputRels) => {\n outputRels[\"returnValue\"](value);\n });\n }, [value]);\n\n useEffect(() => {\n inputs[\"setValue\"]((val) => {\n switch (true) {\n case isEmpty(val): {\n setValue(\"\");\n break;\n }\n case isString(val) || isNumber(val):\n setValue(val);\n break;\n case isObject(val):\n setValue(val[data.name]);\n break;\n default:\n break;\n }\n });\n\n // 设置禁用\n inputs[\"setDisabled\"]?.(() => {\n data.disabled = true;\n });\n\n // 设置启用\n inputs[\"setEnabled\"]?.(() => {\n data.disabled = false;\n });\n }, []);\n\n const buttonCx = useMemo(() => {\n return cx({\n [css.button]: true,\n [css.show]: !paasword,\n [css.hide]: paasword,\n });\n }, [paasword]);\n\n const onTogglePassword = useCallback(() => {\n setPassword(!paasword);\n }, [paasword]);\n\n const onChange = useCallback((e) => {\n let value = e.detail.value;\n setValue(value);\n\n parentSlot?._inputs[\"onChange\"]?.({\n id: props.id,\n name: props.name,\n value,\n });\n outputs[\"onChange\"](value);\n }, []);\n\n const onBlur = useCallback((e) => {\n let value = e.detail.value;\n outputs[\"onBlur\"](value);\n }, []);\n\n return (\n <View\n className={cx({\n [css.password]: true,\n \"mybricks-password\": !isH5(),\n \"mybricks-h5Password\": isH5(),\n })}\n >\n <Input\n value={value}\n password={paasword}\n type={data.type}\n placeholder={data.placeholder}\n onChange={onChange}\n disabled={data.disabled}\n onBlur={onBlur}\n maxlength={data.maxlength || 20}\n />\n <View className={buttonCx} onClick={onTogglePassword}></View>\n </View>\n );\n}\n"
2472
+ "content": "import React, { useCallback, useMemo, useState, useEffect } from \"react\";\nimport { isNumber, isObject, isString, isEmpty } from \"./../utils/core/type\";\nimport { View } from \"@tarojs/components\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport { Input } from \"brickd-mobile\";\nimport { isH5 } from \"../utils/env\";\n\nexport default function (props) {\n const { env, data, inputs, outputs, slots, parentSlot } = props;\n\n const [value, setValue] = useState(data.value);\n const [paasword, setPassword] = useState(true);\n\n useEffect(() => {\n parentSlot?._inputs[\"setProps\"]?.({\n id: props.id,\n name: props.name,\n value: {\n visible: props.style.display !== \"none\",\n },\n });\n }, [props.style.display]);\n\n useEffect(() => {\n inputs[\"getValue\"]((val, outputRels) => {\n outputRels[\"returnValue\"](value);\n });\n }, [value]);\n\n useEffect(() => {\n inputs[\"setValue\"]((val) => {\n switch (true) {\n case isEmpty(val): {\n setValue(\"\");\n break;\n }\n case isString(val) || isNumber(val):\n setValue(val);\n break;\n case isObject(val):\n setValue(val[data.name]);\n break;\n default:\n break;\n }\n });\n\n // 设置禁用\n inputs[\"setDisabled\"]?.(() => {\n data.disabled = true;\n });\n\n // 设置启用\n inputs[\"setEnabled\"]?.(() => {\n data.disabled = false;\n });\n }, []);\n\n const buttonCx = useMemo(() => {\n return cx({\n [css.button]: true,\n [css.show]: !paasword,\n [css.hide]: paasword,\n });\n }, [paasword]);\n\n const onTogglePassword = useCallback(() => {\n setPassword(!paasword);\n }, [paasword]);\n\n const onChange = useCallback((e) => {\n let value = e.detail.value;\n setValue(value);\n\n parentSlot?._inputs[\"onChange\"]?.({\n id: props.id,\n name: props.name,\n value,\n });\n outputs[\"onChange\"](value);\n }, []);\n\n const onBlur = useCallback((e) => {\n let value = e.detail.value;\n outputs[\"onBlur\"](value);\n }, []);\n\n return (\n <View\n className={cx({\n [css.password]: true,\n \"mybricks-password\": !isH5(),\n \"mybricks-h5Password\": isH5(),\n })}\n >\n <Input\n value={value}\n password={paasword}\n type={data.type}\n placeholder={data.placeholder}\n onChange={onChange}\n disabled={data.disabled}\n onBlur={onBlur}\n maxlength={data.maxlength || 20}\n />\n <View className={buttonCx} onClick={onTogglePassword}></View>\n </View>\n );\n}\n"
2473
2473
  },
2474
2474
  {
2475
2475
  "path": "src/components/formPassword/style.less",
@@ -2537,7 +2537,7 @@
2537
2537
  },
2538
2538
  {
2539
2539
  "path": "src/components/formPhoneSMS/runtime.tsx",
2540
- "content": "import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { View, Button } from \"@tarojs/components\";\nimport { Input } from \"brickd-mobile\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport * as Taro from \"@tarojs/taro\";\nimport useFormItemValue from \"../utils/hooks/useFormItemValue.ts\";\nimport { isEmpty, isString, isNumber, isObject } from \"./../utils/type\";\nimport { isH5 } from \"../utils/env\";\nimport cx from \"classnames\";\n\nexport default function (props) {\n const { env, data, inputs, outputs, slots, parentSlot } = props;\n\n const [value, setValue, getValue] = useFormItemValue(data.value, (val) => {\n //\n parentSlot?._inputs[\"onChange\"]?.({\n id: props.id,\n name: props.name,\n value: val,\n });\n\n //\n outputs[\"onChange\"](val);\n });\n\n //\n const timer = useRef(null);\n const countDownRef = useRef(null);\n\n useEffect(() => {\n parentSlot?._inputs[\"setProps\"]?.({\n id: props.id,\n name: props.name,\n value: {\n visible: props.style.display !== \"none\",\n },\n });\n }, [props.style.display]);\n\n useEffect(() => {\n /* 设置值 */\n inputs[\"setValue\"]((val, outputRels) => {\n let result;\n\n switch (true) {\n case isEmpty(val): {\n result = \"\";\n break;\n }\n case isString(val) || isNumber(val): {\n result = `${val}`;\n break;\n }\n default:\n // 其他类型的值,直接返回\n return;\n }\n\n setValue(result);\n outputRels[\"setValueComplete\"]?.(result); // 表单容器调用 setValue 时,没有 outputRels\n });\n\n /* 获取值 */\n inputs[\"getValue\"]((val, outputRels) => {\n outputRels[\"returnValue\"](getValue());\n });\n\n /* 重置值 */\n inputs[\"resetValue\"]((val, outputRels) => {\n setValue(\"\");\n outputRels[\"resetValueComplete\"]?.(\"\");\n });\n\n /* 设置标题 */\n inputs[\"setLabel\"]?.((val) => {\n if (!isString(val)) {\n return;\n }\n\n parentSlot?._inputs[\"setProps\"]?.({\n id: props.id,\n name: props.name,\n value: {\n label: val,\n },\n });\n });\n\n /* 设置提示内容 */\n inputs[\"setPlaceholder\"]((val) => {\n data.placeholder = val;\n });\n\n /* 清除倒计时 */\n inputs[\"clearCountDown\"]((val) => {\n clearTimeout(timer.current);\n countDownRef.current = 0;\n data.buttonText = data.buttonTextRetry;\n });\n\n /* 设置禁用 */\n inputs[\"setDisabled\"]?.((val, outputRels) => {\n data.disabled = !!val;\n outputRels[\"setDisabledComplete\"]?.(data.disabled);\n });\n }, []);\n\n const countDown = useCallback(\n (cd, callback) => {\n countDownRef.current = cd;\n data.buttonText = `${cd}s 后重试`;\n\n if (typeof callback === \"function\") {\n callback();\n }\n\n timer.current = setTimeout(() => {\n countDownRef.current--;\n\n if (countDownRef.current > 0) {\n countDown(countDownRef.current);\n } else {\n data.buttonText = data.buttonTextRetry;\n }\n }, 1000);\n },\n [data.buttonTextRetry]\n );\n\n const onChange = useCallback((e) => {\n let value = e.detail.value;\n setValue(value);\n }, []);\n\n const onBlur = useCallback((e) => {\n let value = e.detail.value;\n outputs[\"onBlur\"](value);\n }, []);\n\n const onCodeSend = useCallback(\n (e) => {\n if (!env.runtime) {\n return;\n }\n\n e.stopPropagation();\n\n // 禁用时不可点击\n if (data.disabled) {\n return;\n }\n\n // 倒计时中不可点击\n if (countDownRef.current > 0) {\n return;\n }\n\n // 发送验证码,并开始倒计时\n countDown(data.smsCountdown, () => {\n outputs[\"onCodeSend\"]();\n });\n },\n [countDownRef.current]\n );\n\n return (\n <View\n className={cx({\n [css.phoneSMS]: true,\n \"mybricks-phoneSMS\": !isH5(),\n \"mybricks-h5PhoneSMS\": isH5(),\n })}\n >\n <Input\n className={css.input}\n value={value}\n placeholder={data.placeholder}\n onChange={onChange}\n onBlur={onBlur}\n />\n <Button\n className={cx({\n [css.button]: true,\n [css.enabled]: !data.disabled && countDownRef.current <= 0,\n \"mybricks-button\": !data.disabled && countDownRef.current <= 0,\n [css.disabled]: data.disabled || countDownRef.current > 0,\n \"mybricks-button-disabled\": data.disabled || countDownRef.current > 0,\n })}\n onClick={onCodeSend}\n >\n {data.buttonText}\n </Button>\n </View>\n );\n}\n"
2540
+ "content": "import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { View, Button } from \"@tarojs/components\";\nimport { Input } from \"brickd-mobile\";\nimport css from \"./style.less\";\nimport cx from \"classnames\";\nimport * as Taro from \"@tarojs/taro\";\nimport useFormItemValue from \"../utils/hooks/useFormItemValue.ts\";\nimport { isEmpty, isString, isNumber, isObject } from \"./../utils/type\";\nimport { isH5 } from \"../utils/env\";\n\nexport default function (props) {\n const { env, data, inputs, outputs, slots, parentSlot } = props;\n\n const [value, setValue, getValue] = useFormItemValue(data.value, (val) => {\n //\n parentSlot?._inputs[\"onChange\"]?.({\n id: props.id,\n name: props.name,\n value: val,\n });\n\n //\n outputs[\"onChange\"](val);\n });\n\n //\n const timer = useRef(null);\n const countDownRef = useRef(null);\n\n useEffect(() => {\n parentSlot?._inputs[\"setProps\"]?.({\n id: props.id,\n name: props.name,\n value: {\n visible: props.style.display !== \"none\",\n },\n });\n }, [props.style.display]);\n\n useEffect(() => {\n /* 设置值 */\n inputs[\"setValue\"]((val, outputRels) => {\n let result;\n\n switch (true) {\n case isEmpty(val): {\n result = \"\";\n break;\n }\n case isString(val) || isNumber(val): {\n result = `${val}`;\n break;\n }\n default:\n // 其他类型的值,直接返回\n return;\n }\n\n setValue(result);\n outputRels[\"setValueComplete\"]?.(result); // 表单容器调用 setValue 时,没有 outputRels\n });\n\n /* 获取值 */\n inputs[\"getValue\"]((val, outputRels) => {\n outputRels[\"returnValue\"](getValue());\n });\n\n /* 重置值 */\n inputs[\"resetValue\"]((val, outputRels) => {\n setValue(\"\");\n outputRels[\"resetValueComplete\"]?.(\"\");\n });\n\n /* 设置标题 */\n inputs[\"setLabel\"]?.((val) => {\n if (!isString(val)) {\n return;\n }\n\n parentSlot?._inputs[\"setProps\"]?.({\n id: props.id,\n name: props.name,\n value: {\n label: val,\n },\n });\n });\n\n /* 设置提示内容 */\n inputs[\"setPlaceholder\"]((val) => {\n data.placeholder = val;\n });\n\n /* 清除倒计时 */\n inputs[\"clearCountDown\"]((val) => {\n clearTimeout(timer.current);\n countDownRef.current = 0;\n data.buttonText = data.buttonTextRetry;\n });\n\n /* 设置禁用 */\n inputs[\"setDisabled\"]?.((val, outputRels) => {\n data.disabled = !!val;\n outputRels[\"setDisabledComplete\"]?.(data.disabled);\n });\n }, []);\n\n const countDown = useCallback(\n (cd, callback) => {\n countDownRef.current = cd;\n data.buttonText = `${cd}s 后重试`;\n\n if (typeof callback === \"function\") {\n callback();\n }\n\n timer.current = setTimeout(() => {\n countDownRef.current--;\n\n if (countDownRef.current > 0) {\n countDown(countDownRef.current);\n } else {\n data.buttonText = data.buttonTextRetry;\n }\n }, 1000);\n },\n [data.buttonTextRetry]\n );\n\n const onChange = useCallback((e) => {\n let value = e.detail.value;\n setValue(value);\n }, []);\n\n const onBlur = useCallback((e) => {\n let value = e.detail.value;\n outputs[\"onBlur\"](value);\n }, []);\n\n const onCodeSend = useCallback(\n (e) => {\n if (!env.runtime) {\n return;\n }\n\n e.stopPropagation();\n\n // 禁用时不可点击\n if (data.disabled) {\n return;\n }\n\n // 倒计时中不可点击\n if (countDownRef.current > 0) {\n return;\n }\n\n // 发送验证码,并开始倒计时\n countDown(data.smsCountdown, () => {\n outputs[\"onCodeSend\"]();\n });\n },\n [countDownRef.current]\n );\n\n return (\n <View\n className={cx({\n [css.phoneSMS]: true,\n \"mybricks-phoneSMS\": !isH5(),\n \"mybricks-h5PhoneSMS\": isH5(),\n })}\n >\n <Input\n className={css.input}\n value={value}\n placeholder={data.placeholder}\n onChange={onChange}\n onBlur={onBlur}\n />\n <Button\n className={cx({\n [css.button]: true,\n [css.enabled]: !data.disabled && countDownRef.current <= 0,\n \"mybricks-button\": !data.disabled && countDownRef.current <= 0,\n [css.disabled]: data.disabled || countDownRef.current > 0,\n \"mybricks-button-disabled\": data.disabled || countDownRef.current > 0,\n })}\n onClick={onCodeSend}\n >\n {data.buttonText}\n </Button>\n </View>\n );\n}\n"
2541
2541
  },
2542
2542
  {
2543
2543
  "path": "src/components/formPhoneSMS/style.less",
@@ -5217,7 +5217,7 @@
5217
5217
  },
5218
5218
  {
5219
5219
  "path": "src/components/index.ts",
5220
- "content": "// 系统\nexport { default as SystemPage } from \"./system/systemPage/runtime\";\n\n// 基础\nexport { default as Image } from \"./image/runtime\";\nexport { default as Video } from \"./video/runtime\";\nexport { default as Button } from \"./button/runtime\";\nexport { default as Text } from \"./text/runtime\";\nexport { default as RichText } from \"./richText/runtime\";\nexport { default as Swiper } from \"./swiper/runtime\";\nexport { default as Icon } from \"./icon/runtime\";\n\n// 容器\nexport { default as ContainerBasic } from \"./containerBasic/runtime\";\n// export { default as Tabs2 } from \"./tabs2/runtime\";\nexport { default as ContainerList } from \"./containerList/runtime\";\n// export { default as ContainerWaterfall } from \"./containerWaterfall/runtime\";\n// export { default as ContainerCondition2 } from \"./containerCondition2/runtime\";\n// export { default as Sidebar2 } from \"./sidebar2/runtime\";\n\n// 数据录入\n// export { default as FormContainer } from \"./formContainer/runtime\";\nexport { default as FormInput } from \"./formInput/runtime\";\nexport { default as FormStepper } from \"./formStepper/runtime\";\n// export { default as FormTextarea } from \"./formTextarea/runtime\";\n// export { default as FormPassword } from \"./formPassword/runtime\";\n// export { default as FormRadio } from \"./formRadio/runtime\";\n// export { default as FormCheckbox } from \"./formCheckbox/runtime\";\n// export { default as FormSwitch } from \"./formSwitch/runtime\";\n// export { default as FormRate } from \"./formRate/runtime\";\n// export { default as FormSelect } from \"./formSelect/runtime\";\n// export { default as FormDatetime } from \"./formDatetime/runtime\";\n// export { default as FormLocation } from \"./formLocation/runtime\";\n// export { default as FormPhoneNumber } from \"./formPhoneNumber/runtime\";\n// export { default as FormPhoneSMS } from \"./formPhoneSMS/runtime\";\n// export { default as FormImageUploader } from \"./formImageUploader/runtime\";\n// export { default as FormFileUploader } from \"./formFileUploader/runtime\";\n// export { default as FormEditor } from \"./formEditor/runtime\";\n// export { default as SearchBar } from \"./searchBar/runtime\";\n// export { default as SmsInput } from \"./smsInput/runtime\";\n// export { default as FormItemContainer } from \"./formItemContainer/runtime\";\n// export { default as FormAdditionContainer } from \"./formAdditionContainer/runtime\";\n\n// 数据展示\n// export { default as Table } from \"./table/runtime\";\nexport { default as Cell } from \"./cell/runtime\";\n// export { default as CheckList } from \"./checkList/runtime\";\n// export { default as FilterContainer } from \"./filter-coms/contaniner/runtime\";\n// export { default as Calendar } from \"./calendar/runtime\";\n// export { default as Cascader } from \"./cascader/runtime\";\n// export { default as Qrcode } from \"./qrcode/runtime\";\n\n// 微信\n// export { default as Map } from \"./map/runtime\";\n// export { default as AdPopup } from \"./ad-popup/runtime\";\n// export { default as ChannelVideo } from \"./channelVideo/runtime\";\n\n// 其它\n// export { default as Line } from \"./line/runtime\";\n// export { default as Anchor } from \"./anchor/runtime\";\n// export { default as Support } from \"./support/runtime\";\n"
5220
+ "content": "// 系统\nexport { default as SystemPage } from \"./system/systemPage/runtime\";\n\n// 基础\nexport { default as Image } from \"./image/runtime\";\nexport { default as Video } from \"./video/runtime\";\nexport { default as Button } from \"./button/runtime\";\nexport { default as Text } from \"./text/runtime\";\nexport { default as RichText } from \"./richText/runtime\";\nexport { default as Swiper } from \"./swiper/runtime\";\nexport { default as Icon } from \"./icon/runtime\";\n\n// 容器\nexport { default as ContainerBasic } from \"./containerBasic/runtime\";\nexport { default as Tabs2 } from \"./tabs2/runtime\";\nexport { default as ContainerList } from \"./containerList/runtime\";\nexport { default as Popup } from \"./popup/runtime\";\n// export { default as ContainerWaterfall } from \"./containerWaterfall/runtime\";\n// export { default as ContainerCondition2 } from \"./containerCondition2/runtime\";\nexport { default as Sidebar2 } from \"./sidebar2/runtime\";\n\n// 数据录入\nexport { default as FormContainer } from \"./formContainer/runtime\";\nexport { default as FormInput } from \"./formInput/runtime\";\nexport { default as FormStepper } from \"./formStepper/runtime\";\nexport { default as FormTextarea } from \"./formTextarea/runtime\";\nexport { default as FormPassword } from \"./formPassword/runtime\";\nexport { default as FormRadio } from \"./formRadio/runtime\";\nexport { default as FormCheckbox } from \"./formCheckbox/runtime\";\nexport { default as FormSwitch } from \"./formSwitch/runtime\";\nexport { default as FormRate } from \"./formRate/runtime\";\nexport { default as FormSelect } from \"./formSelect/runtime\";\nexport { default as FormDatetime } from \"./formDatetime/runtime\";\nexport { default as FormLocation } from \"./formLocation/runtime\";\nexport { default as FormPhoneNumber } from \"./formPhoneNumber/runtime\";\nexport { default as FormPhoneSMS } from \"./formPhoneSMS/runtime\";\nexport { default as FormImageUploader } from \"./formImageUploader/runtime\";\n// export { default as FormFileUploader } from \"./formFileUploader/runtime\";\n// export { default as FormEditor } from \"./formEditor/runtime\";\nexport { default as SearchBar } from \"./searchBar/runtime\";\nexport { default as SmsInput } from \"./smsInput/runtime\";\n// export { default as FormItemContainer } from \"./formItemContainer/runtime\";\nexport { default as FormAdditionContainer } from \"./formAdditionContainer/runtime\";\n\n// 数据展示\nexport { default as Table } from \"./table/runtime\";\nexport { default as Cell } from \"./cell/runtime\";\n// export { default as CheckList } from \"./checkList/runtime\";\nexport { default as FilterContainer } from \"./filter-coms/contaniner/runtime\";\nexport { default as FilterSort } from \"./filter-coms/sort/runtime\";\nexport { default as FilterSwitch } from \"./filter-coms/switch/runtime\";\nexport { default as FilterCustom } from \"./filter-coms/custom/runtime\";\n// export { default as Calendar } from \"./calendar/runtime\";\n// export { default as Cascader } from \"./cascader/runtime\";\n// export { default as Qrcode } from \"./qrcode/runtime\";\n\n// 微信\nexport { default as Map } from \"./map/runtime\";\nexport { default as AdPopup } from \"./ad-popup/runtime\";\nexport { default as ChannelVideo } from \"./channelVideo/runtime\";\n\n// 其它\nexport { default as Line } from \"./line/runtime\";\n// export { default as Anchor } from \"./anchor/runtime\";\nexport { default as Support } from \"./support/runtime\";\n"
5221
5221
  }
5222
5222
  ]
5223
5223
  },
@@ -5399,15 +5399,15 @@
5399
5399
  "children": [
5400
5400
  {
5401
5401
  "path": "src/core/utils/ComContext.tsx",
5402
- "content": "import { createContext, useContext } from 'react'\nimport type { ComContextStore } from './useContext'\nexport { pageRouter } from './pageRouter'\n\nconst ComContext = createContext<ComContextStore | undefined>(undefined)\n\nexport function useAppContext(): ComContextStore {\n const context = useContext(ComContext)\n if (!context) {\n throw new Error('useAppContext must be used within a ComContext.Provider')\n }\n return context\n}\n\nexport default ComContext\n"
5402
+ "content": "import { createContext, useContext } from 'react'\nimport type { ComContextStore } from './useContext'\nexport { pageRouter } from './pageRouter'\n\nconst ComContext = createContext<ComContextStore | undefined>(undefined)\nconst SlotContext = createContext<any>(null)\n\nexport function useAppContext(): ComContextStore {\n const context = useContext(ComContext)\n if (!context) {\n throw new Error('useAppContext must be used within a ComContext.Provider')\n }\n return context\n}\n\nexport function useParentSlot<T = any>(): T | null {\n return useContext(SlotContext)\n}\n\nexport const SlotProvider = SlotContext.Provider\n\nexport default ComContext\n"
5403
5403
  },
5404
5404
  {
5405
5405
  "path": "src/core/utils/hooks.ts",
5406
- "content": "import { useState, useRef, useMemo } from 'react';\nimport { SUBJECT_SUBSCRIBE } from '../mybricks/constant';\nimport { createReactiveInputHandler } from '../mybricks/createReactiveInputHandler';\n\n/**\n * 深度代理,支持自动路径初始化和响应式更新(鸿蒙化处理方案)\n */\nexport function deepProxy(target: any, onSet?: () => void): any {\n if (target === null || typeof target !== 'object' || target.__isProxy) {\n return target;\n }\n\n return new Proxy(target, {\n get(obj, prop) {\n if (prop === '__isProxy') return true;\n if (prop === 'toJSON') return () => obj;\n\n let value = obj[prop];\n\n // 只有在访问不存在的对象属性时,才自动创建(实现类似 ensure 的效果)\n // 特意排除 MyBricks 内置的方法名,以便在生成代码中进行初始化判断\n const mybricksMethods = ['get', 'set', 'changed', 'reset', 'registerChange', 'call', 'apply', 'bind', 'push', 'pop'];\n if (value === undefined && typeof prop === 'string' && !mybricksMethods.includes(prop)) {\n value = obj[prop] = {};\n }\n\n if (typeof value === 'object' && value !== null && !value.__isProxy) {\n obj[prop] = deepProxy(value, onSet);\n }\n\n return obj[prop];\n },\n set(obj, prop, value) {\n const result = Reflect.set(obj, prop, value);\n if (onSet) onSet();\n return result;\n }\n });\n}\n\nexport function useModel(rawData: any) {\n const [, forceUpdate] = useState({});\n const dataRef = useRef(rawData || {});\n\n return useMemo(() => {\n return deepProxy(dataRef.current, () => forceUpdate({}));\n }, []);\n}\n\nexport function useBindInputs(scope: any, id: string, initialHandlers?: Record<string, any>) {\n const handlersRef = useRef<Record<string, any>>({ ...initialHandlers });\n\n // 同步最新的 initialHandlers\n if (initialHandlers) {\n Object.assign(handlersRef.current, initialHandlers);\n }\n\n return useMemo(() => {\n const proxy = new Proxy({}, {\n get: (_target, pin: string) => {\n return (arg: any, ...args: any[]) => {\n if (typeof arg === 'function') {\n // 组件注册回调\n handlersRef.current[pin] = arg;\n } else {\n // 逻辑流触发输入\n const handler = handlersRef.current[pin];\n\n if (typeof handler === 'function') {\n if (pin === '_setData') {\n return handler(arg, ...args);\n }\n // 构造 createReactiveInputHandler 需要的参数\n return createReactiveInputHandler({\n input: handler,\n value: arg,\n rels: {}, // 这里可以扩展 output 关联\n title: id\n });\n }\n }\n };\n }\n });\n\n // 将代理对象挂载到作用域,供外部 comRefs.current.id.pin() 调用\n scope[id] = proxy;\n return proxy;\n }, [scope, id]);\n}\n\nexport function useBindEvents(props: any) {\n return useMemo(() => {\n const _events: Record<string, any> = {};\n\n // 预处理已存在的事件\n Object.keys(props).forEach(key => {\n if (key.startsWith('on') && typeof props[key] === 'function') {\n const handler = props[key];\n const wrapped = (...args: any[]) => handler(...args);\n wrapped.getConnections = () => [{ id: 'default' }];\n _events[key] = wrapped;\n }\n });\n\n return new Proxy(_events, {\n get(target, key: string) {\n if (typeof key === 'string' && key.startsWith('on')) {\n if (target[key]) {\n return target[key];\n }\n // 对未连接的事件返回兜底函数\n const emptyFn = () => { };\n emptyFn.getConnections = () => [];\n return emptyFn;\n }\n return target[key];\n }\n });\n }, [props]);\n}\n"
5406
+ "content": "import { useState, useRef, useMemo } from 'react';\nimport { SUBJECT_SUBSCRIBE } from '../mybricks/constant';\nimport { createReactiveInputHandler } from '../mybricks/createReactiveInputHandler';\n\n/**\n * 深度代理,支持自动路径初始化和响应式更新(鸿蒙化处理方案)\n */\nexport function deepProxy(target: any, onSet?: () => void): any {\n if (target === null || typeof target !== 'object' || target.__isProxy) {\n return target;\n }\n\n return new Proxy(target, {\n get(obj, prop) {\n if (prop === '__isProxy') return true;\n if (prop === 'toJSON') return () => obj;\n\n let value = obj[prop];\n\n // 只代理已存在的对象属性,不自动创建空对象\n // 避免访问不存在的属性(如 disabled)时污染原始数据\n if (typeof value === 'object' && value !== null && !value.__isProxy) {\n obj[prop] = deepProxy(value, onSet);\n }\n\n return obj[prop];\n },\n set(obj, prop, value) {\n const result = Reflect.set(obj, prop, value);\n if (onSet) onSet();\n return result;\n }\n });\n}\n\nexport function useModel(rawData: any) {\n const [, forceUpdate] = useState({});\n const dataRef = useRef(rawData || {});\n\n return useMemo(() => {\n return deepProxy(dataRef.current, () => forceUpdate({}));\n }, []);\n}\n\nexport function useBindInputs(scope: any, id: string, initialHandlers?: Record<string, any>) {\n const handlersRef = useRef<Record<string, any>>({ ...initialHandlers });\n\n // 同步最新的 initialHandlers\n if (initialHandlers) {\n Object.assign(handlersRef.current, initialHandlers);\n }\n\n return useMemo(() => {\n const proxy = new Proxy({}, {\n get: (_target, pin: string) => {\n return (arg: any, ...args: any[]) => {\n if (typeof arg === 'function') {\n // 组件注册回调\n handlersRef.current[pin] = arg;\n } else {\n // 逻辑流触发输入\n const handler = handlersRef.current[pin];\n\n if (typeof handler === 'function') {\n if (pin === '_setData') {\n return handler(arg, ...args);\n }\n // 构造 createReactiveInputHandler 需要的参数\n return createReactiveInputHandler({\n input: handler,\n value: arg,\n rels: {}, // 这里可以扩展 output 关联\n title: id\n });\n }\n }\n };\n }\n });\n\n // 将代理对象挂载到作用域,供外部 comRefs.current.id.pin() 调用\n if (scope && scope.current) {\n scope.current[id] = proxy;\n }\n return proxy;\n }, [scope, id]);\n}\n\nexport function useBindEvents(props: any, context?: { id: string, name: string, parentSlot?: any }) {\n return useMemo(() => {\n const _events: Record<string, any> = {};\n\n // 预处理已存在的事件\n Object.keys(props).forEach(key => {\n if (key.startsWith('on') && typeof props[key] === 'function') {\n const handler = props[key];\n const wrapped = (originalValue: any) => {\n // 鸿蒙/render-web 规范:如果是在插槽中触发事件,且存在父级协议,则自动封装元数据\n // 这解决了 FormContainer 等组件识别子项的需求\n const value = context ? {\n id: context.id,\n name: context.name,\n value: originalValue\n } : originalValue;\n \n return handler(value);\n };\n wrapped.getConnections = () => [{ id: 'default' }];\n _events[key] = wrapped;\n }\n });\n\n return new Proxy(_events, {\n get(target, key: string) {\n if (typeof key === 'string' && key.startsWith('on')) {\n if (target[key]) {\n return target[key];\n }\n // 对未连接的事件返回兜底函数\n const emptyFn = () => { };\n emptyFn.getConnections = () => [];\n return emptyFn;\n }\n return target[key];\n }\n });\n }, [props, context]);\n}\n"
5407
5407
  },
5408
5408
  {
5409
5409
  "path": "src/core/utils/index.ts",
5410
- "content": "export * from './hooks';\nexport { WithCom, WithWrapper } from './with';\nexport { PopupRenderer } from './PopupRenderer';\nexport { pageRouter, router } from './pageRouter';\nexport { popupRouter, subscribePopupRouter, closeActivePopupRouter } from './popupRouter';\nexport { useAppContext } from './ComContext';\nexport { createVariable, createFx, merge } from '../mybricks';\nexport { SUBJECT_SUBSCRIBE, SUBJECT_VALUE } from '../mybricks/constant';\n"
5410
+ "content": "export * from './hooks';\nexport { WithCom, WithWrapper } from './with';\nexport { PopupRenderer } from './PopupRenderer';\nexport { pageRouter, router } from './pageRouter';\nexport { popupRouter, subscribePopupRouter, closeActivePopupRouter } from './popupRouter';\nexport { useAppContext, SlotProvider } from './ComContext';\nexport { useAppCreateContext } from './useContext';\nexport { createVariable, createFx, merge } from '../mybricks';\nexport { SUBJECT_SUBSCRIBE, SUBJECT_VALUE } from '../mybricks/constant';\nexport * from './slots';\n"
5411
5411
  },
5412
5412
  {
5413
5413
  "path": "src/core/utils/pageRouter.ts",
@@ -5421,13 +5421,17 @@
5421
5421
  "path": "src/core/utils/popupRouter.ts",
5422
5422
  "content": "import { Page } from '../mybricks';\n\n/**\n * 弹窗状态管理(解耦版)\n * 弹窗在 Taro 中被视为组件,不走路由栈\n */\nclass PopupStore {\n private activePopup: any = null;\n private listeners: Set<(state: any) => void> = new Set();\n\n /** 订阅弹窗状态变化 */\n subscribe(listener: (state: any) => void) {\n this.listeners.add(listener);\n // 返回取消订阅函数\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private notify() {\n this.listeners.forEach((l) =>\n l(\n this.activePopup || {\n visible: false,\n name: '',\n value: null,\n controller: null,\n },\n ),\n );\n }\n\n /** 获取弹窗参数 */\n getParams(name: string) {\n if (this.activePopup && this.activePopup.name === name) {\n return {\n value: this.activePopup.value,\n controller: this.activePopup.controller,\n };\n }\n return { value: null };\n }\n\n /** 获取当前激活的弹窗名称 */\n getActiveName(): string | undefined {\n return this.activePopup?.name;\n }\n\n /** 打开弹窗 */\n push(name: string, { value, controller }: any) {\n this.activePopup = { visible: true, name, value, controller };\n this.notify();\n }\n\n /** 行为同 push */\n replace(name: string, params: any) {\n this.push(name, params);\n }\n\n /** 关闭弹窗 */\n pop() {\n this.activePopup = null;\n this.notify();\n }\n}\n\nconst popupStore = new PopupStore();\n\n/**\n * 订阅弹窗状态变化(统一入口,避免外部依赖内部实现细节)\n */\nexport const subscribePopupRouter = (listener: (state: any) => void) => {\n return popupStore.subscribe(listener);\n};\n\n/** 弹窗控制器(命名与文件名保持一致) */\nexport const popupRouter = new Page(popupStore);\n\n/**\n * 关闭当前激活的弹窗(用于 TabBar 点击等场景)\n * 不需要知道弹窗 id\n */\nexport const closeActivePopupRouter = () => {\n const name = popupStore.getActiveName();\n if (!name) return;\n // 走 Page.close,保证 close/commit/cancel 等控制器语义保持一致\n popupRouter.close(name);\n};\n\n\n"
5423
5423
  },
5424
+ {
5425
+ "path": "src/core/utils/slots.tsx",
5426
+ "content": "import React, { useMemo, useRef, useEffect } from \"react\";\nimport ComContext, { SlotProvider, useAppContext, useParentSlot } from \"./ComContext\";\nimport { createReactiveInputHandler } from \"../mybricks/createReactiveInputHandler\";\n\ntype AnyRecord = Record<string, any>;\n\ntype SlotState = {\n inputs: any;\n outputs: any;\n _inputs: any;\n /** scopeId -> scoped comRefs(每个 scope 一套,避免列表多实例覆盖) */\n _scopedComRefs?: Record<string, any>;\n _render?: any;\n render: (params?: any) => any;\n};\n\nfunction createChannelProxy(title: string) {\n const handlersMap: Record<string, any> = {};\n return new Proxy(\n {},\n {\n get: (_t, pin: string) => {\n return (arg: any) => {\n if (typeof arg === \"function\") {\n handlersMap[pin] = arg;\n return;\n }\n const handler = handlersMap[pin];\n if (typeof handler === \"function\") {\n return createReactiveInputHandler({\n input: handler,\n value: arg,\n rels: {},\n title,\n });\n }\n };\n },\n },\n );\n}\n\n/**\n * 参考鸿蒙的 createSlotsIO:\n * - 确保每个 slot 都具备 inputs / outputs / _inputs 三套通道,避免 runtime 访问时报 undefined\n * - render 时通过 SlotProvider 注入 parentSlot(slot 内子组件可 useParentSlot 获取)\n */\nexport function useEnhancedSlots(rawSlots: any, id: string) {\n const slotStoreRef = useRef<Record<string, SlotState>>({});\n\n return useMemo(() => {\n if (!rawSlots) return {};\n const nextSlots: AnyRecord = {};\n\n Object.entries(rawSlots).forEach(([slotKey, slotDef]: any) => {\n const state =\n slotStoreRef.current[slotKey] ||\n (slotStoreRef.current[slotKey] = {\n inputs: createChannelProxy(`${id}.${slotKey}.inputs`),\n outputs: createChannelProxy(`${id}.${slotKey}.outputs`),\n _inputs: createChannelProxy(`${id}.${slotKey}._inputs`),\n _scopedComRefs: {},\n _render: undefined,\n render: (params?: any) => {\n const r = state._render;\n // 只有存在 key 或 index 时才认为是“多实例作用域插槽”,需要实例隔离\n const rawScope = params?.key ?? params?.inputValues?.index ?? params?.inputValues?.itemData?.id;\n \n const SlotComp = r;\n const content = r ? <SlotComp {...(params || {})} /> : null;\n\n if (rawScope === undefined || rawScope === null) {\n return (\n <SlotProvider value={{ ...state, params }}>\n {content}\n </SlotProvider>\n );\n }\n\n const scopeId = `${id}.${slotKey}::${String(rawScope)}`;\n const scopedComRefs =\n (state._scopedComRefs![scopeId] ||= { current: { $inputs: {} } });\n return (\n <SlotProvider value={{ ...state, params }}>\n <ScopedComContextProvider comRefs={scopedComRefs} scopeId={scopeId}>\n {content}\n </ScopedComContextProvider>\n </SlotProvider>\n );\n },\n });\n\n state._render = slotDef?.render;\n nextSlots[slotKey] = {\n ...(slotDef || {}),\n render: state.render,\n inputs: state.inputs,\n outputs: state.outputs,\n _inputs: state._inputs,\n };\n });\n\n return nextSlots;\n }, [rawSlots, id]);\n}\n\nexport function ScopedComContextProvider(props: {\n comRefs?: any;\n scopeId: string;\n children: React.ReactNode;\n}) {\n const parent = useAppContext();\n const value = useMemo(() => {\n // 如果没有显式传 comRefs,则沿用父级的,但依然带上新的 scopeId\n return {\n ...parent,\n comRefs: props.comRefs || parent.comRefs,\n $scopeId: props.scopeId,\n } as any;\n }, [parent, props.comRefs, props.scopeId]);\n\n return <ComContext.Provider value={value}>{props.children}</ComContext.Provider>;\n}\n\n/** parentSlot 解析:props 优先,其次用 SlotProvider 注入的 context */\nexport function useResolvedParentSlot(parentSlotProp: any) {\n const parentSlotFromCtx = useParentSlot();\n return parentSlotProp ?? parentSlotFromCtx;\n}\n"
5427
+ },
5424
5428
  {
5425
5429
  "path": "src/core/utils/useContext.ts",
5426
- "content": "import { useRef, useState } from 'react'\nimport { deepProxy } from './hooks'\n\nexport interface ComContextStore {\n comRefs: any;\n appContext: any; \n popupState: {\n visible: boolean;\n name: string;\n value: any;\n controller: any;\n };\n setPopupState: (state: any) => void;\n}\n\nexport function useAppCreateContext(): ComContextStore {\n const comRefs = useRef<any>(deepProxy({}));\n const [popupState, setPopupState] = useState({\n visible: false,\n name: '',\n value: null,\n controller: null\n });\n\n const appContext: any = {\n canvas: {\n id: \"u_7VvVn\", // 使用 data 中的 id\n },\n runtime: {\n debug: false,\n },\n edit: false,\n isH5: false,\n isDesigner: false,\n isPreview: false,\n isRelease: false,\n isDebug: false,\n isLocal: false,\n isTest: false,\n };\n return {\n comRefs,\n appContext,\n popupState,\n setPopupState\n }\n}\n"
5430
+ "content": "import { useRef, useState, useMemo } from 'react'\nimport { deepProxy } from './hooks'\n\nexport interface ComContextStore {\n comRefs: any;\n outputs: any;\n appContext: any; \n popupState: {\n visible: boolean;\n name: string;\n value: any;\n controller: any;\n };\n setPopupState: (state: any) => void;\n}\n\nexport function useAppCreateContext(): ComContextStore {\n // 约定:场景级 inputs 统一挂载到 $inputs,避免与组件 runtime 的 inputs 命名冲突\n // 同时可避免 `Cannot set property 'open' of undefined`\n const comRefs = useRef<any>(deepProxy({ $inputs: {} }));\n const outputs = useRef<any>(deepProxy({}));\n const [popupState, setPopupState] = useState({\n visible: false,\n name: '',\n value: null,\n controller: null\n });\n\n const appContext = useRef({\n canvas: {\n id: \"u_7VvVn\", // 使用 data 中的 id\n },\n runtime: {\n debug: false,\n },\n edit: false,\n isH5: false,\n isDesigner: false,\n isPreview: false,\n isRelease: false,\n isDebug: false,\n isLocal: false,\n isTest: false,\n tabBar: [],\n useTabBar: false,\n }).current;\n\n return useMemo(() => ({\n comRefs,\n outputs,\n appContext,\n popupState,\n setPopupState\n }), [popupState]);\n}\n"
5427
5431
  },
5428
5432
  {
5429
5433
  "path": "src/core/utils/with.tsx",
5430
- "content": "import React, { useState, useEffect } from 'react';\nimport { View } from '@tarojs/components';\nimport { useModel, useBindInputs, useBindEvents, subscribePopupRouter, closeActivePopupRouter } from './index';\nimport { useAppCreateContext } from './useContext';\nimport ComContext, { useAppContext } from './ComContext';\n// @ts-ignore 运行时由宿主项目提供 @tarojs/taro\nimport { useTabItemTap } from '@tarojs/taro';\n\ninterface WithComProps {\n component: React.ComponentType<any>;\n intputRef?: any;\n id?: string;\n data?: any;\n className?: string;\n style?: any;\n [key: string]: any;\n}\n\nexport const WithCom: React.FC<WithComProps> = (props) => {\n const { component: Component, id = '', data, className, style, ...rest } = props;\n const { comRefs, appContext } = useAppContext();\n const env = appContext;\n\n const isPopup = (Component as any).isPopup;\n const [show, setShow] = useState(true);\n const [dynamicStyle, setDynamicStyle] = useState({});\n\n // 数据模型\n const _data = useModel(data || {});\n\n // 内置通用能力\n const handlers = {\n _setStyle: (style: any) => {\n setDynamicStyle((prev) => ({ ...prev, ...style }));\n },\n _setData: (path: string, value: any) => {\n const paths = path.split('.');\n let current = _data;\n for (let i = 0; i < paths.length - 1; i++) {\n if (!current[paths[i]]) current[paths[i]] = {};\n current = current[paths[i]];\n }\n current[paths[paths.length - 1]] = value;\n }\n };\n\n if (!isPopup) {\n Object.assign(handlers, {\n show: () => setShow(true),\n hide: () => setShow(false),\n showOrHide: () => setShow((prev) => !prev),\n });\n }\n\n // 绑定输入\n const inputProxy = useBindInputs(comRefs, id, handlers);\n const eventProxy = useBindEvents(rest);\n\n comRefs.current[id] = inputProxy;\n\n return (\n show || isPopup ? (\n <View className={className} style={{ ...style, ...dynamicStyle }} >\n <Component\n {...rest}\n inputs={inputProxy}\n outputs={eventProxy}\n data={_data}\n env={env}\n id={id}\n style={style}\n />\n </View>\n ) : null\n );\n};\n\nexport const WithWrapper = (Component: React.ComponentType<any>) => {\n return function WrappedComponent(props: any) {\n const contextStore = useAppCreateContext();\n const { setPopupState } = contextStore;\n const isPopup = (Component as any).isPopup;\n\n // 通过发布订阅模式解耦弹窗状态变化\n useEffect(() => {\n return subscribePopupRouter((state) => setPopupState(state));\n }, [setPopupState]);\n\n // 点击 TabBar 时自动关闭弹窗(自定义 TabBar 或原生 TabBar 点击均会触发)\n useTabItemTap(() => {\n if (isPopup) return;\n closeActivePopupRouter();\n });\n\n return (\n <ComContext.Provider value={contextStore}>\n <Component {...props} />\n </ComContext.Provider>\n );\n };\n};\n"
5434
+ "content": "import React, { useState, useEffect } from 'react';\n// @ts-ignore 运行时由宿主项目提供 @tarojs/components\nimport { View } from '@tarojs/components';\nimport { useModel, useBindInputs, useBindEvents, subscribePopupRouter, closeActivePopupRouter } from './index';\nimport { useAppCreateContext } from './useContext';\nimport ComContext, { useAppContext } from './ComContext';\nimport { useEnhancedSlots, useResolvedParentSlot } from './slots';\n// @ts-ignore 运行时由宿主项目提供 @tarojs/taro\nimport { useTabItemTap } from '@tarojs/taro';\n\ninterface WithComProps {\n component: React.ComponentType<any>;\n intputRef?: any;\n id?: string;\n data?: any;\n className?: string;\n style?: any;\n /** 插槽传入的原始数据 */\n inputValues?: any;\n /** 插槽数据到组件输入的映射: { pinId: slotKey } */\n inputValuesMapping?: Record<string, string>;\n [key: string]: any;\n}\n\nexport const WithCom: React.FC<WithComProps> = (props) => {\n const { component: Component, id = '', data, className, style, inputValues, inputValuesMapping, ...rest } = props;\n const { comRefs, appContext } = useAppContext();\n const env = appContext; //TODO: 需要根据实际情况修改\n\n const isPopup = (Component as any).isPopup;\n const [show, setShow] = useState(true);\n const [dynamicStyle, setDynamicStyle] = useState({});\n\n\n //数据模型\n const _data = useModel(data || {});\n\n // 内置通用能力\n const handlers = {\n _setStyle: (style: any) => {\n setDynamicStyle((prev) => ({ ...prev, ...style }));\n },\n _setData: (path: string, value: any) => {\n const paths = path.split('.');\n let current = _data;\n for (let i = 0; i < paths.length - 1; i++) {\n if (!current[paths[i]]) current[paths[i]] = {};\n current = current[paths[i]];\n }\n current[paths[paths.length - 1]] = value;\n }\n };\n\n if (!isPopup) {\n Object.assign(handlers, {\n show: () => setShow(true),\n hide: () => setShow(false),\n showOrHide: () => setShow((prev) => !prev),\n });\n }\n\n // 绑定输入,传入初始 handlers\n const inputProxy = useBindInputs(comRefs, id, handlers);\n\n const { slots: rawSlots, parentSlot: parentSlotProp, ...restProps } = rest as any;\n const { outputs: globalOutputs } = useAppContext();\n const parentSlot = useResolvedParentSlot(parentSlotProp);\n\n // 绑定事件,带上上下文(用于事件流自动封装 id/name)\n const eventProxy = useBindEvents(restProps, { \n id, \n name: props.name || id, \n parentSlot \n });\n\n // 注册到全局 IO 管理\n if (globalOutputs && globalOutputs.current) {\n globalOutputs.current[id] = eventProxy;\n }\n\n // 鸿蒙规范:确保 comRefs 中挂载的是最新的 inputProxy\n comRefs.current[id] = inputProxy;\n\n const enhancedSlots = useEnhancedSlots(rawSlots, id);\n\n const jsx = (\n <Component\n {...restProps}\n inputs={inputProxy}\n outputs={eventProxy}\n slots={enhancedSlots}\n parentSlot={parentSlot}\n data={_data}\n env={env}\n id={id}\n style={style}\n />\n );\n\n // 鸿蒙化处理:支持 itemWrap 协议\n if (parentSlot?.params?.itemWrap) {\n return parentSlot.params.itemWrap({\n id,\n name: props.name || id,\n jsx,\n def: (Component as any).def,\n inputs: inputProxy,\n outputs: eventProxy,\n style\n });\n }\n\n return (\n show || isPopup ? (\n <View className={className} style={{ ...style, ...dynamicStyle }} >\n {jsx}\n </View>\n ) : null\n );\n};\n\nexport const WithWrapper = (Component: React.ComponentType<any>) => {\n return function WrappedComponent(props: any) {\n const contextStore = useAppCreateContext();\n const { setPopupState } = contextStore;\n const isPopup = (Component as any).isPopup;\n\n // 通过发布订阅模式解耦弹窗状态变化\n useEffect(() => {\n return subscribePopupRouter((state) => setPopupState(state));\n }, [setPopupState]);\n\n // 点击 TabBar 时自动关闭弹窗(自定义 TabBar 或原生 TabBar 点击均会触发)\n useTabItemTap(() => {\n if (isPopup) return;\n closeActivePopupRouter();\n });\n\n return (\n <ComContext.Provider value={contextStore}>\n <Component {...props} />\n </ComContext.Provider>\n );\n };\n};\n"
5431
5435
  }
5432
5436
  ]
5433
5437
  }
@@ -5443,7 +5447,7 @@
5443
5447
  },
5444
5448
  {
5445
5449
  "path": "src/app.less",
5446
- "content": "/* 隐藏 React Refresh 的全屏错误遮罩 */\n#react-refresh-overlay, \niframe[id=\"react-refresh-overlay\"] {\n display: none !important;\n}\n\n.mybricks_taro_systemPage {\n height: 100vh !important;\n}"
5450
+ "content": "/* 隐藏 React Refresh 的全屏错误遮罩 */\n#react-refresh-overlay, \niframe[id=\"react-refresh-overlay\"] {\n display: none !important;\n}\n\n.mybricks_taro_systemPage {\n height: 100vh !important;\n}\n\nview {\n box-sizing: border-box;\n}\n"
5447
5451
  },
5448
5452
  {
5449
5453
  "path": "src/app.ts",
@@ -147,5 +147,7 @@ export interface BaseConfig extends ToTaroCodeConfig {
147
147
  typeDef: any;
148
148
  schema: any;
149
149
  }) => void;
150
+ /** 根据 ID 获取 DSL 中的稳定组件名(参考鸿蒙实现) */
151
+ getDslComNameById?: (id: string) => string | undefined;
150
152
  }
151
153
  export default toCodeTaro;
@@ -109,7 +109,7 @@ ${indent}}, appContext)
109
109
  `;
110
110
  });
111
111
  process.nodesInvocation.forEach((props) => {
112
- var _a, _b, _c, _d;
112
+ var _a, _b, _c;
113
113
  const { componentType, category, runType } = props;
114
114
  let nextValue = getNextValue(props, config, event);
115
115
  const isSameScope = checkIsSameScope(event, props);
@@ -189,14 +189,8 @@ ${indent}${nextCode}this.${currentProvider.name}_Fxs.${props.meta.ioProxy.id}(${
189
189
  }
190
190
  }
191
191
  } else {
192
- const currentProvider = getCurrentProvider({ isSameScope, props }, config);
193
- currentProvider.controllers.add(props.meta.id);
194
- const componentController = ((_d = config.getComponentController) == null ? void 0 : _d.call(config, {
195
- com: props.meta,
196
- scene: config.getCurrentScene()
197
- })) || `controller_${props.meta.id}`;
198
192
  code += `${indent}/** 调用 ${props.meta.title} 的 ${props.title} */
199
- ${indent}${nextCode}this.${currentProvider.name}.${componentController}.${props.id}(${nextValue})`;
193
+ ${indent}${nextCode}this.${props.meta.id}.${props.id}(${nextValue})`;
200
194
  }
201
195
  });
202
196
  if (["fx", "extension-api", "extension-bus"].includes(event.type)) {
@@ -302,17 +296,18 @@ var getNextValueWithParam = (param, config, event) => {
302
296
  return `${componentNameWithId}_result.${param.id}`;
303
297
  };
304
298
  var getCurrentProvider = (params, config) => {
305
- var _a;
299
+ var _a, _b, _c;
306
300
  const providerMap = config.getProviderMap();
307
301
  const { isSameScope, props } = params;
308
302
  const { meta } = props;
309
303
  const { parentComId, frameId } = meta;
310
304
  const providerName = ((_a = config.getProviderName) == null ? void 0 : _a.call(config, { com: meta, scene: config.getCurrentScene() })) || (!parentComId ? "slot_Index" : `slot_${frameId[0].toUpperCase() + frameId.slice(1)}_${parentComId}`);
311
- const provider = providerMap[providerName];
312
- if (!isSameScope) {
313
- config.addConsumer(provider);
305
+ const provider = providerMap == null ? void 0 : providerMap[providerName];
306
+ const safeProvider = provider || ((_b = config.getCurrentProvider) == null ? void 0 : _b.call(config));
307
+ if (!isSameScope && safeProvider) {
308
+ (_c = config.addConsumer) == null ? void 0 : _c.call(config, safeProvider);
314
309
  }
315
- return provider;
310
+ return safeProvider;
316
311
  };
317
312
  var generateJsCalculationComponentCode = (params) => {
318
313
  var _a;
@@ -5,6 +5,7 @@ export type ChildResult = {
5
5
  cssContent: string;
6
6
  slots: string[];
7
7
  scopeSlots: string[];
8
+ childrenResults?: any[];
8
9
  };
9
10
  /**
10
11
  * 统一处理子节点(com, dom, module)
@@ -41,7 +41,9 @@ var processChildren = (children, config) => {
41
41
  let cssContent = "";
42
42
  const slots = [];
43
43
  const scopeSlots = [];
44
+ const allChildrenResults = [];
44
45
  children.forEach((child) => {
46
+ var _a, _b, _c, _d;
45
47
  let result;
46
48
  if (child.type === "com") {
47
49
  result = (0, import_handleCom.default)(child, config);
@@ -51,6 +53,18 @@ var processChildren = (children, config) => {
51
53
  result = (0, import_handleDom.default)(child, config);
52
54
  }
53
55
  if (result) {
56
+ if (child.type === "com") {
57
+ const comId = child.id || ((_a = child.meta) == null ? void 0 : _a.id);
58
+ allChildrenResults.push({
59
+ ...result,
60
+ id: comId,
61
+ // 优先使用 handleCom 解析出的稳定名称 (如 comName 别名)
62
+ name: result.name || child.name || ((_c = (_b = child.props) == null ? void 0 : _b.data) == null ? void 0 : _c.name) || ((_d = child.meta) == null ? void 0 : _d.title) || comId,
63
+ type: child.type,
64
+ meta: child.meta,
65
+ props: child.props
66
+ });
67
+ }
54
68
  if (result.ui) {
55
69
  uiCode += (uiCode ? "\n" : "") + result.ui;
56
70
  }
@@ -73,7 +87,8 @@ var processChildren = (children, config) => {
73
87
  js: jsCode,
74
88
  cssContent,
75
89
  slots,
76
- scopeSlots
90
+ scopeSlots,
91
+ childrenResults: allChildrenResults
77
92
  };
78
93
  };
79
94
  // Annotate the CommonJS export names for ESM import in node:
@@ -36,6 +36,30 @@ __export(converter_exports, {
36
36
  module.exports = __toCommonJS(converter_exports);
37
37
  var import_pxtransform = __toESM(require("./pxtransform"));
38
38
  var import_string = require("../common/string");
39
+ var DIMENSION_PROPS = /^(width|height|padding|margin|top|right|bottom|left|fontSize|borderRadius|borderWidth|gap|rowGap|columnGap)/i;
40
+ var MIXED_PROPS = /^(lineHeight)/i;
41
+ var MIN_MAX_PROPS = /^(min|max)/i;
42
+ function isDimensionProp(key) {
43
+ if (DIMENSION_PROPS.test(key))
44
+ return true;
45
+ if (MIN_MAX_PROPS.test(key)) {
46
+ const subKey = key.replace(/^(min|max)/i, "");
47
+ const normalizedSubKey = subKey.charAt(0).toLowerCase() + subKey.slice(1);
48
+ return DIMENSION_PROPS.test(normalizedSubKey);
49
+ }
50
+ return false;
51
+ }
52
+ function transformStyleValue(key, value) {
53
+ if (typeof value !== "string" && typeof value !== "number")
54
+ return value;
55
+ if (isDimensionProp(key)) {
56
+ return (0, import_pxtransform.default)(value);
57
+ }
58
+ if (MIXED_PROPS.test(key)) {
59
+ return typeof value === "string" ? (0, import_pxtransform.default)(value) : value;
60
+ }
61
+ return value;
62
+ }
39
63
  var convertRootStyle = (style) => {
40
64
  const rootStyle = {};
41
65
  Object.entries(style || {}).forEach(([key, value]) => {
@@ -54,7 +78,7 @@ var convertRootStyle = (style) => {
54
78
  }
55
79
  } else {
56
80
  const camelLayoutKey = (0, import_string.kebabToCamel)(lKey);
57
- rootStyle[camelLayoutKey] = (0, import_pxtransform.default)(lValue);
81
+ rootStyle[camelLayoutKey] = transformStyleValue(camelLayoutKey, lValue);
58
82
  }
59
83
  });
60
84
  } else if (value === "flex-row") {
@@ -69,9 +93,7 @@ var convertRootStyle = (style) => {
69
93
  return;
70
94
  }
71
95
  const camelKey = key.includes("-") ? (0, import_string.kebabToCamel)(key) : key;
72
- if (typeof value === "string" || typeof value === "number") {
73
- rootStyle[camelKey] = (0, import_pxtransform.default)(value);
74
- }
96
+ rootStyle[camelKey] = transformStyleValue(camelKey, value);
75
97
  });
76
98
  return rootStyle;
77
99
  };
@@ -97,7 +119,8 @@ var convertComponentStyle = (style) => {
97
119
  transformedCss["position"] = lValue;
98
120
  }
99
121
  } else {
100
- transformedCss[(0, import_string.kebabToCamel)(lKey)] = lValue;
122
+ const camelLKey = (0, import_string.kebabToCamel)(lKey);
123
+ transformedCss[camelLKey] = transformStyleValue(camelLKey, lValue);
101
124
  }
102
125
  });
103
126
  } else if (cssValue === "flex-row") {
@@ -111,7 +134,7 @@ var convertComponentStyle = (style) => {
111
134
  }
112
135
  } else {
113
136
  const camelKey = cssKey.includes("-") ? (0, import_string.kebabToCamel)(cssKey) : cssKey;
114
- transformedCss[camelKey] = (0, import_pxtransform.default)(cssValue);
137
+ transformedCss[camelKey] = transformStyleValue(camelKey, cssValue);
115
138
  }
116
139
  });
117
140
  resultStyle[selector] = transformedCss;
@@ -128,11 +151,15 @@ var convertStyleAryToCss = (styleAry, parentSelector) => {
128
151
  return styleAry.map(({ selector, css }) => {
129
152
  if (!selector || !css)
130
153
  return "";
131
- let finalSelector = selector;
132
- if (finalSelector.startsWith(">")) {
133
- finalSelector = `${prefix}${finalSelector}`;
134
- } else {
135
- finalSelector = `${prefix}${finalSelector}`;
154
+ let finalSelector = selector.trim();
155
+ if (parentSelector) {
156
+ const prefix2 = `.${parentSelector}`;
157
+ if (finalSelector.startsWith(">")) {
158
+ const subSelector = finalSelector.substring(1).trim();
159
+ finalSelector = `${prefix2} ${subSelector}, ${prefix2}${subSelector}`;
160
+ } else {
161
+ finalSelector = `${prefix2} ${finalSelector}, ${prefix2}${finalSelector}`;
162
+ }
136
163
  }
137
164
  const transformedCss = {};
138
165
  Object.entries(css).forEach(([key, value]) => {
@@ -147,7 +174,8 @@ var convertStyleAryToCss = (styleAry, parentSelector) => {
147
174
  transformedCss["position"] = lValue;
148
175
  }
149
176
  } else {
150
- transformedCss[(0, import_string.kebabToCamel)(lKey)] = lValue;
177
+ const camelLKey = (0, import_string.kebabToCamel)(lKey);
178
+ transformedCss[camelLKey] = transformStyleValue(camelLKey, lValue);
151
179
  }
152
180
  });
153
181
  } else if (value === "flex-row") {
@@ -160,13 +188,13 @@ var convertStyleAryToCss = (styleAry, parentSelector) => {
160
188
  transformedCss["position"] = "relative";
161
189
  }
162
190
  } else {
163
- transformedCss[(0, import_string.kebabToCamel)(key)] = value;
191
+ const camelKey = (0, import_string.kebabToCamel)(key);
192
+ transformedCss[camelKey] = transformStyleValue(camelKey, value);
164
193
  }
165
194
  });
166
195
  const cssString = Object.entries(transformedCss).map(([key, value]) => {
167
196
  const kebabKey = (0, import_string.camelToKebab)(key);
168
- const formattedValue = typeof value === "number" ? `${value}px` : value;
169
- return ` ${kebabKey}: ${formattedValue};`;
197
+ return ` ${kebabKey}: ${value};`;
170
198
  }).join("\n");
171
199
  return `${finalSelector} {
172
200
  ${cssString}
@@ -1,28 +1,4 @@
1
1
  /**
2
- * px 转 rpx 适配方法
3
- * 用于 Taro/小程序样式转换
4
- *
5
- * 转换规则:
6
- * - 如果 px 值 <= MIN_PX_THRESHOLD,保持为 px(避免 1px 边框等问题)
7
- * - 否则转换为 rpx(px * 2,基于 375px 设计稿)
8
- *
9
- * 注意:Taro 中 rpx 需要作为字符串使用,如 "20rpx"
2
+ * 样式转换主函数
10
3
  */
11
- /** 最小 px 阈值,小于等于此值的 px 不转换为 rpx */
12
- export declare const MIN_PX_THRESHOLD = 1;
13
- /** rem 基准值,默认 px = 1rem */
14
- export declare const REM_BASE = 20;
15
- /**
16
- * 将 px 值转换为 rpx(用于 Taro 小程序)
17
- * @param value - 样式值,可以是字符串(如 "10px")或数字(如 10)
18
- * @returns 转换后的值,字符串类型(如 "20rpx")或数字(如 1,用于小于等于 MIN_PX_THRESHOLD 的情况)
19
- */
20
- export declare function pxToRpx(value: string | number): string | number;
21
- /**
22
- * 将 px 值转换为 rem(用于 H5/Web)
23
- * @param value - 样式值,可以是字符串(如 "16px")或数字(如 16)
24
- * @returns 转换后的值,字符串类型(如 "1rem")或数字(如 1,用于小于等于 MIN_PX_THRESHOLD 的情况)
25
- */
26
- export declare function pxToRem(value: string | number): string | number;
27
- declare const pxtransform: (value: string | number, type?: "rpx" | "rem") => string | number;
28
- export default pxtransform;
4
+ export default function pxtransform(value: any, target?: "rpx" | "rem"): any;