Pārlūkot izejas kodu

feat(任务管理): 添加油站新增任务抽屉组件

实现油站新增任务功能,包括创建任务表单组件和配置数据
weieryang 1 mēnesi atpakaļ
vecāks
revīzija
fce4b4f873

+ 276 - 0
.trae/rules/project_rules.md

@@ -0,0 +1,276 @@
1
+- 表单组件配置参考下面内容
2
+```
3
+[
4
+    {
5
+      // 组件需要在 #/adapter.ts内注册,并加上类型
6
+      component: 'Input',
7
+      // 对应组件的参数
8
+      componentProps: {
9
+        placeholder: '请输入用户名',
10
+      },
11
+      // 字段名
12
+      fieldName: 'username',
13
+      // 界面显示的label
14
+      label: '字符串',
15
+      rules: 'required',
16
+    },
17
+    {
18
+      // 组件需要在 #/adapter.ts内注册,并加上类型
19
+      component: 'ApiSelect',
20
+      // 对应组件的参数
21
+      componentProps: {
22
+        // 菜单接口转options格式
23
+        afterFetch: (data: { name: string; path: string }[]) => {
24
+          return data.map((item: any) => ({
25
+            label: item.name,
26
+            value: item.path,
27
+          }));
28
+        },
29
+        // 菜单接口
30
+        api: getAllMenusApi,
31
+      },
32
+      // 字段名
33
+      fieldName: 'api',
34
+      // 界面显示的label
35
+      label: 'ApiSelect',
36
+    },
37
+    {
38
+      component: 'ApiSelect',
39
+      // 对应组件的参数
40
+      componentProps: () => {
41
+        return {
42
+          api: fetchRemoteOptions,
43
+          // 禁止本地过滤
44
+          filterOption: false,
45
+          // 如果正在获取数据,使用插槽显示一个loading
46
+          notFoundContent: fetching.value ? undefined : null,
47
+          // 搜索词变化时记录下来, 使用useDebounceFn防抖。
48
+          onSearch: useDebounceFn((value: string) => {
49
+            keyword.value = value;
50
+          }, 300),
51
+          // 远程搜索参数。当搜索词变化时,params也会更新
52
+          params: {
53
+            keyword: keyword.value || undefined,
54
+          },
55
+          showSearch: true,
56
+        };
57
+      },
58
+      // 字段名
59
+      fieldName: 'remoteSearch',
60
+      // 界面显示的label
61
+      label: '远程搜索',
62
+      renderComponentContent: () => {
63
+        return {
64
+          notFoundContent: fetching.value ? h(Spin) : undefined,
65
+        };
66
+      },
67
+    },
68
+    {
69
+      component: 'ApiTreeSelect',
70
+      // 对应组件的参数
71
+      componentProps: {
72
+        // 菜单接口
73
+        api: getAllMenusApi,
74
+        childrenField: 'children',
75
+        // 菜单接口转options格式
76
+        labelField: 'name',
77
+        valueField: 'path',
78
+      },
79
+      // 字段名
80
+      fieldName: 'apiTree',
81
+      // 界面显示的label
82
+      label: 'ApiTreeSelect',
83
+    },
84
+    {
85
+      component: 'InputPassword',
86
+      componentProps: {
87
+        placeholder: '请输入密码',
88
+      },
89
+      fieldName: 'password',
90
+      label: '密码',
91
+    },
92
+    {
93
+      component: 'InputNumber',
94
+      componentProps: {
95
+        placeholder: '请输入',
96
+      },
97
+      fieldName: 'number',
98
+      label: '数字(带后缀)',
99
+      suffix: () => '¥',
100
+    },
101
+    {
102
+      component: 'IconPicker',
103
+      fieldName: 'icon',
104
+      label: '图标',
105
+    },
106
+    {
107
+      component: 'Select',
108
+      componentProps: {
109
+        allowClear: true,
110
+        filterOption: true,
111
+        options: [
112
+          {
113
+            label: '选项1',
114
+            value: '1',
115
+          },
116
+          {
117
+            label: '选项2',
118
+            value: '2',
119
+          },
120
+        ],
121
+        placeholder: '请选择',
122
+        showSearch: true,
123
+      },
124
+      fieldName: 'options',
125
+      label: '下拉选',
126
+    },
127
+    {
128
+      component: 'RadioGroup',
129
+      componentProps: {
130
+        options: [
131
+          {
132
+            label: '选项1',
133
+            value: '1',
134
+          },
135
+          {
136
+            label: '选项2',
137
+            value: '2',
138
+          },
139
+        ],
140
+      },
141
+      fieldName: 'radioGroup',
142
+      label: '单选组',
143
+    },
144
+    {
145
+      component: 'Radio',
146
+      fieldName: 'radio',
147
+      label: '',
148
+      renderComponentContent: () => {
149
+        return {
150
+          default: () => ['Radio'],
151
+        };
152
+      },
153
+    },
154
+    {
155
+      component: 'CheckboxGroup',
156
+      componentProps: {
157
+        name: 'cname',
158
+        options: [
159
+          {
160
+            label: '选项1',
161
+            value: '1',
162
+          },
163
+          {
164
+            label: '选项2',
165
+            value: '2',
166
+          },
167
+        ],
168
+      },
169
+      fieldName: 'checkboxGroup',
170
+      label: '多选组',
171
+    },
172
+    {
173
+      component: 'Checkbox',
174
+      fieldName: 'checkbox',
175
+      label: '',
176
+      renderComponentContent: () => {
177
+        return {
178
+          default: () => ['我已阅读并同意'],
179
+        };
180
+      },
181
+    },
182
+    {
183
+      component: 'Mentions',
184
+      componentProps: {
185
+        options: [
186
+          {
187
+            label: 'afc163',
188
+            value: 'afc163',
189
+          },
190
+          {
191
+            label: 'zombieJ',
192
+            value: 'zombieJ',
193
+          },
194
+        ],
195
+        placeholder: '请输入',
196
+      },
197
+      fieldName: 'mentions',
198
+      label: '提及',
199
+    },
200
+    {
201
+      component: 'Rate',
202
+      fieldName: 'rate',
203
+      label: '评分',
204
+    },
205
+    {
206
+      component: 'Switch',
207
+      componentProps: {
208
+        class: 'w-auto',
209
+      },
210
+      fieldName: 'switch',
211
+      label: '开关',
212
+    },
213
+    {
214
+      component: 'DatePicker',
215
+      fieldName: 'datePicker',
216
+      label: '日期选择框',
217
+    },
218
+    {
219
+      component: 'RangePicker',
220
+      fieldName: 'rangePicker',
221
+      label: '范围选择器',
222
+    },
223
+    {
224
+      component: 'TimePicker',
225
+      fieldName: 'timePicker',
226
+      label: '时间选择框',
227
+    },
228
+    {
229
+      component: 'TreeSelect',
230
+      componentProps: {
231
+        allowClear: true,
232
+        placeholder: '请选择',
233
+        showSearch: true,
234
+        treeData: [
235
+          {
236
+            label: 'root 1',
237
+            value: 'root 1',
238
+            children: [
239
+              {
240
+                label: 'parent 1',
241
+                value: 'parent 1',
242
+                children: [
243
+                  {
244
+                    label: 'parent 1-0',
245
+                    value: 'parent 1-0',
246
+                    children: [
247
+                      {
248
+                        label: 'my leaf',
249
+                        value: 'leaf1',
250
+                      },
251
+                      {
252
+                        label: 'your leaf',
253
+                        value: 'leaf2',
254
+                      },
255
+                    ],
256
+                  },
257
+                  {
258
+                    label: 'parent 1-1',
259
+                    value: 'parent 1-1',
260
+                  },
261
+                ],
262
+              },
263
+              {
264
+                label: 'parent 2',
265
+                value: 'parent 2',
266
+              },
267
+            ],
268
+          },
269
+        ],
270
+        treeNodeFilterProp: 'label',
271
+      },
272
+      fieldName: 'treeSelect',
273
+      label: '树选择',
274
+    },
275
+  ]
276
+```

