Przeglądaj źródła

feat: 新建违规记录

wyf 7 miesięcy temu
rodzic
commit
d17d4382b8

+ 8 - 1
src/api/traffic-violation/traffic-act.ts

@@ -16,6 +16,13 @@ export function getActTableList(data: QueryPageRequest<ActTableQuery>) {
   });
 }
 
+export function getActDataDetail(id: number) {
+  return http.request<ActTableData>({
+    url: `/trafficViolation/queryTrafficViolation?id=${id}`,
+    method: 'post',
+  });
+}
+
 export function noticeActData(violationIds: number[] | number) {
   return http.request({
     url: `/trafficViolation/updateTrafficViolationNotice?trafficViolationIds=${violationIds}`,
@@ -40,7 +47,7 @@ export function updateActData(data: UpdateActQuery) {
 
 export function createActData(data: CreateActQuery) {
   return http.request({
-    url: '/trafficViolation/addTrafficViolation',
+    url: '/trafficViolation/saveTrafficViolation',
     method: 'post',
     data,
   });

+ 12 - 1
src/components/formItems/selectableInput/SelectableInput.vue

@@ -7,7 +7,13 @@
       clearable
     >
       <template #prepend>
-        <el-select v-model="selectValue" style="width: 100px" value-key="value" :validate-event="false">
+        <el-select
+          v-model="selectValue"
+          style="width: 100px"
+          value-key="value"
+          :validate-event="false"
+          @change="modelValue = ''"
+        >
           <el-option
             v-for="item in options"
             :key="item.value"
@@ -45,10 +51,15 @@
       value: modelValue.value,
     };
   }
+  function clearValue() {
+    selectValue.value = props.options[0];
+    modelValue.value = undefined;
+  }
 
   defineExpose({
     setValue,
     getValue,
+    clearValue,
   });
 </script>
 

+ 1 - 1
src/views/traffic/regulation/utils.ts

@@ -1,5 +1,5 @@
 import { uploadFileApi, UPLOAD_BIZ_TYPE } from '@/api/minio';
-import type { FileItem } from '@/views/disaster/types';
+import type { FileItem } from './types';
 
 export function stringToArray(str?: string): number[] | undefined {
   if (!str) return undefined;

+ 24 - 5
src/views/traffic/violation/act/Act.vue

@@ -19,11 +19,16 @@
               <SelectableInput ref="selectableInputRef" :options="ACT_TABLE_SEARCH_OPTIONS" />
               <div class="select-box--item">
                 <span>违规类型:</span>
-                <el-select v-model="searchData.violationType" placeholder="请选择违规类型" class="select-box--select">
+                <el-select
+                  v-model="searchData.violationType"
+                  placeholder="请选择违规类型"
+                  class="select-box--select"
+                  clearable
+                >
                   <el-option
                     v-for="item in ACT_VIOLATION_TYPE_OPTIONS"
                     :key="item.value"
-                    :value="item"
+                    :value="item.value"
                     :label="item.label"
                     :disabled="item.disabled"
                   />
@@ -31,11 +36,16 @@
               </div>
               <div class="select-box--item">
                 <span>通知状态:</span>
-                <el-select v-model="searchData.isNotice" placeholder="请选择通知状态" class="select-box--select">
+                <el-select
+                  v-model="searchData.isNotice"
+                  placeholder="请选择通知状态"
+                  class="select-box--select"
+                  clearable
+                >
                   <el-option
                     v-for="item in ACT_NOTICE_STATE_OPTIONS"
                     :key="item.value"
-                    :value="item"
+                    :value="item.value"
                     :label="item.label"
                     :disabled="item.disabled"
                   />
@@ -54,7 +64,7 @@
             </section>
             <section class="search-btn">
               <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="">重置</el-button>
+              <el-button @click="handleReset">重置</el-button>
               <el-button @click="">导出</el-button>
             </section>
           </div>
@@ -168,6 +178,15 @@
     getTabelData();
   }
 
+  function handleReset() {
+    selectableInputRef.value?.clearValue();
+    searchData.carNumber = undefined;
+    searchData.violateName = undefined;
+    searchData.deptName = undefined;
+    searchData.violationType = undefined;
+    searchData.searchTime = undefined;
+  }
+
   // 表格
   const basicTableRef = ref<InstanceType<typeof BasicTable>>();
 

+ 120 - 3
src/views/traffic/violation/act/components/ActCreate.vue

@@ -1,7 +1,124 @@
 <template>
-  <div> </div>
+  <main class="safety-platform-container__main">
+    <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig">
+      <template #violateType>
+        <el-select v-model="ruleFormData.violateType" placeholder="请选择违规类型">
+          <el-option
+            v-for="item in ACT_VIOLATION_TYPE_OPTIONS"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          ></el-option>
+        </el-select>
+        <div v-if="ruleFormData.violateType === ACT_VIOLATION_TYPE.SPEEDING" style="margin-top: 10px; width: 100%">
+          <el-form-item
+            label-width="auto"
+            label="车速:"
+            prop="speed"
+            :rules="[
+              {
+                required: true,
+                validator: (_rule, value, callback) => {
+                  if (!value) return callback(new Error('请输入车速'));
+                  if (value > 999) {
+                    callback(new Error('超过车速时速上限'));
+                  } else {
+                    callback();
+                  }
+                },
+                trigger: 'blur',
+              },
+            ]"
+          >
+            <el-input v-model.number="ruleFormData.speed" placeholder="请输入车速" type="number" min="0">
+              <template #suffix>km/h</template>
+            </el-input>
+          </el-form-item>
+        </div>
+      </template>
+      <template #capturePhotos>
+        <UploadImages :maxCount="9" ref="uploadImagesRef" @upload-success="handleUploadChange" />
+      </template>
+    </BasicForm>
+  </main>
+  <footer class="safety-platform-container__footer">
+    <el-button @click="router.back()">取消</el-button>
+    <el-button type="primary" @click="handleSubmit">提交</el-button>
+  </footer>
+  <UploadLoading :form-loading="formLoading" v-if="formLoading" />
 </template>
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+  import { onMounted, ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import BasicForm from '@/components/BasicForm.vue';
+  import UploadLoading from '@/components/UploadLoading.vue';
+  import UploadImages from '@/views/disaster/disaster-control/src/components/UploadImages.vue';
+  import { useFormConfigHook } from '@/hooks/useFormConfigHook';
+  import { useUserInfoHook } from '@/views/disaster/hooks';
+  import { createActData } from '@/api/traffic-violation/traffic-act';
+  import { CreateActRuleForm, CreateActQuery } from '../types';
+  import { ACT_FORM_CONFIG, ACT_FORM_DATA, ACT_FORM_RULES } from '../configs/form';
+  import { ACT_VIOLATION_TYPE, ACT_VIOLATION_TYPE_OPTIONS } from '../constants';
+  import { formatImageList } from '../utils';
 
-<style scoped></style>
+  const { realname } = useUserInfoHook();
+
+  const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
+    useFormConfigHook<CreateActRuleForm>(ACT_FORM_CONFIG, ACT_FORM_DATA, ACT_FORM_RULES);
+
+  const basicFormRef = ref<InstanceType<typeof BasicForm>>();
+  const uploadImagesRef = ref<InstanceType<typeof UploadImages>>();
+  const handleValidate = async () => {
+    if (!basicFormRef.value) return;
+    const res = await basicFormRef.value.validateForm();
+    return res;
+  };
+
+  const getFormData = async () => {
+    if (ruleFormData.violateType !== ACT_VIOLATION_TYPE.SPEEDING) {
+      ruleFormData.speed = null;
+    }
+    cloneRuleFormData();
+    const res: CreateActQuery = {
+      createSource: 1,
+      ...ruleFormData,
+      violateType: ruleFormData.violateType!,
+      capturePhotos: JSON.stringify(await formatImageList(ruleFormData.capturePhotos)),
+    };
+
+    return res;
+  };
+
+  const formLoading = ref(false);
+  const router = useRouter();
+  const handleSubmit = async () => {
+    const res = await handleValidate();
+    if (!res) return;
+    try {
+      formLoading.value = true;
+      const params = await getFormData();
+      await createActData(params);
+      ElMessage.success('创建成功');
+      router.back();
+    } catch (e) {
+      console.log(e);
+    } finally {
+      formLoading.value = false;
+    }
+  };
+  const handleUploadChange = () => {
+    ruleFormData.capturePhotos = uploadImagesRef.value!.getUploadedImages();
+  };
+
+  onMounted(() => {
+    ruleFormData.creatName = realname;
+    cloneRuleFormData();
+    beforeRouteLeave();
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+</style>

+ 110 - 3
src/views/traffic/violation/act/components/ActEdit.vue

@@ -1,7 +1,114 @@
 <template>
-  <div> </div>
+  <main class="safety-platform-container__main">
+    <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig">
+      <template #violateType>
+        <el-select v-model="ruleFormData.violateType" placeholder="请选择违规类型">
+          <el-option
+            v-for="item in ACT_VIOLATION_TYPE_OPTIONS"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          ></el-option>
+        </el-select>
+        <div v-if="ruleFormData.violateType === ACT_VIOLATION_TYPE.SPEEDING" style="margin-top: 10px; width: 100%">
+          <el-form-item
+            label-width="auto"
+            label="车速:"
+            prop="speed"
+            :rules="[
+              {
+                required: true,
+                validator: (_rule, value, callback) => {
+                  console.log(value);
+                  if (!value) return callback(new Error('请输入车速'));
+                  if (value > 999) {
+                    callback(new Error('超过车速时速上限'));
+                  } else {
+                    callback();
+                  }
+                },
+                trigger: 'blur',
+              },
+            ]"
+          >
+            <el-input v-model.number="ruleFormData.speed" placeholder="请输入车速" type="number" min="0">
+              <template #suffix>km/h</template>
+            </el-input>
+          </el-form-item>
+        </div>
+      </template>
+      <template #capturePhotos>
+        <UploadImages
+          :maxCount="9"
+          ref="uploadImagesRef"
+          :image-list="recordImageList"
+          @upload-success="handleUploadChange"
+        />
+      </template>
+    </BasicForm>
+  </main>
+  <footer class="safety-platform-container__footer">
+    <el-button @click="router.back()">取消</el-button>
+    <el-button type="primary" @click="handleSubmit">提交</el-button>
+  </footer>
+  <UploadLoading :form-loading="formLoading" v-if="formLoading" />
 </template>
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+  import { onMounted, ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { ElMessage } from 'element-plus';
+  import BasicForm from '@/components/BasicForm.vue';
+  import UploadLoading from '@/components/UploadLoading.vue';
+  import UploadImages from '@/views/disaster/disaster-control/src/components/UploadImages.vue';
+  import { useFormConfigHook } from '@/hooks/useFormConfigHook';
+  import { useUserInfoHook } from '@/views/disaster/hooks';
+  import { getActDataDetail, updateActData } from '@/api/traffic-violation/traffic-act';
+  import { CreateActRuleForm, CreateActQuery, ActTableData } from '../types';
+  import { ACT_FORM_CONFIG, ACT_FORM_DATA, ACT_FORM_RULES } from '../configs/form';
+  import { ACT_VIOLATION_TYPE, ACT_VIOLATION_TYPE_OPTIONS } from '../constants';
 
-<style scoped></style>
+  const props = defineProps<{
+    id: number;
+  }>();
+
+  const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
+    useFormConfigHook<CreateActRuleForm>(ACT_FORM_CONFIG, ACT_FORM_DATA, ACT_FORM_RULES);
+
+  const basicFormRef = ref<InstanceType<typeof BasicForm>>();
+  const uploadImagesRef = ref<InstanceType<typeof UploadImages>>();
+
+  const handleValidate = async () => {
+    if (!basicFormRef.value) return;
+    const res = await basicFormRef.value.validateForm();
+    return res;
+  };
+
+  const actDetail = ref<ActTableData>();
+  const getDetail = async () => {
+    actDetail.value = await getActDataDetail(props.id);
+  };
+
+  const getFormData = async () => {};
+
+  const formLoading = ref(false);
+  const router = useRouter();
+  const handleSubmit = async () => {
+    const res = await handleValidate();
+    if (!res) return;
+  };
+
+  const handleUploadChange = () => {
+    ruleFormData.capturePhotos = uploadImagesRef.value!.getUploadedImages();
+  };
+
+  onMounted(async () => {
+    await getDetail();
+    cloneRuleFormData();
+    beforeRouteLeave();
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-details-layout.scss' as *;
+</style>

+ 79 - 0
src/views/traffic/violation/act/configs/form.ts

@@ -0,0 +1,79 @@
+import { FormConfig } from '@/types/basic-form';
+
+export const ACT_FORM_CONFIG: FormConfig[] = [
+  {
+    prop: 'carNumber',
+    label: '车牌号:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入违规车辆车牌号码',
+      maxlength: 8,
+      showWordLimit: false,
+    },
+  },
+  {
+    prop: 'violateType',
+    slot: 'violateType',
+    label: '违规类型:',
+  },
+  {
+    prop: 'violateLocation',
+    label: '违规地点:',
+    component: 'ElInput',
+    componentProps: {
+      placeholder: '请输入违规地点',
+    },
+  },
+  {
+    prop: 'captureTime',
+    label: '抓拍时间:',
+    component: 'ElDatePicker',
+    componentProps: {
+      placeholder: '请选择抓拍时间',
+      type: 'datetime',
+      format: 'YYYY-MM-DD HH:mm',
+      dateFormat: 'MMM DD, YYYY',
+      timeFormat: 'HH:mm',
+      valueFormat: 'YYYY-MM-DD HH:mm',
+      disabledDate: (time) => time.getTime() > Date.now(),
+    },
+  },
+  {
+    prop: 'capturePhotos',
+    label: '抓拍图片:',
+    slot: 'capturePhotos',
+  },
+  {
+    label: '备注:',
+    prop: 'remark',
+    component: 'ElInput',
+    componentProps: {
+      type: 'textarea',
+      rows: 5,
+    },
+  },
+  {
+    label: '创建人:',
+    prop: 'creatName',
+    component: 'ElInput',
+    componentProps: {
+      disabled: true,
+    },
+  },
+];
+
+export const ACT_FORM_DATA = {
+  carNumber: '',
+  violateType: null,
+  captureTime: '',
+  capturePhotos: [],
+  violateLocation: '',
+  remark: '',
+  creatName: '',
+};
+
+export const ACT_FORM_RULES = {
+  carNumber: [{ required: true, message: '车牌号格式错误', trigger: 'blur' }],
+  violateType: [{ required: true, message: '请选择违规类型', trigger: 'change' }],
+  captureTime: [{ required: true, message: '请选择抓拍时间', trigger: 'change' }],
+};

+ 3 - 3
src/views/traffic/violation/act/constants.ts

@@ -31,17 +31,17 @@ export const ACT_VIOLATION_TYPE_LABEL = {
 export const ACT_VIOLATION_TYPE_OPTIONS: SelectOption[] = [
   {
     label: ACT_VIOLATION_TYPE_LABEL[ACT_VIOLATION_TYPE.SPEEDING],
-    value: [ACT_VIOLATION_TYPE.SPEEDING],
+    value: ACT_VIOLATION_TYPE.SPEEDING,
     disabled: false,
   },
   {
     label: ACT_VIOLATION_TYPE_LABEL[ACT_VIOLATION_TYPE.WRONG_WAY_DRIVING],
-    value: [ACT_VIOLATION_TYPE.WRONG_WAY_DRIVING],
+    value: ACT_VIOLATION_TYPE.WRONG_WAY_DRIVING,
     disabled: false,
   },
   {
     label: ACT_VIOLATION_TYPE_LABEL[ACT_VIOLATION_TYPE.ILLEGAL_PARKING],
-    value: [ACT_VIOLATION_TYPE.ILLEGAL_PARKING],
+    value: ACT_VIOLATION_TYPE.ILLEGAL_PARKING,
     disabled: false,
   },
 ];

+ 13 - 14
src/views/traffic/violation/act/types.ts

@@ -1,9 +1,7 @@
-export interface FileItem {
-  fileId: number;
-  fileName: string;
-  fileType: string;
-  fileSize: string;
-  fileUrl?: string;
+export interface ImageItem {
+  url: string;
+  name?: string;
+  size?: number;
   file?: File;
 }
 
@@ -42,29 +40,30 @@ export interface ActTableData {
 
 export interface CreateActRuleForm {
   carNumber: string;
-  violateType: number; //1-超速 2-逆行 3-违规停车
-  speed?: number;
+  violateType: number | null; //1-超速 2-逆行 3-违规停车
+  speed?: number | null;
   violateLocation?: string;
   captureTime: string;
-  capturePhotos?: FileItem[];
+  capturePhotos?: ImageItem[];
   remark?: string;
   creatName?: string;
 }
 
 export interface CreateActQuery {
+  createSource: 1;
   carNumber: string;
   violateType: number; //1-超速 2-逆行 3-违规停车
-  speed?: number;
+  speed?: number | null;
   violateLocation?: string;
   captureTime: string;
-  capturePhotos?: FileItem[];
+  capturePhotos?: string;
   remark?: string;
   creatName?: string;
 }
 
-export interface UpdateActRuleForm extends CreateActRuleForm {
-  id: number;
-}
+// export interface UpdateActRuleForm extends CreateActRuleForm {
+//   id: number;
+// }
 export interface UpdateActQuery extends CreateActQuery {
   id: number;
 }

+ 37 - 0
src/views/traffic/violation/act/utils.ts

@@ -0,0 +1,37 @@
+import { uploadFileApi, UPLOAD_BIZ_TYPE } from '@/api/minio';
+import type { ImageItem } from './types';
+
+export function stringToArray(str?: string): number[] | undefined {
+  if (!str) return undefined;
+  return JSON.parse('[' + str + ']');
+}
+
+export function unformatImage(file?: string) {
+  if (!file) return undefined;
+  const fileData: ImageItem[] = JSON.parse(file);
+  return fileData;
+}
+
+const formatImage = async (data: ImageItem) => {
+  if (!data.file) return data;
+  const fileName = data.file.name;
+  const fileSize = data.size;
+  const res = await uploadFileApi({ bizType: UPLOAD_BIZ_TYPE.ATTACHMENT, fileName, file: data.file });
+  const fileUrl = res.url;
+  return {
+    fileName,
+    fileSize,
+    fileUrl,
+  };
+};
+
+export const formatImageList = async (data: ImageItem[] | undefined) => {
+  if (!data || data.length === 0) return null;
+  const res = await Promise.all(
+    data.map(async (item) => {
+      const res = await formatImage(item);
+      return res;
+    }),
+  );
+  return res;
+};