瀏覽代碼

feat(safetyManagement): 新增设备编码管理功能

- 添加设备编码列表页面,支持查询和操作
- 实现设备编码的新增和编辑功能
- 优化学校场地选择的接口调用
- 修复部分组件的样式问题
zhangtao 1 月之前
父節點
當前提交
3864fc52e4

+ 4 - 4
.env.development

@@ -7,14 +7,14 @@ VITE_PUBLIC_PATH = /
 
 
 # 跨域代理,您可以配置多个 ,请注意,没有换行符
-# VITE_PROXY = [["/jeecgboot","http://192.168.1.34:8080/jeecg-boot"],["/upload","http://192.168.1.34:8080/jeecg-boot"]]
-VITE_PROXY = [["/jeecgboot","http://192.168.0.11:8080/jeecg-boot"],["/upload","http://192.168.0.11:8080/upload"]]
+VITE_PROXY = [["/jeecgboot","http://192.168.1.34:8080/jeecg-boot"],["/upload","http://192.168.1.34:8080/jeecg-boot"]]
+# VITE_PROXY = [["/jeecgboot","http://192.168.0.11:8080/jeecg-boot"],["/upload","http://192.168.0.11:8080/upload"]]
 # VITE_PROXY = [["/jeecgboot","http://192.168.1.253:8080/jeecg-boot"],["/upload","http://192.168.1.253:8080/upload"]]
 # VITE_PROXY = [["/jeecgboot","http://192.168.1.166:8080/jeecg-boot"],["/upload","http://192.168.1.166:8080/upload"]]
 
 #后台接口全路径地址(必填)
-# VITE_GLOB_DOMAIN_URL=http://192.168.1.34:8080/jeecg-boot #//黄、
-VITE_GLOB_DOMAIN_URL=http://192.168.0.11:8080/jeecg-boot  #李
+VITE_GLOB_DOMAIN_URL=http://192.168.1.34:8080/jeecg-boot #//黄、
+# VITE_GLOB_DOMAIN_URL=http://192.168.0.11:8080/jeecg-boot  #李
 # VITE_GLOB_DOMAIN_URL=http://192.168.1.253:8080/jeecg-boot  #张
 # VITE_GLOB_DOMAIN_URL=http://192.168.1.166:8080/jeecg-boot  #张
 

+ 6 - 5
src/components/ZtCustomTable/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <BasicTable @register="registerTable" @edit-change="onEditChange" :pagination="false" :data-source="modelValue" rowKey="id">
+  <BasicTable @register="registerTable" @edit-change="onEditChange" :pagination="false" autoCreateKey :data-source="modelValue" :rowKey="handleKey">
     <template #headerCell="{ column }">
       <template v-if="column.dataIndex == 'operation' && showAction">
         <div class="text-18px cursor-pointer" @click="handleAdd">
@@ -24,8 +24,8 @@
   import { PlusCircleOutlined, MinusCircleOutlined } from '@ant-design/icons-vue';
   import { BasicTable, BasicColumn } from '/@/components/Table';
   import { useListPage } from '/@/hooks/system/useListPage';
-  import _ from 'lodash-es';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import dayjs from 'dayjs';
   interface Props {
     tableColumn: BasicColumn[];
     value: any;
@@ -51,8 +51,6 @@
       return props.value;
     },
     set(val) {
-      // console.log(val, '重新设置');
-
       emit('update:value', val);
     },
   });
@@ -98,7 +96,7 @@
     const newRow: DataRow = {};
     inputFields.forEach((field) => {
       newRow[String(field)] = null;
-      // newRow['id'] = dayjs().valueOf();
+      newRow['id'] = dayjs().valueOf();
     });
     return newRow;
   };
@@ -114,4 +112,7 @@
       deletIdVaule.value = ([] as string[]).concat(deletIdVaule.value);
     }
   }
+  function handleKey(row) {
+    return row.id;
+  }
 </script>

+ 1 - 1
src/views/businessManagement/courses/courses.data.ts