+ 114 - 0
apps/web-ele/src/views/schedule/view/components/create/config-data.tsx

@@ -0,0 +1,114 @@
1
+import type { FormSchemaGetter } from '#/adapter/form';
2
+
3
+export const drawerFormSchema: FormSchemaGetter = () => [
4
+  {
5
+    component: 'Select',
6
+    fieldName: 'executivePost',
7
+    label: '执行岗位',
8
+    rules: 'required',
9
+    componentProps: {
10
+      placeholder: '请选择执行岗位',
11
+      allowClear: true,
12
+      options: [],
13
+    },
14
+  },
15
+  {
16
+    component: 'ApiSelect',
17
+    fieldName: 'executor',
18
+    label: '执行人',
19
+    rules: 'required',
20
+    componentProps: {
21
+      placeholder: '请选择执行人',
22
+      allowClear: true,
23
+      showSearch: true,
24
+      filterOption: false,
25
+    },
26
+  },
27
+  {
28
+    component: 'Input',
29
+    fieldName: 'taskName',
30
+    label: '任务名称',
31
+    rules: 'required',
32
+    componentProps: {
33
+      placeholder: '请输入任务名称',
34
+      maxlength: 100,
35
+    },
36
+  },
37
+  {
38
+    component: 'DatePicker',
39
+    fieldName: 'startTime',
40
+    label: '开始时间',
41
+    rules: 'required',
42
+    componentProps: {
43
+      placeholder: '请选择开始时间',
44
+      showTime: true,
45
+      format: 'YYYY-MM-DD HH:mm:ss',
46
+    },
47
+  },
48
+  {
49
+    component: 'DatePicker',
50
+    fieldName: 'endTime',
51
+    label: '截至时间',
52
+    rules: 'required',
53
+    componentProps: {
54
+      placeholder: '请选择截至时间',
55
+      showTime: true,
56
+      format: 'YYYY-MM-DD HH:mm:ss',
57
+    },
58
+  },
59
+  {
60
+    component: 'Select',
61
+    fieldName: 'taskType',
62
+    label: '任务类型',
63
+    rules: 'required',
64
+    componentProps: {
65
+      placeholder: '请选择任务类型',
66
+      allowClear: true,
67
+      options: [],
68
+    },
69
+  },
70
+  {
71
+    component: 'ApiSelect',
72
+    fieldName: 'ccPerson',
73
+    label: '抄送人',
74
+    componentProps: {
75
+      placeholder: '请选择抄送人',
76
+      allowClear: true,
77
+      showSearch: true,
78
+      filterOption: false,
79
+    },
80
+  },
81
+  {
82
+    component: 'Input',
83
+    fieldName: 'taskDescription',
84
+    label: '任务描述',
85
+    componentProps: {
86
+      type: 'textarea',
87
+      placeholder: '请输入任务描述',
88
+      maxlength: 500,
89
+      showWordLimit: true,
90
+      rows: 4,
91
+    },
92
+  },
93
+  {
94
+    component: 'Upload',
95
+    fieldName: 'descriptionImage',
96
+    label: '描述图片',
97
+    componentProps: {
98
+      accept: 'image/*',
99
+      listType: 'picture-card',
100
+      maxCount: 1,
101
+    },
102
+  },
103
+  {
104
+    component: 'Upload',
105
+    fieldName: 'attachment',
106
+    label: '附件',
107
+    componentProps: {
108
+      // accept: 'image/*',
109
+      listType: 'text',
110
+      limit: 5,
111
+      multiple: true,
112
+    },
113
+  },
114
+];

