zhytech-ui-mobile 1.0.1 → 1.0.2

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 (81) hide show
  1. package/.eslintignore +11 -0
  2. package/.eslintrc.cjs +253 -0
  3. package/.prettierignore +0 -0
  4. package/.prettierrc.json +9 -0
  5. package/.vscode/settings.json +131 -0
  6. package/index.html +39 -0
  7. package/package.json +3 -7
  8. package/shims-uni.d.ts +7 -0
  9. package/src/App.vue +24 -0
  10. package/src/components/dynamicForm/componentRenderer.vue +207 -0
  11. package/src/components/dynamicForm/components/advanced/index.ts +13 -0
  12. package/src/components/dynamicForm/components/advanced/upload.vue +108 -0
  13. package/src/components/dynamicForm/components/advanced/uploadImage.vue +107 -0
  14. package/src/components/dynamicForm/components/answerSheetPopup/answerSheetItem.vue +58 -0
  15. package/src/components/dynamicForm/components/answerSheetPopup/index.vue +111 -0
  16. package/src/components/dynamicForm/components/application/employee.vue +140 -0
  17. package/src/components/dynamicForm/components/application/grade.vue +183 -0
  18. package/src/components/dynamicForm/components/application/index.ts +14 -0
  19. package/src/components/dynamicForm/components/application/post.vue +136 -0
  20. package/src/components/dynamicForm/components/base/checkbox.vue +143 -0
  21. package/src/components/dynamicForm/components/base/index.ts +15 -0
  22. package/src/components/dynamicForm/components/base/input.vue +99 -0
  23. package/src/components/dynamicForm/components/base/label.vue +29 -0
  24. package/src/components/dynamicForm/components/base/radio.vue +155 -0
  25. package/src/components/dynamicForm/components/componentType.ts +16 -0
  26. package/src/components/dynamicForm/components/layout/groupLayout.vue +103 -0
  27. package/src/components/dynamicForm/components/layout/index.ts +12 -0
  28. package/src/components/dynamicForm/formRenderer.vue +567 -0
  29. package/src/components/dynamicForm/index.ts +21 -0
  30. package/src/components/dynamicForm/types/componentAttribute/advanced/uploadAttribute.ts +35 -0
  31. package/src/components/dynamicForm/types/componentAttribute/application/employeeAttribute.ts +42 -0
  32. package/src/components/dynamicForm/types/componentAttribute/application/gradeAttribute.ts +54 -0
  33. package/src/components/dynamicForm/types/componentAttribute/application/postAttribute.ts +42 -0
  34. package/src/components/dynamicForm/types/componentAttribute/base/checkboxAttribute.ts +38 -0
  35. package/src/components/dynamicForm/types/componentAttribute/base/inputAttribute.ts +31 -0
  36. package/src/components/dynamicForm/types/componentAttribute/base/radioAttribute.ts +30 -0
  37. package/src/components/dynamicForm/types/componentAttribute/baseAttribute.ts +110 -0
  38. package/src/components/dynamicForm/types/componentAttribute/editAttribute.ts +70 -0
  39. package/src/components/dynamicForm/types/componentAttribute/index.ts +37 -0
  40. package/src/components/dynamicForm/types/componentAttribute/layout/groupLayoutAttribute.ts +39 -0
  41. package/src/components/dynamicForm/types/documentView.ts +110 -0
  42. package/src/components/dynamicForm/types/enum.ts +109 -0
  43. package/src/components/dynamicForm/types/formAttribute.ts +93 -0
  44. package/src/components/dynamicForm/types/uploadOption.ts +31 -0
  45. package/src/env.d.ts +8 -0
  46. package/src/hooks/useMessage.ts +44 -0
  47. package/src/hooks/useToast.ts +29 -0
  48. package/src/hooks/useUtils.ts +201 -0
  49. package/src/index.ts +59 -0
  50. package/src/main.ts +19 -0
  51. package/src/manifest.json +72 -0
  52. package/src/pages/dynamicFormDemo.vue +1260 -0
  53. package/src/pages/dynamicFormExaminationDemo.vue +567 -0
  54. package/src/pages.json +58 -0
  55. package/src/shime-uni.d.ts +6 -0
  56. package/src/uni.scss +76 -0
  57. package/src/unocss/index.ts +20 -0
  58. package/src/unocss/rules.ts +139 -0
  59. package/src/unocss/shortcuts.ts +53 -0
  60. package/src/unocss/theme/index.ts +13 -0
  61. package/src/unocss/theme/themeOption/dark.ts +35 -0
  62. package/src/unocss/theme/themeOption/primary.ts +33 -0
  63. package/src/unocss/variants.ts +110 -0
  64. package/tsconfig.json +19 -0
  65. package/uno.config.ts +63 -0
  66. package/vite.config.ts +83 -0
  67. package/dist/build/h5/style.css +0 -1
  68. package/dist/build/h5/zhytech-ui-mobile.es.js +0 -19690
  69. package/dist/build/h5/zhytech-ui-mobile.umd.js +0 -6
  70. /package/{dist/build/h5 → src}/static/iconfont/iconfont.css +0 -0
  71. /package/{dist/build/h5 → src}/static/iconfont/iconfont.ttf +0 -0
  72. /package/{dist/build/h5 → src}/static/iconfont/iconfont.woff +0 -0
  73. /package/{dist/build/h5 → src}/static/iconfont/iconfont.woff2 +0 -0
  74. /package/{dist/build/h5 → src}/static/scss/actionSheet.scss +0 -0
  75. /package/{dist/build/h5 → src}/static/scss/button.scss +0 -0
  76. /package/{dist/build/h5 → src}/static/scss/checkbox.scss +0 -0
  77. /package/{dist/build/h5 → src}/static/scss/form.scss +0 -0
  78. /package/{dist/build/h5 → src}/static/scss/index.scss +0 -0
  79. /package/{dist/build/h5 → src}/static/scss/input.scss +0 -0
  80. /package/{dist/build/h5 → src}/static/scss/picker.scss +0 -0
  81. /package/{dist/build/h5 → src}/static/scss/radio.scss +0 -0