@@ -150,7 +150,7 @@ export const formSchema: FormSchema[] = [
     componentProps: {
       api: Business,
       labelField: 'name',
-      valueField: 'tenantId',
+      valueField: 'id',
       params: {
         orgCode: 'A01',
         type: 0,

+ 36 - 7
src/views/businessManagement/schoolOpen/index.vue

@@ -13,6 +13,19 @@
           <TypographyTitle :level="4">封面、配套保险、使用须知</TypographyTitle>
           <Divider></Divider>
         </template>
+        <template #tenant="{ model, field }">
+          <ApiSelect
+            :api="Business"
+            labelField="name"
+            valueField="tenantId"
+            :params="{
+              orgCode: userInfo?.orgCode,
+              type: 0,
+            }"
+            v-model:value="model[field]"
+            @change="handleOption"
+          ></ApiSelect>
+        </template>
         <template #ZtCustomTable1="{ model, field }">
           <ZtCustomTable :tableColumn="ScheduleArrangementColums" :showAction="getIsMerchant" v-model:value="model[field]" :count="3"></ZtCustomTable>
         </template>
@@ -32,14 +45,16 @@
 </template>
 <script lang="ts" setup name="business-management-schoolOpen">
   import { TypographyTitle, Divider } from 'ant-design-vue';
-  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { BasicForm, useForm, ApiSelect } from '/@/components/Form/index';
   import ZtCustomTable from '/@/components/ZtCustomTable/index.vue';
+  import { Business } from '../gymnasiumBag/gymnasiumBag.api';
   import { useUserStore } from '/@/store/modules/user';
   import { formSchema, ScheduleArrangementColums } from './schoolOpen.data';
   import { getDetails, saveOrUpdate } from './schoolOpen.api';
   import { ref, unref } from 'vue';
   import { useRouter } from 'vue-router';
   import { storeToRefs } from 'pinia';
+  const { userInfo } = storeToRefs(useUserStore());
   const router = useRouter();
   const { getIsMerchant } = storeToRefs(useUserStore());
   const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate, getFieldsValue }] = useForm({
@@ -90,6 +105,26 @@
   async function getData() {
     const res = await getDetails({ orgCode: useUserStore().userInfo?.orgCode });
     if (!res.teachingDay) return;
+    handleCommonSet(res);
+    console.log(JSON.parse(res.noTeachingDay));
+  }
+  getData();
+
+  function handleClick() {
+    router.push({ path: '/informationManagement/teachorNoteach' });
+  }
+  async function handleOption(options) {
+    const res = await Business({ orgCode: useUserStore().userInfo?.orgCode, type: 0 });
+    const orgCode = res.find((it) => it.tenantId == options).orgCode;
+    if (orgCode) {
+      const res = await getDetails({ orgCode: orgCode });
+      if (!res.noTeachingDay) return;
+
+      handleCommonSet(res);
+    }
+  }
+
+  function handleCommonSet(res) {
     setFieldsValue({
       ...res,
       insureIds: res.insureIds.split(','),
@@ -102,11 +137,5 @@
         return { ...it, time: [it.startTime, it.endTime] };
       }),
     });
-    console.log(JSON.parse(res.noTeachingDay));
-  }
-  getData();
-
-  function handleClick() {
-    router.push({ path: '/informationManagement/teachorNoteach' });
   }
 </script>

+ 2 - 10
src/views/businessManagement/schoolOpen/schoolOpen.data.ts

@@ -1,6 +1,6 @@
 import { BasicColumn, FormSchema } from '/@/components/Table';
 import { getInsureList } from '/@/api/common/api';
-import { Business } from '../gymnasiumBag/gymnasiumBag.api';
+
 import { useUserStore } from '/@/store/modules/user';
 import { storeToRefs } from 'pinia';
 const { getIsMerchant, userInfo } = storeToRefs(useUserStore());
