Commit 07d508e19019df7925bdc04d47438a16bf0aac6c

Authored by 黄 x
1 parent 0923c9a5

fix(front): 设备添加采用分步完善

@@ -7,26 +7,47 @@ @@ -7,26 +7,47 @@
7 width="500px" 7 width="500px"
8 @ok="handleSubmit" 8 @ok="handleSubmit"
9 > 9 >
10 - <BasicForm @register="registerForm"/> 10 + <BasicForm @register="registerForm">
  11 + <template #iconSelect>
  12 + <Upload
  13 + name="avatar"
  14 + list-type="picture-card"
  15 + class="avatar-uploader"
  16 + :show-upload-list="false"
  17 + :customRequest="customUpload"
  18 + :before-upload="beforeUpload"
  19 + >
  20 + <img v-if="devicePic" :src="devicePic" alt="avatar"/>
  21 + <div v-else>
  22 + <loading-outlined v-if="loading"></loading-outlined>
  23 + <plus-outlined v-else></plus-outlined>
  24 + <div class="ant-upload-text">图片上传</div>
  25 + </div>
  26 + </Upload>
  27 + </template>
  28 + </BasicForm>
11 </BasicDrawer> 29 </BasicDrawer>
12 </template> 30 </template>
13 <script lang="ts"> 31 <script lang="ts">
14 import { defineComponent, ref, computed, unref } from 'vue'; 32 import { defineComponent, ref, computed, unref } from 'vue';
15 import { BasicForm, useForm } from '/@/components/Form'; 33 import { BasicForm, useForm } from '/@/components/Form';
16 - import { formSchema } from './config.data'; 34 + import { formSchema } from './device.data';
17 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; 35 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
18 import {saveOrEditMessageConfig} from "/@/api/message/config"; 36 import {saveOrEditMessageConfig} from "/@/api/message/config";
19 import {useMessage} from "/@/hooks/web/useMessage"; 37 import {useMessage} from "/@/hooks/web/useMessage";
  38 + import {upload} from "/@/api/oss/ossFileUploader";
  39 + import {message, Upload} from "ant-design-vue";
  40 + import {FileItem} from "/@/components/Upload/src/typing";
20 41
21 export default defineComponent({ 42 export default defineComponent({
22 - name: 'ConfigDrawer',  
23 - components: { BasicDrawer, BasicForm }, 43 + name: 'DeviceDrawer',
  44 + components: { BasicDrawer, BasicForm,Upload },
24 emits: ['success', 'register'], 45 emits: ['success', 'register'],
25 setup(_, { emit }) { 46 setup(_, { emit }) {
26 const isUpdate = ref(true); 47 const isUpdate = ref(true);
27 - 48 + const devicePic = ref("");
28 const [registerForm, { validate,setFieldsValue,resetFields }] = useForm({ 49 const [registerForm, { validate,setFieldsValue,resetFields }] = useForm({
29 - labelWidth: 120, 50 + labelWidth: 80,
30 schemas: formSchema, 51 schemas: formSchema,
31 showActionButtonGroup: false, 52 showActionButtonGroup: false,
32 }); 53 });
@@ -46,7 +67,7 @@ @@ -46,7 +67,7 @@
46 } 67 }
47 }); 68 });
48 69
49 - const getTitle = computed(() => (!unref(isUpdate) ? '新增消息配置' : '编辑消息配置')); 70 + const getTitle = computed(() => (!unref(isUpdate) ? '新增设备' : '编辑设备'));
50 71
51 async function handleSubmit() { 72 async function handleSubmit() {
52 try { 73 try {
@@ -78,13 +99,43 @@ @@ -78,13 +99,43 @@
78 setDrawerProps({ confirmLoading: false }); 99 setDrawerProps({ confirmLoading: false });
79 } 100 }
80 } 101 }
  102 + async function customUpload({file}) {
  103 + if (beforeUpload(file)) {
  104 + const formData = new FormData()
  105 + formData.append('file', file)
  106 + const response = await upload(formData);
  107 + if (response.fileStaticUri) {
  108 + devicePic.value = response.fileStaticUri;
  109 + }
  110 + }
  111 + }
81 112
  113 + const beforeUpload = (file: FileItem) => {
  114 + const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  115 + if (!isJpgOrPng) {
  116 + message.error('只能上传图片文件!');
  117 + }
  118 + const isLt2M = file.size as number / 1024 / 1024 < 2;
  119 + if (!isLt2M) {
  120 + message.error('图片大小不能超过2MB!');
  121 + }
  122 + return isJpgOrPng && isLt2M;
  123 + };
