Sfoglia il codice sorgente

Merge branch 'master' of http://192.168.100.32:3000/fanfan/xt-front

fan 2 anni fa
parent
commit
13ef16af31
50 ha cambiato i file con 2245 aggiunte e 915 eliminazioni
  1. 1 1
      .env
  2. 2 1
      .vscode/settings.json
  3. 415 115
      pnpm-lock.yaml
  4. 29 0
      src/api/biz/management/wardInfo.ts
  5. 18 0
      src/api/biz/management/wardType.ts
  6. 13 0
      src/api/biz/parameter/parameter.ts
  7. 12 0
      src/api/common/index.ts
  8. 10 1
      src/api/monitor/LogApi.ts
  9. 12 0
      src/api/sys/userAccountApi.ts
  10. 6 2
      src/components/Form/src/components/TextEditor.vue
  11. 14 4
      src/components/TableCard/src/BasicTable.vue
  12. 0 1
      src/components/Upload/src/UploadModal.vue
  13. 3 8
      src/layouts/default/header/components/user-dropdown/index.vue
  14. 26 8
      src/utils/file/base64Conver.ts
  15. 4 0
      src/utils/filters.ts
  16. 11 0
      src/views/biz/management/parameter/data.ts
  17. 353 0
      src/views/biz/management/parameter/index.vue
  18. 97 0
      src/views/biz/management/ward/data.ts
  19. 291 0
      src/views/biz/management/ward/index.vue
  20. 67 0
      src/views/biz/management/ward/wardInfoFormModal.vue
  21. 57 0
      src/views/biz/management/ward/wardTypeFormModal.vue
  22. 2 0
      src/views/infra/job/jobLog/index.vue
  23. 0 2
      src/views/infra/job/jobLog/viewDrawer.vue
  24. 4 0
      src/views/infra/job/jobTable/index.vue
  25. 2 4
      src/views/infra/job/jobTable/viewDrawer.vue
  26. 32 13
      src/views/infra/numStrategy/index.vue
  27. 1 1
      src/views/infra/storage/list/data.ts
  28. 16 11
      src/views/infra/storage/list/index.vue
  29. 1 1
      src/views/infra/storage/oss/data.ts
  30. 25 12
      src/views/infra/storage/oss/index.vue
  31. 1 1
      src/views/infra/storage/oss/uploadModel.vue
  32. 153 0
      src/views/monitor/loginLog/importModal/importView.vue
  33. 89 61
      src/views/monitor/loginLog/index.vue
  34. 7 34
      src/views/monitor/onlineUser/index.vue
  35. 12 39
      src/views/monitor/operLog/index.vue
  36. 0 92
      src/views/sys/account/center/Application.vue
  37. 0 97
      src/views/sys/account/center/Article.vue
  38. 0 71
      src/views/sys/account/center/Project.vue
  39. 58 0
      src/views/sys/account/center/basicInformation.vue
  40. 93 0
      src/views/sys/account/center/data.ts
  41. 0 132
      src/views/sys/account/center/data.tsx
  42. 65 0
      src/views/sys/account/center/editPws.vue
  43. 62 147
      src/views/sys/account/center/index.vue
  44. 80 0
      src/views/sys/account/center/socialContact.vue
  45. 17 12
      src/views/sys/sysPortal/index.vue
  46. 1 0
      src/views/sys/sysSetting/data.ts
  47. 45 0
      src/views/sys/sysSetting/disclaimerConfig.vue
  48. 7 22
      src/views/sys/sysSetting/index.vue
  49. 15 11
      src/views/sys/sysSetting/sysConfig.vue
  50. 16 11
      src/views/sys/sysTenant/package/index.vue

+ 1 - 1
.env

@@ -1,5 +1,5 @@
 # port
-VITE_PORT = 3100
+VITE_PORT = 3200
 
 # spa-title
 VITE_GLOB_APP_TITLE = 后台管理系统

+ 2 - 1
.vscode/settings.json

@@ -27,5 +27,6 @@
   "editor.codeActionsOnSave": {
     "source.fixAll.eslint": true
   },
-  "iconify.excludes": ["el"]
+  "iconify.excludes": ["el"],
+  "vue.codeActions.enabled": false
 }

File diff suppressed because it is too large
+ 415 - 115
pnpm-lock.yaml


+ 29 - 0
src/api/biz/management/wardInfo.ts

@@ -0,0 +1,29 @@
+import { defHttp } from '/@/utils/http/axios';
+import { setParams } from '/@/utils/index';
+enum Api {
+  getWardInfo = '/biz/sys/wardInfo/query/page',
+  add = '/biz/sys/wardInfo/add',
+  edit = '/biz/sys/wardInfo/edit',
+  changeStatus = '/biz/sys/wardInfo/modify/status/',
+  WardInfoById = '/biz/sys/wardInfo/detail',
+}
+
+export const getWardInfo = (params?: object) => {
+  return defHttp.post({ url: Api.getWardInfo, params: setParams(params) });
+};
+
+export const wardInfoAdd = (params?: object) => {
+  return defHttp.post({ url: Api.add, params: params });
+};
+
+export const wardInfoEdit = (params?: object) => {
+  return defHttp.post({ url: Api.edit, params: params });
+};
+
+export const changeStatus = (id: string) => {
+  return defHttp.post({ url: Api.changeStatus + id });
+};
+
+export const wardInfoById = (id?: string | number, params?: object) => {
+  return defHttp.get({ url: Api.WardInfoById + '/' + id, params: params });
+};

+ 18 - 0
src/api/biz/management/wardType.ts

@@ -0,0 +1,18 @@
+import { defHttp } from '/@/utils/http/axios';
+enum Api {
+  add = '/biz/sys/wardProperties/add',
+  get = '/biz/sys/wardProperties/query/list',
+  delete = '/biz/sys/wardProperties/removeByIds',
+}
+
+export const wardTypeAdd = (params?: object) => {
+  return defHttp.post({ url: Api.add, params: params });
+};
+
+export const getAttrList = (params?: object) => {
+  return defHttp.post({ url: Api.get, params: params });
+};
+
+export const deleteWardType = (params?: object) => {
+  return defHttp.post({ url: Api.delete, params: params });
+};

+ 13 - 0
src/api/biz/parameter/parameter.ts

@@ -0,0 +1,13 @@
+import { defHttp } from '/@/utils/http/axios';
+enum Api {
+  edit = '/biz/sys/params/edit',
+  get = '/biz/sys/params/query/list',
+}
+
+export const get = (params?: object) => {
+  return defHttp.post({ url: Api.get, params: params });
+};
+
+export const edit = (params?: object) => {
+  return defHttp.post({ url: Api.edit, params: params });
+};

+ 12 - 0
src/api/common/index.ts