@@ -11,15 +11,7 @@ export const formSchema: FormSchema[] = [
     component: 'ApiSelect',
     required: true,
     labelWidth: 120,
-    componentProps: {
-      api: Business,
-      labelField: 'name',
-      valueField: 'tenantId',
-      params: {
-        orgCode: userInfo.value?.orgCode,
-        type: 0,
-      },
-    },
+    slot: 'tenant',
     colProps: {
       span: 14,
       xs: 24,

+ 3 - 0
src/views/informationManagement/cUserInfo/cUserInfo.data.ts

@@ -27,6 +27,9 @@ export const columns: BasicColumn[] = [
     title: '是否实名',
     align: 'center',
     dataIndex: 'realNameStatus',
+    customRender: ({ record }) => {
+      return record.realNameStatus == 1 ? '已实名' : '未实名';
+    },
   },
   {
     title: '家庭成员数(包括自己)',

+ 1 - 1
src/views/informationManagement/shopInfo/index.vue

@@ -85,7 +85,7 @@
     setFieldsValue({
       ...res,
       categoryId: res.categoryId ? res.categoryId.split(',') : null,
-      Time: res.Time ? [res.startTime, res.endTime].join(',') : null,
+      Time: res.startTime ? [res.startTime, res.endTime].join(',') : null,
       down: vr ? vr[vr.length - 1] : null,
       up: vr ? vr[4] : null,
       before: vr ? vr[0] : null,

+ 4 - 3
src/views/informationManagement/teachorNoteach/index.vue

@@ -32,7 +32,7 @@
     dayCellClassNames: (arg) => {
       const { date } = arg;
       const day = date.getDay();
-      if (day === 0 || day === 6) {
+      if ((day === 0 || day === 6) && isNonTeachingDay(date)) {
         return ['weekend-cell'];
       }
       // const isPast = date < today && !dayjs(date).isSame(dayjs(today), 'day');
@@ -44,14 +44,15 @@
       if (isNonTeachingDay(date)) {
         return ['non-teaching-day'];
       }
+
       return [];
     },
     select: handleClick,
   });
   function handleClick(selectInfo) {
     const clickedDate = selectInfo.start;
-    const day = dayjs(clickedDate).day();
-    if (isPast(clickedDate) || day == 0 || day == 6) {
+    // const day = dayjs(clickedDate).day();
+    if (isPast(clickedDate)) {
       //今天之前的日期不允许点击//或者是周末也不允许点击
       return;
     }

+ 29 - 0
src/views/safetyManagement/Turnstile/Turnstile.api.ts

@@ -0,0 +1,29 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/com/AppDevice/list',
+  save = '/com/AppDevice/add',
+  edit = '/com/AppDevice/edit',
+  queryById = '/com/AppDevice/queryById',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });
+
+/**
+ * 保存或者更新
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({ url: url, params });
+};
+
+export const getDetaile = (params) => {
+  return defHttp.get({ url: Api.queryById, params });
+};

+ 187 - 0
src/views/safetyManagement/Turnstile/Turnstile.data.ts

@@ -0,0 +1,187 @@
+import { storeToRefs } from 'pinia';
+import { Business } from '../../businessManagement/gymnasiumBag/gymnasiumBag.api';
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { useUserStore } from '/@/store/modules/user';
+const { userInfo } = storeToRefs(useUserStore());
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '设备编码',
+    align: 'center',
+    dataIndex: 'deviceSerial',
+  },
+  {
+    title: '学校场地',
+    align: 'center',
+    dataIndex: 'siteName',
+  },
+  {
+    title: '编号',
+    align: 'center',
+    dataIndex: 'deviceNo',
+  },
+  {
+    title: '备注',
+    align: 'center',
+    dataIndex: 'remark',
+  },
+  {
+    title: '进/出口',
+    align: 'center',
+    dataIndex: 'deviceType',
+    customRender: ({ record }) => {
+      return record.deviceType === 2 ? '进口' : '出口';
+    },
+  },
+  {
+    title: '更新时间',
+    align: 'center',
+    dataIndex: 'updateTime',
+  },
+];
+//查询数据
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '门店名称',
+    field: 'siteId',
+    component: 'ApiSelect',
+    colProps: { span: 6 },
+    componentProps: {
+      api: Business,
+      labelField: 'name',
+      valueField: 'id',
+      params: {
+        orgCode: userInfo.value?.orgCode,
+        type: 0,
+      },
+    },
+  },
+  {
+    label: '设备编码',
+    field: 'deviceSerial',
+    component: 'Input',
+    colProps: { span: 6 },
+  },
+];
+//表单数据
+export const formSchema: FormSchema[] = [
+  {
+    label: '设备编号',
+    field: 'deviceSerial',
+    component: 'Input',
+    required: true,
+  },
+  {
+    label: '门店名称',
+    field: 'siteId',
+    component: 'ApiSelect',
+    required: true,
+    componentProps: {
+      api: Business,
+      labelField: 'name',
+      valueField: 'id',
+      params: {
+        orgCode: userInfo.value?.orgCode,
+        type: 0,
+      },
+    },
+    show: false,
+  },
+  {
+    label: '编号',
+    field: 'deviceNo',
+    component: 'Select',
+    componentProps: {
+      options: [
+        {
+          label: '1',
+          value: 1,
+        },
+        {
+          label: '2',
+          value: 2,
+        },
+        {
+          label: '3',
+          value: 3,
+        },
+        {
+          label: '4',
+          value: 4,
+        },
+
+        {
+          label: '5',
+          value: 5,
+        },
+
+        {
+          label: '6',
+          value: 6,
+        },
+
+        {
+          label: '7',
+          value: 7,
+        },
+        {
+          label: '8',
+          value: 8,
+        },
+        {
+          label: '9',
+          value: 9,
+        },
+        {
+          label: '10',
+          value: 10,
+        },
+      ],
+    },
+    required: true,
+    show: false,
+  },
+  {
+    label: '进/出口',
+    field: 'deviceType',
+    component: 'Select',
+    componentProps: {
+      options: [
+        {
+          label: '出口',
+          value: 1,
+        },
+        {
+          label: '进口',
+          value: 2,
+        },
+      ],
+    },
+    required: true,
+    show: false,
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'Input',
+    show: false,
+  },
+
+  // TODO 主键隐藏字段,目前写死为ID
+  {
+    label: '',
+    field: 'id',
+    component: 'Input',
+    show: false,
+  },
+];
+
+/**
+ * 流程表单调用这个方法获取formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+  // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
+  return formSchema;
+}

+ 100 - 0
src/views/safetyManagement/Turnstile/components/TurnstileModel.vue

@@ -0,0 +1,100 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1150" @ok="handleSubmit">
+    <BasicForm @register="registerForm"> </BasicForm>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+  import { InputNumber, FormItem, Select } from 'ant-design-vue';
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from '../Turnstile.data';
+  import { saveOrUpdate } from '../Turnstile.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+  // Emits声明
+  const emit = defineEmits(['register', 'success']);
+  const isUpdate = ref(true);
+  const isDetail = ref(false);
+
+  //表单配置
+  const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField, updateSchema }] = useForm({
+    labelWidth: 150,
+    schemas: formSchema,
+    showActionButtonGroup: false,
+    baseColProps: { span: 24 },
+  });
+
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    await resetFields();
+    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
+    isUpdate.value = !!data?.isUpdate;
+    isDetail.value = !!data?.showFooter;
+    if (unref(isUpdate)) {
+      //表单赋值
+      await setFieldsValue({
+        ...data.data,
+      });
+      updateSchema([
+        {
+          field: 'siteId',
+          show: true,
+        },
+        {
+          field: 'deviceNo',
+          show: true,
+        },
+        {
+          field: 'deviceType',
+          show: true,
+        },
+        {
+          field: 'remark',
+          show: true,
+        },
+      ]);
+    }
+    // 隐藏底部时禁用整个表单
+    setProps({ disabled: !data?.showFooter });
+  });
+  //设置标题
+  const title = computed(() => (!unref(isUpdate) ? '添加设备编码' : '绑定场地'));
+  //表单提交事件
+  async function handleSubmit(v) {
+    try {
+      let values = await validate();
+      setModalProps({ confirmLoading: true });
+      //提交表单
+      await saveOrUpdate(values, isUpdate.value);
+      //关闭弹窗
+      closeModal();
+      //刷新列表
+      emit('success');
+    } catch ({ errorFields }) {
+      if (errorFields) {
+        const firstField = errorFields[0];
+        if (firstField) {
+          scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
+        }
+      }
+      return Promise.reject(errorFields);
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  /** 时间和数字输入框样式 */
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+
+  :deep(.ant-calendar-picker) {
+    width: 100%;
+  }
+</style>

+ 101 - 0
src/views/safetyManagement/Turnstile/index.vue

@@ -0,0 +1,101 @@
+<template>
+  <div>
+    <!--引用表格-->
+    <BasicTable @register="registerTable" :rowSelection="rowSelection">
+      <!--插槽:table标题-->
+      <template #tableTitle>
+        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
+      </template>
+      <!--操作栏-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" />
+      </template>
+    </BasicTable>
+
+    <!-- 表单区域 -->
+    <TurnstileModel @register="registerModal" @success="handleSuccess"></TurnstileModel>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, computed, unref } from 'vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import TurnstileModel from './components/TurnstileModel.vue';
+  import { columns, searchFormSchema } from './Turnstile.data';
+  import { list, getDetaile } from './Turnstile.api';
+  const queryParam = reactive<any>({});
+  //注册model
+  const [registerModal, { openModal }] = useModal();
+  //注册table数据
+  const { tableContext } = useListPage({
+    tableProps: {
+      api: list,
+      columns,
+      canResize: false,
+      formConfig: {
+        //labelWidth: 120,
+        schemas: searchFormSchema,
+        autoSubmitOnEnter: true,
+        showAdvancedButton: true,
+        fieldMapToNumber: [],
+        fieldMapToTime: [],
+      },
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: (params) => {
+        return Object.assign(params, queryParam);
+      },
+    },
+  });
+
+  const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
+
+  /**
+   * 新增事件
+   */
+  function handleAdd() {
+    openModal(true, {
+      isUpdate: false,
+      showFooter: true,
+    });
+  }
+  /**
+   * 编辑事件
+   */
+  async function handleEdit(record: Recordable) {
+    const data = await getDetaile({ id: record.id });
+    openModal(true, {
+      data,
+      isUpdate: true,
+      showFooter: true,
+    });
+  }
+  /**
+   * 成功回调
+   */
+  function handleSuccess() {
+    (selectedRowKeys.value = []) && reload();
+  }
+  /**
+   * 操作栏
+   */
+  function getTableAction(record) {
+    return [
+      {
+        label: '绑定场地',
+        onClick: handleEdit.bind(null, record),
+      },
+    ];
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-picker),
+  :deep(.ant-input-number) {
+    width: 100%;
+  }
+</style>