@@ -0,0 +1,207 @@
1
+ <!--
2
+ * FilePath : \src\components\dynamicForm\componentRenderer.vue
3
+ * Author : 苏军志
4
+ * Date : 2024-03-04 20:56
5
+ * LastEditors : 苏军志
6
+ * LastEditTime : 2025-09-05 10:44
7
+ * Description : 组件渲染器
8
+ * CodeIterationRecord:
9
+ -->
10
+ <template>
11
+ <template v-for="(component, index) in components" :key="index">
12
+ <!-- 非隐藏项 -->
13
+ <view
14
+ v-if="
15
+ (getByDynamicCondition(component, 'show') && !onePageItemFlag) || (onePageItemFlag && currentShowIDArray.includes(component.id))
16
+ "
17
+ class="p-5"
18
+ >
19
+ <!-- 容器组件直接显示 且(非一项一页 或 一项一页的当前项)-->
20
+ <component
21
+ v-if="component.isLayout"
22
+ :is="(componentTypes as any)[component.type]"
23
+ v-model:component="components[index]"
24
+ :componentID="component.id"
25
+ :disabled="disabled"
26
+ :labelPosition="labelPosition"
27
+ :showDescription="showDescription"
28
+ :onePageItemFlag="onePageItemFlag"
29
+ :currentShowIDArray="currentShowIDArray"
30
+ @validate="(prop:string) => emits('validate', prop)"
31
+ />
32
+
33
+ <wd-cell
34
+ v-else
35
+ :custom-class="`zhy form-item form-prop-${component.id}`"
36
+ :vertical="labelPosition === 'top' || component.props.labelNewLine"
37
+ :title-width="`${
38
+ component.props.showLabel && !(labelPosition === 'top' || component.props.labelNewLine) ? component.props.labelWidth : ''
39
+ }px`"
40
+ :prop="component.id"
41
+ :rules="component.rules"
42
+ >
43
+ <template #label>
44
+ <wd-text
45
+ custom-class="zhy"
46
+ :text="component.props.showLabel ? component.props.label + ':' : ''"
47
+ :size="`${onePageItemFlag ? 18 : component.props.fontSize > 14 ? component.props.fontSize : 14}px`"
48
+ :bold="component.props.isBold"
49
+ :color="component.props.color || '#000000'"
50
+ ></wd-text>
51
+ </template>
52
+ <component
53
+ :class="{ 'sons-html-*:fs-18!': onePageItemFlag }"
54
+ :is="(componentTypes as any)[component.type]"
55
+ v-model:componentProps="component.props"
56
+ v-model:datas="formData.datas"
57
+ :componentID="component.id"
58
+ :disabled="disabled || getByDynamicCondition(component, 'disabled')"
59
+ :readonly="getByDynamicCondition(component, 'readonly')"
60
+ :showDescription="showDescription"
61
+ :key="setValidateWatch(component.id, formData.datas[component.id])"
62
+ />
63
+ </wd-cell>
64
+ </view>
65
+ </template>
66
+ </template>
67
+
68
+ <script setup lang="ts">
69
+ import { baseComponent, advancedComponent, applicationComponent, layoutComponent } from "./components/componentType";
70
+ import type { dynamicFormData, formAttribute } from "./types/formAttribute";
71
+ const componentTypes: Record<string, any> = { ...baseComponent, ...advancedComponent, ...applicationComponent, ...layoutComponent };
72
+ const { computedExpression } = useUtils();
73
+ const props = defineProps({
74
+ /**
75
+ * 组件
76
+ */
77
+ components: {
78
+ type: Array as PropType<Record<string, any>[]>,
79
+ required: true
80
+ },
81
+ /**
82
+ * 标题位置
83
+ */
84
+ labelPosition: {
85
+ type: String,
86
+ default: "top"
87
+ },
88
+ /**
89
+ * 是否禁用
90
+ */
91
+ disabled: {
92
+ type: Boolean,
93
+ default: false
94
+ },
95
+ /**
96
+ * 是否显示组件说明
97
+ */
98
+ showDescription: {
99
+ type: Boolean,
100
+ default: false
101
+ },
102
+ /**
103
+ * 一页一项标记
104
+ */
105
+ onePageItemFlag: {
106
+ type: Boolean,
107
+ default: false
108
+ },
109
+ /**
110
+ * 一页一项时的当前显示项目ID集合
111
+ */
112
+ currentShowIDArray: {
113
+ type: Array<String>,
114
+ default: []
115
+ }
116
+ });
117
+ const { components, showDescription, onePageItemFlag, currentShowIDArray } = toRefs(props);
118
+ // 获取父组件中的变量
119
+ let formData = inject("formData") as Ref<dynamicFormData<formAttribute>>;
120
+ // 动态条件处理方式,后续迭代只需在此对象中添加属性即可
121
+ const dynamicConditionType: Record<string, Function> = {
122
+ // 显示动态条件
123
+ show: (componentProp: Record<string, any>) => {
124
+ if (componentProp.showFlag === undefined) {
125
+ return String(true);
126
+ }
127
+ let condition = "";
128
+ if (componentProp.showFlag) {
129
+ condition = componentProp.hiddenConditionExpression
130
+ ? `!(${componentProp.hiddenConditionExpression})`
131
+ : String(componentProp.showFlag);
132
+ } else {
133
+ condition = componentProp.showConditionExpression || String(componentProp.showFlag);
134
+ }
135
+ return condition;
136
+ },
137
+ // 禁用动态条件
138
+ disabled: (componentProp: Record<string, any>) => {
139
+ return componentProp.disabledConditionExpression || String(componentProp.disabled === undefined ? false : componentProp.disabled);
140
+ },
141
+ // 只读动态条件
142
+ readonly: (componentProp: Record<string, any>) => {
143
+ return componentProp.readonlyConditionExpression || String(componentProp.readonly === undefined ? false : componentProp.readonly);
144
+ }
145
+ };
146
+ /**
147
+ * @description: 动态条件计算
148
+ * @param component 组件
149
+ * @param propType 组件属性名称
150
+ * @return
151
+ */
152
+ const getByDynamicCondition = (component: Record<string, any>, propType: string) => {
153
+ let condition = dynamicConditionType[propType](component.props);
154
+ if (!condition) {
155
+ return undefined;
156
+ }
157
+ if (!["true", "false"].includes(condition)) {
158
+ const idTemplates = condition.match(/{(.*?)}/g);
159
+ idTemplates.forEach((idTemplate: string) => {
160
+ const id = idTemplate.replace(/\{|}/g, "");
161
+ let value = formData.value.datas[id];
162
+ // 找到目标组件
163
+ const paramComponent = components.value.find((paramComponent) => paramComponent.id === id);
164
+ if (paramComponent?.type === "grade") {
165
+ value = formData.value.datas[id]?.score;
166
+ }
167
+ condition = condition.replace(idTemplate, value || "");
168
+ });
169
+ }
170
+ const result = computedExpression(condition);
171
+ // 如果此组件满足隐藏条件,且有值,则清空值
172
+ if (propType === "show" && !result) {
173
+ let components: Record<string, any>[] = [];
174
+ // 如果是布局组件 循环子组件 删除值
175
+ if (component.isLayout && component.children?.length) {
176
+ components = component.children;
177
+ } else {
178
+ components = [component];
179
+ }
180
+ components.forEach((tempComponent) => {
181
+ if (!formData.value.datas[tempComponent.id]) {
182
+ return;
183
+ }
184
+ if (tempComponent.type === "grade") {
185
+ delete formData.value.datas[tempComponent.id].score;
186
+ } else {
187
+ delete formData.value.datas[tempComponent.id];
188
+ }
189
+ });
190
+ }
191
+ return result;
192
+ };
193
+
194
+ // #region form表单验证
195
+ const watchID = ref<string>();
196
+ const watchData = ref<any>();
197
+ const setValidateWatch = (id: string, data: any) => {
198
+ watchID.value = id;
199
+ watchData.value = data;
200
+ return id;
201
+ };
202
+ const emits = defineEmits(["validate"]);
203
+ watch(watchData, () => {
204
+ emits("validate", watchID.value);
205
+ });
206
+ // #endregion
207
+ </script>
@@ -0,0 +1,13 @@
1
+ /*
2
+ * FilePath : \src\components\dynamicForm\components\advanced\index.ts
3
+ * Author : 苏军志
4
+ * Date : 2024-03-04 19:04
5
+ * LastEditors : 苏军志
6
+ * LastEditTime : 2025-08-24 19:30
7
+ * Description : 高级组件集合
8
+ * CodeIterationRecord:
9
+ */
10
+
11
+ import upload from "./upload.vue";
12
+ import uploadImage from "./uploadImage.vue";
13
+ export { upload, uploadImage };
@@ -0,0 +1,108 @@
1
+ <!--
2
+ * FilePath : \src\components\dynamicForm\components\advanced\upload.vue
3
+ * Author : 苏军志
4
+ * Date : 2024-07-16 10:29
5
+ * LastEditors : 苏军志
6
+ * LastEditTime : 2025-09-05 08:48
7
+ * Description : 文件上传(upload)组件渲染器
8
+ * CodeIterationRecord:
9
+ -->
10
+ <template>
11
+ <div class="zhy-form-component-upload">
12
+ <wd-upload
13
+ ref="upload"
14
+ :file-list="fileList"
15
+ :action="uploadOptions.serverApi"
16
+ :multiple="componentProps.multiple"
17
+ :limit="componentProps.fileLimit"
18
+ :auto-upload="uploadOptions?.autoUpload"
19
+ :extension="componentProps.fileType?.split(',')"
20
+ accept="all"
21
+ :before-upload="beforeUpload"
22
+ :before-remove="beforeRemove"
23
+ >
24
+ <wd-button>上传文件</wd-button>
25
+ </wd-upload>
26
+ <view
27
+ v-if="showDescription && (formTypeEnum.Examination || componentProps.description)"
28
+ class="dynamic-form-item-description show-description"
29
+ >
30
+ <wd-icon custom-class="description-tip" class-prefix="iconfont zhy" name="tip" color="#ff0000" />
31
+ {{ `${formType === formTypeEnum.Form ? "项目说明" : "答案解析"}:${componentProps.description || "无"}` }}
32
+ </view>
33
+ <!-- message消息的挂载点。 -->
34
+ <wd-message-box></wd-message-box>
35
+ </div>
36
+ </template>
37
+ <script setup lang="ts">
38
+ import { formTypeEnum } from "../../types/enum";
39
+ import type { baseAttribute, uploadAttribute } from "../../types/componentAttribute/index";
40
+ import type { uploadOption } from "../../types/uploadOption";
41
+ type componentType = baseAttribute & uploadAttribute;
42
+ const props = defineProps({
43
+ /**
44
+ * 组件ID
45
+ */
46
+ componentID: {
47
+ type: [String, Number],
48
+ required: true
49
+ },
50
+ /**
51
+ * 表单数据
52
+ */
53
+ datas: {
54
+ type: Object as PropType<Record<string, any>>,
55
+ required: true
56
+ },
57
+ /**
58
+ * 组件属性
59
+ */
60
+ componentProps: {
61
+ type: Object as PropType<componentType>,
62
+ required: true
63
+ },
64
+ /**
65
+ * 是否项目说明/答案解析
66
+ */
67
+ showDescription: {
68
+ type: Boolean,
69
+ default: false
70
+ }
71
+ });
72
+ // 表单类型
73
+ const formType = inject("formType") as ComputedRef<string>;
74
+ const { checkFile } = useUtils();
75
+ const upload = ref<any>();
76
+ // 获取父组件中的变量
77
+ const uploadOptions = inject("uploadOptions") as uploadOption;
78
+ const { datas, componentProps } = toRefs(props);
79
+ const fileList = ref<Record<string, any>[]>(datas.value[props.componentID] ?? []);
80
+
81
+ const { confirm } = useMessage();
82
+ // 因为wd-upload组件的change事件无法正确触发,使用before-remove和before-upload钩子做处理
83
+ const beforeRemove = ({ file, resolve }: any) => {
84
+ confirm("确定删除该文件吗?", "提示", (flag: boolean) => {
85
+ if (flag) {
86
+ datas.value[props.componentID] = datas.value[props.componentID].filter((item: any) => item.name !== file.name);
87
+ }
88
+ resolve(flag);
89
+ });
90
+ };
91
+ const beforeUpload = ({ files, fileList, resolve }: any) => {
92
+ let errorNumber = 0;
93
+ files.forEach((file: any) => {
94
+ const checkPass = checkFile(file, fileList, componentProps.value.fileSize, componentProps.value.fileSizeUnit, false);
95
+ if (!checkPass) {
96
+ errorNumber++;
97
+ }
98
+ });
99
+ if (errorNumber === 0) {
100
+ if (!datas.value[props.componentID]) {
101
+ datas.value[props.componentID] = [];
102
+ }
103
+ datas.value[props.componentID] = [...datas.value[props.componentID], ...files];
104
+ resolve(true);
105
+ }
106
+ resolve(false);
107
+ };
108
+ </script>
@@ -0,0 +1,107 @@
1
+ <!--
2
+ * FilePath : \src\components\dynamicForm\components\advanced\uploadImage.vue
3
+ * Author : 苏军志
4
+ * Date : 2024-07-16 10:29
5
+ * LastEditors : 苏军志
6
+ * LastEditTime : 2025-09-03 17:01
7
+ * Description : 图片上传(uploadImage)组件渲染器
8
+ * CodeIterationRecord:
9
+ -->
10
+ <template>
11
+ <div class="zhy-form-component-upload-image">
12
+ <wd-upload
13
+ ref="upload"
14
+ :file-list="fileList"
15
+ :action="uploadOptions.serverApi"
16
+ :multiple="componentProps.multiple"
17
+ :limit="componentProps.fileLimit"
18
+ :auto-upload="uploadOptions?.autoUpload"
19
+ :extension="componentProps.fileType?.split(',')"
20
+ accept="media"
21
+ :before-upload="beforeUpload"
22
+ :before-remove="beforeRemove"
23
+ >
24
+ </wd-upload>
25
+ <view
26
+ v-if="showDescription && (formTypeEnum.Examination || componentProps.description)"
27
+ class="dynamic-form-item-description show-description"
28
+ >
29
+ <wd-icon custom-class="description-tip" class-prefix="iconfont zhy" name="tip" color="#ff0000" />
30
+ {{ `${formType === formTypeEnum.Form ? "项目说明" : "答案解析"}:${componentProps.description || "无"}` }}
31
+ </view>
32
+ <!-- message消息的挂载点。 -->
33
+ <wd-message-box></wd-message-box>
34
+ </div>
35
+ </template>
36
+ <script setup lang="ts">
37
+ import { formTypeEnum } from "../../types/enum";
38
+ import type { baseAttribute, uploadAttribute } from "../../types/componentAttribute/index";
39
+ import type { uploadOption } from "../../types/uploadOption";
40
+ type componentType = baseAttribute & uploadAttribute;
41
+ const props = defineProps({
42
+ /**
43
+ * 组件ID
44
+ */
45
+ componentID: {
46
+ type: [String, Number],
47
+ required: true
48
+ },
49
+ /**
50
+ * 表单数据
51
+ */
52
+ datas: {
53
+ type: Object as PropType<Record<string, any>>,
54
+ required: true
55
+ },
56
+ /**
57
+ * 组件属性
58
+ */
59
+ componentProps: {
60
+ type: Object as PropType<componentType>,
61
+ required: true
62
+ },
63
+ /**
64
+ * 是否项目说明/答案解析
65
+ */
66
+ showDescription: {
67
+ type: Boolean,
68
+ default: false
69
+ }
70
+ });
71
+ // 表单类型
72
+ const formType = inject("formType") as ComputedRef<string>;
73
+ const { checkFile } = useUtils();
74
+ const upload = ref<any>();
75
+ // 获取父组件中的变量
76
+ const uploadOptions = inject("uploadOptions") as uploadOption;
77
+ const { datas, componentProps } = toRefs(props);
78
+ const fileList = ref<Record<string, any>[]>(datas.value[props.componentID] ?? []);
79
+
80
+ const { confirm } = useMessage();
81
+ // 因为wd-upload组件的change事件无法正确触发,使用before-remove和before-upload钩子做处理
82
+ const beforeRemove = ({ file, resolve }: any) => {
83
+ confirm("确定删除该文件吗?", "提示", (flag: boolean) => {
84
+ if (flag) {
85
+ datas.value[props.componentID] = datas.value[props.componentID].filter((item: any) => item.name !== file.name);
86
+ }
87
+ resolve(flag);
88
+ });
89
+ };
90
+ const beforeUpload = ({ files, fileList, resolve }: any) => {
91
+ let errorNumber = 0;
92
+ files.forEach((file: any) => {
93
+ const checkPass = checkFile(file, fileList, componentProps.value.fileSize, componentProps.value.fileSizeUnit, false);
94
+ if (!checkPass) {
95
+ errorNumber++;
96
+ }
97
+ });
98
+ if (errorNumber === 0) {
99
+ if (!datas.value[props.componentID]) {
100
+ datas.value[props.componentID] = [];
101
+ }
102
+ datas.value[props.componentID] = [...datas.value[props.componentID], ...files];
103
+ resolve(true);
104
+ }
105
+ resolve(false);
106
+ };
107
+ </script>
@@ -0,0 +1,58 @@
1
+ <!--
2
+ * FilePath : \src\components\dynamicForm\components\answerSheetPopup\answerSheetItem.vue
3
+ * Author : 苏军志
4
+ * Date : 2025-05-15 09:30
5
+ * LastEditors : 苏军志
6
+ * LastEditTime : 2025-09-04 19:27
7
+ * Description : 答题卡项
8
+ * CodeIterationRecord:
9
+ -->
10
+ <template>
11
+ <view class="answer-sheet-item w-full">
12
+ <template v-for="(answerSheet, index) in answerSheetData" :key="index">
13
+ <view class="aline-left pl-10 my-15 b-1-#cccccc first:mt-5" v-if="answerSheet.children?.length">
14
+ <view class="w-full fs-16 mt-4">{{ answerSheet.name }}</view>
15
+ <template v-for="(item, index) in answerSheet.children" :key="index">
16
+ <answer-sheet-item
17
+ v-if="item.children?.length"
18
+ :answerSheetData="item.children"
19
+ :tagType="tagType"
20
+ @showItem="showItem"
21
+ ></answer-sheet-item>
22
+ <template v-else>
23
+ <wd-tag
24
+ v-if="tagType[item.status]?.value"
25
+ custom-class="my-10! mx-5! py-2! px-18! fs-18!"
26
+ :type="tagType[item.status]?.value"
27
+ @click="showItem(item.id)"
28
+ >
29
+ {{ item.name }}
30
+ </wd-tag>
31
+ <wd-tag v-else custom-class="my-10! mx-5! py-2! px-18! fs-18!" @click="showItem(item.id)">
32
+ {{ item.name }}
33
+ </wd-tag>
34
+ </template>
35
+ </template>
36
+ </view>
37
+ </template>
38
+ </view>
39
+ </template>
40
+ <script setup lang="ts">
41
+ defineProps({
42
+ /**
43
+ * 组件
44
+ */
45
+ answerSheetData: {
46
+ type: Array as PropType<Record<string, any>[]>,
47
+ required: true
48
+ },
49
+ tagType: {
50
+ type: Object as PropType<Record<number, any>>,
51
+ required: true
52
+ }
53
+ });
54
+ const emits = defineEmits(["showItem"]);
55
+ const showItem = (id: string) => {
56
+ emits("showItem", id);
57
+ };
58
+ </script>
@@ -0,0 +1,111 @@
1
+ <!--
2
+ * FilePath : \src\components\dynamicForm\components\answerSheetPopup\index.vue
3
+ * Author : 苏军志
4
+ * Date : 2025-05-15 10:11
5
+ * LastEditors : 苏军志
6
+ * LastEditTime : 2025-09-04 19:47
7
+ * Description : 答题卡
8
+ * CodeIterationRecord:
9
+ -->
10
+ <template>
11
+ <wd-action-sheet custom-class="zhy answer-sheet-popup" v-model="show" title="答题卡">
12
+ <view class="y-aline-start overflow-y-auto h-[calc(100%-50px)]">
13
+ <view class="my-10 ml-20 mr-0 fs-16 fw-bold">
14
+ <text>说明:</text>
15
+ <template v-for="(type, index) in Object.values(tagType)" :key="index">
16
+ <wd-tag v-if="type.value" custom-class="zhy mr-10! py-4! px-10!" :type="type.value"> {{ type.label }} </wd-tag>
17
+ <wd-tag v-else custom-class="zhy mr-10! py-4! px-10!"> {{ type.label }} </wd-tag>
18
+ </template>
19
+ </view>
20
+ <view class="flex-auto h-full overflow-y-auto px-10 box-border">
21
+ <answer-sheet-item :answerSheetData="answerSheetData" :tagType="tagType" @showItem="showItem"></answer-sheet-item>
22
+ </view>
23
+ </view>
24
+ </wd-action-sheet>
25
+ </template>
26
+ <script setup lang="ts">
27
+ import type { dynamicFormData, formAttribute } from "../../types/formAttribute";
28
+ import answerSheetItem from "./answerSheetItem.vue";
29
+ const props = defineProps({
30
+ /**
31
+ * @description: 表单数据,包含表单属性,组件集合、初始数据
32
+ */
33
+ formData: {
34
+ type: Object as PropType<dynamicFormData<formAttribute>>,
35
+ required: true
36
+ },
37
+ /**
38
+ * @description: 是否显示正确与否
39
+ */
40
+ showCorrectOrNot: {
41
+ type: Boolean,
42
+ default: true
43
+ },
44
+ /**
45
+ * @description: 弹出框高度
46
+ */
47
+ height: {
48
+ type: String,
49
+ default: "360px"
50
+ }
51
+ });
52
+ const show = ref<boolean>(true);
53
+ const answerSheetData = ref<Record<string, any>[]>([]);
54
+ const tagType = ref<Record<number, any>>({});
55
+ onMounted(() => {
56
+ tagType.value = {
57
+ 1: { value: "", label: "未作答" },
58
+ 2: { value: "success", label: "已作答" }
59
+ };
60
+ if (props.showCorrectOrNot) {
61
+ tagType.value[2].label = "正确";
62
+ tagType.value[3] = { value: "danger", label: "错误" };
63
+ }
64
+ answerSheetData.value = getAnswerSheetData(props.formData.components);
65
+ });
66
+ import { componentTypeEnum } from "../../types/enum";
67
+ /**
68
+ * @description: 获取答题卡数据
69
+ * @param components
70
+ * @param any
71
+ * @return
72
+ */
73
+ const getAnswerSheetData = (components: Record<string, any>[]) => {
74
+ let result: Record<string, any>[] = [];
75
+ let index = 1;
76
+ components.forEach((component) => {
77
+ if (component.type === componentTypeEnum.LABEL) {
78
+ return;
79
+ }
80
+ let status: number = 0;
81
+ if (props.showCorrectOrNot) {
82
+ status = !props.formData.datas[component.id] ? 1 : component.props.examinationStatus;
83
+ } else {
84
+ status = !props.formData.datas[component.id] ? 1 : 2;
85
+ }
86
+ const data: Record<string, any> = {
87
+ id: component.id,
88
+ name: index,
89
+ status
90
+ };
91
+ // 递归处理子组件
92
+ if (component.isLayout && component.children?.length) {
93
+ data.name = component.props.label.replace(/([^)]*)/g, "");
94
+ data.children = getAnswerSheetData(component.children);
95
+ }
96
+ result.push(data);
97
+ index++;
98
+ });
99
+ return result;
100
+ };
101
+ const emits = defineEmits(["showItem"]);
102
+ const showItem = (id: string) => {
103
+ show.value = false;
104
+ emits("showItem", id);
105
+ };
106
+ </script>
107
+ <style lang="scss">
108
+ :deep(.zhy.answer-sheet-popup) {
109
+ height: v-bind(height);
110
+ }
111
+ </style>