+ 64 - 0
apps/web-ele/src/views/schedule/view/components/create/index.vue

@@ -0,0 +1,64 @@
1
+<script lang="ts" setup>
2
+import { useVbenDrawer } from '@vben/common-ui';
3
+
4
+import { useVbenForm } from '#/adapter/form';
5
+
6
+import { drawerFormSchema } from './config-data';
7
+
8
+defineOptions({
9
+  name: 'CreateTaskDrawer',
10
+});
11
+
12
+const [Form, formApi] = useVbenForm({
13
+  schema: drawerFormSchema(),
14
+  showDefaultActions: false,
15
+});
16
+const [Drawer, drawerApi] = useVbenDrawer({
17
+  onCancel() {
18
+    drawerApi.close();
19
+  },
20
+  onConfirm: async () => {
21
+    await formApi.submitForm();
22
+    drawerApi.close();
23
+  },
24
+  onOpenChange(isOpen: boolean) {
25
+    if (isOpen) {
26
+      const { values } = drawerApi.getData<Record<string, any>>();
27
+      if (values) {
28
+        formApi.setValues(values);
29
+      }
30
+    }
31
+  },
32
+  title: '油站新增任务',
33
+});
34
+</script>
35
+<template>
36
+  <Drawer>
37
+    <Form>
38
+      <!-- <template #attachment="scope">
39
+        <ElUpload
40
+          v-model:file-list="scope.attachment"
41
+          action="#"
42
+          accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx"
43
+          list-type="text"
44
+          show-file-list
45
+          :auto-upload="true"
46
+          :drag="false"
47
+          :limit="5"
48
+          :multiple="true"
49
+          class="mb-4"
50
+        >
51
+          <div class="flex items-center justify-center">
52
+            <ElIcon class="el-upload__icon">
53
+              <Plus size="18" />
54
+            </ElIcon>
55
+            <div class="el-upload__text">
56
+              <div>上传附件</div>
57
+              <div class="text-xs text-gray-500">最多上传5个附件</div>
58
+            </div>
59
+          </div>
60
+        </ElUpload>
61
+      </template> -->
62
+    </Form>
63
+  </Drawer>
64
+</template>