@@ -11,6 +11,8 @@ enum Api {
   getSystemTime = '/system/sysConfig/getTime',
   getDownloadUrl = '/sys/storage/file/download/',
   getPreviewUrl = '/sys/storage/file/preview/',
+  excelSheetDetail = '/excel/sheet/detail',
+  tempDownload = '/archives/patientBasic/export',
 }
 
 export function listDictModel(params?: object) {
@@ -52,3 +54,13 @@ export function getDownloadUrl(id) {
 export function getPreviewUrl(id) {
   return defHttp.get({ url: Api.getPreviewUrl + id });
 }
+
+// 导入数据
+export const excelSheetDetail = (id: string) => {
+  return defHttp.get({ url: Api.excelSheetDetail + '/' + id });
+};
+
+// 下载模板
+export const tempDownload = (params?: Array<string | number>) => {
+  return defHttp.post({ url: Api.tempDownload, params: params });
+};

+ 10 - 1
src/api/monitor/LogApi.ts

@@ -7,7 +7,8 @@ enum Api {
   LogAdd = '/sys/log/add',
   LogEdit = '/sys/log/edit',
   LogRemove = '/sys/log/removeByIds',
-  LogExport = '/sys/log/removeByIds',
+  LogExport = '/sys/log/export/operation',
+  LogLoginExport = '/sys/log/export/login',
 }
 
 /**
@@ -137,3 +138,11 @@ export const LogRemove = (params: Array<string | number>) => {
 export const LogExport = (params: Array<string | number>) => {
   return defHttp.post({ url: Api.LogExport, params: params });
 };
+
+/**
+ * @description: 导出登录/等出,权限 - sys:log:export:login
+ * @method: POST
+ */
+export const LogLoginExport = (params: Array<string | number>) => {
+  return defHttp.post({ url: Api.LogLoginExport, params: params });
+};

+ 12 - 0
src/api/sys/userAccountApi.ts

@@ -0,0 +1,12 @@
+import { defHttp } from '/@/utils/http/axios';
+enum Api {
+  userAccountInfo = '/user/accountInfo',
+  editPsw = '/oauth2/psw/updatePswl',
+}
+
+export const userAccountInfo = () => {
+  return defHttp.get({ url: Api.userAccountInfo });
+};
+export function editPsw(params: object) {
+  return defHttp.post({ url: Api.editPsw, params: params });
+}

+ 6 - 2
src/components/Form/src/components/TextEditor.vue

@@ -3,7 +3,12 @@
 -->
 <template>
   <div v-bind="attrs" button-style="solid" class="text-editor">
-    <WangEditor :modelValue="state" :height="height" @change="handleChange" />
+    <WangEditor
+      :modelValue="state"
+      placeholder="请输入免责声明..."
+      :height="height"
+      @change="handleChange"
+    />
   </div>
 </template>
 <script lang="ts">
@@ -30,7 +35,6 @@
       console.log('🚀 ~ file: TextEditor.vue:30 ~ setup ~ state:', state);
 
       function handleChange(data) {
-        // console.log('data', data);
         emit('change', data);
       }
 

+ 14 - 4
src/components/TableCard/src/BasicTable.vue

@@ -65,6 +65,7 @@
   import { useTableFooter } from './hooks/useTableFooter';
   import { useDesign } from '/@/hooks/web/useDesign';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import { downloadByBase64 } from '/@/utils/file/download';
 
   import { omit } from 'lodash-es';
   import { basicProps } from './props';
@@ -235,13 +236,22 @@
       // 批量导出
       async function batchExport() {
         // const selectRow = getSelectRows();
-        if (!innerPropsRef.value.batchDelApi || !isFunction(innerPropsRef.value.batchDelApi)) {
+        if (
+          !innerPropsRef.value.batchExportApi ||
+          !isFunction(innerPropsRef.value.batchExportApi)
+        ) {
           createMessage.error('未找到批量操作入口!');
           return;
         }
-        // const keys = getSelectRowKeys();
-        // await innerPropsRef.value.batchDelApi(keys);
-        // createMessage.success('删除成功!');
+        const keys = getSelectRowKeys();
+        await innerPropsRef.value.batchExportApi(keys).then(res => {
+          console.log('res::::::::', res && res);
+          if (res && res.fileName && res.base64) {
+            downloadByBase64(res.base64, res.fileName + '.xlsx');
+          } else {
+            createMessage.error('获取导出文件失败!');
+          }
+        });
       }
       const {
         getViewColumns,

+ 0 - 1
src/components/Upload/src/UploadModal.vue

@@ -222,7 +222,6 @@
           const data = await Promise.all(
             uploadFileList.map(item => {
               const result = uploadApiByItem(item);
-              console.log('result::::::::::::', result);
               return result;
             }),
           );

+ 3 - 8
src/layouts/default/header/components/user-dropdown/index.vue

@@ -13,13 +13,7 @@
       <Menu @click="handleMenuClick">
         <MenuDivider v-if="getShowDoc" />
         <!-- <MenuItem key="accountCenter" :text="'个人中心'" icon="grommet-icons:user" /> -->
-        <MenuItem key="accountSetting" :text="'账户设置'" icon="grommet-icons:user-settings" />
-        <!-- <MenuItem
-          v-if="getUseLockPage"
-          key="lock"
-          :text="t('layout.header.tooltipLock')"
-          icon="ion:lock-closed-outline"
-        /> -->
+        <MenuItem key="accountSetting" :text="'个人中心'" icon="grommet-icons:user-settings" />
         <MenuItem key="clear" text="清空缓存" icon="rest|iconfont" />
         <MenuItem key="logout" text="退出" icon="poweroff|iconfont" />
       </Menu>
@@ -62,6 +56,7 @@
     name: 'UserDropdown',
     components: {
       Dropdown,
+      // eslint-disable-next-line vue/no-reserved-component-names
       Menu,
       MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
       MenuDivider: Menu.Divider,
@@ -114,7 +109,7 @@
 
       // account setting
       function handleClickAccountSetting() {
-        go('/account/setting');
+        go('/sys/accountCenter');
       }
 
       function handleMenuClick(e: { key: MenuEvent }) {

+ 26 - 8
src/utils/file/base64Conver.ts

@@ -2,17 +2,35 @@
  * @description: base64 to blob
  */
 export function dataURLtoBlob(base64Buf: string): Blob {
+  // 导出专用
   const arr = base64Buf.split(',');
-  const typeItem = arr[0];
-  const mime = typeItem.match(/:(.*?);/)![1];
-  const bstr = window.atob(arr[1]);
-  let n = bstr.length;
-  const u8arr = new Uint8Array(n);
-  while (n--) {
-    u8arr[n] = bstr.charCodeAt(n);
+  if (arr.length == 1) {
+    const typeItem = arr[0];
+    debugger;
+    // const mime = typeItem.match(/:(.*?);/)![1];
+    const bstr = window.atob(typeItem);
+    let n = bstr.length;
+    const u8arr = new Uint8Array(n);
+    while (n--) {
+      u8arr[n] = bstr.charCodeAt(n);
+    }
+    return new Blob([u8arr], {
+      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+    });
+  } else {
+    const typeItem = arr[0];
+    debugger;
+    const mime = typeItem.match(/:(.*?);/)![1];
+    const bstr = window.atob(arr[1]);
+    let n = bstr.length;
+    const u8arr = new Uint8Array(n);
+    while (n--) {
+      u8arr[n] = bstr.charCodeAt(n);
+    }
+    return new Blob([u8arr], { type: mime });
   }
-  return new Blob([u8arr], { type: mime });
 }
+
 /**
  * img url to base64
  * @param url

+ 4 - 0
src/utils/filters.ts

@@ -94,6 +94,10 @@ export const SexFilters = [
   { text: '女', value: '2' },
   { text: '未知', value: '3' },
 ];
+export const gender = [
+  { label: '男', value: '1' },
+  { label: '女', value: '2' },
+];
 
 export const SexSelect = [
   { label: '男', value: 1 },

+ 11 - 0
src/views/biz/management/parameter/data.ts

@@ -0,0 +1,11 @@
+import { BasicColumn } from '/@/components/Table';
+export const columns: BasicColumn[] = [
+  {
+    title: '类型',
+    dataIndex: 'typeName',
+  },
+  {
+    title: '内容',
+    dataIndex: 'contents',
+  },
+];

+ 353 - 0
src/views/biz/management/parameter/index.vue

@@ -0,0 +1,353 @@
+<template>
+  <div>
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'contents'">
+          <div>
+            <a-input
+              v-if="editableData[record.key]"
+              v-model:value="editableData[record.key][column.dataIndex]"
+              placeholder="请输入参数内容"
+            />
+            <template v-else>
+              {{ record.contents }}
+            </template>
+          </div>
+        </template>
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                auth: ['biz:sys:params:add', 'biz:sys:params:edit', 'biz:sys:params:remove'],
+                ifShow: editableData[record.key] ? false : true,
+                icon: 'icon-edit|iconfont',
+                tooltip: '编辑',
+                label: '',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                auth: ['biz:sys:params:add', 'biz:sys:params:edit', 'biz:sys:params:remove'],
+                ifShow: editableData[record.key] ? true : false,
+                icon: 'icon-check|iconfont',
+                tooltip: '保存',
+                label: '',
+                onClick: handleSave.bind(null, record),
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref, UnwrapRef, reactive } from 'vue'; // onBeforeMount,
+
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { cloneDeep } from 'lodash-es';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { get, edit } from '/@/api/biz/parameter/parameter';
+  import { columns } from './data';
+
+  interface DataItem {
+    typeName: string;
+    contents: string;
+    typeIndex: string;
+    key: number;
+  }
+  const dataList = ref([]);
+  const resObj = ref({});
+  const { createMessage } = useMessage();
+  const editableData: UnwrapRef<Record<string, DataItem>> = reactive({});
+  const [registerTable, { reload }] = useTable({
+    api: getData,
+    rowKey: 'id',
+    columns,
+    striped: false,
+    pagination: false,
+    showIndexColumn: false,
+    formConfig: {
+      labelWidth: 120,
+      autoSubmitOnEnter: true,
+      baseColProps: { xs: 24, sm: 12, md: 12, lg: 8 },
+      resetButtonOptions: {
+        preIcon: 'icon-delete|iconfont',
+      },
+      submitButtonOptions: {
+        preIcon: 'icon-search|iconfont',
+      },
+    },
+    useSearchForm: false,
+    bordered: true,
+    actionColumn: {
+      width: 320,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+  });
+  // 获取数据结构变更
+  async function getData() {
+    dataList.value = [];
+    const res = await get();
+    resObj.value = res;
+    Object.keys(res).forEach(value => {
+      switch (value) {
+        case 'firstScheduleBedTime': {
+          const data = {
+            typeName: '自动排床-首次排床日期',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'autoFlowPreDialysis': {
+          const data = {
+            typeName: '透前准备流程自动',
+            contents: res[value],
+            typeIndex: value,
+            key: 2,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'autoFlowAfterDialysis': {
+          const data = {
+            typeName: '透后称量流程自动',
+            contents: res[value],
+            typeIndex: value,
+            key: 3,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'autoFlowDisinfect': {
+          const data = { typeName: '消毒流程自动', contents: res[value], typeIndex: value, key: 4 };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'urrMin': {
+          const data = { typeName: 'URR最小值(%)', contents: res[value], typeIndex: value, key: 5 };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'sktVMin': {
+          const data = { typeName: 'sKt/V最小值', contents: res[value], typeIndex: value, key: 6 };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'weightUpRate': {
+          const data = {
+            typeName: '体重增长率(%)',
+            contents: res[value],
+            typeIndex: value,
+            key: 7,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'preDialysisPressureMax': {
+          const data = {
+            typeName: '透前收缩压最大值(mmHg)',
+            contents: res[value],
+            typeIndex: value,
+            key: 8,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'albMin': {
+          const data = {
+            typeName: 'Alb最小值(h/L)',
+            contents: res[value],
+            typeIndex: value,
+            key: 9,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'hbMin': {
+          const data = {
+            typeName: 'HB最小值(g/L)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'hbMax': {
+          const data = {
+            typeName: 'HB最大值(g/L)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+        case 'ipthMin': {
+          const data = {
+            typeName: 'IPTH最小值(ng/dL)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+        case 'ipthMax': {
+          const data = {
+            typeName: 'IPTH最大值(ng/dL)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+        case 'caMin': {
+          const data = {
+            typeName: 'Ca最小值(mmol/L)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+        case 'caMax': {
+          const data = {
+            typeName: 'Ca最大值(mmol/L)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+        case 'ppMin': {
+          const data = {
+            typeName: 'P最小值(mmol/L)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'ppMax': {
+          const data = {
+            typeName: 'P最大值(mmol/L)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'calciumPhosphorusMin': {
+          const data = {
+            typeName: '钙磷乘积最小值(mg2/dl2)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'calciumPhosphorusMiddle': {
+          const data = {
+            typeName: '钙磷乘积中间值(mg2/dl2)',
+            contents: res[value],
+            typeIndex: value,
+            key: 1,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'calciumPhosphorusMax': {
+          const data = {
+            typeName: '钙磷乘积最大值(mg2/dl2)',
+            contents: res[value],
+            typeIndex: value,
+            key: 20,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'phaseEvalInterval': {
+          const data = {
+            typeName: '阶段评估时间(天)',
+            contents: res[value],
+            typeIndex: value,
+            key: 21,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'phaseEvalAlarmInterval': {
+          const data = {
+            typeName: '阶段评估提前提醒时间(天)',
+            contents: res[value],
+            typeIndex: value,
+            key: 22,
+          };
+          dataList.value.push(data);
+          break;
+        }
+
+        case 'newPatientDay': {
+          const data = {
+            typeName: '新患者建档时间(天)',
+            contents: res[value],
+            typeIndex: value,
+            key: 23,
+          };
+          dataList.value.push(data);
+          break;
+        }
+      }
+    });
+    return dataList.value;
+  }
+
+  function handleBeforeFetch() {}
+
+  function handleEdit(record) {
+    const key = record.key;
+    editableData[key] = cloneDeep(dataList.value.filter(item => key === item.key)[0]);
+  }
+
+  async function handleSave(record) {
+    const key = record.key;
+    const editObj = resObj.value;
+    const editName = editableData[key].typeName;
+    console.log('record.contents:::::::', editableData[key].contents);
+    editObj[editableData[key].typeIndex] = Number(
+      parseFloat(editableData[key].contents).toFixed(2),
+    );
+    await edit(resObj.value);
+    delete editableData[key];
+    createMessage.success('参数[' + editName + ']修改成功!');
+    reload();
+  }
+</script>

+ 97 - 0
src/views/biz/management/ward/data.ts

@@ -0,0 +1,97 @@
+import { BasicColumn, FormSchema } from '/@/components/Table';
+import { getAttrList } from '/@/api/biz/management/wardType';
+export const wardInfoColumns: BasicColumn[] = [
+  {
+    title: '病区名',
+    dataIndex: 'name',
+  },
+  {
+    title: '病区属性',
+    dataIndex: 'propertiesName',
+  },
+  {
+    title: '状态',
+    dataIndex: 'disable',
+  },
+  {
+    title: '排序',
+    dataIndex: 'sort',
+  },
+];
+
+// 表单属性新增编辑
+export const wardTypeDataFormSchema: FormSchema[] = [
+  {
+    label: '病区类型',
+    field: 'positive',
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+      options: [
+        { label: '阴性区', value: 0 },
+        { label: '阳性区', value: 1 },
+      ],
+      disabled: false,
+    },
+    defaultValue: 0,
+  },
+  {
+    label: '属性名称',
+    field: 'name',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入属性名称',
+    },
+  },
+];
+
+// 表单信息新增编辑
+export const wardInfoDataFormSchema: FormSchema[] = [
+  {
+    label: '病区属性',
+    field: 'propertiesId',
+    component: 'ApiSelect',
+    required: true,
+    componentProps: {
+      api: getAttrList,
+      mode: 'single',
+      labelField: 'name',
+      valueField: 'id',
+      resultField: 'data',
+      placeholder: '请输入存储配置',
+    },
+  },
+  {
+    label: '病区名',
+    field: 'name',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入病区名',
+    },
+  },
+  {
+    label: '排序',
+    field: 'sort',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入排序',
+    },
+  },
+  {
+    label: '状态',
+    field: 'disable',
+    component: 'Input',
+    defaultValue: '0',
+    show: false,
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '请输入备注',
+    },
+  },
+];

+ 291 - 0
src/views/biz/management/ward/index.vue

@@ -0,0 +1,291 @@
+<template>
+  <PageWrapper>
+    <Card
+      :bordered="false"
+      :active-tab-key="activeKey"
+      :tab-list="tabList"
+      style="height: 800px"
+      @tabChange="
+        key => {
+          activeKey = key;
+        }
+      "
+    >
+      <p :key="0" v-if="activeKey == 0">
+        <Row>
+          <Col :span="6">
+            <Card title="病区属性" style="height: 680px">
+              <template #extra
+                ><Button size="small" v-auth="['sys:wardProperties:add']" @click="handleAddAttr"
+                  >+</Button
+                ></template
+              >
+              <Row>
+                <div class="type-title">阴性</div>
+              </Row>
+              <Row v-for="item in attributeFeminineList" :key="item.key" style="margin: 4px 0">
+                <Tag
+                  class="attrs"
+                  closable
+                  @close="handleDeleteAttr(item.key)"
+                  @click="handleSelectWard(item)"
+                  :value="item.key"
+                  >{{ item.label }}
+                </Tag>
+              </Row>
+              <Row>
+                <div class="type-title">阳性</div>
+              </Row>
+              <Row v-for="item in attributePositiveList" :key="item.key" style="margin: 4px 0">
+                <Tag
+                  class="attrs"
+                  closable
+                  @close="handleDeleteAttr(item.key)"
+                  @click="handleSelectWard(item)"
+                  :value="item.key"
+                  >{{ item.label }}</Tag
+                >
+              </Row>
+            </Card>
+          </Col>
+          <Col :span="1" />
+          <Col :span="17">
+            <Card title="病区信息" style="height: 680px">
+              <template #extra
+                ><Button size="small" v-auth="['sys:wardInfo:add']" @click="handleAddInfo"
+                  >+</Button
+                ></template
+              >
+              <BasicTable @register="registerTable">
+                <template #bodyCell="{ column, record }">
+                  <template v-if="column.key === 'disable'">
+                    <Tag :color="formatDictColor(disableOptions, record.disable)">
+                      {{ formatDictValue(disableOptions, record.disable) }}
+                    </Tag>
+                  </template>
+                  <template v-if="column.key === 'action'">
+                    <TableAction
+                      :actions="[
+                        {
+                          auth: 'sys:wardInfo:status',
+                          icon: 'icon-minus-square|iconfont',
+                          tooltip: '停用',
+                          label: '',
+                          color: 'error',
+                          ifShow: record.disable == 0,
+                          popConfirm: {
+                            title: '是否确认停用',
+                            placement: 'left',
+                            confirm: handleChangeState.bind(null, record),
+                          },
+                        },
+                        {
+                          auth: 'sys:wardInfo:status',
+                          icon: 'icon-plus|iconfont',
+                          tooltip: '启用',
+                          label: '',
+                          ifShow: record.disable == 1,
+                          color: 'error',
+                          popConfirm: {
+                            title: '是否确认启用',
+                            placement: 'left',
+                            confirm: handleChangeState.bind(null, record),
+                          },
+                        },
+                        {
+                          auth: 'sys:wardInfo:edit',
+                          icon: 'icon-edit|iconfont',
+                          tooltip: '编辑',
+                          label: '',
+                          onClick: handleEdit.bind(null, record),
+                        },
+                      ]"
+                    />
+                  </template>
+                </template>
+              </BasicTable>
+            </Card>
+          </Col>
+        </Row>
+      </p>
+      <p :key="1" v-else> 班次设置 </p>
+    </Card>
+  </PageWrapper>
+  <WardTypeFormModal @register="registerWardTypeModal" @success="handleWardTypeSuccess" />
+  <WardInfoFormModal @register="registerWardInfoModal" @success="handleWardInfoSuccess" />
+</template>
+
+<script lang="ts" setup>
+  import { onBeforeMount, ref } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { Button, Card, Row, Col, Tag } from 'ant-design-vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { wardInfoColumns } from './data';
+  import WardTypeFormModal from './wardTypeFormModal.vue';
+  import WardInfoFormModal from './wardInfoFormModal.vue';
+  import { getWardInfo, changeStatus } from '/@/api/biz/management/wardInfo';
+  import { getAttrList, deleteWardType } from '/@/api/biz/management/wardType';
+  import { useModal } from '/@/components/Modal';
+  import { listDictModel } from '/@/api/common';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { formatDictColor, formatDictValue } from '/@/utils';
+  const { createConfirm, createMessage } = useMessage();
+  const tabList = ref([
+    { key: 0, tab: '病区管理', type: 'WARD' },
+    { key: 1, tab: '工作日班次', type: 'WORK' },
+  ]);
+  const activeKey = ref(0);
+  const attributePositiveList = ref([]); // 阳性列表
+  const attributeFeminineList = ref([]); // 阴性列表
+  const disableOptions = ref();
+  onBeforeMount(async () => {
+    disableOptions.value = await listDictModel({ dictCode: 'sys_disable_type' });
+    await getWardType();
+  });
+  const [registerTable, { reload }] = useTable({
+    api: getWardInfo,
+    rowKey: 'id',
+    columns: wardInfoColumns,
+    showIndexColumn: false,
+    formConfig: {
+      labelWidth: 120,
+      autoSubmitOnEnter: true,
+      baseColProps: { xs: 24, sm: 12, md: 12, lg: 8 },
+      resetButtonOptions: {
+        preIcon: 'icon-delete|iconfont',
+      },
+      submitButtonOptions: {
+        preIcon: 'icon-search|iconfont',
+      },
+    },
+    useSearchForm: false,
+    bordered: true,
+    actionColumn: {
+      width: 320,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    sortFn: handleSortFn,
+  });
+
+  const [registerWardTypeModal, { openModal: openWardTypeModal }] = useModal();
+  const [registerWardInfoModal, { openModal: openWardInfoModal }] = useModal();
+  const tableSort = ref([
+    {
+      field: 'sort',
+      direction: 'ASC',
+    },
+  ]);
+  const selectType = ref();
+
+  // 方法区
+  // 获取病区信息前事件
+  function handleBeforeFetch(params) {
+    return { ...params, propertiesId: selectType.value, orders: tableSort.value };
+  }
+
+  function handleSortFn() {}
+  // 打开新增属性方法
+  function handleAddAttr() {
+    openWardTypeModal(true, {
+      isUpdate: false,
+    });
+  }
+  // 打开删除属性方法
+  async function handleDeleteAttr(id) {
+    createConfirm({
+      content: '你确定要删除?',
+      iconType: 'warning',
+      onOk: async () => {
+        await deleteWardType([id]);
+        createMessage.success('属性删除成功!');
+        await getWardType();
+      },
+      onCancel: async () => {
+        attributePositiveList.value = [];
+        attributeFeminineList.value = [];
+        await getWardType();
+      },
+    });
+  }
+  //打开新增病区信息方法
+  function handleAddInfo() {
+    openWardInfoModal(true, {
+      isUpdate: false,
+    });
+  }
+
+  // 打开编辑病区信息方法
+  function handleEdit(record: Recordable) {
+    openWardInfoModal(true, {
+      record,
+      isUpdate: true,
+    });
+  }
+  // 停用启用病区方法
+  async function handleChangeState(record) {
+    const notes = record.disable == 0 ? '停用' : '启用';
+    createConfirm({
+      content: '是否确定要' + notes + '此病区',
+      iconType: 'warning',
+      onOk: async () => {
+        await changeStatus(record.id);
+        createMessage.success('病区' + notes + '成功!');
+        reload();
+      },
+    });
+  }
+
+  // 保存成功回调事件
+  async function handleWardTypeSuccess() {
+    await getWardType();
+  }
+
+  function handleWardInfoSuccess() {
+    reload();
+  }
+
+  async function getWardType() {
+    const attrList = await getAttrList();
+    attributePositiveList.value = []; // 阳性列表
+    attributeFeminineList.value = [];
+    attrList.forEach(item => {
+      if (item.positive) {
+        attributePositiveList.value.push({ label: item.name, key: item.id });
+      } else {
+        attributeFeminineList.value.push({ label: item.name, key: item.id });
+      }
+    });
+  }
+  // 通过病区属性搜索病区信息
+  function handleSelectWard(e) {
+    selectType.value = e.key;
+    reload();
+  }
+</script>
+
+<style lang="less" scoped>
+  ::v-deep(.ant-card-head) {
+    background-color: #f6f8fa !important;
+  }
+
+  .type-title {
+    font-size: 14px;
+    font-weight: bold;
+    color: #000a18;
+  }
+
+  .attrs {
+    background: #fff !important;
+    height: 32px !important;
+    line-height: 32px !important;
+    font-weight: 400 !important;
+    color: #17233d !important;
+    font-size: 14px !important;
+    border-radius: 4px !important;
+    border: 1px solid #c2ccd4 !important;
+  }
+</style>
+
+function $forceUpdate() { throw new Error('Function not implemented.'); }

+ 67 - 0
src/views/biz/management/ward/wardInfoFormModal.vue

@@ -0,0 +1,67 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    @ok="handleSubmit"
+  >
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { wardInfoDataFormSchema } from './data';
+
+  import { wardInfoAdd, wardInfoEdit, wardInfoById } from '/@/api/biz/management/wardInfo';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增病区信息' : '编辑病区信息'));
+  const isUpdate = ref(false);
+  const rowId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { resetFields, validate, setFieldsValue }] = useForm({
+    layout: 'vertical',
+    showResetButton: true,
+    labelWidth: 100,
+    schemas: wardInfoDataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+
+    if (unref(isUpdate)) {
+      rowId.value = data.record.id;
+      const resData = await wardInfoById(data.record.id);
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      !unref(isUpdate)
+        ? await wardInfoAdd({ ...values })
+        : await wardInfoEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', { isUpdate: unref(isUpdate), values: { ...values, id: rowId.value } });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 57 - 0
src/views/biz/management/ward/wardTypeFormModal.vue

@@ -0,0 +1,57 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    @ok="handleSubmit"
+  >
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { wardTypeDataFormSchema } from './data';
+
+  import { wardTypeAdd } from '/@/api/biz/management/wardType';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增病区属性' : '编辑病区属性'));
+  const isUpdate = ref(false);
+  const rowId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { resetFields, validate }] = useForm({
+    layout: 'vertical',
+    showResetButton: true,
+    labelWidth: 100,
+    schemas: wardTypeDataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      await wardTypeAdd({ ...values });
+      createMessage.success('新增成功!');
+      closeModal();
+      emit('success', { isUpdate: unref(isUpdate), values: { ...values, configId: rowId.value } });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 2 - 0
src/views/infra/job/jobLog/index.vue

@@ -21,11 +21,13 @@
           <TableAction
             :actions="[
               {
+                auth: 'quartz:job:query',
                 tooltip: '查看',
                 icon: 'icon-eye|iconfont',
                 onClick: handleView.bind(null, record),
               },
               {
+                auth: 'quartz:jobLog:remove',
                 icon: 'icon-delete|iconfont',
                 tooltip: '删除',
                 // label: '删除',

+ 0 - 2
src/views/infra/job/jobLog/viewDrawer.vue

@@ -30,10 +30,8 @@
     typeStatus.value = await listDictModel({ dictCode: 'sys_common_result' });
   });
   const [registerDrawer] = useDrawerInner(async data => {
-    console.log('::::::::::', data.record);
     const resData = data.record;
     // const resData = await smsChannelDetail(data.record.id);
-    console.log('::::::::::', data.record);
     descData.value = {
       ...resData,
       jobGroup: formatDictValue(typeOptions.value, resData.jobGroup),

+ 4 - 0
src/views/infra/job/jobTable/index.vue

@@ -24,23 +24,27 @@
           <TableAction
             :actions="[
               {
+                auth: 'quartz:job:query',
                 tooltip: '查看',
                 icon: 'icon-eye|iconfont',
                 onClick: handleView.bind(null, record),
               },
               {
+                auth: 'quartz:job:edit',
                 tooltip: '编辑',
                 // label: '编辑',
                 icon: 'icon-edit|iconfont',
                 onClick: handleEditTable.bind(null, record),
               },
               {
+                auth: 'quartz:job:edit',
                 tooltip: '立即执行',
                 icon: 'icon-send|iconfont',
                 // label: '立即执行',
                 onClick: handleExecute.bind(null, record),
               },
               {
+                auth: 'quartz:job:remove',
                 icon: 'icon-delete|iconfont',
                 tooltip: '删除',
                 // label: '删除',

+ 2 - 4
src/views/infra/job/jobTable/viewDrawer.vue

@@ -14,6 +14,7 @@
   import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
   import { Description, useDescription } from '/@/components/Description';
   import { viewSchema } from './data';
+  import { infraJobDetail } from '/@/api/infra/jobApi';
 
   // import { smsChannelDetail } from '/@/api/sys/smsChannelApi';
   import { listDictModel } from '/@/api/common';
@@ -30,10 +31,7 @@
     typeStatus.value = await listDictModel({ dictCode: 'quartz_job_status' });
   });
   const [registerDrawer] = useDrawerInner(async data => {
-    console.log('::::::::::', data.record);
-    const resData = data.record;
-    // const resData = await smsChannelDetail(data.record.id);
-    console.log('::::::::::', data.record);
+    const resData = await infraJobDetail(data.record.jobId);
     descData.value = {
       ...resData,
       jobGroup: formatDictValue(typeOptions.value, resData.jobGroup),

+ 32 - 13
src/views/infra/numStrategy/index.vue

@@ -1,5 +1,11 @@
 <template>
-  <div>
+  <div class="m-4">
+    <div>
+      <XTTitle title="登录日志" :go-back="true" :right-data="titleData" @click="handleImport" />
+      <div class="flex items-center justify-between my-4">
+        <XTForm :form-data="formData" @change="callForm" />
+      </div>
+    </div>
     <BasicTable @register="registerTable">
       <template #bodyCell="{ column, record }">
         <template v-if="column.key === 'type'">
@@ -40,7 +46,7 @@
           />
         </template>
       </template>
-      <template #toolbar>
+      <!-- <template #toolbar>
         <Button
           v-auth="['sys:numStrategy:add']"
           type="primary"
@@ -58,7 +64,7 @@
         >
           批量删除
         </Button>
-      </template>
+      </template> -->
     </BasicTable>
     <FormDrawer @register="registerDrawer" @success="handleSuccess" />
     <ViewDrawer @register="registerDrawerView" @success="handleSuccess" />
@@ -67,15 +73,15 @@
 <script lang="ts" setup>
   import { onBeforeMount, ref } from 'vue';
   import { Tag } from 'ant-design-vue';
-  import { Button } from '/@/components/Button';
+  // import { Button } from '/@/components/Button';
 
-  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { BasicTable, useTable, TableAction } from '/@/components/TableCard';
 
   // import { useModal } from '/@/components/Modal';
   import { useMessage } from '/@/hooks/web/useMessage';
   import FormDrawer from './formDrawer.vue';
   import ViewDrawer from './viewDrawer.vue';
-  import { columns, searchFormSchema } from './data';
+  import { columns } from './data';
 
   import {
     serialNumStrategyQueryPage,
@@ -90,6 +96,16 @@
     typeOptions.value = await listDictModel({ dictCode: 'sys_numbering_type' });
   });
 
+  // formdata
+  const formData = [
+    {
+      name: 'opName',
+      componentType: 'Input',
+      placeholder: '请输入操作名称',
+      width: 200,
+      prefix: 'icon-xt-search',
+    },
+  ];
   const { createConfirm, createMessage } = useMessage();
   // const [registerModal, { openModal }] = useModal();
   const [registerDrawer, { openDrawer }] = useDrawer();
@@ -103,7 +119,6 @@
   ]) as any;
 
   const [registerTable, { reload, getSelectRowKeys }] = useTable({
-    title: '编号策略 ',
     api: serialNumStrategyQueryPage,
     rowKey: 'id',
     columns,
@@ -111,7 +126,6 @@
     rowSelection: { type: 'checkbox' },
     formConfig: {
       labelWidth: 120,
-      schemas: searchFormSchema,
       autoSubmitOnEnter: true,
       baseColProps: { xs: 24, sm: 12, md: 12, lg: 8 },
       resetButtonOptions: {
@@ -140,11 +154,11 @@
   }
 
   // 新增按钮事件
-  function handleCreate() {
-    openDrawer(true, {
-      isUpdate: false,
-    });
-  }
+  // function handleCreate() {
+  //   openDrawer(true, {
+  //     isUpdate: false,
+  //   });
+  // }
 
   // 编辑按钮事件
   function handleEdit(record: Recordable) {
@@ -197,4 +211,9 @@
     console.log(values);
     await reload();
   }
+  // 查询回调函数
+  async function callForm(data) {
+    opName.value = data.opName;
+    await reload();
+  }
 </script>

+ 1 - 1
src/views/infra/storage/list/data.ts

@@ -28,7 +28,7 @@ export const columns: BasicColumn[] = [
 export const searchFormSchema: FormSchema[] = [
   {
     label: '文件名称',
-    field: 'name',
+    field: 'realName',
     component: 'Input',
     componentProps: {
       placeholder: '请输入文件名称',

+ 16 - 11
src/views/infra/storage/list/index.vue

@@ -104,7 +104,7 @@
     },
   ]) as any;
 
-  const [registerTable, { reload, getSelectRowKeys }] = useTable({
+  const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({
     title: ' ',
     api: storageRecordQueryPage,
     rowKey: 'id',
@@ -163,16 +163,21 @@
       createMessage.success('删除成功!');
       await reload();
     } else {
-      createConfirm({
-        content: '你确定要删除?',
-        iconType: 'warning',
-        onOk: async () => {
-          const keys = getSelectRowKeys();
-          await storageRecordRemove(keys);
-          createMessage.success('删除成功!');
-          await reload();
-        },
-      });
+      const keys = getSelectRowKeys();
+      if (keys.length > 0) {
+        createConfirm({
+          content: '是否确定要删除此附件?',
+          iconType: 'warning',
+          onOk: async () => {
+            await storageRecordRemove(keys);
+            createMessage.success('删除成功!');
+            await reload();
+            clearSelectedRowKeys();
+          },
+        });
+      } else {
+        createMessage.warning('请选择要删除的数据');
+      }
     }
   }
 

+ 1 - 1
src/views/infra/storage/oss/data.ts

@@ -44,7 +44,7 @@ export const searchFormSchema: FormSchema[] = [
         },
         {
           label: '本地存储',
-          value: '1ocal',
+          value: 'local',
         },
         {
           label: 'minio存储',

+ 25 - 12
src/views/infra/storage/oss/index.vue

@@ -7,6 +7,13 @@
             {{ commonDict(record.master, 0) }}
           </Tag>
         </template>
+        <template v-if="column.key === 'type'">
+          <span v-if="record.type == 'ali'">阿里云</span>
+          <span v-else-if="record.type == 'db'">数据库</span>
+          <span v-else-if="record.type == 'local'">本地存储</span>
+          <span v-else-if="record.type == 'minio'">minio存储</span>
+        </template>
+
         <template v-if="column.key === 'action'">
           <TableAction
             :actions="[
@@ -49,7 +56,7 @@
                 label: '删除',
                 color: 'error',
                 popConfirm: {
-                  title: '是否确认删除',
+                  title: '是否确定要删除此配置?',
                   placement: 'left',
                   confirm: handleDelete.bind(null, record),
                 },
@@ -122,7 +129,7 @@
     },
   ]) as any;
 
-  const [registerTable, { reload, getSelectRowKeys }] = useTable({
+  const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({
     title: '文件存储配置 ',
     api: infraStorageconfigQueryPage,
     rowKey: 'id',
@@ -181,16 +188,22 @@
       createMessage.success('删除成功!');
       await reload();
     } else {
-      createConfirm({
-        content: '你确定要删除?',
-        iconType: 'warning',
-        onOk: async () => {
-          const keys = getSelectRowKeys();
-          await infraStorageconfigRemove(keys);
-          createMessage.success('删除成功!');
-          await reload();
-        },
-      });
+      const keys = getSelectRowKeys();
+      if (keys.length > 0) {
+        createConfirm({
+          content: '你确定要删除?',
+          iconType: 'warning',
+          onOk: async () => {
+            const keys = getSelectRowKeys();
+            await infraStorageconfigRemove(keys);
+            createMessage.success('删除成功!');
+            await reload();
+            clearSelectedRowKeys();
+          },
+        });
+      } else {
+        createMessage.warning('请选择要删除的数据');
+      }
     }
   }
   // 设置主配置

+ 1 - 1
src/views/infra/storage/oss/uploadModel.vue

@@ -1,7 +1,7 @@
 <template>
   <BasicUpload
     @register="registerModal"
-    :maxSize="20"
+    :maxSize="10"
     :maxNumber="5"
     @change="handleChange"
     :api="uploadApi"

+ 153 - 0
src/views/monitor/loginLog/importModal/importView.vue

@@ -0,0 +1,153 @@
+<template>
+  <BasicModal
+    @register="registerModal"
+    width="500px"
+    title="导入数据"
+    @ok="handleSubmit"
+    @cancel="handleCancel"
+  >
+    <Card style="background-color: cornsilk" v-if="statsUpload == 'done'">
+      <h2 style="text-align: center">数据导入中...</h2>
+      <h5 style="text-align: center">请不要离开此页面</h5>
+      <Progress :percent="percentProgress" :showInfo="false" status="active" />
+    </Card>
+    <Card style="background-color: cornsilk" v-else-if="statsUpload == 'success'">
+      <h2 style="text-align: center">成功</h2>
+      <h2 style="text-align: center"><a @click="goOnImport">继续导入</a></h2>
+    </Card>
+    <Card style="background-color: cornsilk" v-else-if="statsUpload == 'fail'">
+      <h2 style="text-align: center">失败</h2>
+      <h2 style="text-align: center" @click="getExportExcel">失败文件</h2>
+      <div style="text-align: center">
+        <span> 失败: {{ importExcelInfo.fail }}</span>
+        <span> 成功: {{ importExcelInfo.success }}</span>
+        <span> 总数: {{ importExcelInfo.total }}</span>
+      </div>
+    </Card>
+    <Card style="background-color: cornsilk" v-else>
+      <h2 style="text-align: center">选择需要导入的文件</h2>
+      <h5 style="text-align: center"
+        >若您是第一次上传文件,可先下载<a @click="downloadFile">文件模板</a>,文件大小不超过5M</h5
+      >
+      <div class="flex flex-col justify-center">
+        <Upload
+          class="button-style"
+          name="file"
+          :beforeUpload="handleBeforeUpload"
+          :maxCount="1"
+          :action="uploadApi"
+          @change="onChange"
+        >
+          <Button type="primary"> 点击上传 </Button>
+        </Upload>
+      </div>
+      <!-- //v-auth="['dialysis:patientbasic:import']" -->
+    </Card>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { onBeforeMount, onUnmounted, reactive, ref } from 'vue';
+  import { Card } from 'ant-design-vue';
+  import { Progress } from 'ant-design-vue';
+  import { Upload } from 'ant-design-vue';
+  import { Button } from '/@/components/Button';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { excelSheetDetail, getDownloadUrl, tempDownload } from '/@/api/common/index';
+  const globSetting = useGlobSetting();
+  import { listDictModel } from '/@/api/common';
+  import { downloadByBase64, downloadByUrl } from '/@/utils/file/download';
+  const uploadApi = globSetting.apiUrl + '/archives/patientBasic/import/batch';
+  const fileUpload = ref();
+  const typeOptions = ref();
+  const statsUpload = ref('default');
+  const timer = ref(null);
+  const exportFileId = ref('');
+  const percentProgress = ref(0);
+  const importExcelInfo = reactive({
+    success: 0,
+    fail: 0,
+    total: 0,
+  });
+
+  onBeforeMount(async () => {
+    typeOptions.value = await listDictModel({ dictCode: 'sys_login_log_type' });
+
+    fileUpload.value = '';
+  });
+  // closeModal
+  const [registerModal, { setModalProps }] = useModalInner(async data => {
+    console.log('🚀 ~ file: code.vue:21 ~ data:', data);
+    setModalProps({ confirmLoading: false });
+  });
+  async function handleBeforeUpload(file) {
+    console.log('file', file);
+    fileUpload.value = file;
+  }
+  async function handleSubmit() {
+    console.log('11111');
+  }
+  function handleCancel() {
+    statsUpload.value = 'default';
+  }
+  function goOnImport() {
+    statsUpload.value = 'default';
+  }
+  async function onChange(file) {
+    console.log('onChange', file);
+    console.log('file.file.status', file.file.status);
+    if (file.file.status === 'done') {
+      statsUpload.value = 'done';
+      const id = file.file.response.data;
+      if (id) {
+        timer.value = setInterval(async function () {
+          const importStats = await excelSheetDetail(id);
+          console.log('importStats', importStats);
+          importExcelInfo.fail = importStats.failCount;
+          importExcelInfo.success = importStats.successCount;
+          importExcelInfo.total = importStats.totalCount;
+          // 进度条
+          percentProgress.value =
+            ((importStats.failCount + importStats.successCount) / importStats.totalCount) * 100;
+          console.log('进度条:', percentProgress.value);
+          const isFinish =
+            importStats.failCount + importStats.successCount == importStats.totalCount;
+          if (isFinish) {
+            clearInterval(timer.value);
+            setTimeout(() => {
+              if (importStats.failCount) {
+                statsUpload.value = 'fail';
+                exportFileId.value = importStats.exportFileId;
+                percentProgress.value = 0;
+              } else {
+                statsUpload.value = 'success';
+                percentProgress.value = 0;
+              }
+            }, 3000);
+          }
+        }, 1000);
+      }
+    }
+  }
+
+  async function getExportExcel() {
+    const res = await getDownloadUrl(exportFileId.value);
+    console.log('res', res);
+    downloadByUrl({ url: res });
+  }
+  async function downloadFile() {
+    const res = await tempDownload([]);
+    downloadByBase64(res.base64, res.fileName + '.xlsx');
+  }
+  onUnmounted(() => {
+    timer.value ? clearInterval(timer.value) : null;
+  });
+</script>
+<style lang="less" scoped>
+  .button-style {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+    align-items: center;
+  }
+</style>

+ 89 - 61
src/views/monitor/loginLog/index.vue

@@ -1,5 +1,18 @@
 <template>
-  <div>
+  <div class="m-4">
+    <div>
+      <XTTitle title="登录日志" :go-back="true" :right-data="titleData" @click="handleImport" />
+      <div class="flex items-center justify-between my-4">
+        <XTTab
+          type="opLog"
+          :width="120"
+          :selected="tabSelected"
+          :data="typeOptions"
+          @item-click="callTab"
+        />
+        <XTForm :form-data="formData" @change="callForm" />
+      </div>
+    </div>
     <BasicTable @register="registerTable">
       <template #bodyCell="{ column, record }">
         <template v-if="column.key === 'type'">
@@ -44,63 +57,62 @@
           />
         </template>
       </template>
-      <template #toolbar>
-        <!-- <Button
-          v-auth="['sys:log:add']"
-          type="primary"
-          @click="handleCreate"
-          preIcon="icon-plus|iconfont"
-        >
-          新增
-        </Button> -->
-        <Button
-          v-auth="['sys:log:remove']"
-          type="primary"
-          danger
-          @click="handleDelete(null)"
-          preIcon="icon-delete|iconfont"
-        >
-          批量删除
-        </Button>
-      </template>
     </BasicTable>
     <FormDrawer @register="registerDrawer" @success="handleSuccess" />
     <ViewDrawer @register="registerDrawerView" @success="handleSuccess" />
+    <importView @register="registerModal" />
   </div>
 </template>
 <script lang="ts" setup>
   import { onBeforeMount, ref } from 'vue';
   import { Tag } from 'ant-design-vue';
-  import { Button } from '/@/components/Button';
-
-  import { BasicTable, useTable, TableAction } from '/@/components/Table';
-
-  // import { useModal } from '/@/components/Modal';
+  import { BasicTable, useTable, TableAction } from '/@/components/TableCard';
   import { useMessage } from '/@/hooks/web/useMessage';
   import FormDrawer from './formDrawer.vue';
   import ViewDrawer from './viewDrawer.vue';
-  import { columns, searchFormSchema } from './data';
+  import importView from './importModal/importView.vue';
+  import { columns } from './data';
 
-  import { LogQueryPage, LogRemove } from '/@/api/monitor/LogApi';
+  import { LogQueryPage, LogRemove, LogLoginExport } from '/@/api/monitor/LogApi';
   import { listDictModel } from '/@/api/common';
   import { formatDictColor, formatDictValue } from '/@/utils'; //
   import { useDrawer } from '/@/components/Drawer';
 
-  import { useAppStore } from '/@/store/modules/app';
+  import { XTTitle } from '/@/components/XTTitle/index';
+  import { XTTab } from '/@/components/XTTab/index';
+  import { XTForm } from '/@/components/XTForm/index';
+  import { useModal } from '/@/components/Modal';
 
+  // 标题组件右侧按钮
+  const titleData = [
+    {
+      type: 'import',
+      icon: 'icon-xt-import_default',
+    },
+  ];
+  // formdata
+  const formData = [
+    {
+      name: 'opName',
+      componentType: 'Input',
+      placeholder: '请输入操作名称',
+      width: 200,
+      prefix: 'icon-xt-search',
+    },
+  ];
   const typeOptions = ref();
   const responseTypeOptions = ref();
   const resultJsonOptions = ref();
-  const useApp = useAppStore();
-  console.log(useApp.getMenuSetting);
+  // 操作名称
+  const opName = ref('');
+  // tab 切换选中
+  const tabSelected = ref();
   onBeforeMount(async () => {
-    typeOptions.value = await listDictModel({ dictCode: 'sys_login_log_type' });
     responseTypeOptions.value = await listDictModel({ dictCode: 'sys_response_type' });
     resultJsonOptions.value = await listDictModel({ dictCode: 'sys_response_type' });
   });
-
   const { createConfirm, createMessage } = useMessage();
-  // const [registerModal, { openModal }] = useModal();
+  const [registerModal, { openModal }] = useModal();
   const [registerDrawer] = useDrawer(); // , { openDrawer }
   const [registerDrawerView, { openDrawer: openDrawerView }] = useDrawer();
 
@@ -111,16 +123,19 @@
     },
   ]) as any;
 
-  const [registerTable, { reload, getSelectRowKeys }] = useTable({
+  const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({
     title: '',
     api: LogQueryPage,
+    batchDelApi: LogRemove,
+    batchExportApi: LogLoginExport,
+    exportAuthList: ['sys:log:export:login'],
+    delAuthList: ['sys:log:remove'],
     rowKey: 'id',
     columns,
     showIndexColumn: true,
     rowSelection: { type: 'checkbox' },
     formConfig: {
       labelWidth: 120,
-      schemas: searchFormSchema,
       autoSubmitOnEnter: true,
       baseColProps: { xs: 24, sm: 12, md: 12, lg: 8 },
       resetButtonOptions: {
@@ -148,20 +163,10 @@
     });
   }
 
-  //   // 新增按钮事件
-  //   function handleCreate() {
-  //     openDrawer(true, {
-  //       isUpdate: false,
-  //     });
-  //   }
-
-  //   // 编辑按钮事件
-  //   function handleEdit(record: Recordable) {
-  //     openDrawer(true, {
-  //       record,
-  //       isUpdate: true,
-  //     });
-  //   }
+  // 导入按钮
+  function handleImport() {
+    openModal(true);
+  }
 
   // 删除按钮事件
   async function handleDelete(record: Recordable) {
@@ -170,16 +175,21 @@
       createMessage.success('删除成功!');
       await reload();
     } else {
-      createConfirm({
-        content: '你确定要删除?',
-        iconType: 'warning',
-        onOk: async () => {
-          const keys = getSelectRowKeys();
-          await LogRemove(keys);
-          createMessage.success('删除成功!');
-          await reload();
-        },
-      });
+      const keys = getSelectRowKeys();
+      if (keys.length > 0) {
+        createConfirm({
+          content: '你确定要删除?',
+          iconType: 'warning',
+          onOk: async () => {
+            await LogRemove(keys);
+            createMessage.success('删除成功!');
+            await reload();
+            clearSelectedRowKeys();
+          },
+        });
+      } else {
+        createMessage.warning('请选择要删除的数据');
+      }
     }
   }
   // 表格点击字段排序
@@ -196,8 +206,16 @@
   }
 
   // 表格请求之前,对参数进行处理, 添加默认 排序
-  function handleBeforeFetch(params) {
-    return { ...params, orders: tableSort.value };
+  async function handleBeforeFetch(params) {
+    if (!typeOptions.value && !tabSelected.value) {
+      typeOptions.value = await listDictModel({ dictCode: 'sys_login_log_type' });
+      typeOptions.value = typeOptions.value.map(ele => {
+        ele.key = ele.value;
+        return ele;
+      });
+      tabSelected.value = typeOptions.value[0].value;
+    }
+    return { ...params, orders: tableSort.value, type: tabSelected.value, opName: opName.value };
   }
 
   // 弹窗回调事件
@@ -206,4 +224,14 @@
     console.log(values);
     await reload();
   }
+
+  // 组件回调
+  async function callTab(data) {
+    tabSelected.value = data.value;
+    await reload();
+  }
+  async function callForm(data) {
+    opName.value = data.opName;
+    await reload();
+  }
 </script>

+ 7 - 34
src/views/monitor/onlineUser/index.vue

@@ -1,25 +1,8 @@
 <template>
   <div class="p-4">
-    <!-- <Row class="mb-2">
-      <Card class="w-1/4" style="width: 25%; text-align: center">
-        <img src="http://n.sinaimg.cn/sinacn12/149/w539h410/20181011/9663-hktxqai5109344.jpg" />
-        <p>当前会话数:16</p>
-      </Card>
-
-      <Card class="w-1/4" style="width: 25%; text-align: center">
-        <img src="http://n.sinaimg.cn/sinacn12/149/w539h410/20181011/9663-hktxqai5109344.jpg" />
-        <p>最大令牌发放数:162</p>
-      </Card>
-
-      <Card class="w-1/4" style="width: 25%; text-align: center">
-        <img src="http://n.sinaimg.cn/sinacn12/149/w539h410/20181011/9663-hktxqai5109344.jpg" />
-        <p>一小时内新增:0</p>
-      </Card>
-      <Card class="w-1/4" style="width: 25%; text-align: center">
-        <img src="http://n.sinaimg.cn/sinacn12/149/w539h410/20181011/9663-hktxqai5109344.jpg" />
-        <p>B/C端占比:1/0</p>
-      </Card>
-    </Row> -->
+    <div>
+      <XTTitle title="在线用户" :go-back="true" />
+    </div>
     <BasicTable @register="registerTable">
       <template #bodyCell="{ column, record }">
         <template v-if="column.key === 'tenantName'">
@@ -28,11 +11,6 @@
         <template v-if="column.key === 'lastLoginTime'">
           <span>{{ formatToDateTime(record.lastLoginTime) }}</span>
         </template>
-        <!-- <template #userPlatform="{ record }">
-        <Tag :color="record.userPlatform === 'WEB' ? 'success' : 'processing'">
-          {{ formatDictValue(sysUserPlatformOptions, record.userPlatform) }}
-        </Tag>
-      </template> -->
         <template v-if="column.key === 'action'">
           <TableAction
             :actions="[
@@ -42,6 +20,7 @@
                 onClick: handletoken.bind(null, record),
               },
               {
+                auth: 'monitor:online:forceLogout',
                 tooltip: '强制退出',
                 label: '强制退出',
                 color: 'error',
@@ -60,17 +39,16 @@
 </template>
 <script lang="ts" setup>
   import { onBeforeMount, ref } from 'vue';
-  // import { Tag } from 'ant-design-vue';
-  import { BasicTable, useTable, TableAction } from '/@/components/Table';
-  // import { Card, Row } from 'ant-design-vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/TableCard';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { columns, searchFormSchema } from './data';
   import formDrawer from './FormDrawer.vue';
   import { onlineList, onlineForceLogout } from '/@/api/monitor/onlineApi';
   import { listDictModel } from '/@/api/common';
   import { formatToDateTime } from '/@/utils/dateUtil';
-  // import { formatDictValue } from '/@/utils';
   import { useDrawer } from '/@/components/Drawer';
+
+  import { XTTitle } from '/@/components/XTTitle/index';
   const [registerDrawer, { openDrawer }] = useDrawer();
 
   const sysUserPlatformOptions = ref([]);
@@ -80,7 +58,6 @@
 
   const { createMessage } = useMessage();
   const [registerTable, { reload }] = useTable({
-    title: '在线用户列表 ',
     api: onlineList,
     rowKey: 'token',
     columns,
@@ -97,12 +74,10 @@
       },
     },
     showIndexColumn: true,
-    // useSearchForm: true,
     showTableSetting: true,
     bordered: true,
     striped: true,
     actionColumn: {
-      // auth: ['monitor:onlineUser:forceLogout'],
       width: 160,
       title: '操作',
       dataIndex: 'action',
@@ -116,14 +91,12 @@
 
   // 强制退出按钮事件
   async function handleForce(record: Recordable) {
-    console.log(record, '2222222222222222');
     await onlineForceLogout({ sessionId: record.sessionId, loginType: 'login' });
     createMessage.success('强退成功!');
     await reload();
   }
   // 退出token
   async function handletoken(record) {
-    console.log(record, '2222222222222222');
     openDrawer(true, {
       record,
     });

+ 12 - 39
src/views/monitor/operLog/index.vue

@@ -42,7 +42,7 @@
                 onClick: handleView.bind(null, record),
               },
               {
-                auth: 'sys:log:remove	',
+                auth: 'sys:log:remove',
                 icon: 'icon-delete|iconfont',
                 tooltip: '删除',
                 label: '删除',
@@ -57,17 +57,6 @@
           />
         </template>
       </template>
-      <template #toolbar>
-        <Button
-          v-auth="['sys:log:remove']"
-          type="primary"
-          danger
-          @click="handleDelete(null)"
-          preIcon="icon-delete|iconfont"
-        >
-          批量删除
-        </Button>
-      </template>
     </BasicTable>
     <FormDrawer @register="registerDrawer" @success="handleSuccess" />
     <ViewDrawer @register="registerDrawerView" @success="handleSuccess" />
@@ -76,11 +65,8 @@
 <script lang="ts" setup>
   import { onBeforeMount, ref } from 'vue';
   import { Tag } from 'ant-design-vue';
-  import { Button } from '/@/components/Button';
-
   import { BasicTable, useTable, TableAction } from '/@/components/TableCard';
 
-  // import { useModal } from '/@/components/Modal';
   import { useMessage } from '/@/hooks/web/useMessage';
   import FormDrawer from './formDrawer.vue';
   import ViewDrawer from './viewDrawer.vue';
@@ -114,14 +100,8 @@
   const responseTypeOptions = ref();
   const resultJsonOptions = ref();
   onBeforeMount(async () => {
-    typeOptions.value = await listDictModel({ dictCode: 'sys_log_type' });
     responseTypeOptions.value = await listDictModel({ dictCode: 'sys_response_type' });
     resultJsonOptions.value = await listDictModel({ dictCode: 'sys_response_type' });
-    typeOptions.value = typeOptions.value.map(ele => {
-      ele.key = ele.value;
-      return ele;
-    });
-    tabSelected.value = typeOptions.value[0].value;
   });
 
   const { createConfirm, createMessage } = useMessage();
@@ -139,8 +119,8 @@
   const [registerTable, { reload, getSelectRowKeys }] = useTable({
     api: LogQueryPage,
     batchDelApi: LogRemove,
-    batchExportApi: LogExport, // 目前调用的是删除接口
-    exportAuthList: ['sys:log:export'],
+    batchExportApi: LogExport,
+    exportAuthList: ['sys:log:export:operation'],
     delAuthList: ['sys:log:remove'],
     rowKey: 'id',
     columns,
@@ -163,21 +143,6 @@
     });
   }
 
-  //   // 新增按钮事件
-  //   function handleCreate() {
-  //     openDrawer(true, {
-  //       isUpdate: false,
-  //     });
-  //   }
-
-  //   // 编辑按钮事件
-  //   function handleEdit(record: Recordable) {
-  //     openDrawer(true, {
-  //       record,
-  //       isUpdate: true,
-  //     });
-  //   }
-
   // 删除按钮事件
   async function handleDelete(record: Recordable) {
     if (record) {
@@ -211,7 +176,15 @@
   }
 
   // 表格请求之前,对参数进行处理, 添加默认 排序
-  function handleBeforeFetch(params) {
+  async function handleBeforeFetch(params) {
+    if (!typeOptions.value && !tabSelected.value) {
+      typeOptions.value = await listDictModel({ dictCode: 'sys_log_type' });
+      typeOptions.value = typeOptions.value.map(ele => {
+        ele.key = ele.value;
+        return ele;
+      });
+      tabSelected.value = typeOptions.value[0].value;
+    }
     return { ...params, orders: tableSort.value, type: tabSelected.value, opName: opName.value };
   }
 

+ 0 - 92
src/views/sys/account/center/Application.vue

@@ -1,92 +0,0 @@
-<template>
-  <List :class="prefixCls">
-    <a-row :gutter="16">
-      <template v-for="item in list" :key="item.title">
-        <a-col :span="6">
-          <ListItem>
-            <Card :hoverable="true" :class="`${prefixCls}__card`">
-              <div :class="`${prefixCls}__card-title`">
-                <Icon class="icon" v-if="item.icon" :icon="item.icon" :color="item.color" />
-                {{ item.title }}
-              </div>
-              <div :class="`${prefixCls}__card-num`">
-                活跃用户:<span>{{ item.active }}</span> 万
-              </div>
-              <div :class="`${prefixCls}__card-num`">
-                新增用户:<span>{{ item.new }}</span>
-              </div>
-              <Icon
-                :class="`${prefixCls}__card-download`"
-                v-if="item.download"
-                :icon="item.download"
-              />
-            </Card>
-          </ListItem>
-        </a-col>
-      </template>
-    </a-row>
-  </List>
-</template>
-<script lang="ts">
-  import { defineComponent } from 'vue';
-  import { List, Card, Row, Col } from 'ant-design-vue';
-  import Icon from '/@/components/Icon/index';
-  import { applicationList } from './data';
-
-  export default defineComponent({
-    components: {
-      List,
-      ListItem: List.Item,
-      Card,
-      Icon,
-      [Row.name]: Row,
-      [Col.name]: Col,
-    },
-    setup() {
-      return {
-        prefixCls: 'account-center-application',
-        list: applicationList,
-      };
-    },
-  });
-</script>
-<style lang="less">
-  .account-center-application {
-    &__card {
-      width: 100%;
-      margin-bottom: -12px;
-
-      .ant-card-body {
-        padding: 16px;
-      }
-
-      &-title {
-        margin-bottom: 5px;
-        font-size: 16px;
-        font-weight: 500;
-
-        .icon {
-          margin-top: -5px;
-          font-size: 22px;
-        }
-      }
-
-      &-num {
-        margin-left: 24px;
-        line-height: 36px;
-        color: @text-color-secondary;
-
-        span {
-          margin-left: 5px;
-          font-size: 18px;
-        }
-      }
-
-      &-download {
-        float: right;
-        font-size: 20px !important;
-        color: @primary-color;
-      }
-    }
-  }
-</style>

+ 0 - 97
src/views/sys/account/center/Article.vue

@@ -1,97 +0,0 @@
-<template>
-  <List item-layout="vertical" :class="prefixCls">
-    <template v-for="item in list" :key="item.title">
-      <ListItem>
-        <ListItemMeta>
-          <template #description>
-            <div :class="`${prefixCls}__content`">
-              {{ item.content }}
-            </div>
-          </template>
-          <template #title>
-            <p :class="`${prefixCls}__title`">
-              {{ item.title }}
-            </p>
-            <div>
-              <template v-for="tag in item.description" :key="tag">
-                <Tag class="mb-2">
-                  {{ tag }}
-                </Tag>
-              </template>
-            </div>
-          </template>
-        </ListItemMeta>
-        <div>
-          <template v-for="action in actions" :key="action.text">
-            <div :class="`${prefixCls}__action`">
-              <Icon
-                v-if="action.icon"
-                :class="`${prefixCls}__action-icon`"
-                :icon="action.icon"
-                :color="action.color"
-              />
-              {{ action.text }}
-            </div>
-          </template>
-          <span :class="`${prefixCls}__time`">{{ item.time }}</span>
-        </div>
-      </ListItem>
-    </template>
-  </List>
-</template>
-<script lang="ts">
-  import { defineComponent } from 'vue';
-  import { List, Tag } from 'ant-design-vue';
-  import Icon from '/@/components/Icon/index';
-  import { actions, articleList } from './data';
-
-  export default defineComponent({
-    components: {
-      List,
-      ListItem: List.Item,
-      ListItemMeta: List.Item.Meta,
-      Tag,
-      Icon,
-    },
-    setup() {
-      return {
-        prefixCls: 'account-center-article',
-        list: articleList,
-        actions,
-      };
-    },
-  });
-</script>
-<style lang="less" scoped>
-  .account-center-article {
-    &__title {
-      margin-bottom: 12px;
-      font-size: 18px;
-    }
-
-    &__content {
-      color: rgba(0, 0, 0, 0.65);
-    }
-
-    &__action {
-      display: inline-block;
-      padding: 0 16px;
-      color: rgba(0, 0, 0, 0.45);
-
-      &:nth-child(1),
-      &:nth-child(2) {
-        border-right: 1px solid rgba(206, 206, 206, 0.4);
-      }
-
-      &-icon {
-        margin-right: 3px;
-      }
-    }
-
-    &__time {
-      position: absolute;
-      right: 20px;
-      color: rgba(0, 0, 0, 0.45);
-    }
-  }
-</style>

+ 0 - 71
src/views/sys/account/center/Project.vue

@@ -1,71 +0,0 @@
-<template>
-  <List :class="prefixCls">
-    <a-row :gutter="16">
-      <template v-for="item in list" :key="item.title">
-        <a-col :span="6">
-          <ListItem>
-            <Card :hoverable="true" :class="`${prefixCls}__card`">
-              <img :src="demoImg" />
-              <div :class="`${prefixCls}__card-title`">
-                {{ item.title }}
-              </div>
-              <div :class="`${prefixCls}__card-content`">
-                {{ item.content }}
-              </div>
-            </Card>
-          </ListItem>
-        </a-col>
-      </template>
-    </a-row>
-  </List>
-</template>
-<script lang="ts">
-  import { defineComponent } from 'vue';
-  import { List, Card, Row, Col } from 'ant-design-vue';
-  import demoImg from '/@/assets/images/header.png';
-  import { projectList } from './data';
-
-  export default defineComponent({
-    components: {
-      List,
-      ListItem: List.Item,
-      Card,
-      [Row.name]: Row,
-      [Col.name]: Col,
-    },
-    setup() {
-      return {
-        prefixCls: 'account-center-project',
-        list: projectList,
-        demoImg,
-      };
-    },
-  });
-</script>
-<style lang="less">
-  .account-center-project {
-    &__card {
-      width: 100%;
-
-      .ant-card-body {
-        padding: 0 0 24px 0;
-      }
-
-      img {
-        width: 100%;
-        height: 130px;
-      }
-
-      &-title {
-        margin: 5px 10px;
-        font-size: 16px;
-        font-weight: 500;
-        color: rgba(0, 0, 0, 0.85);
-      }
-
-      &-content {
-        margin: 5px 10px;
-      }
-    }
-  }
-</style>

+ 58 - 0
src/views/sys/account/center/basicInformation.vue

@@ -0,0 +1,58 @@
+<template>
+  <div>
+    <BasicForm @register="registerForm" layout="vertical" />
+    <a-button @click="handleReset">重置</a-button>
+    <a-button type="primary" @click="handleSubmit">保存</a-button>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { BasicForm, useForm } from '/@/components/Form';
+  // import { useMessage } from '/@/hooks/web/useMessage';
+  import { InformationFormSchema } from './data';
+  // import { Upload } from 'ant-design-vue';
+  import { userAccountInfo } from '/@/api/sys/userAccountApi';
+  import { onMounted } from 'vue';
+
+  onMounted(async () => {
+    const res = await userAccountInfo();
+    console.log('res::::::::::::', res);
+    await setFieldsValue({ ...res.basicInfo });
+  });
+
+  // const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, validate }] = useForm({
+    labelWidth: 120,
+    schemas: InformationFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+    baseColProps: {
+      span: 18,
+    },
+    wrapperCol: {
+      span: 24,
+    },
+  });
+  // 提交按钮事件
+  async function handleSubmit() {
+    const values = await validate();
+    console.log('abc', values);
+    //   await sysSettingEdit(values);
+    //   createMessage.success('修改成功');
+  }
+  async function handleReset() {
+    const res = await userAccountInfo();
+
+    await setFieldsValue(res);
+  }
+</script>
+
+<style lang="less" scoped>
+  .img-avatar {
+    display: block;
+    width: 60px;
+    height: 60px;
+  }
+</style>

+ 93 - 0
src/views/sys/account/center/data.ts

@@ -0,0 +1,93 @@
+import { FormSchema } from '/@/components/Form';
+import { gender } from '/@/utils/filters';
+
+export const InformationFormSchema: FormSchema[] = [
+  {
+    label: '用户昵称',
+    field: 'username',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入用户昵称',
+    },
+  },
+  {
+    label: '手机号码',
+    field: 'phone',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入手机号码',
+    },
+    rules: [{ len: 11 }],
+  },
+  {
+    label: '用户邮箱',
+    field: 'email',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入用户邮箱',
+    },
+  },
+  {
+    label: '性别',
+    field: 'gender',
+    required: true,
+    component: 'RadioGroup',
+    componentProps: {
+      options: gender,
+    },
+  },
+];
+export const pswFormSchema: FormSchema[] = [
+  {
+    label: '旧密码',
+    field: 'oldPsw',
+    required: true,
+    component: 'InputPassword',
+    componentProps: {
+      placeholder: '请输入旧密码',
+    },
+  },
+  {
+    label: '新密码',
+    field: 'newPsw',
+    required: true,
+    component: 'InputPassword',
+    componentProps: {
+      placeholder: '请输入新密码',
+    },
+    rules: [
+      {
+        min: 6,
+      },
+    ],
+  },
+  {
+    label: '确认密码',
+    field: 'confirmPsw',
+    required: true,
+    component: 'InputPassword',
+    componentProps: {
+      rules: [{ min: 6 }],
+      placeholder: '请重新输入新密码',
+    },
+    dynamicRules: ({ values }) => {
+      return [
+        {
+          required: true,
+          validator: (_, value) => {
+            if (!value) {
+              return Promise.reject('不能为空');
+            }
+            if (value !== values.newPsw) {
+              return Promise.reject('两次输入的密码不一致!');
+            }
+            return Promise.resolve();
+          },
+        },
+      ];
+    },
+  },
+];

+ 0 - 132
src/views/sys/account/center/data.tsx

@@ -1,132 +0,0 @@
-export interface ListItem {
-  title: string;
-  icon: string;
-  color?: string;
-}
-
-export interface TabItem {
-  key: string;
-  name: string;
-  component: string;
-}
-
-export const tags: string[] = [
-  '很有想法的',
-  '专注设计',
-  '川妹子',
-  '大长腿',
-  '海纳百川',
-  '前端开发',
-  'vue3',
-];
-<span class="iconify" data-icon="jam:codepen-circle" data-inline="false"></span>;
-export const teams: ListItem[] = [
-  {
-    icon: 'ri:alipay-fill',
-    title: '科学搬砖组',
-    color: '#ff4000',
-  },
-  {
-    icon: 'emojione-monotone:letter-a',
-    title: '中二少年团',
-    color: '#7c51b8',
-  },
-  {
-    icon: 'ri:alipay-fill',
-    title: '高逼格设计',
-    color: '#00adf7',
-  },
-  {
-    icon: 'jam:codepen-circle',
-    title: '程序员日常',
-    color: '#00adf7',
-  },
-  {
-    icon: 'fa:behance-square',
-    title: '科学搬砖组',
-    color: '#7c51b8',
-  },
-  {
-    icon: 'jam:codepen-circle',
-    title: '程序员日常',
-    color: '#ff4000',
-  },
-];
-
-export const details: ListItem[] = [
-  {
-    icon: 'ic:outline-contacts',
-    title: '交互专家',
-  },
-  {
-    icon: 'grommet-icons:cluster',
-    title: '某某某事业群',
-  },
-  {
-    icon: 'bx:bx-home-circle',
-    title: '福建省厦门市',
-  },
-];
-
-export const achieveList: TabItem[] = [
-  {
-    key: '1',
-    name: '文章',
-    component: 'Article',
-  },
-  {
-    key: '2',
-    name: '应用',
-    component: 'Application',
-  },
-  {
-    key: '3',
-    name: '项目',
-    component: 'Project',
-  },
-];
-
-export const actions: any[] = [
-  { icon: 'clarity:star-line', text: '156', color: '#018ffb' },
-  { icon: 'bx:bxs-like', text: '156', color: '#459ae8' },
-  { icon: 'bx:bxs-message-dots', text: '2', color: '#42d27d' },
-];
-
-export const articleList = (() => {
-  const result: any[] = [];
-  for (let i = 0; i < 4; i++) {
-    result.push({
-      title: 'Vben Admin',
-      description: ['Vben', '设计语言', 'Typescript'],
-      content: '基于Vue Next, TypeScript, Ant Design实现的一套完整的企业级后台管理系统。',
-      time: '2020-11-14 11:20',
-    });
-  }
-  return result;
-})();
-
-export const applicationList = (() => {
-  const result: any[] = [];
-  for (let i = 0; i < 8; i++) {
-    result.push({
-      title: 'Vben Admin',
-      icon: 'emojione-monotone:letter-a',
-      color: '#1890ff',
-      active: '100',
-      new: '1,799',
-      download: 'bx:bx-download',
-    });
-  }
-  return result;
-})();
-
-export const projectList = (() => {
-  const result: any[] = [];
-  for (let i = 0; i < 8; i++) {
-    result.push({
-      title: 'Vben Admin',
-      content: '基于Vue Next, TypeScript, Ant Design实现的一套完整的企业级后台管理系统。',
-    });
-  }
-  return result;
-})();

+ 65 - 0
src/views/sys/account/center/editPws.vue

@@ -0,0 +1,65 @@
+<template>
+  <div>
+    <BasicForm @register="registerForm" layout="vertical" />
+    <a-button @click="handleReset">重置</a-button>
+    <a-button type="primary" @click="handleSubmit">保存</a-button>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { pswFormSchema } from './data';
+  // import { Upload } from 'ant-design-vue';
+  import { userAccountInfo, editPsw } from '/@/api/sys/userAccountApi';
+  import { onMounted, ref } from 'vue';
+  const id = ref('');
+  // const errorMsg = ref(null);
+  onMounted(async () => {
+    const res = await userAccountInfo();
+    console.log('res::::::::::::', res.basicInfo.org.id);
+    id.value = res.basicInfo.org.id;
+    await setFieldsValue({ ...res.basicInfo });
+  });
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, validate }] = useForm({
+    labelWidth: 120,
+    schemas: pswFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+    baseColProps: {
+      span: 18,
+    },
+    wrapperCol: {
+      span: 24,
+    },
+  });
+  // 提交按钮事件
+  async function handleSubmit() {
+    const values = await validate();
+    console.log('abc', values);
+    values.id = id.value;
+    try {
+      await editPsw(values);
+      createMessage.success('修改成功');
+    } catch (err) {
+      console.log('error', err);
+      // console.log('Error', );
+    }
+  }
+  async function handleReset() {
+    const res = await userAccountInfo();
+
+    await setFieldsValue(res);
+  }
+</script>
+
+<style lang="less" scoped>
+  .img-avatar {
+    display: block;
+    width: 60px;
+    height: 60px;
+  }
+</style>

+ 62 - 147
src/views/sys/account/center/index.vue

@@ -1,155 +1,70 @@
 <template>
-  <div :class="prefixCls">
-    <a-row :class="`${prefixCls}-top`">
-      <a-col :span="9" :class="`${prefixCls}-col`">
-        <a-row>
-          <a-col :span="8">
-            <div :class="`${prefixCls}-top__avatar`">
-              <img width="70" :src="avatar" />
-              <span>Vben</span>
-              <div>海纳百川,有容乃大</div>
-            </div>
-          </a-col>
-          <a-col :span="16">
-            <div :class="`${prefixCls}-top__detail`">
-              <template v-for="detail in details" :key="detail.title">
-                <p>
-                  <Icon :icon="detail.icon" />
-                  {{ detail.title }}
-                </p>
-              </template>
-            </div>
-          </a-col>
-        </a-row>
-      </a-col>
-      <a-col :span="7" :class="`${prefixCls}-col`">
-        <CollapseContainer title="标签" :canExpan="false">
-          <template v-for="tag in tags" :key="tag">
-            <Tag class="mb-2">
-              {{ tag }}
-            </Tag>
-          </template>
-        </CollapseContainer>
-      </a-col>
-      <a-col :span="8" :class="`${prefixCls}-col`">
-        <CollapseContainer :class="`${prefixCls}-top__team`" title="团队" :canExpan="false">
-          <div v-for="(team, index) in teams" :key="index" :class="`${prefixCls}-top__team-item`">
-            <Icon :icon="team.icon" :color="team.color" />
-            <span>{{ team.title }}</span>
-          </div>
-        </CollapseContainer>
-      </a-col>
-    </a-row>
-    <div :class="`${prefixCls}-bottom`">
-      <Tabs>
-        <template v-for="item in achieveList" :key="item.key">
-          <TabPane :tab="item.name">
-            <component :is="item.component" />
-          </TabPane>
-        </template>
-      </Tabs>
-    </div>
-  </div>
+  <PageWrapper dense contentFullHeight contentClass="flex">
+    <a-card class="w-1/2 xl:w-1/2">
+      <List>
+        <h1>11111</h1>
+        <ListItem>11111</ListItem>
+        <ListItem>22222</ListItem>
+        <ListItem>33333</ListItem>
+        <ListItem>44444</ListItem>
+      </List>
+    </a-card>
+    <a-card
+      class="w-1/2 xl:w-1/2"
+      :active-tab-key="noTitleKey"
+      :tab-list="tabListNoTitle"
+      @tabChange="key => onTabChange(key, 'noTitleKey')"
+    >
+      <p v-if="noTitleKey === 'basicInformation'">
+        <basicInformation />
+      </p>
+      <p v-if="noTitleKey === 'editPws'">
+        <editPws />
+      </p>
+      <p v-if="noTitleKey === 'socialContact'">
+        <socialContact />
+      </p>
+    </a-card>
+  </PageWrapper>
 </template>
 
-<script lang="ts">
-  import { Tag, Tabs, Row, Col } from 'ant-design-vue';
-  import { defineComponent, computed } from 'vue';
-  import { CollapseContainer } from '/@/components/Container/index';
-  import Icon from '/@/components/Icon/index';
-  import Article from './Article.vue';
-  import Application from './Application.vue';
-  import Project from './Project.vue';
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { List, ListItem } from 'ant-design-vue';
+  import { PageWrapper } from '/@/components/Page';
+  // import { userAccountInfo } from '/@/api/sys/userAccountApi';
+  import basicInformation from './basicInformation.vue';
+  import editPws from './editPws.vue';
+  import socialContact from './socialContact.vue';
+  // onMounted(async () => {
+  //   const res = await userAccountInfo();
+  //   console.log(res);
+  // });
 
-  import headerImg from '/@/assets/images/header.png';
-  import { tags, teams, details, achieveList } from './data';
-  import { useUserStore } from '/@/store/modules/user';
-
-  export default defineComponent({
-    components: {
-      CollapseContainer,
-      Icon,
-      Tag,
-      Tabs,
-      TabPane: Tabs.TabPane,
-      Article,
-      Application,
-      Project,
-      [Row.name]: Row,
-      [Col.name]: Col,
+  const key = ref('basicInformation');
+  const noTitleKey = ref('basicInformation');
+  // :tab-list="tabListNoTitle"
+  const tabListNoTitle = [
+    {
+      key: 'basicInformation',
+      tab: '基本资料',
     },
-    setup() {
-      const userStore = useUserStore();
-      const avatar = computed(() => userStore.getUserInfo.avatar || headerImg);
-      return {
-        prefixCls: 'account-center',
-        avatar,
-        tags,
-        teams,
-        details,
-        achieveList,
-      };
+    {
+      key: 'editPws',
+      tab: '修改密码',
     },
-  });
-</script>
-<style lang="less" scoped>
-  .account-center {
-    &-col:not(:last-child) {
-      padding: 0 10px;
-
-      &:not(:last-child) {
-        border-right: 1px dashed rgb(206, 206, 206, 0.5);
-      }
-    }
-
-    &-top {
-      padding: 10px;
-      margin: 16px 16px 12px 16px;
-      background-color: @component-background;
-      border-radius: 3px;
-
-      &__avatar {
-        text-align: center;
-
-        img {
-          margin: auto;
-          border-radius: 50%;
-        }
-
-        span {
-          display: block;
-          font-size: 20px;
-          font-weight: 500;
-        }
-
-        div {
-          margin-top: 3px;
-          font-size: 12px;
-        }
-      }
-
-      &__detail {
-        padding-left: 20px;
-        margin-top: 15px;
-      }
-
-      &__team {
-        &-item {
-          display: inline-block;
-          padding: 4px 24px;
-        }
-
-        span {
-          margin-left: 3px;
-        }
-      }
-    }
+    {
+      key: 'socialContact',
+      tab: '社交信息',
+    },
+  ];
 
-    &-bottom {
-      padding: 10px;
-      margin: 0 16px 16px 16px;
-      background-color: @component-background;
-      border-radius: 3px;
+  const onTabChange = (value, type) => {
+    if (type === 'key') {
+      key.value = value;
+    } else if (type === 'noTitleKey') {
+      noTitleKey.value = value;
     }
-  }
-</style>
+  };
+</script>
+<style lang="less"></style>

+ 80 - 0
src/views/sys/account/center/socialContact.vue

@@ -0,0 +1,80 @@
+<template>
+  <div>
+    <BasicForm @register="registerForm" layout="vertical">
+      <template #logo>
+        <Upload
+          name="file"
+          :showUploadList="false"
+          :beforeUpload="handleBeforeUpload"
+          accept=".jpg,.jpeg,.gif,.png,.webp"
+        >
+          <img :src="logo" class="img-avatar" />
+        </Upload>
+      </template>
+    </BasicForm>
+    <a-button @click="handleReset">重置</a-button>
+    <a-button type="primary" @click="handleSubmit">保存</a-button>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { InformationFormSchema } from './data';
+  import { Upload } from 'ant-design-vue';
+  import { sysSettingEdit, sysSettingDetail, sysSettingDefault } from '/@/api/sys/sysSettingApi';
+  import { onMounted, ref } from 'vue';
+  import { fileToBase64 } from '/@/utils/file/base64Conver';
+
+  onMounted(async () => {
+    const res = await sysSettingDetail();
+    logo.value = res.logo;
+    await setFieldsValue({ ...res });
+  });
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, validate }] = useForm({
+    labelWidth: 120,
+    schemas: InformationFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+    baseColProps: {
+      span: 18,
+    },
+    wrapperCol: {
+      span: 24,
+    },
+  });
+
+  const logo = ref(null);
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    const values = await validate();
+    console.log('abc', values);
+    if (logo.value) {
+      values.logo = logo.value;
+    }
+    await sysSettingEdit(values);
+    createMessage.success('修改成功');
+  }
+  async function handleReset() {
+    const res = await sysSettingDefault();
+
+    await setFieldsValue(res);
+  }
+  async function handleBeforeUpload(file) {
+    logo.value = await fileToBase64(file);
+    return false;
+  }
+</script>
+
+<style lang="less" scoped>
+  .img-avatar {
+    display: block;
+    width: 60px;
+    height: 60px;
+  }
+</style>

+ 17 - 12
src/views/sys/sysPortal/index.vue

@@ -112,7 +112,7 @@
     },
   ]) as any;
 
-  const [registerTable, { reload, getSelectRowKeys }] = useTable({
+  const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({
     title: ' ',
     api: sysPortalQueryPage,
     rowKey: 'id',
@@ -171,19 +171,24 @@
       createMessage.success('删除成功!');
       await reload();
     } else {
-      createConfirm({
-        content: '你确定要删除?',
-        iconType: 'warning',
-        onOk: async () => {
-          const keys = getSelectRowKeys();
-          await sysPortalRemove(keys);
-          createMessage.success('删除成功!');
-          await reload();
-        },
-      });
+      const keys = getSelectRowKeys();
+      if (keys.length > 0) {
+        createConfirm({
+          content: '你确定要删除?',
+          iconType: 'warning',
+          onOk: async () => {
+            const keys = getSelectRowKeys();
+            await sysPortalRemove(keys);
+            createMessage.success('删除成功!');
+            await reload();
+            clearSelectedRowKeys();
+          },
+        });
+      } else {
+        createMessage.warning('请选择要删除的数据');
+      }
     }
   }
-
   // 分配菜单按钮事件
   function handlePortalMenu(record: Recordable) {
     console.log(record);

+ 1 - 0
src/views/sys/sysSetting/data.ts

@@ -55,6 +55,7 @@ export const dataFormSchema: FormSchema[] = [
     componentProps: {
       placeholder: '请输入系统默认密码',
     },
+    rules: [{ min: 6 }],
     colProps: {
       span: 8,
     },

+ 45 - 0
src/views/sys/sysSetting/disclaimerConfig.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="disclaimerEditor">
+    <div>
+      <TextEditor :height="600" @change="handleChange" />
+    </div>
+    <div class="btn">
+      <a-button @click="handleReset" v-auth="['sys:config:edit']" class="mr-4">重置</a-button>
+      <a-button type="primary" @click="handleSubmit" v-auth="['sys:config:edit']">保存</a-button>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import TextEditor from '/@/components/Form/src/components/TextEditor.vue';
+  import { useMessage } from '/@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+
+  const disclaimerContent = ref();
+  function handleReset() {}
+
+  function handleSubmit() {
+    console.log(disclaimerContent.value);
+    if (!disclaimerContent.value) {
+      createMessage.error('请输入免责声明后提交');
+      return;
+    }
+  }
+
+  function handleChange(val) {
+    console.log(val);
+    disclaimerContent.value = val;
+  }
+</script>
+<style lang="less">
+  .disclaimerEditor {
+    width: 50%;
+    margin-left: 25%;
+  }
+
+  .btn {
+    margin-top: 30px;
+    float: right;
+  }
+</style>

+ 7 - 22
src/views/sys/sysSetting/index.vue

@@ -9,16 +9,17 @@
     <p v-if="noTitleKey === 'sysConfig'">
       <SysConfig />
     </p>
+
+    <p v-if="noTitleKey === 'disclaimerConfig'">
+      <DisclaimerConfig />
+    </p>
   </a-card>
 </template>
 
 <script setup lang="ts">
   import { ref } from 'vue';
   import SysConfig from './sysConfig.vue';
-
-  defineOptions({
-    name: 'SysSeting',
-  });
+  import DisclaimerConfig from './disclaimerConfig.vue';
   const key = ref('sysConfig');
   const noTitleKey = ref('sysConfig');
 
@@ -28,24 +29,8 @@
       tab: '系统配置',
     },
     {
-      key: 'emailConfig',
-      tab: '邮件配置',
-    },
-    {
-      key: 'smsConfig',
-      tab: '短信配置',
-    },
-    {
-      key: 'fileConfig',
-      tab: '文件配置',
-    },
-    {
-      key: 'thirdConfig',
-      tab: '第三方配置',
-    },
-    {
-      key: 'otherConfig',
-      tab: '其他配置',
+      key: 'disclaimerConfig',
+      tab: '免责声明',
     },
   ];
 

+ 15 - 11
src/views/sys/sysSetting/sysConfig.vue

@@ -7,13 +7,16 @@
           :showUploadList="false"
           :beforeUpload="handleBeforeUpload"
           accept=".jpg,.jpeg,.gif,.png,.webp"
+          :disabled="!hasPermission('sys:config:edit')"
         >
           <img :src="logo" class="img-avatar" />
         </Upload>
       </template>
     </BasicForm>
-    <a-button @click="handleReset">重置</a-button>
-    <a-button type="primary" @click="handleSubmit">保存</a-button>
+    <div>
+      <a-button @click="handleReset" v-auth="['sys:config:edit']" class="mr-4">重置</a-button>
+      <a-button type="primary" @click="handleSubmit" v-auth="['sys:config:edit']">保存</a-button>
+    </div>
   </div>
 </template>
 
@@ -25,7 +28,8 @@
   import { sysSettingEdit, sysSettingDetail, sysSettingDefault } from '/@/api/sys/sysSettingApi';
   import { onMounted, ref } from 'vue';
   import { fileToBase64 } from '/@/utils/file/base64Conver';
-
+  import { usePermission } from '/@/hooks/web/usePermission';
+  const { hasPermission } = usePermission();
   onMounted(async () => {
     const res = await sysSettingDetail();
     logo.value = res.logo;
@@ -34,17 +38,17 @@
 
   const { createMessage } = useMessage();
   const [registerForm, { setFieldsValue, validate }] = useForm({
-    labelWidth: 120,
-    schemas: dataFormSchema,
-    showActionButtonGroup: false,
-    actionColOptions: {
-      span: 23,
-    },
+    layout: 'vertical',
+    labelWidth: '100%',
     baseColProps: {
       span: 8,
     },
-    wrapperCol: {
-      span: 23,
+    baseRowGutter: [16, 8],
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    disabled: !hasPermission('sys:config:edit'),
+    actionColOptions: {
+      span: 4,
     },
   });
 

+ 16 - 11
src/views/sys/sysTenant/package/index.vue

@@ -128,7 +128,7 @@
     },
   ]) as any;
 
-  const [registerTable, { reload, getSelectRowKeys }] = useTable({
+  const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({
     title: ' ',
     api: sysTenantPackageQueryPage,
     rowKey: 'id',
@@ -187,16 +187,21 @@
       createMessage.success('删除成功!');
       await reload();
     } else {
-      createConfirm({
-        content: '你确定要删除?',
-        iconType: 'warning',
-        onOk: async () => {
-          const keys = getSelectRowKeys();
-          await sysTenantPackageRemove(keys);
-          createMessage.success('删除成功!');
-          await reload();
-        },
-      });
+      const keys = getSelectRowKeys();
+      if (keys.length > 0) {
+        createConfirm({
+          content: '你确定要删除?',
+          iconType: 'warning',
+          onOk: async () => {
+            await sysTenantPackageRemove(keys);
+            createMessage.success('删除成功!');
+            await reload();
+            clearSelectedRowKeys();
+          },
+        });
+      } else {
+        createMessage.warning('请选择要删除的数据');
+      }
     }
   }
   // 分配菜单按钮事件

Some files were not shown because too many files changed in this diff