82 return { 124 return {
83 registerDrawer, 125 registerDrawer,
84 registerForm, 126 registerForm,
85 getTitle, 127 getTitle,
86 handleSubmit, 128 handleSubmit,
  129 + beforeUpload,
  130 + customUpload,
  131 + devicePic
87 }; 132 };
88 }, 133 },
89 }); 134 });
90 </script> 135 </script>
  136 +<style>
  137 +.ant-upload-select-picture-card i {
  138 + font-size: 32px;
  139 + color: #999;
  140 +}
  141 +</style>
  1 +<template>
  2 + <BasicModal
  3 + v-bind="$attrs"
  4 + width="55rem"
  5 + @register="register"
  6 + :title=getTitle
  7 + @visible-change="handleVisibleChange"
  8 + >
  9 + <div class="step-form-form">
  10 + <a-steps :current="current">
  11 + <a-step title="填写设备信息" />
  12 + <a-step title="确认转账信息" />
  13 + <a-step title="完成" />
  14 + </a-steps>
  15 + </div>
  16 + <div class="mt-5">
  17 + <DeviceStep1 @next="handleStep1Next" v-show="current === 0" />
  18 + <DeviceStep2
  19 + @prev="handleStepPrev"
  20 + @next="handleStep2Next"
  21 + v-show="current === 1"
  22 + v-if="initStep2"
  23 + />
  24 + <DeviceStep3 v-show="current === 2" @redo="handleRedo" v-if="initStep3" />
  25 + </div>
  26 + </BasicModal>
  27 +</template>
  28 +<script lang="ts">
  29 +import {defineComponent, ref, nextTick, computed, unref, reactive, toRefs} from 'vue';
  30 + import { BasicModal, useModalInner } from '/@/components/Modal';
  31 + import { BasicForm, FormSchema, useForm } from '/@/components/Form';
  32 + import DeviceStep1 from "/@/views/device/step/DeviceStep1.vue";
  33 + import DeviceStep2 from "/@/views/device/step/DeviceStep2.vue";
  34 + import DeviceStep3 from "/@/views/device/step/DeviceStep3.vue";
  35 + import { Steps } from "ant-design-vue";
  36 + const schemas: FormSchema[] = [
  37 + {
  38 + field: 'field1',
  39 + component: 'Input',
  40 + label: '字段1',
  41 + colProps: {
  42 + span: 24,
  43 + },
  44 + defaultValue: '111',
  45 + },
  46 + {
  47 + field: 'field2',
  48 + component: 'Input',
  49 + label: '字段2',
  50 + colProps: {
  51 + span: 24,
  52 + },
  53 + },
  54 + ];
  55 + export default defineComponent({
  56 + name:'DeviceModal',
  57 + components: { BasicModal, BasicForm, DeviceStep1, DeviceStep2, DeviceStep3,
  58 + [Steps.name]: Steps,
  59 + [Steps.Step.name]: Steps.Step, },
  60 + props: {
  61 + userData: { type: Object },
  62 + },
  63 + setup(props) {
  64 + const state = reactive({
  65 + initStep2: false,
  66 + initStep3: false,
  67 + });
  68 + const current = ref(0);
  69 + const isUpdate = ref(true);
  70 + const modelRef = ref({});
  71 + const getTitle = computed(() => (!unref(isUpdate) ? '新增设备' : '编辑设备'));
  72 + const [
  73 + registerForm,
  74 + {
  75 + // setFieldsValue,
  76 + // setProps
  77 + },
  78 + ] = useForm({
  79 + labelWidth: 120,
  80 + schemas,
  81 + showActionButtonGroup: false,
  82 + actionColOptions: {
  83 + span: 24,
  84 + },
  85 + });
  86 +
  87 + const [register] = useModalInner((data) => {
  88 + isUpdate.value = !!data?.isUpdate;
  89 + data && onDataReceive(data);
  90 + });
  91 +
  92 + function handleStepPrev() {
  93 + current.value--;
  94 + }
  95 + function handleStep1Next(step1Values: any) {
  96 + current.value++;
  97 + state.initStep2 = true;
  98 + console.log(step1Values);
  99 + }
  100 + function handleStep2Next(step2Values: any) {
  101 + current.value++;
  102 + state.initStep3 = true;
  103 + console.log(step2Values);
  104 + }
  105 + function handleRedo() {
  106 + current.value = 0;
  107 + state.initSetp2 = false;
  108 + state.initSetp3 = false;
  109 + }
  110 +
  111 +
  112 + function onDataReceive(data) {
  113 + console.log('Data Received', data);
  114 + // 方式1;
  115 + // setFieldsValue({
  116 + // field2: data.data,
  117 + // field1: data.info,
  118 + // });
  119 +
  120 + // // 方式2
  121 + modelRef.value = { field2: data.data, field1: data.info };
  122 +
  123 + // setProps({
  124 + // model:{ field2: data.data, field1: data.info }
  125 + // })
  126 + }
  127 +
  128 + function handleVisibleChange(v) {
  129 + v && props.userData && nextTick(() => onDataReceive(props.userData));
  130 + }
  131 +
  132 + return { register, schemas, registerForm, model: modelRef, getTitle,handleVisibleChange,
  133 + current, ...toRefs(state), handleStepPrev, handleStep1Next, handleStep2Next, handleRedo};
  134 + },
  135 + });
  136 +</script>