+ 16 - 2
apps/web-ele/src/views/schedule/view/index.vue

@@ -1,11 +1,12 @@
1 1
 <script lang="ts" setup>
2 2
 import { computed, ref, watch } from 'vue';
3 3
 
4
-import { Page } from '@vben/common-ui';
4
+import { Page, useVbenDrawer } from '@vben/common-ui';
5 5
 
6 6
 import dayjs from 'dayjs';
7 7
 import { ElRadioButton, ElRadioGroup } from 'element-plus';
8 8
 
9
+import CreateTasK from './components/create/index.vue';
9 10
 import Day from './components/day/index.vue';
10 11
 import Month from './components/month/index.vue';
11 12
 import Week from './components/week/index.vue';
@@ -47,6 +48,16 @@ const year = computed(() => {
47 48
 const weekNumber = computed(() => {
48 49
   return dayjs(searchParams.value.date).week();
49 50
 });
51
+
52
+const [CreateTaskDrawer, createTaskDrawerApi] = useVbenDrawer({
53
+  // 连接抽离的组件
54
+  connectedComponent: CreateTasK,
55
+  // placement: 'left',
56
+});
57
+
58
+const createTaskDrawerEvent = () => {
59
+  createTaskDrawerApi.setData({ isUpdate: false }).open();
60
+};
50 61
 </script>
51 62
 
52 63
 <template>
@@ -105,7 +116,9 @@ const weekNumber = computed(() => {
105 116
         </div>
106 117
 
107 118
         <div class="flex items-center space-x-4">
108
-          <ElButton type="primary"> 油站新增任务 </ElButton>
119
+          <ElButton type="primary" @click="createTaskDrawerEvent">
120
+            油站新增任务
121
+          </ElButton>
109 122
           <ElButton type="primary"> 访客新增任务 </ElButton>
110 123
           <ElButton type="primary"> 任务转接 </ElButton>
111 124
         </div>
@@ -120,5 +133,6 @@ const weekNumber = computed(() => {
120 133
       />
121 134
       <Month v-if="searchParams.timeType === 'month'" :year="year" />
122 135
     </ElCard>
136
+    <CreateTaskDrawer />
123 137
   </Page>
124 138
 </template>