Browse Source

系统管理细节补充

zhangjie 1 year ago
parent
commit
d39b447c6e

+ 7 - 2
src/components/common/select-area/index.vue

@@ -5,6 +5,7 @@
     :clearable="clearable"
     :disabled="disabled"
     :valueType="valueType"
+    :placeholder="placeholder"
     @change="onChange"
   >
   </t-cascader>
@@ -22,6 +23,7 @@ const props = defineProps({
   level: { type: Number, default: 3 },
   clearable: { type: Boolean, default: true },
   disabled: { type: Boolean, default: false },
+  placeholder: { type: String, default: '请选择' },
   valueType: { type: String, default: 'single' },
 });
 
@@ -36,7 +38,7 @@ const initData = () => {
 initData();
 
 const getVal = (list) => {
-  if (!list.length) return;
+  if (!list.length) return ['', '', ''].slice(0, props.level);
   return list.map((item) => areaCodeMap[item]);
 };
 
@@ -55,13 +57,16 @@ const onChange = (val) => {
 const parseCode = (val) => {
   let value = val;
   if (props.valueType !== 'single') {
+    if (val.some((item) => !item)) {
+      return val;
+    }
     value = val.join('_');
   }
 
   const validName = Object.keys(areaCodeMap).find((item) =>
     item.includes(value)
   );
-  if (!validName) return;
+  if (!validName) return val;
 
   const validCode = areaCodeMap[validName].split('_');
   return props.valueType === 'single'

+ 9 - 2
src/components/common/select-customer/index.vue

@@ -30,6 +30,10 @@ const props = defineProps({
   type: { type: String, default: '' },
   typeRequired: { type: Boolean, default: false },
 });
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
 
 const search = async () => {
   if (props.typeRequired && !props.type) return;
@@ -43,7 +47,6 @@ const search = async () => {
 };
 
 const onChange = () => {
-  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
   const selectedData = isMultiple
     ? optionList.value.filter(
         (item) => selected.value && selected.value.includes(item.id)
@@ -66,7 +69,11 @@ watch(
 watch(
   () => props.type,
   (val, oldval) => {
-    if (val !== oldval) search();
+    if (val !== oldval) {
+      search();
+      emit('update:modelValue', null);
+      emit('change', isMultiple ? [] : null);
+    }
   }
 );
 </script>

+ 4 - 1
src/components/common/select-product/index.vue

@@ -28,6 +28,10 @@ const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
   modelValue: { type: [Number, String, Array], default: '' },
 });
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
 
 const search = async () => {
   optionList.value = [];
@@ -39,7 +43,6 @@ const search = async () => {
 };
 
 const onChange = () => {
-  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
   const selectedData = isMultiple
     ? optionList.value.filter(
         (item) => selected.value && selected.value.includes(item.id)

+ 4 - 1
src/components/common/select-role/index.vue

@@ -28,6 +28,10 @@ const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
   modelValue: { type: [Number, String, Array], default: '' },
 });
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
 
 const search = async () => {
   optionList.value = [];
@@ -38,7 +42,6 @@ const search = async () => {
 };
 
 const onChange = () => {
-  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
   const selectedData = isMultiple
     ? optionList.value.filter(
         (item) => selected.value && selected.value.includes(item.id)

+ 9 - 2
src/components/common/select-service-level/index.vue

@@ -30,6 +30,10 @@ const props = defineProps({
   type: { type: String, default: '' },
   typeRequired: { type: Boolean, default: false },
 });
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
 
 const search = async () => {
   if (props.typeRequired && !props.type) return;
@@ -44,7 +48,6 @@ const search = async () => {
 };
 
 const onChange = () => {
-  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
   const selectedData = isMultiple
     ? optionList.value.filter(
         (item) => selected.value && selected.value.includes(item.id)
@@ -67,7 +70,11 @@ watch(
 watch(
   () => props.type,
   (val, oldval) => {
-    if (val !== oldval) search();
+    if (val !== oldval) {
+      search();
+      emit('update:modelValue', null);
+      emit('change', isMultiple ? [] : null);
+    }
   }
 );
 </script>

+ 11 - 3
src/components/common/select-service-unit/index.vue

@@ -16,7 +16,7 @@
 </template>
 
 <script setup name="SelectServiceUnit">
-import { onMounted, ref, useAttrs, watch } from 'vue';
+import { onMounted, ref, useAttrs, watch, computed } from 'vue';
 import { serviceUnitListApi } from '@/api/service-unit';
 
 let optionList = ref([]);
@@ -36,6 +36,11 @@ const props = defineProps({
   },
 });
 
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
+
 const search = async () => {
   optionList.value = [];
 
@@ -47,7 +52,6 @@ const search = async () => {
 };
 
 const onChange = () => {
-  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
   const selectedData = isMultiple
     ? optionList.value.filter(
         (item) => selected.value && selected.value.includes(item.id)
@@ -70,7 +74,11 @@ watch(
 watch(
   () => props.filterParams,
   (val, oldval) => {
-    if (JSON.stringify(val) !== JSON.stringify(oldval)) search();
+    if (JSON.stringify(val) !== JSON.stringify(oldval)) {
+      search();
+      emit('update:modelValue', null);
+      emit('change', isMultiple ? [] : null);
+    }
   },
   {
     deep: true,

+ 9 - 2
src/components/common/select-supplier/index.vue

@@ -30,6 +30,10 @@ const props = defineProps({
   type: { type: String, default: '' },
   typeRequired: { type: Boolean, default: false },
 });
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
 
 const search = async () => {
   if (props.typeRequired && !props.type) return;
@@ -44,7 +48,6 @@ const search = async () => {
 };
 
 const onChange = () => {
-  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
   const selectedData = isMultiple
     ? optionList.value.filter(
         (item) => selected.value && selected.value.includes(item.id)
@@ -67,7 +70,11 @@ watch(
 watch(
   () => props.type,
   (val, oldval) => {
-    if (val !== oldval) search();
+    if (val !== oldval) {
+      search();
+      emit('update:modelValue', null);
+      emit('change', isMultiple ? [] : null);
+    }
   }
 );
 </script>

+ 14 - 1
src/components/common/select-type-user/index.vue

@@ -29,6 +29,10 @@ const props = defineProps({
   modelValue: { type: [Number, String, Array], default: '' },
   type: { type: String, default: '' },
 });
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
 
 const search = async () => {
   if (!props.type) return;
@@ -40,7 +44,6 @@ const search = async () => {
 };
 
 const onChange = () => {
-  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
   const selectedData = isMultiple
     ? optionList.value.filter(
         (item) => selected.value && selected.value.includes(item.id)
@@ -60,4 +63,14 @@ watch(
     selected.value = val;
   }
 );
+watch(
+  () => props.type,
+  (val, oldval) => {
+    if (val !== oldval) {
+      search();
+      emit('update:modelValue', null);
+      emit('change', isMultiple ? [] : null);
+    }
+  }
+);
 </script>

+ 13 - 2
src/views/system/config-manage/checkin-manage/edit-checkin-dialog.vue

@@ -21,7 +21,13 @@
         </t-col>
         <t-col :span="6">
           <t-form-item label="适用考勤对象" name="dingRoleIds">
-            <select-role v-model="formData.dingRoleIds" multiple> </select-role>
+            <select-role
+              v-model="formData.dingRoleIds"
+              :min-collapsed-num="2"
+              :filterable="false"
+              multiple
+            >
+            </select-role>
           </t-form-item>
         </t-col>
         <t-col :span="6">
@@ -45,7 +51,12 @@
         </t-col>
         <t-col :span="6">
           <t-form-item label="审核角色" name="approveRoleIds">
-            <select-role v-model="formData.approveRoleIds" multiple>
+            <select-role
+              v-model="formData.approveRoleIds"
+              :min-collapsed-num="2"
+              :filterable="false"
+              multiple
+            >
             </select-role>
           </t-form-item>
         </t-col>

+ 6 - 5
src/views/system/config-manage/checkin-manage/index.vue

@@ -2,9 +2,7 @@
   <div class="registration-query flex flex-col h-full">
     <SearchForm :fields="fields" :params="params">
       <template #service="{ item, params }">
-        <select-service-unit
-          v-model="params[item.prop]"
-        ></select-service-unit>
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
       </template>
     </SearchForm>
     <div class="flex-1 page-wrap">
@@ -45,6 +43,9 @@
               .join(',')
           }}
         </template>
+        <template #status="{ col, row }">
+          {{ serviceUnitStatusFilter(row[col.colKey]) }}
+        </template>
         <template #create-time="{ col, row }">
           {{ timestampFilter(row[col.colKey]) }}
         </template>
@@ -66,7 +67,7 @@ import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
 import useFetchTable from '@/hooks/useFetchTable';
 import EditCheckinDialog from './edit-checkin-dialog.vue';
 import { checkinQueryApi, checkinDeleteApi } from '@/api/system';
-import { timestampFilter } from '@/utils/filter';
+import { timestampFilter, serviceUnitStatusFilter } from '@/utils/filter';
 
 const showEditCheckinDialog = ref(false);
 const curRow = ref(null);
@@ -102,7 +103,7 @@ const params = reactive({
 
 const columns = [
   { colKey: 'service', title: '服务单元' },
-  { colKey: 'status', title: '发布状态' },
+  { colKey: 'status', title: '发布状态', cell: 'status', width: 100 },
   { colKey: 'name', title: '考勤组名称' },
   { colKey: 'dingObjs', title: '适用考勤对象', cell: 'ding-objs' },
   { colKey: 'supplier', title: '适用供应商' },

+ 55 - 11
src/views/system/config-manage/customer-manage/edit-customer-dialog.vue

@@ -6,6 +6,7 @@
     attach="body"
     :closeOnOverlayClick="false"
     @close="emit('update:visible', false)"
+    @opened="dialogOpened"
   >
     <t-form ref="formRef" :data="formData" :rules="rules" :labelWidth="100">
       <t-row :gutter="[0, 20]">
@@ -26,9 +27,21 @@
             </t-select>
           </t-form-item>
         </t-col>
-        <t-col :span="6">
-          <t-form-item label="客户地址" name="address">
-            <t-input v-model="formData.address" clearable />
+        <t-col :span="12">
+          <t-form-item label="客户地址" name="province" required-mark>
+            <select-area
+              v-model="pca"
+              value-type="full"
+              placeholder="请选择省市区"
+              @change="areaChagne"
+            ></select-area>
+          </t-form-item>
+          <t-form-item name="address" :required-mark="false">
+            <t-input
+              v-model="formData.address"
+              clearable
+              placeholder="请输入详细地址"
+            />
           </t-form-item>
         </t-col>
         <t-col :span="6">
@@ -75,14 +88,16 @@ import { MessagePlugin } from 'tdesign-vue-next';
 import useClearDialog from '@/hooks/useClearDialog';
 import { CUSTOMER_TYPE } from '@/config/constants';
 import { customerEditApi } from '@/api/system';
-const emit = defineEmits(['update:visible', 'success']);
-const formRef = ref(null);
 
+const emit = defineEmits(['update:visible', 'success']);
 const props = defineProps({
   visible: Boolean,
   curRow: Object,
 });
 
+const formRef = ref(null);
+let pca = ref([]);
+
 const title = computed(() => {
   return (isEdit.value ? '编辑' : '新增') + '客户';
 });
@@ -92,13 +107,15 @@ const { formData, isEdit } = useClearDialog(
     id: null,
     name: '',
     type: '',
-    province: '湖北省',
-    city: '武汉市',
-    area: '洪山区',
-    address: '光谷梦工厂',
+    province: '',
+    city: '',
+    area: '',
+    address: '',
     managerId: null,
     levelId: null,
     peoperDay: 1,
+    sync: true,
+    enable: true,
   },
   props,
   formRef,
@@ -129,11 +146,28 @@ const rules = {
   type: [
     { required: true, message: '类型必选', type: 'error', trigger: 'change' },
   ],
+  province: [
+    {
+      validator: () => {
+        if (!formData.province || !formData.city || !formData.area)
+          return { result: false, message: '省市区必选' };
+
+        return { result: true, type: 'success' };
+      },
+      type: 'error',
+      trigger: 'change',
+    },
+  ],
   address: [
-    { required: true, message: '地址必填', type: 'error', trigger: 'change' },
+    {
+      required: true,
+      message: '详细地址必填',
+      type: 'error',
+      trigger: 'change',
+    },
     {
       max: 100,
-      message: '至多需要100个字',
+      message: '详细地址至多需要100个字',
       type: 'error',
       trigger: 'change',
     },
@@ -143,6 +177,16 @@ const rules = {
   ],
 };
 
+const dialogOpened = () => {
+  pca.value = [formData.province, formData.city, formData.area];
+};
+
+const areaChagne = (val) => {
+  formData.province = val[0];
+  formData.city = val[1];
+  formData.area = val[2];
+};
+
 const save = async () => {
   const valid = await formRef.value.validate();
   if (valid !== true) return;

+ 2 - 0
src/views/system/config-manage/device-manage/edit-device-dialog.vue

@@ -104,6 +104,8 @@ const { formData, isEdit } = useClearDialog(
     status: '',
     location: '',
     scanCount: null,
+    sync: true,
+    enable: true,
   },
   props,
   formRef,

+ 84 - 17
src/views/system/config-manage/service-level-manage/edit-service-level-dialog.vue

@@ -2,19 +2,20 @@
   <my-dialog
     :visible="visible"
     :header="title"
-    :width="800"
+    :width="600"
     attach="body"
     :closeOnOverlayClick="false"
     @close="emit('update:visible', false)"
+    @opened="dialogOpened"
   >
     <t-form ref="formRef" :data="formData" :rules="rules" :labelWidth="120">
       <t-row :gutter="[0, 20]">
-        <t-col :span="6">
+        <t-col :span="12">
           <t-form-item label="服务档位名称" name="level">
             <t-input v-model="formData.level" clearable />
           </t-form-item>
         </t-col>
-        <t-col :span="6">
+        <t-col :span="12">
           <t-form-item label="业务类型" name="type">
             <t-select v-model="formData.type">
               <t-option
@@ -27,24 +28,53 @@
           </t-form-item>
         </t-col>
         <t-col :span="12">
-          <t-form-item label="项目角色配置" name="roleList">
-            <!-- TODO: -->
-            <t-select v-model="formData.roleList">
-              <t-option
-                v-for="(val, key) in CUSTOMER_TYPE"
-                :key="key"
-                :label="val"
-                :value="key"
-              />
-            </t-select>
+          <t-form-item label="项目角色配置" name="roleList" required-mark>
+            <div>
+              <t-table
+                size="small"
+                row-key="roleId"
+                :columns="roleColumns"
+                :data="formData.roleList"
+                bordered
+              >
+                <template #quota="{ row }">
+                  <t-input-number
+                    v-model="row.quota"
+                    theme="column"
+                    :decimalPlaces="0"
+                    :max="1000"
+                    :min="1"
+                    style="width: 100px"
+                  ></t-input-number>
+                </template>
+              </t-table>
+            </div>
+          </t-form-item>
+          <t-form-item>
+            <div class="flex justify-between items-center" style="width: 100%">
+              <select-role
+                v-model="selectedRoleId"
+                @change="roleChange"
+              ></select-role>
+              <t-button
+                class="m-l-10px"
+                theme="primary"
+                :disabled="!selectedRole"
+                @click="toAddRole"
+                >添加</t-button
+              >
+            </div>
           </t-form-item>
         </t-col>
         <t-col :span="12">
           <t-form-item label="设备配额">
             <t-input-number
               v-model="formData.devices"
+              theme="column"
               :decimalPlaces="0"
+              :max="1000"
               :min="1"
+              style="width: 140px"
             />
           </t-form-item>
         </t-col>
@@ -66,13 +96,15 @@ import { CUSTOMER_TYPE } from '@/config/constants';
 import { serviceLevelEditApi } from '@/api/system';
 
 const emit = defineEmits(['update:visible', 'success']);
-const formRef = ref(null);
-
 const props = defineProps({
   visible: Boolean,
   curRow: Object,
 });
 
+const formRef = ref(null);
+let selectedRole = ref(null);
+let selectedRoleId = ref(null);
+
 const title = computed(() => {
   return (isEdit.value ? '编辑' : '新增') + '服务档位';
 });
@@ -83,6 +115,7 @@ const { formData, isEdit } = useClearDialog(
     level: '',
     type: '',
     devices: null,
+    enable: true,
     roleList: [],
   },
   props,
@@ -119,13 +152,47 @@ const rules = {
   ],
   roleList: [
     {
-      required: true,
-      message: '项目角色必须配置',
+      validator: (val) => {
+        if (!val || !val.length)
+          return { result: false, message: '请填写至少一个角色' };
+
+        if (val.some((item) => !item.quota))
+          return { result: false, message: '有角色未设置配额' };
+
+        return { result: true, type: 'success' };
+      },
       type: 'error',
       trigger: 'change',
     },
   ],
 };
+
+const roleColumns = [
+  { colKey: 'roleName', title: '项目角色' },
+  { colKey: 'quota', title: '配额', cell: 'quota', width: 130 },
+];
+
+const dialogOpened = () => {
+  selectedRole.value = null;
+  selectedRoleId.value = null;
+};
+
+const roleChange = (role) => {
+  selectedRole.value = role;
+};
+const toAddRole = () => {
+  if (formData.roleList.some((item) => item.roleId === selectedRole.value.id)) {
+    MessagePlugin.error('当前角色已经被选过!');
+    return;
+  }
+
+  formData.roleList.push({
+    quota: 1,
+    roleId: selectedRole.value.id,
+    roleName: selectedRole.value.name,
+  });
+};
+
 const save = async () => {
   const valid = await formRef.value.validate();
   if (valid !== true) return;

+ 5 - 2
src/views/system/notice-log/notice-manage/notice-message-dialog.vue

@@ -105,13 +105,16 @@ const fields = ref([
     options: [
       {
         label: '已阅',
-        value: true,
+        value: 'true',
       },
       {
         label: '未阅',
-        value: false,
+        value: 'false',
       },
     ],
+    attrs: {
+      clearable: true,
+    },
   },
   {
     type: 'buttons',