src/views/device/device.data.ts renamed from src/views/device/config.data.ts
1 import { BasicColumn } from '/@/components/Table'; 1 import { BasicColumn } from '/@/components/Table';
2 import { FormSchema } from '/@/components/Table'; 2 import { FormSchema } from '/@/components/Table';
3 import {findDictItemByCode} from "/@/api/system/dict"; 3 import {findDictItemByCode} from "/@/api/system/dict";
4 -import {MessageEnum} from "/@/enums/messageEnum";  
5 import {DeviceTypeEnum,DeviceState} from "/@/api/device/model/deviceModel"; 4 import {DeviceTypeEnum,DeviceState} from "/@/api/device/model/deviceModel";
6 export const columns: BasicColumn[] = [ 5 export const columns: BasicColumn[] = [
7 { 6 {
8 title: '设备名称', 7 title: '设备名称',
9 dataIndex: 'name', 8 dataIndex: 'name',
10 - width: 200, 9 + width: 120,
11 }, 10 },
12 { 11 {
13 title: '设备类型', 12 title: '设备类型',
14 dataIndex: 'deviceType', 13 dataIndex: 'deviceType',
15 - width: 200, 14 + width: 100,
16 slots:{customRender:'deviceType'}, 15 slots:{customRender:'deviceType'},
17 }, 16 },
18 { 17 {
19 title: '设备配置', 18 title: '设备配置',
20 dataIndex: 'deviceProfile.name', 19 dataIndex: 'deviceProfile.name',
21 - width: 180, 20 + width: 160,
22 slots: { customRender: 'deviceProfile' }, 21 slots: { customRender: 'deviceProfile' },
23 }, 22 },
  23 +
  24 + {
  25 + title: '所属组织',
  26 + dataIndex: 'organizationId',
  27 + slots: { customRender: 'organizationId' },
  28 + },
24 { 29 {
25 title: '标签', 30 title: '标签',
26 dataIndex: 'label', 31 dataIndex: 'label',
27 width: 180 32 width: 180
28 }, 33 },
29 { 34 {
30 - title: '配置信息',  
31 - dataIndex: 'deviceInfo',  
32 - width: 180,  
33 - slots: { customRender: 'config' },  
34 - },  
35 - {  
36 title: '状态', 35 title: '状态',
37 dataIndex: 'deviceState', 36 dataIndex: 'deviceState',
38 width: 120, 37 width: 120,
@@ -44,11 +43,6 @@ export const columns: BasicColumn[] = [ @@ -44,11 +43,6 @@ export const columns: BasicColumn[] = [
44 dataIndex: 'lastConnectTime', 43 dataIndex: 'lastConnectTime',
45 width: 180, 44 width: 180,
46 }, 45 },
47 - {  
48 - title: '创建时间',  
49 - dataIndex: 'createTime',  
50 - width: 180,  
51 - },  
52 ]; 46 ];
53 47
54 export const searchFormSchema: FormSchema[] = [ 48 export const searchFormSchema: FormSchema[] = [
@@ -87,116 +81,43 @@ export const searchFormSchema: FormSchema[] = [ @@ -87,116 +81,43 @@ export const searchFormSchema: FormSchema[] = [
87 ]; 81 ];
88 82
89 83
90 -export const isMessage = (type:string)=>{  
91 - return type===MessageEnum.IS_SMS;  
92 -}  
93 -export const isEmail = (type:string)=>{  
94 - return type===MessageEnum.IS_EMAIL;  
95 -}  
96 -  
97 export const formSchema: FormSchema[] = [ 84 export const formSchema: FormSchema[] = [
98 { 85 {
99 - field: 'configName',  
100 - label: '配置名称',  
101 - required: true,  
102 - component:'Input' 86 + field: 'icon',
  87 + label: '设备图片: ',
  88 + slot: 'iconSelect',
  89 + component: 'Input',
103 }, 90 },
104 { 91 {
105 - field: 'messageType',  
106 - label: '消息类型', 92 + field: 'name',
  93 + label: '设备名称',
107 required: true, 94 required: true,
108 - component: 'ApiSelect',  
109 - componentProps: {  
110 - api:findDictItemByCode,  
111 - params:{  
112 - dictCode:"message_type"  
113 - },  
114 - labelField:'itemText',  
115 - valueField:'itemValue',  
116 - }, 95 + component:'Input',
  96 + componentProps:{
  97 + maxLength:30
  98 + }
117 }, 99 },
118 { 100 {
119 - field: 'platformType',  
120 - label: '平台类型', 101 + field: 'deviceType',
  102 + label: '设备类型',
121 required: true, 103 required: true,
122 component: 'ApiSelect', 104 component: 'ApiSelect',
123 componentProps: { 105 componentProps: {
124 api:findDictItemByCode, 106 api:findDictItemByCode,
125 params:{ 107 params:{
126 - dictCode:"platform_type" 108 + dictCode:"device_type"
127 }, 109 },
128 labelField:'itemText', 110 labelField:'itemText',
129 valueField:'itemValue', 111 valueField:'itemValue',
130 }, 112 },
131 - ifShow:({values}) => isMessage(Reflect.get(values,'messageType')),  
132 - },  
133 - {  
134 - field: 'accessKeyId',  
135 - label: 'accessKeyId',  
136 - required: true,  
137 - component:'Input',  
138 - ifShow:({values}) => isMessage(Reflect.get(values,'messageType')),  
139 - },  
140 - {  
141 - field: 'accessKeySecret',  
142 - label: 'accessKeySecret',  
143 - required: true,  
144 - component:'Input',  
145 - ifShow:({values}) => isMessage(Reflect.get(values,'messageType')),  
146 }, 113 },
147 { 114 {
148 - field: 'host',  
149 - label: '服务器地址',  
150 - defaultValue:'smtp.163.com',  
151 - required: true, 115 + field: 'label',
  116 + label: '设备标签',
152 component:'Input', 117 component:'Input',
153 - ifShow:({values}) => isEmail(Reflect.get(values,'messageType')),  
154 - },  
155 - {  
156 - field: 'port',  
157 - label: '端口',  
158 - defaultValue: 25,  
159 - required: true,  
160 - component:'InputNumber',  
161 - ifShow:({values}) => isEmail(Reflect.get(values,'messageType')),  
162 - },  
163 - {  
164 - field: 'username',  
165 - label: '用户名',  
166 - required: true,  
167 - component:'Input',  
168 - ifShow:({values}) => isEmail(Reflect.get(values,'messageType')),  
169 - },  
170 - {  
171 - field: 'password',  
172 - label: '密码',  
173 - required: true,  
174 - component:'InputPassword',  
175 - ifShow:({values}) => isEmail(Reflect.get(values,'messageType')),  
176 - },  
177 - {  
178 - field: 'config',  
179 - label: '消息配置',  
180 - component:'Input',  
181 - show:false,  
182 - },  
183 - {  
184 - field: 'id',  
185 - label: '主键',  
186 - component:'Input',  
187 - show:false,  
188 - },  
189 - {  
190 - field: 'status',  
191 - label: '状态',  
192 - component: 'RadioButtonGroup',  
193 - defaultValue: 0,  
194 - componentProps: {  
195 - options: [  
196 - { label: '启用', value: 1 },  
197 - { label: '停用', value: 0 },  
198 - ],  
199 - }, 118 + componentProps:{
  119 + maxLength:255
  120 + }
200 }, 121 },
201 { 122 {
202 label: '备注', 123 label: '备注',
1 <template> 1 <template>
2 - <div>  
3 - <BasicTable @register="registerTable"> 2 + <PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
  3 + <OrganizationIdTree class="w-1/6 xl:w-1/5" @select="handleSelect" />
  4 + <BasicTable @register="registerTable" class="w-5/6 xl:w-4/5">
4 <template #toolbar> 5 <template #toolbar>
5 <a-button type="primary" @click="handleCreate"> 新增设备 </a-button> 6 <a-button type="primary" @click="handleCreate"> 新增设备 </a-button>
6 </template> 7 </template>
7 - <template #config="{record}">  
8 - <a-button type="link" class="ml-2" @click="showData(record)"> 查看配置 </a-button>  
9 - </template>  
10 <template #deviceProfile="{record}"> 8 <template #deviceProfile="{record}">
11 <a-button type="link" class="ml-2" @click="goDeviceProfile"> {{record.deviceProfile.name}} </a-button> 9 <a-button type="link" class="ml-2" @click="goDeviceProfile"> {{record.deviceProfile.name}} </a-button>
12 </template> 10 </template>
  11 + <template #organizationId = "{record}">
  12 + {{record.organizationDTO.name}}
  13 + </template>
13 <template #deviceType = "{record}"> 14 <template #deviceType = "{record}">
14 <Tag color="success" class="ml-2"> 15 <Tag color="success" class="ml-2">
15 {{record.deviceType==DeviceTypeEnum.GATEWAY ?'网关设备': 16 {{record.deviceType==DeviceTypeEnum.GATEWAY ?'网关设备':
@@ -44,29 +45,38 @@ @@ -44,29 +45,38 @@
44 </template> 45 </template>
45 </BasicTable> 46 </BasicTable>
46 <ConfigDrawer @register="registerDrawer" @success="handleSuccess" /> 47 <ConfigDrawer @register="registerDrawer" @success="handleSuccess" />
47 - </div> 48 + <DeviceModal @register="registerModal" @success="handleSuccess"></DeviceModal>
  49 + </PageWrapper>
48 </template> 50 </template>
49 <script lang="ts"> 51 <script lang="ts">
50 -import { defineComponent,h} from 'vue';  
51 -import {DeviceState, DeviceTypeEnum} from "/@/api/device/model/deviceModel";  
52 -import { BasicTable, useTable, TableAction } from '/@/components/Table';  
53 -import { useDrawer } from '/@/components/Drawer';  
54 -import ConfigDrawer from './DeviceDrawer.vue';  
55 -import { columns, searchFormSchema } from './config.data';  
56 -import {Modal, Tag} from 'ant-design-vue';  
57 -import { CodeEditor,JsonPreview } from '/@/components/CodeEditor';  
58 -import {useMessage} from "/@/hooks/web/useMessage";  
59 -import {deleteDevice, devicePage} from "/@/api/device/deviceManager";  
60 -import {PageEnum} from "/@/enums/pageEnum";  
61 -import {useGo} from "/@/hooks/web/usePage"; 52 +import {defineComponent, reactive,} from 'vue';
  53 + import { DeviceState, DeviceTypeEnum } from "/@/api/device/model/deviceModel";
  54 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  55 + import { useDrawer } from '/@/components/Drawer';
  56 + import ConfigDrawer from './DeviceDrawer.vue';
  57 + import { columns, searchFormSchema } from './device.data';
  58 + import { Tag } from 'ant-design-vue';
  59 + import { CodeEditor } from '/@/components/CodeEditor';
  60 + import { useMessage } from "/@/hooks/web/useMessage";
  61 + import { deleteDevice, devicePage } from "/@/api/device/deviceManager";
  62 + import { PageEnum } from "/@/enums/pageEnum";
  63 + import { useGo } from "/@/hooks/web/usePage";
  64 + import { PageWrapper } from "/@/components/Page";
  65 + import OrganizationIdTree from "/@/views/common/OrganizationIdTree.vue";
  66 + import { useModal } from "/@/components/Modal";
  67 + import DeviceModal from "/@/views/device/DeviceModal.vue";
  68 +
62 69
63 export default defineComponent({ 70 export default defineComponent({
64 name: 'DeviceManagement', 71 name: 'DeviceManagement',
65 - components: { BasicTable, ConfigDrawer, TableAction ,CodeEditor,Tag}, 72 + components: { BasicTable, ConfigDrawer,PageWrapper, TableAction ,OrganizationIdTree,CodeEditor,Tag,
  73 + DeviceModal},
66 setup() { 74 setup() {
67 const [registerDrawer, { openDrawer }] = useDrawer(); 75 const [registerDrawer, { openDrawer }] = useDrawer();
68 const {createMessage} = useMessage(); 76 const {createMessage} = useMessage();
69 const go = useGo(); 77 const go = useGo();
  78 + const searchInfo = reactive<Recordable>({});
  79 + const [registerModal, { openModal }] = useModal();
70 const [registerTable, { reload }] = useTable({ 80 const [registerTable, { reload }] = useTable({
71 title: '设备列表', 81 title: '设备列表',
72 api: devicePage, 82 api: devicePage,
@@ -79,6 +89,7 @@ export default defineComponent({ @@ -79,6 +89,7 @@ export default defineComponent({
79 showTableSetting: true, 89 showTableSetting: true,
80 bordered: true, 90 bordered: true,
81 showIndexColumn: false, 91 showIndexColumn: false,
  92 + searchInfo:searchInfo,
82 actionColumn: { 93 actionColumn: {
83 width: 180, 94 width: 180,
84 title: '操作', 95 title: '操作',
@@ -89,9 +100,12 @@ export default defineComponent({ @@ -89,9 +100,12 @@ export default defineComponent({
89 }); 100 });
90 101
91 function handleCreate() { 102 function handleCreate() {
92 - openDrawer(true, { 103 + // openDrawer(true, {
  104 + // isUpdate: false,
  105 + // });
  106 + openModal(true,{
93 isUpdate: false, 107 isUpdate: false,
94 - }); 108 + })
95 } 109 }
96 110
97 function handleEdit(record: Recordable) { 111 function handleEdit(record: Recordable) {
@@ -112,12 +126,9 @@ export default defineComponent({ @@ -112,12 +126,9 @@ export default defineComponent({
112 function handleSuccess() { 126 function handleSuccess() {
113 reload(); 127 reload();
114 } 128 }
115 - function showData(record: Recordable){  
116 - Modal.info({  
117 - title: '当前配置',  
118 - width:480,  
119 - content: h(JsonPreview, { data: JSON.parse(JSON.stringify(record.deviceInfo)) }),  
120 - }); 129 + function handleSelect(organization) {
  130 + searchInfo.organizationId = organization;
  131 + handleSuccess();
121 } 132 }
122 function goDeviceProfile(){ 133 function goDeviceProfile(){
123 go(PageEnum.DEVICE_PROFILE) 134 go(PageEnum.DEVICE_PROFILE)
@@ -125,14 +136,16 @@ export default defineComponent({ @@ -125,14 +136,16 @@ export default defineComponent({
125 return { 136 return {
126 registerTable, 137 registerTable,
127 registerDrawer, 138 registerDrawer,
128 - showData,  
129 handleCreate, 139 handleCreate,
130 handleEdit, 140 handleEdit,
131 handleDelete, 141 handleDelete,
132 handleSuccess, 142 handleSuccess,
133 goDeviceProfile, 143 goDeviceProfile,
134 DeviceTypeEnum, 144 DeviceTypeEnum,
135 - DeviceState 145 + DeviceState,
  146 + handleSelect,
  147 + searchInfo,
  148 + registerModal
136 }; 149 };
137 }, 150 },
138 }); 151 });
  1 +<template>
  2 + <div class="step1">
  3 + <div class="step1-form">
  4 + <BasicForm @register="register">
  5 + <template #iconSelect>
  6 + <Upload
  7 + name="avatar"
  8 + list-type="picture-card"
  9 + class="avatar-uploader"
  10 + :show-upload-list="false"
  11 + :customRequest="customUpload"
  12 + :before-upload="beforeUpload"
  13 + >
  14 + <img v-if="devicePic" :src="devicePic" alt="avatar"/>
  15 + <div v-else>
  16 + <loading-outlined v-if="loading"></loading-outlined>
  17 + <plus-outlined v-else></plus-outlined>
  18 + <div class="ant-upload-text">图片上传</div>
  19 + </div>
  20 + </Upload>
  21 + </template>
  22 + </BasicForm>
  23 + </div>
  24 + </div>
  25 +</template>
  26 +<script lang="ts">
  27 +import {defineComponent, ref} from 'vue';
  28 + import { BasicForm, useForm } from '/@/components/Form';
  29 + import { step1Schemas } from './data';
  30 + import {Select, Input, Divider, Upload, message} from 'ant-design-vue';
  31 + import {upload} from "/@/api/oss/ossFileUploader";
  32 + import {FileItem} from "/@/components/Upload/src/typing";
  33 + export default defineComponent({
  34 + components: {
  35 + BasicForm,
  36 + [Select.name]: Select,
  37 + ASelectOption: Select.Option,
  38 + [Input.name]: Input,
  39 + [Input.Group.name]: Input.Group,
  40 + [Divider.name]: Divider,
  41 + Upload
  42 + },
  43 + emits: ['next'],
  44 + setup(_, { emit }) {
  45 + const devicePic = ref("");
  46 + const [register, { validate }] = useForm({
  47 + labelWidth: 100,
  48 + schemas: step1Schemas,
  49 + actionColOptions: {
  50 + span: 14,
  51 + },
  52 + showResetButton: false,
  53 + submitButtonOptions: {
  54 + text: '下一步',
  55 + },
  56 + submitFunc: customSubmitFunc,
  57 + });
  58 +
  59 + async function customSubmitFunc() {
  60 + try {
  61 + const values = await validate();
  62 + emit('next', values);
  63 + } catch (error) {}
  64 + }
  65 + async function customUpload({file}) {
  66 + if (beforeUpload(file)) {
  67 + const formData = new FormData()
  68 + formData.append('file', file)
  69 + const response = await upload(formData);
  70 + if (response.fileStaticUri) {
  71 + devicePic.value = response.fileStaticUri;
  72 + }
  73 + }
  74 + }
  75 +
  76 + const beforeUpload = (file: FileItem) => {
  77 + const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  78 + if (!isJpgOrPng) {
  79 + message.error('只能上传图片文件!');
  80 + }
  81 + const isLt2M = file.size as number / 1024 / 1024 < 2;
  82 + if (!isLt2M) {
  83 + message.error('图片大小不能超过2MB!');
  84 + }
  85 + return isJpgOrPng && isLt2M;
  86 + };
  87 +
  88 + return { register, beforeUpload, customUpload, devicePic };
  89 + },
  90 + });
  91 +</script>
  92 +<style lang="less" scoped>
  93 + .step1 {
  94 + &-form {
  95 + width: 450px;
  96 + margin: 0 auto;
  97 + }
  98 +
  99 + h3 {
  100 + margin: 0 0 12px;
  101 + font-size: 16px;
  102 + line-height: 32px;
  103 + color: @text-color;
  104 + }
  105 +
  106 + h4 {
  107 + margin: 0 0 4px;
  108 + font-size: 14px;
  109 + line-height: 22px;
  110 + color: @text-color;
  111 + }
  112 +
  113 + p {
  114 + color: @text-color;
  115 + }
  116 + }
  117 +
  118 + .pay-select {
  119 + width: 20%;
  120 + }
  121 +
  122 + .pay-input {
  123 + width: 70%;
  124 + }
  125 +</style>
  1 +<template>
  2 + <div class="step2">
  3 + <a-alert message="确认转账后,资金将直接打入对方账户,无法退回。" show-icon />
  4 + <a-descriptions :column="1" class="mt-5">
  5 + <a-descriptions-item label="付款账户"> ant-design@alipay.com </a-descriptions-item>
  6 + <a-descriptions-item label="收款账户"> test@example.com </a-descriptions-item>
  7 + <a-descriptions-item label="收款人姓名"> Vben </a-descriptions-item>
  8 + <a-descriptions-item label="转账金额"> 500元 </a-descriptions-item>
  9 + </a-descriptions>
  10 + <a-divider />
  11 + <BasicForm @register="register" />
  12 + </div>
  13 +</template>
  14 +<script lang="ts">
  15 + import { defineComponent } from 'vue';
  16 + import { BasicForm, useForm } from '/@/components/Form';
  17 + import { step2Schemas } from './data';
  18 + import { Alert, Divider, Descriptions } from 'ant-design-vue';
  19 +
  20 + export default defineComponent({
  21 + components: {
  22 + BasicForm,
  23 + [Alert.name]: Alert,
  24 + [Divider.name]: Divider,
  25 + [Descriptions.name]: Descriptions,
  26 + [Descriptions.Item.name]: Descriptions.Item,
  27 + },
  28 + emits: ['next', 'prev'],
  29 + setup(_, { emit }) {
  30 + const [register, { validate, setProps }] = useForm({
  31 + labelWidth: 80,
  32 + schemas: step2Schemas,
  33 + actionColOptions: {
  34 + span: 14,
  35 + },
  36 + resetButtonOptions: {
  37 + text: '上一步',
  38 + },
  39 + submitButtonOptions: {
  40 + text: '提交',
  41 + },
  42 + resetFunc: customResetFunc,
  43 + submitFunc: customSubmitFunc,
  44 + });
  45 +
  46 + async function customResetFunc() {
  47 + emit('prev');
  48 + }
  49 +
  50 + async function customSubmitFunc() {
  51 + try {
  52 + const values = await validate();
  53 + setProps({
  54 + submitButtonOptions: {
  55 + loading: true,
  56 + },
  57 + });
  58 + setTimeout(() => {
  59 + setProps({
  60 + submitButtonOptions: {
  61 + loading: false,
  62 + },
  63 + });
  64 + emit('next', values);
  65 + }, 1500);
  66 + } catch (error) {}
  67 + }
  68 +
  69 + return { register };
  70 + },
  71 + });
  72 +</script>
  73 +<style lang="less" scoped>
  74 + .step2 {
  75 + width: 450px;
  76 + margin: 0 auto;
  77 + }
  78 +</style>
  1 +<template>
  2 + <div class="step3">
  3 + <a-result status="success" title="操作成功" sub-title="预计两小时内到账">
  4 + <template #extra>
  5 + <a-button type="primary" @click="redo"> 再转一笔 </a-button>
  6 + <a-button> 查看账单 </a-button>
  7 + </template>
  8 + </a-result>
  9 + <div class="desc-wrap">
  10 + <a-descriptions :column="1" class="mt-5">
  11 + <a-descriptions-item label="付款账户"> ant-design@alipay.com </a-descriptions-item>
  12 + <a-descriptions-item label="收款账户"> test@example.com </a-descriptions-item>
  13 + <a-descriptions-item label="收款人姓名"> Vben </a-descriptions-item>
  14 + <a-descriptions-item label="转账金额"> 500元 </a-descriptions-item>
  15 + </a-descriptions>
  16 + </div>
  17 + </div>
  18 +</template>
  19 +<script lang="ts">
  20 + import { defineComponent } from 'vue';
  21 + import { Result, Descriptions } from 'ant-design-vue';
  22 + export default defineComponent({
  23 + components: {
  24 + [Result.name]: Result,
  25 + [Descriptions.name]: Descriptions,
  26 + [Descriptions.Item.name]: Descriptions.Item,
  27 + },
  28 + emits: ['redo'],
  29 + setup(_, { emit }) {
  30 + return {
  31 + redo: () => {
  32 + emit('redo');
  33 + },
  34 + };
  35 + },
  36 + });
  37 +</script>
  38 +<style lang="less" scoped>
  39 + .step3 {
  40 + width: 600px;
  41 + margin: 0 auto;
  42 + }
  43 +
  44 + .desc-wrap {
  45 + padding: 24px 40px;
  46 + margin-top: 24px;
  47 + background-color: @background-color-light;
  48 + }
  49 +</style>
  1 +<template>
  2 + <PageWrapper
  3 + title="分步表单"
  4 + contentBackground
  5 + content=" 将一个冗长或用户不熟悉的表单任务分成多个步骤,指导用户完成。"
  6 + contentClass="p-4"
  7 + >
  8 + <div class="step-form-form">
  9 + <a-steps :current="current">
  10 + <a-step title="填写转账信息" />
  11 + <a-step title="确认转账信息" />
  12 + <a-step title="完成" />
  13 + </a-steps>
  14 + </div>
  15 + <div class="mt-5">
  16 + <Step1 @next="handleStep1Next" v-show="current === 0" />
  17 + <Step2
  18 + @prev="handleStepPrev"
  19 + @next="handleStep2Next"
  20 + v-show="current === 1"
  21 + v-if="initSetp2"
  22 + />
  23 + <Step3 v-show="current === 2" @redo="handleRedo" v-if="initSetp3" />
  24 + </div>
  25 + </PageWrapper>
  26 +</template>
  27 +<script lang="ts">
  28 + import { defineComponent, ref, reactive, toRefs } from 'vue';
  29 + import Step1 from './DeviceStep1.vue';
  30 + import Step2 from './DeviceStep2.vue';
  31 + import Step3 from './DeviceStep3.vue';
  32 + import { PageWrapper } from '/@/components/Page';
  33 + import { Steps } from 'ant-design-vue';
  34 +
  35 + export default defineComponent({
  36 + name: 'FormStepPage',
  37 + components: {
  38 + Step1,
  39 + Step2,
  40 + Step3,
  41 + PageWrapper,
  42 + [Steps.name]: Steps,
  43 + [Steps.Step.name]: Steps.Step,
  44 + },
  45 + setup() {
  46 + const current = ref(0);
  47 +
  48 + const state = reactive({
  49 + initSetp2: false,
  50 + initSetp3: false,
  51 + });
  52 +
  53 + function handleStep1Next(step1Values: any) {
  54 + current.value++;
  55 + state.initSetp2 = true;
  56 + console.log(step1Values);
  57 + }
  58 +
  59 + function handleStepPrev() {
  60 + current.value--;
  61 + }
  62 +
  63 + function handleStep2Next(step2Values: any) {
  64 + current.value++;
  65 + state.initSetp3 = true;
  66 + console.log(step2Values);
  67 + }
  68 +
  69 + function handleRedo() {
  70 + current.value = 0;
  71 + state.initSetp2 = false;
  72 + state.initSetp3 = false;
  73 + }
  74 +
  75 + return {
  76 + current,
  77 + handleStep1Next,
  78 + handleStep2Next,
  79 + handleRedo,
  80 + handleStepPrev,
  81 + ...toRefs(state),
  82 + };
  83 + },
  84 + });
  85 +</script>
  86 +<style lang="less" scoped>
  87 + .step-form-content {
  88 + padding: 24px;
  89 + background-color: @component-background;
  90 + }
  91 +
  92 + .step-form-form {
  93 + width: 750px;
  94 + margin: 0 auto;
  95 + }
  96 +</style>
  1 +import { FormSchema } from '/@/components/Form';
  2 +import {findDictItemByCode} from "/@/api/system/dict";
  3 +
  4 +export const step1Schemas: FormSchema[] = [
  5 + {
  6 + field: 'icon',
  7 + label: '设备图片: ',
  8 + slot: 'iconSelect',
  9 + component: 'Input',
  10 + },
  11 + {
  12 + field: 'name',
  13 + label: '设备名称',
  14 + required: true,
  15 + component:'Input',
  16 + componentProps:{
  17 + maxLength:30
  18 + }
  19 + },
  20 + {
  21 + field: 'deviceType',
  22 + label: '设备类型',
  23 + required: true,
  24 + component: 'ApiSelect',
  25 + componentProps: {
  26 + api:findDictItemByCode,
  27 + params:{
  28 + dictCode:"device_type"
  29 + },
  30 + labelField:'itemText',
  31 + valueField:'itemValue',
  32 + },
  33 + },
  34 + {
  35 + field: 'label',
  36 + label: '设备标签',
  37 + component:'Input',
  38 + componentProps:{
  39 + maxLength:255
  40 + }
  41 + },
  42 + {
  43 + label: '备注',
  44 + field: 'remark',
  45 + component: 'InputTextArea',
  46 + }
  47 +];
  48 +
  49 +export const step2Schemas: FormSchema[] = [
  50 + {
  51 + field: 'pwd',
  52 + component: 'InputPassword',
  53 + label: '支付密码',
  54 + required: true,
  55 + defaultValue: '123456',
  56 + },
  57 +];