vera_min 1 anno fa
parent
commit
df9cf4d9be
56 ha cambiato i file con 515 aggiunte e 5635 eliminazioni
  1. 11 1
      .env.development
  2. 1 5021
      package-lock.json
  3. 1 0
      package.json
  4. BIN
      public/imgs/notice/icon_c.png
  5. BIN
      public/imgs/notice/icon_m.png
  6. 1 1
      src/api/device/modbus.ts
  7. 1 0
      src/api/model/system/menu.ts
  8. 6 0
      src/api/scene/index.ts
  9. 12 2
      src/api/system/index.ts
  10. 8 17
      src/components/pagination/index.vue
  11. 18 4
      src/utils/download.ts
  12. 4 1
      src/utils/origin.ts
  13. 2 1
      src/utils/request.ts
  14. 1 0
      src/utils/request_ice104.ts
  15. 1 0
      src/utils/request_modbus.ts
  16. 8 0
      src/utils/validator.ts
  17. 3 3
      src/views/iot/certificate/index.vue
  18. 1 2
      src/views/iot/device/channel/component/detail.vue
  19. 18 3
      src/views/iot/device/instance/component/excel.vue
  20. 1 1
      src/views/iot/device/product/component/editOption.vue
  21. 16 2
      src/views/iot/device/product/component/editPro.vue
  22. 4 4
      src/views/iot/device/product/component/typeItem.vue
  23. 3 3
      src/views/iot/device/product/detail.vue
  24. 1 2
      src/views/iot/device/template/component/dataAreaDialog.vue
  25. 1 1
      src/views/iot/device/template/component/edit.vue
  26. 9 8
      src/views/iot/iotmanager/dashboard.vue
  27. 1 1
      src/views/iot/network/server/component/list.vue
  28. 1 1
      src/views/iot/network/tunnel/component/list.vue
  29. 0 0
      src/views/iot/noticeservices/config/index.vue
  30. 0 460
      src/views/iot/noticeservices/config/template.vue
  31. 2 2
      src/views/iot/scene/list/index.vue
  32. 0 1
      src/views/iot/scene/manage/component/actionItem.vue
  33. 2 0
      src/views/iot/scene/manage/component/actionType/deviceOut.vue
  34. 36 11
      src/views/iot/scene/manage/component/sceneItem.vue
  35. 8 5
      src/views/iot/scene/manage/detail.vue
  36. 9 2
      src/views/iot/scene/manage/edit.vue
  37. 1 1
      src/views/personal/index.vue
  38. 1 1
      src/views/system/api/component/bind.vue
  39. 16 2
      src/views/system/api/component/edit.vue
  40. 16 2
      src/views/system/api/index.vue
  41. 0 14
      src/views/system/application/edit.vue
  42. 1 5
      src/views/system/application/index.vue
  43. 1 1
      src/views/system/assess/totalIndex/component/addItem.vue
  44. 1 2
      src/views/system/assess/totalIndex/index.vue
  45. 1 23
      src/views/system/basicConfig/index.vue
  46. 1 1
      src/views/system/dict/index.vue
  47. 7 11
      src/views/system/manage/role/component/permission.vue
  48. 3 1
      src/views/system/manage/user/component/editUser.vue
  49. 76 0
      src/views/system/menu/component/api.vue
  50. 71 0
      src/views/system/menu/component/bind.vue
  51. 1 0
      src/views/system/menu/component/list.vue
  52. 7 1
      src/views/system/menu/index.vue
  53. 63 0
      src/views/system/monitor/lastLinesLog/index.vue
  54. 3 3
      src/views/system/monitor/plugin/index.vue
  55. 11 1
      vite.config.ts
  56. 43 6
      yarn.lock

+ 11 - 1
.env.development

@@ -1,4 +1,14 @@
 # VITE_SERVER_PROTOCOL = 'http:'
 # VITE_SERVER_HOSTNAME = '127.0.0.1:8200'
 VITE_SERVER_PROTOCOL = 'https:'
-VITE_SERVER_HOSTNAME = 'zhgy.sagoo.cn'
+VITE_SERVER_HOSTNAME = 'zhgy.sagoo.cn'
+
+# VITE_SERVER_PROTOCOL = 'http:'
+# VITE_SERVER_HOSTNAME = 'iot.server.mydig.net'
+
+
+# VITE_SERVER_PROTOCOL = 'http:'
+# VITE_SERVER_HOSTNAME = 'liangzi.server.mydig.net'
+
+# VITE_SERVER_PROTOCOL = 'http:'
+# VITE_SERVER_HOSTNAME = 'pg.server.mydig.net'

File diff suppressed because it is too large
+ 1 - 5021
package-lock.json


+ 1 - 0
package.json

@@ -73,6 +73,7 @@
     "sass-loader": "^12.6.0",
     "typescript": "^4.6.2",
     "vite": "^2.9.16",
+    "vite-plugin-compression": "^0.5.1",
     "vue-eslint-parser": "^8.3.0"
   },
   "browserslist": [

BIN
public/imgs/notice/icon_c.png


BIN
public/imgs/notice/icon_m.png


+ 1 - 1
src/api/device/modbus.ts

@@ -34,7 +34,7 @@ export default {
     deleteTemplate: (data: object) => post('/template/delete', data),
     editTemplate: (data: object) => post('/template/edit', data),
     importFile: (data: object, config: object) => post('/data_area/import', data, config),
-    exportFile: (params: object) => get('/data_area/import', params, {
+    exportFile: (params: object) => get('/data_area/export', params, {
       headers: {
         responseType: 'blob'
       }

+ 1 - 0
src/api/model/system/menu.ts

@@ -23,6 +23,7 @@ export interface ApiRow {
   parentId?: number; // parentId
   menuIds: number[]; // 名称
   types: 1 | 2; // 1 分类 2接口
+  apiTypes: string; // IOT modbus 104
   method: string; // 请求方式
   name: string; // 名称
   address: string; // 接口地址

+ 6 - 0
src/api/scene/index.ts

@@ -15,5 +15,11 @@ export default {
     editDetail: (data: any) => put('/scene/detail/edit', data),
     delDetail: (ids: number) => del('/scene/detail/delete', { ids }),
     getOneDetail: (data: any) => get('/scene/detail/list', data),
+  },
+  log:{
+    getList: (data: any) => get('/scene/scene_log/list', data),
+    getDetail: (data: any) => get('/scene/scene_log/get', data),
+    del: (ids: number) => del('/scene/scene_log/delete', { ids }),
+
   }
 }

+ 12 - 2
src/api/system/index.ts

@@ -16,6 +16,7 @@ export default {
     del: (id: number) => del('/system/api/del', { id }),
     edit: (data: object) => put('/system/api/edit', data),
     bindMenus: (bindMenus: any[]) => post('/system/api/bindMenus', { bindMenus }),
+    import: () => post('/system/api/import'),
   },
   menu: {
     getList: (params: object) => get('/system/menu/tree', params),
@@ -31,6 +32,10 @@ export default {
       edit: (data: object) => put('/system/menu/button/edit', data),
       setStatus: (id: number, menuId: number, status: number) => put('/system/menu/button/editStatus', { id, menuId, status })
     },
+    api: {
+      getList: (params: object) => get('/system/menu/api/tree', params),
+      add: (data: object) => post('/system/menu/api/add', data),
+    },
     list: {
       getList: (params: object) => get('/system/menu/column/tree', params),
       detail: (id: number) => get('/system/menu/column/detail', { id }),
@@ -159,8 +164,8 @@ export default {
     changeStatus: (data: object) => post('/system/blacklist/status', data),
   },
   basicConfig: {
-    getDetails: () => get('/getBaseSetting'),
-    setDetails: (data: object) => put('/editBaseSetting', data),
+    getDetails: (params: object) => get('/common/getSysConfigSetting', params),
+    setDetails: (data: object) => put('/common/editSysConfigSetting', data),
   },
   certificate: {
     getList: () => get('/system/certificate/getAll')
@@ -171,4 +176,9 @@ export default {
     queryThingConfig: (params: object) => get('/operate/remoteconf/queryThingConfig', params),
     saveThisConfig: (params: object) => post('/operate/remoteconf/addThingConfig', params),
   },
+  lastLinesLog: {
+    getList: (params: object) => get('/system/monitor/listLogs', params),
+    detail: (params: object) => get('/system/monitor/lastLinesLog', params),
+    down:(params:object)=>file('system/monitor/downloadLog',params),
+  },
 }

+ 8 - 17
src/components/pagination/index.vue

@@ -1,21 +1,11 @@
 <template>
-  <div :class="{'hidden':hidden}" class="pagination-container">
-    <el-pagination
-        :background="background"
-        v-model:current-page="currentPage"
-        v-model:page-size="pageSize"
-        :layout="layout"
-        :page-sizes="pageSizes"
-        :pager-count="pagerCount"
-        :total="total"
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-    />
+  <div :class="{ 'hidden': hidden }" class="pagination-container">
+    <el-pagination :background="background" v-model:current-page="currentPage" v-model:page-size="pageSize" :layout="layout" :page-sizes="pageSizes" :pager-count="pagerCount" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
   </div>
 </template>
 
 <script lang="ts">
-import { toRefs, defineComponent,computed } from 'vue';
+import { toRefs, defineComponent, computed } from 'vue';
 const props = {
   total: {
     required: true,
@@ -32,7 +22,7 @@ const props = {
   pageSizes: {
     type: Array,
     default() {
-      return [10, 20, 30, 50]
+      return [10, 20, 30, 50, 100, 200, 300, 500]
     }
   },
   // 移动端页码按钮的数量端默认值5
@@ -56,8 +46,8 @@ const props = {
 export default defineComponent({
   name: 'pagination',
   props: props,
-  setup(props,{emit}){
-    const { page,limit,pageSizes } = toRefs(props);
+  setup(props, { emit }) {
+    const { page, limit, pageSizes } = toRefs(props);
     const currentPage = computed({
       get() {
         return page.value;
@@ -78,7 +68,7 @@ export default defineComponent({
       currentPage.value = 1
       emit('pagination', { page: currentPage.value, limit: val })
     };
-    const handleCurrentChange=(val:number) => {
+    const handleCurrentChange = (val: number) => {
       emit('pagination', { page: val, limit: pageSizes.value })
     }
     return {
@@ -96,6 +86,7 @@ export default defineComponent({
   background: #fff;
   padding: 32px 16px;
 }
+
 .pagination-container.hidden {
   display: none;
 }

+ 18 - 4
src/utils/download.ts

@@ -1,10 +1,24 @@
 import download from 'downloadjs';
+import { ElMessage } from 'element-plus';
 
 const downloadFile = (res: any, fileName: string = '导出日志.xlsx') => {
-  // 用split是避免多次取值重复都好分割的情况,比如 
-  // attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx", attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx"
-  const lastFileName = res.headers['content-disposition'] ? res.headers['content-disposition'].split(',')[0].replaceAll('attachment; filename="', '').replaceAll('"', '') : fileName;
-  download(res.data, lastFileName, res.headers['content-type']);
+
+  // 判断是不是返回的是json,是json说明报错了,否则返回的是文本流
+  const decoder = new TextDecoder('utf-8');
+  const text = decoder.decode(res.data);
+
+  try {
+    const errJson = JSON.parse(text)
+    if (errJson.message) {
+      ElMessage.closeAll()
+      ElMessage.error(errJson.message)
+    }
+  } catch {
+    // 用split是避免多次取值重复都好分割的情况,比如 
+    // attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx", attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx"
+    const lastFileName = res.headers['content-disposition'] ? res.headers['content-disposition'].split(',')[0].replaceAll('attachment; filename="', '').replaceAll('"', '') : fileName;
+    download(res.data, lastFileName, res.headers['content-type']);
+  }
 }
 
 export default downloadFile

+ 4 - 1
src/utils/origin.ts

@@ -12,7 +12,10 @@ export default function getOrigin(suffix: string = '', type: string = 'http') {
     return `ws://${import.meta.env.VITE_SERVER_HOSTNAME}${suffix}`
   }
 
-  if (import.meta.env.VITE_SERVER_HOSTNAME) {
+  // 如果配置的第三方服务直接是http开头的,就直接使用即可
+  if (suffix.includes('http')) {
+    return suffix
+  } else if (import.meta.env.VITE_SERVER_HOSTNAME) {
     return `${import.meta.env.VITE_SERVER_PROTOCOL}//${import.meta.env.VITE_SERVER_HOSTNAME}${suffix}`
   } else {
     return `${window.location.protocol}//${window.location.host}${suffix}`

+ 2 - 1
src/utils/request.ts

@@ -44,6 +44,7 @@ service.interceptors.response.use(
 		} else if (code === undefined && res.message === undefined) { // 可能是下载文件
 			return response
 		} else if (code !== 0) {
+			ElMessage.closeAll()
 			ElMessage.error(res.message)
 			return Promise.reject(new Error(res.message))
 		} else {
@@ -127,7 +128,7 @@ export function file(url: string, params?: any, method: 'get' | 'post' = 'get'):
 			url,
 			method,
 			params,
-			timeout: 30000,
+			timeout: 100000,
 			responseType: 'arraybuffer',
 		});
 	} else {

+ 1 - 0
src/utils/request_ice104.ts

@@ -42,6 +42,7 @@ service.interceptors.response.use(
 		} else if (code === undefined && res.message === undefined) { // 可能是下载文件
 			return response
 		} else if (code !== 0) {
+			ElMessage.closeAll()
 			ElMessage.error(res.message)
 			return Promise.reject(new Error(res.message))
 		} else {

+ 1 - 0
src/utils/request_modbus.ts

@@ -41,6 +41,7 @@ service.interceptors.response.use(
 		} else if (code === undefined && res.message === undefined) { // 可能是下载文件
 			return response
 		} else if (code !== 0) {
+			ElMessage.closeAll()
 			ElMessage.error(res.message)
 			return Promise.reject(new Error(res.message))
 		} else {

+ 8 - 0
src/utils/validator.ts

@@ -13,4 +13,12 @@ export const phoneValidate = (rule: any, value: any, callback: any) => {
 
 export const ruleRequired = (message = '不能为空', trigger = 'blur') => {
   return { required: true, message, trigger }
+}
+
+export const validateNoSpace = (rule: any, value: any, callback: any) => {
+  if (/\s/.test(value)) {
+    callback(new Error('名称不能包含空格'));
+  } else {
+    callback();
+  }
 }

+ 3 - 3
src/views/iot/certificate/index.vue

@@ -139,16 +139,16 @@ const resetQuery = (formEl: FormInstance | undefined) => {
 const operate = (type: string, row: any) => {
 	switch (type) {
 		case 'preview':
-			previewRef.value.openDialog(row)
+			previewRef.value.openDialog({ ...row })
 			break
 		case 'add':
 			editParamsRef.value.openDialog()
 			break
 		case 'editParams':
-			editParamsRef.value.openDialog(row)
+			editParamsRef.value.openDialog({...row})
 			break
 		case 'buildConfig':
-			buildConfigRef.value.openDialog(row)
+			buildConfigRef.value.openDialog({ ...row })
 			break
 		case 'delete':
 			ElMessageBox.confirm(`是否确认删除编号为"${row.id}"的数据项?`, '提示', {

+ 1 - 2
src/views/iot/device/channel/component/detail.vue

@@ -156,8 +156,7 @@ export default {
 		// 获取任务list
 		getList() {
 			this.listLoading = true;
-			api.task
-				.getList(this.listQuery)
+			api.task.getList(this.listQuery)
 				.then((res: any) => {
 					this.taskList = res.list || [];
 					this.total = res.Total;

+ 18 - 3
src/views/iot/device/instance/component/excel.vue

@@ -10,8 +10,9 @@
         </el-form-item>
 
         <el-form-item label="导入文件" prop="path" v-if="open_type === 'upload'">
-          <el-upload accept="xls,xlsx,csv" :show-file-list="false"  :data="{ productId:ruleForm.productId }"  :limit="1" :headers="headers" :action="uploadUrl"
-            :on-success="updateImg">
+          <el-upload accept="xls,xlsx,csv" :show-file-list="true" 
+  :data="{ productId:ruleForm.productId }"  :limit="1" :headers="headers" :action="uploadUrl"
+            :on-success="updateImg" :before-upload="beforeAvatarUpload">
             <el-button>
               <el-icon> <ele-Upload /> </el-icon>
               上传文件
@@ -40,7 +41,7 @@
 <script lang="ts">
 import { reactive, toRefs, defineComponent, ref, unref, nextTick } from 'vue';
 import api from '/@/api/device';
-import { ElMessage } from "element-plus";
+import { ElMessage,UploadProps } from "element-plus";
 import downloadFile from '/@/utils/download';
 import getOrigin from '/@/utils/origin';
 
@@ -87,6 +88,19 @@ export default defineComponent({
       }
     });
 
+
+    const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
+      if (!state.ruleForm.productId) {
+        ElMessage.error('请先选择所属产品!');
+        return false;
+      }
+      if (rawFile.size / 1024 / 1024 > 2) {
+        ElMessage.error('文件不能超过2MB!');
+        return false;
+      }
+      return true;
+    };
+
     const updateImg = (res: any) => {
       if (res.code === 0) {
         ElMessage.success('导入成功');
@@ -170,6 +184,7 @@ export default defineComponent({
       uploadUrl,
       headers,
       tagRef,
+      beforeAvatarUpload,
       down,
       updateImg,
       openDialog,

+ 1 - 1
src/views/iot/device/product/component/editOption.vue

@@ -219,7 +219,7 @@ export default defineComponent({
 						}
 
 						state.ruleForm.valueType = state.valueType;
-						ElMessage.success('参数类型修改成功');
+						// ElMessage.success('参数类型修改成功');
 						closeDialog(); // 关闭弹窗
 						emit('editTypeList', state.ruleForm, state.ruleForm.type_data);
 					} else {

+ 16 - 2
src/views/iot/device/product/component/editPro.vue

@@ -101,6 +101,8 @@ import { reactive, toRefs, defineComponent, ref, unref, getCurrentInstance } fro
 import api from '/@/api/device'
 import certApi from '/@/api/certificateManagement';
 import uploadVue from '/@/components/upload/index.vue'
+import { validateNoSpace } from '/@/utils/validator';
+
 import { ElMessage, UploadProps } from 'element-plus'
 import getOrigin from '/@/utils/origin'
 
@@ -173,8 +175,12 @@ export default defineComponent({
 				...form
 			},
 			rules: {
-				name: [{ required: true, message: '产品名称不能为空', trigger: 'change' }],
-				key: [{ required: true, message: '产品标识不能为空', trigger: 'change' }],
+				name: [ { required: true, message: '产品名称不能为空', trigger: 'change' },
+        				{ max: 32, message: '产品名称不能超过32个字符', trigger: 'change' },
+						{ validator: validateNoSpace, message: '产品名称不能包含空格', trigger: 'change' }
+					],
+				key: [{ required: true, message: '产品标识不能为空', trigger: 'change' },
+					{ validator: validateNoSpace, message: '产品标识不能包含空格', trigger: 'change' }],
 				messageProtocol: [{ required: true, message: '消息协议不能为空', trigger: 'change' }],
 				transportProtocol: [{ required: true, message: '传输协议不能为空', trigger: 'change' }],
 				categoryId: [{ required: true, message: '产品分类不能为空', trigger: 'change' }],
@@ -226,9 +232,15 @@ export default defineComponent({
 			state.ruleForm = {
 				...form
 			}
+			const formWrap = unref(formRef) as any;
+			if (formWrap) {
+				formWrap.resetFields();
+			}
 		}
 		// 关闭弹窗
 		const closeDialog = () => {
+			resetForm();
+
 			state.isShowDialog = false
 		}
 		// 取消
@@ -268,6 +280,8 @@ export default defineComponent({
 			})
 		}
 
+
+
 		return {
 			transportProtocolChange,
 			submitLoading,

+ 4 - 4
src/views/iot/device/product/component/typeItem.vue

@@ -2,12 +2,12 @@
   <div class="type-item">
     <template v-if="['int', 'long', 'float', 'double'].includes(valueType.type)">
       <div class="flex-row" style="margin-bottom: 0;">
-        <el-form-item label="取值范围" prop="max" class="flex1">
-          <el-input v-model="valueType.max" type="number" @input="setNull(valueType, 'max', $event)" placeholder="最大值" />
+        <el-form-item label="取值范围" prop="min" class="flex1">
+          <el-input v-model="valueType.min" type="number" @input="setNull(valueType, 'min', $event)" placeholder="最小值" />
         </el-form-item>
         <div class="split" style="margin-bottom: 20px;">~</div>
-        <el-form-item label="" prop="min" class="flex1" label-width="0">
-          <el-input v-model="valueType.min" type="number" @input="setNull(valueType, 'min', $event)" placeholder="最小值" />
+        <el-form-item prop="max" class="flex1" label-width="0">
+          <el-input v-model="valueType.max" type="number" @input="setNull(valueType, 'max', $event)" placeholder="最大值" />
         </el-form-item>
       </div>
       <el-form-item label="单位" prop="unit">

+ 3 - 3
src/views/iot/device/product/detail.vue

@@ -31,11 +31,11 @@
 									<td class="ant-descriptions-item-content" colspan="1">{{ detail.deptName }}</td> -->
 									<th class="ant-descriptions-item-label ant-descriptions-item-colon">设备类型</th>
 									<td class="ant-descriptions-item-content" colspan="1">{{ detail.deviceType }}</td>
-								</tr>
+								</tr> 
 								<tr class="ant-descriptions-row">
 									<th class="ant-descriptions-item-label ant-descriptions-item-colon">消息协议</th>
 									<td class="ant-descriptions-item-content" colspan="1">{{ detail.messageProtocol }}</td>
-									<th class="ant-descriptions-item-label ant-descriptions-item-colon">链接协议</th>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">接入方式</th>
 									<td class="ant-descriptions-item-content" colspan="1">{{ detail.transportProtocol }}
 									</td>
 
@@ -46,7 +46,7 @@
 								</tr>
 							</tbody>
 						</table>
-					</div>
+					</div> 
 				</el-tab-pane>
 				<el-tab-pane label="物模型" name="2">
 					<div class="wu-box">

+ 1 - 2
src/views/iot/device/template/component/dataAreaDialog.vue

@@ -78,8 +78,7 @@ export default {
 	methods: {
 		getList() {
 			this.listLoading = true;
-			api.area
-				.getList({ template_number: this.templateNumber })
+			api.area.getList({ template_number: this.templateNumber })
 				.then((res: any) => {
 					this.list = res.list || [];
 					this.total = res.Total;

+ 1 - 1
src/views/iot/device/template/component/edit.vue

@@ -117,7 +117,7 @@ export default {
 			(this.$refs['dataForm'] as any).validate((valid: boolean) => {
 				if (valid) {
 					api.template.editTemplate(this.temp).then(() => {
-						this.$emit('finish');
+						this.$emit('getList');
 						this.clsoeDialog();
 						ElMessage.success('操作成功!');
 					});

File diff suppressed because it is too large
+ 9 - 8
src/views/iot/iotmanager/dashboard.vue


+ 1 - 1
src/views/iot/network/server/component/list.vue

@@ -140,7 +140,7 @@
       @current-change="onHandleCurrentChange"
       class="mt15"
       :pager-count="5"
-      :page-sizes="[10, 20, 30]"
+      :page-sizes="[10, 20, 30, 50, 100, 200, 300, 500]"
       v-model:current-page="param.page"
       background
       v-model:page-size="param.pageSize"

+ 1 - 1
src/views/iot/network/tunnel/component/list.vue

@@ -38,7 +38,7 @@
             @current-change="onHandleCurrentChange"
             class="mt15"
             :pager-count="5"
-            :page-sizes="[10, 20, 30]"
+            :page-sizes="[10, 20, 30, 50, 100, 200, 300, 500]"
             v-model:current-page="param.page"
             background
             v-model:page-size="param.pageSize"

File diff suppressed because it is too large
+ 0 - 0
src/views/iot/noticeservices/config/index.vue


+ 0 - 460
src/views/iot/noticeservices/config/template.vue

@@ -1,460 +0,0 @@
-<template>
-	<div class="system-dic-container">
-		<el-card shadow="hover">
-			<div class="system-user-search mb15">
-				<el-form :model="tableData.param" ref="queryRef" :inline="true">
-					<el-form-item label="配置名称" prop="name">
-						<el-input
-							v-model="tableData.param.title"
-							placeholder="请输入配置名称"
-							clearable
-							size="default"
-							style="width: 240px"
-							@keyup.enter.native="dataList"
-						/>
-					</el-form-item>
-					
-
-					<el-form-item>
-						<el-button size="default" type="primary" class="ml10" @click="dataList">
-							<el-icon>
-								<ele-Search />
-							</el-icon>
-							查询
-						</el-button>
-						<el-button size="default" @click="resetQuery(queryRef)">
-							<el-icon>
-								<ele-Refresh />
-							</el-icon>
-							重置
-						</el-button>
-						<el-button size="default" type="primary" class="ml10" @click="onOpenAdd">
-							<el-icon>
-								<ele-FolderAdd />
-							</el-icon>
-							新增模版
-						</el-button>
-					</el-form-item>
-				</el-form>
-			</div>
-			<div>
-				<div style="border: 1px solid var(--next-border-color-light)"></div>
-				<el-row>
-					<el-col :span="8" v-for="(item, index) in tableData.data" :key="index"
-						><div class="grid-content card">
-							<div class="ant-card">
-								<div class="ant-card-body">
-									<div class="pro-table-card-item">
-										<div class="card-item-avatar">
-											<img
-												width="88"
-												height="88"
-												:src="'/imgs/notice/'+tableData.param.sendGateway+'.svg'"
-												alt=""
-											/>
-										</div>
-										<div class="card-item-body">
-											<div class="card-item-header">
-												<div class="ellipsis">
-													<div class="ellipsis card-item-header-name" style="width: 100%; height: 45px">{{ item.title }}</div>
-													<div class="card-item-header-name" style="display: none"></div>
-												</div>
-											</div>
-											<div class="card-item-content">
-												<div>
-													<label>通知方式</label>
-													<div class="">
-														<div style="width: 100%">{{item.types==1?'即时发送':'预约发送'}}</div>
-													</div>
-												</div>
-												<div>
-													<label>说明</label>
-													<div class="ellipsis">
-														<div style="width: 100%"></div>
-													</div>
-												</div>
-											</div>
-										</div>
-									</div>
-								</div>
-							</div>
-							<div class="card-tools">
-								<div class="card-button" @click="onOpenEdit(item)">
-									<el-button size="default" type="primary" class="ml10" text bg>
-										<el-icon>
-											<ele-Edit />
-										</el-icon>
-										修改
-									</el-button>
-								</div>
-								<!-- <div class="card-button" @click="onOpenEdit(item)">
-									<el-button size="default" type="primary" text bg>
-										<el-icon>
-											<ele-View />
-										</el-icon>
-										调试
-									</el-button>
-								</div>-->
-
-								<div class="card-button" @click="onOpenEdit(item)">
-									<el-button size="default" type="info" text bg>
-										<el-icon>
-											<ele-Document />
-										</el-icon>
-										通知记录
-									</el-button>
-								</div> 
-
-								<div class="card-button" @click="onRowDel(item)">
-									<el-button size="default" type="info" text bg>
-										<el-icon>
-											<ele-Delete />
-										</el-icon>
-										删除
-									</el-button>
-								</div>
-							</div>
-						</div>
-					</el-col>
-				</el-row>
-			</div>
-			<pagination
-				v-show="tableData.total > 0"
-				:total="tableData.total"
-				v-model:page="tableData.param.pageNum"
-				v-model:limit="tableData.param.pageSize"
-				@pagination="dataList"
-			/>
-		</el-card>
-
-		<EditDic ref="editDicRef" @dataList="dataList" />
-	<!-- 	<LevelDic ref="levelDicRef" @dataList="dataList" /> -->
-	</div>
-</template>
-
-<script lang="ts">
-import { toRefs, reactive, onMounted, ref,getCurrentInstance,defineComponent } from 'vue';
-import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
-import EditDic from './component/temEdit.vue';
-// import LevelDic from './component/level.vue';
-
-import api from '/@/api/notice';
-import { useRoute } from 'vue-router';
-
-// 定义接口来定义对象的类型
-interface TableDataRow {
-	id: number;
-	title: string;
-	types: string;
-	sendGateway: string;
-}
-interface TableDataState {
-	ids: number[];
-	tableData: {
-		data: Array<TableDataRow>;
-		total: number;
-		loading: boolean;
-		param: {
-			pageNum: number;
-			pageSize: number;
-			title: string;
-			sendGateway: string;
-			types: number;
-		};
-	};
-}
-
-export default defineComponent({
-	name: 'setlist',
-	components: { EditDic },
-
-	setup() {
-		
-   		 const { proxy } = getCurrentInstance() as any;
-		// const { notice_send_gateway } = proxy.useDict('notice_send_gateway');
-		const addDicRef = ref();
-		const editDicRef = ref();
-		const detailRef = ref();
-		const queryRef = ref();
-		const route = useRoute();
-
-		const state = reactive<TableDataState>({
-			type:'',
-			tableData: {
-				data: [],
-				total: 0,
-				loading: false,
-				param: {
-					pageNum: 1,
-					pageSize: 20,
-					title: '',
-					sendGateway: '',
-					types: '',
-				},
-			},
-		});
-		// 初始化表格数据
-		const initTableData = () => {
-			state.tableData.param.sendGateway=route.params.id;
-			dataList();
-		};
-		const dataList = () => {
-			state.tableData.loading = true;
-			api.template
-				.getList(state.tableData.param)
-				.then((res: any) => {
-					state.tableData.data = res.Data;
-					state.tableData.total = res.Total;
-				})
-				.finally(() => (state.tableData.loading = false));
-		};
-		// 打开新增菜单弹窗
-		const onOpenAdd = (row?: TableDataRow ) => {
-			editDicRef.value.openDialog(null,state.tableData.param.sendGateway);
-		};
-	
-		// 打开修改模型弹窗
-		const onOpenEdit = (row: TableDataRow ) => {
-			editDicRef.value.openDialog({ ...row },state.tableData.param.sendGateway);
-		};
-
-		const onRowDel = (row?: TableDataRow) => {
-			let msg = '你确定要删除所选数据?';
-			let ids: number[] = [];
-			if (row) {
-				msg = `此操作将永久删除:“${row.title}”,是否继续?`;
-				ids = row.id;
-			} else {
-				ids = state.ids;
-			}
-			if (ids.length === 0) {
-				ElMessage.error('请选择要删除的数据。');
-				return;
-			}
-			ElMessageBox.confirm(msg, '提示', {
-				confirmButtonText: '确认',
-				cancelButtonText: '取消',
-				type: 'warning',
-			})
-				.then(() => {
-					api.template.delete(ids).then(() => {
-						ElMessage.success('删除成功');
-						dataList();
-					});
-				})
-				.catch(() => {});
-		};
-
-		// 页面加载时
-		onMounted(() => {
-			initTableData();
-		});
-		/** 重置按钮操作 */
-		const resetQuery = (formEl: FormInstance | undefined) => {
-			if (!formEl) return;
-			formEl.resetFields();
-			dataList();
-		};
-		const onActionStatus = (item: TableDataRow[]) => {
-			if (item.status == 0) {
-				alarm.common.deploy({ id: item.id }).then((res: any) => {
-					dataList();
-				});
-			} else {
-				alarm.common.undeploy({ id: item.id }).then((res: any) => {
-					dataList();
-				});
-			}
-		};
-
-		return {
-			onActionStatus,
-			addDicRef,
-			editDicRef,
-			detailRef,
-			queryRef,			
-			onOpenAdd,
-			onOpenEdit,
-			onRowDel,
-			dataList,
-			resetQuery,
-			...toRefs(state),
-		};
-	},
-});
-</script>
-<style>
-.el-col-12 {
-	padding: 10px;
-}
-
-.el-button.is-text:not(.is-disabled).is-has-bg {
-	background-color: var(--next-border-color-light);
-}
-.card {
-	padding: 10px;
-}
-.ant-card {
-	box-sizing: border-box;
-	margin: 10px;
-	width: 98%;
-	font-size: 14px;
-	font-variant: tabular-nums;
-	border: 1px solid var(--next-border-color-light);
-
-	line-height: 1.5;
-	list-style: none;
-	font-feature-settings: 'tnum';
-	position: relative;
-	border-radius: 2px;
-	transition: all 0.3s;
-	overflow: hidden;
-}
-.ant-card-body {
-	padding: 24px;
-	zoom: 1;
-	overflow: hidden;
-}
-.pro-table-card-item {
-	display: flex;
-}
-.pro-table-card-item .card-item-avatar {
-	margin-right: 16px;
-}
-.pro-table-card-item .card-item-body {
-	display: flex;
-	flex-direction: column;
-	flex-grow: 1;
-	width: 0;
-}
-.pro-table-card-item .card-item-body .card-item-header {
-	display: flex;
-	width: calc(100% - 86px);
-	margin-bottom: 12px;
-}
-.pro-table-card-item .card-item-body .card-item-content {
-	display: flex;
-	flex-wrap: wrap;
-}
-.pro-table-card-item .card-item-body .card-item-content > div {
-	width: 50%;
-}
-.ellipsis {
-	display: -webkit-box;
-	overflow: hidden;
-	text-align: left;
-	text-overflow: ellipsis;
-	word-break: break-all;
-}
-.card-item-body .card-item-header .card-item-header-name {
-	font-weight: 700;
-	font-size: 16px;
-}
-.card-state {
-	position: absolute;
-	top: 30px;
-	right: -12px;
-	display: flex;
-	justify-content: center;
-	width: 100px;
-	padding: 2px 0;
-	background-color: rgba(89, 149, 245, 0.15);
-	transform: skewX(45deg);
-}
-.card-state.success {
-	background-color: #f6ffed;
-}
-.iot-card .card-warp .card-content .card-state.error {
-	background-color: rgba(229, 0, 18, 0.1);
-}
-.card-state .card-state-content {
-	transform: skewX(-45deg);
-}
-.ant-badge-status-success {
-	background-color: #52c41a;
-}
-.ant-badge-status-error {
-	background-color: #ff4d4f;
-}
-.ant-badge-status-dot {
-	position: relative;
-	top: -1px;
-	display: inline-block;
-	width: 6px;
-	height: 6px;
-	vertical-align: middle;
-	border-radius: 50%;
-}
-
-.card-tools {
-	display: flex;
-	margin-top: 2px;
-}
-.card-tools .card-button:not(:last-child) {
-	margin-right: 8px;
-}
-.card-tools .card-button {
-	display: flex;
-	flex-grow: 1;
-}
-.card-tools .card-button > span,
-.card-tools .card-button button {
-	width: 100%;
-	border-radius: 0;
-}
-
-.ant-btn-link {
-	color: #1d39c4;
-	border-color: transparent;
-	background: transparent;
-	box-shadow: none;
-}
-.ant-badge-status-text {
-	margin-left: 8px;
-	color: rgba(0, 0, 0, 0.85);
-	font-size: 14px;
-}
-.ant-btn {
-	line-height: 1.5715;
-	position: relative;
-	display: inline-block;
-	font-weight: 400;
-	white-space: nowrap;
-	text-align: center;
-	background-image: none;
-	cursor: pointer;
-	transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-	-webkit-user-select: none;
-	-moz-user-select: none;
-	-ms-user-select: none;
-	user-select: none;
-	touch-action: manipulation;
-	height: 32px;
-	padding: 4px 15px;
-	font-size: 14px;
-	border-radius: 2px;
-	color: rgba(0, 0, 0, 0.85);
-
-}
-.ant-btn > .anticon {
-	line-height: 1;
-}
-.ant-btn > span {
-	display: inline-block;
-}
-
-.cardflex {
-	display: flex;
-	justify-content: space-between;
-}
-.statusname {
-	font-size: 30px;
-	margin-top: 10px;
-	margin-bottom: 15px;
-}
-.comtest {
-	margin-top: 20px;
-	height: 30px;
-	line-height: 30px;
-}
-</style>

+ 2 - 2
src/views/iot/scene/list/index.vue

@@ -60,7 +60,7 @@ import { useSearch } from '/@/hooks/useCommon';
 import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
 import { ref } from 'vue';
 const queryRef = ref();
-const { params, tableData, getList, loading } = useSearch<any[]>(api.manage.getList, 'data', { keyWord: '' });
+const { params, tableData, getList, loading } = useSearch<any[]>(api.log.getList, 'data', { keyWord: '' });
 getList();
 
 
@@ -74,7 +74,7 @@ const del = (row: any) => {
 		cancelButtonText: '取消',
 		type: 'warning',
 	}).then(async () => {
-		await api.manage.del(row.id);
+		await api.log.del(row.id);
 		ElMessage.success('删除成功');
 		getList(1);
 	});

+ 0 - 1
src/views/iot/scene/manage/component/actionItem.vue

@@ -94,7 +94,6 @@ const getOneDetail = () => {
             };
           }
         });
-
         actionList.value=combinedArray;
 			})
 		};

+ 2 - 0
src/views/iot/scene/manage/component/actionType/deviceOut.vue

@@ -10,6 +10,8 @@
 
   <el-form-item label="设备:" prop="deviceKey" class="form-item">
     <el-select v-model="fromData.deviceKey" filterable placeholder="请选择设备" @change="saveData">
+      <el-option label="全部" value="all">全部</el-option>
+
       <el-option v-for="it in deviceListData" :key="it.key" :label="it.name" :value="it.key">
         <span style="float: left">{{ it.name }}</span>
         <span style="float: right; font-size: 13px">{{ it.key }}</span>

+ 36 - 11
src/views/iot/scene/manage/component/sceneItem.vue

@@ -70,6 +70,8 @@ import { DocumentAdd, CircleClose, Right } from '@element-plus/icons-vue';
 import vue3cron from '/@/components/vue3cron/vue3cron.vue';
 import api from '/@/api/scene';
 import product from '/@/api/device';
+import datahub from '/@/api/datahub';
+
 import Condition from './condition.vue';
 const { proxy } = getCurrentInstance() as any;
 const scene_type = proxy.useDict('scene_type');
@@ -121,13 +123,13 @@ const props = defineProps({
       'name': '设备离线',
     }, 
     
-    // {
-    //   'key': 'readAttribute',
-    //   'name': '读取属性',
-    // }, {
-    //   'key': 'modifyAttribute',
-    //   'name': '修改属性',
-    // }, 
+    {
+      'key': 'readAttribute',
+      'name': '读取属性',
+    }, {
+      'key': 'modifyAttribute',
+      'name': '修改属性',
+    }, 
     {
       'key': 'reportAttribute',
       'name': '属性上报',
@@ -135,10 +137,10 @@ const props = defineProps({
       'key': 'reportEvent',
       'name': '事件上报',
     },
-    //  {
-    //   'key': 'functionCall',
-    //   'name': '功能调用',
-    // }
+     {
+      'key': 'functionCall',
+      'name': '功能调用',
+    }
   ]
   }
 })
@@ -175,7 +177,30 @@ const getSelectcolumns=(index: number, val: string) => {
     'operator': '',
     'value': ''
   }]];
+
 }
+//获取类型数据
+const getAction = (val: string) => {
+  switch (val) {
+    case 'functionCall':
+      product.tabDeviceFucntion.getList({ key: product_key }).then((res: any) => {
+        functionCallList.value = res
+      })
+      break;
+    case 'readAttribute':
+      datahub.node.getpropertyList({ key: product_key }).then((re: any) => {
+        propertyCallList.value = re;
+      });
+
+      break;
+    case 'modifyAttribute':
+      datahub.node.getpropertyList({ key: product_key }).then((re: any) => {
+        propertyCallList.value = re;
+      });
+      break;
+  }
+}
+
 const getcolumns = (index: number, val: string) => {
   let where = {
     "sceneType": props.sceneType, //场景类型

+ 8 - 5
src/views/iot/scene/manage/detail.vue

@@ -8,6 +8,7 @@
 					? '启用' : '未启用' }}</div>
 			</div>
 			<div class="flex">
+				<div class="desc" style="margin-right: 20px;">场景类型:{{typeList[detail.sceneType]}}</div>
 				<div class="desc">场景描述:{{ detail.description }}</div>
 				<div class="edit" @click="addOrEdit(detail)"><el-link type="primary"> <el-icon>
 							<EditPen color="#409eff" />
@@ -34,7 +35,7 @@
 	</el-card>
 	<el-card style="  margin-top: 15px;">
 		<div class="font20">场景动作</div>
-		<ActionItem v-if="detail.id" :scene_id="detail.id"  :sourceData="sourceData"></ActionItem>
+		<ActionItem v-if="detail.id && sourceData.length>0" :scene_id="detail.id"  :sourceData="sourceData"></ActionItem>
 	</el-card>
 
 	<EditForm ref="editFormRef" @getList="getDetail()"></EditForm>
@@ -50,8 +51,6 @@ import EditForm from './edit.vue';
 import api from '/@/api/scene';
 import product from '/@/api/device';
 import vue3cron from '/@/components/vue3cron/vue3cron.vue';
-import { stat } from 'fs';
-
 
 const editFormRef = ref();
 //原始
@@ -82,6 +81,11 @@ export default defineComponent({
 			dialogVisible:false,
 			developer_status: 2,
 			showstatus: false,
+			typeList:{
+				'device':'设备触发',
+				'manual':'手动触发',
+				'timer':'定时触发',
+			} as any,
 			sourceData: [],
 			timerData: {
 					triggerType:'timer',
@@ -159,7 +163,6 @@ export default defineComponent({
 							...parsedBodyJson
 						};
 					});
-					getProductList();
 					state.sceneList = scenes;
 					state.showstatus = true;
 				})
@@ -177,7 +180,6 @@ export default defineComponent({
 							getOneDetail();
 						});
 					}
-					getProductList();
 
 					state.timer_id=res[0].id
 					state.timerData=JSON.parse(res[0].bodyjson);
@@ -185,6 +187,7 @@ export default defineComponent({
 				})
 			}
 
+			getProductList();
 
 
 

+ 9 - 2
src/views/iot/scene/manage/edit.vue

@@ -1,3 +1,10 @@
+<!--
+ * @Author: yukai
+ * @Date: 2023-09-18 13:15:34
+ * @LastEditors: 
+ * @LastEditTime: 2023-11-02 10:36:07
+ * @Description: 请填写简介
+-->
 <template>
 	<el-dialog
 		class="api-edit"
@@ -8,8 +15,8 @@
 		:close-on-press-escape="false"
 	>
 		<el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="80px">
-			<el-form-item label="场景描述" prop="name">
-				<el-input v-model="formData.name" placeholder="请输入场景描述" />
+			<el-form-item label="场景名称" prop="name">
+				<el-input v-model="formData.name" placeholder="请输入场景名称" />
 			</el-form-item>
       <el-form-item label="触发方式" prop="sceneType">
         <el-radio-group v-model="formData.sceneType">

+ 1 - 1
src/views/personal/index.vue

@@ -14,7 +14,7 @@
 						</div>
 						<div class="personal-user-right">
 							<el-row>
-								<el-col :span="24" class="personal-title mb18">{{ currentTime }},{{ info.userName }},生活变的再糟糕,也不妨碍我变得更好! </el-col>
+								<el-col :span="24" class="personal-title mb18">{{ currentTime }},{{ info.userName }} </el-col>
 								<!-- 昵称 -->
 								<el-col :xs="24" :sm="24" class="personal-item mb6">
 									<div class="personal-item-label">姓名:</div>

+ 1 - 1
src/views/system/api/component/bind.vue

@@ -37,7 +37,7 @@ const formData = reactive<ApiRow>({
 });
 
 const ruleForm = {
-	menuIds: [ruleRequired('关联页面不能为空', 'change')],
+	// menuIds: [ruleRequired('关联页面不能为空', 'change')],
 };
 
 // 加载菜单列表

+ 16 - 2
src/views/system/api/component/edit.vue

@@ -17,6 +17,11 @@
 						},
 					]" :props="{ checkStrictly: true, multiple: false, emitPath: false, value: 'id', label: 'name' }" placeholder="请选择关联页面" clearable class="w100" v-model="formData.parentId"></el-cascader>
 				</el-form-item>
+				<el-form-item label="接口类型" prop="apiTypes">
+					<el-select v-model="formData.apiTypes" filterable clearable placeholder="请选择接口类型" style="width: 100%;">
+						<el-option v-for="dict in api_types" :key="dict.value" :label="dict.label" :value="dict.value"> </el-option>
+					</el-select>
+				</el-form-item>
 				<el-form-item label="分类名称" prop="name">
 					<el-input v-model="formData.name" placeholder="输入接口名称" />
 				</el-form-item>
@@ -34,6 +39,11 @@
 				<el-form-item label="接口地址" prop="address">
 					<el-input v-model="formData.address" placeholder="接口地址" />
 				</el-form-item>
+				<el-form-item label="接口类型" prop="apiTypes">
+					<el-select v-model="formData.apiTypes" filterable clearable placeholder="请选择接口类型" style="width: 100%;">
+						<el-option v-for="dict in api_types" :key="dict.value" :label="dict.label" :value="dict.value"> </el-option>
+					</el-select>
+				</el-form-item>
 				<el-form-item label="访问类型" prop="method">
 					<el-select v-model="formData.method" placeholder="请选择访问类型">
 						<el-option v-for="item in methodOptions" :key="item.value" :label="item.key" :value="item.value"></el-option>
@@ -57,7 +67,7 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive, nextTick, watch } from 'vue';
+import { ref, reactive, nextTick, watch, getCurrentInstance } from 'vue';
 import api from '/@/api/system';
 import { ApiRow } from '/@/api/model/system/menu';
 import { ruleRequired } from '/@/utils/validator';
@@ -65,6 +75,8 @@ import { ElMessage } from 'element-plus';
 import apiDatahub from '/@/api/datahub';
 
 const emit = defineEmits(['getList']);
+const { proxy } = getCurrentInstance() as any;
+const { api_types } = proxy.useDict('api_types');
 
 const showDialog = ref(false);
 const formRef = ref();
@@ -78,6 +90,7 @@ const baseForm: ApiRow = {
 	parentId: undefined,
 	name: '',
 	types: 2,
+	apiTypes: '',
 	address: '',
 	method: '',
 	remark: '',
@@ -98,8 +111,9 @@ watch(
 
 const ruleForm = {
 	parentId: [ruleRequired('上级分类不能为空', 'change')],
-	menuIds: [ruleRequired('关联页面不能为空', 'change')],
+	// menuIds: [ruleRequired('关联页面不能为空', 'change')],
 	method: [ruleRequired('请求方式不能为空', 'change')],
+	apiTypes: [ruleRequired('接口类型不能为空', 'change')],
 	name: [ruleRequired('接口名称不能为空')],
 	address: [ruleRequired('接口地址不能为空')],
 };

+ 16 - 2
src/views/system/api/index.vue

@@ -35,17 +35,23 @@
 							</el-icon>
 							新增接口
 						</el-button>
-						<el-button type="primary" :disabled="!ids.length" @click="bindMenus()" v-auth="'add'">
+						<el-button type="primary" :disabled="!ids.length" @click="bindMenus()" v-auth="'batch'">
 							<el-icon>
 								<ele-Grid />
 							</el-icon>
 							批量绑定菜单
 						</el-button>
+						<el-button type="primary" @click="apiImport()" :loading="importing" v-auth="'import'">
+							<el-icon>
+								<ele-Download />
+							</el-icon>
+							api导入
+						</el-button>
 					</el-form-item>
 				</el-form>
 			</div>
 			<el-table :data="tableData" @selection-change="handleSelectionChange" style="width: 100%" v-loading="loading" :expand-row-keys="[]" row-key="id" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
-				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column v-col="'selection'" type="selection" width="55" align="center" />
 				<!-- <el-table-column type="index" label="序号" width="60" align="center" /> -->
 				<el-table-column prop="id" label="ID" width="100" align="center" />
 				<el-table-column prop="name" v-col="'name'" label="接口名称" show-overflow-tooltip></el-table-column>
@@ -83,6 +89,7 @@ import { useSearch } from '/@/hooks/useCommon'
 const editFormRef = ref()
 const bindRef = ref()
 const queryRef = ref()
+const importing = ref(false)
 const ids = ref<number[]>([])
 
 const { params, tableData, getList, loading } = useSearch<ApiRow[]>(api.api.getList, 'Info', { name: '', address: '', types: -1 })
@@ -103,6 +110,13 @@ const handleSelectionChange = (selection: any[]) => {
 	ids.value = selection.filter(item => item.types === 2).map((item) => item.id);
 };
 
+const apiImport = () => {
+	importing.value = true
+	api.api.import().then(() => {
+		ElMessage.success('导入成功')
+	}).finally(() => importing.value = false)
+};
+
 // 重置表单
 const resetQuery = () => {
 	queryRef.value.resetFields()

+ 0 - 14
src/views/system/application/edit.vue

@@ -28,16 +28,6 @@
 					</template>
 				</el-cascader>
 			</el-form-item>
-			<el-form-item label="角色" prop="roleId">
-				<el-cascader :options="roleData"
-					:props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name' }"
-					placeholder="请选择角色" clearable class="w100" v-model="formData.roleId">
-					<template #default="{ node, data }">
-						<span>{{ data.name }}</span>
-						<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
-					</template>
-				</el-cascader>
-			</el-form-item>
 			<el-form-item label="应用描述" prop="desc">
 				<el-input v-model="formData.desc" type="textarea" :rows="3" />
 			</el-form-item>
@@ -76,10 +66,6 @@ const props = defineProps({
 		type: Array,
 		default: () => [],
 	},
-	roleData: {
-		type: Array,
-		default: () => [],
-	},
 })
 const baseForm = {
 	id: undefined,

+ 1 - 5
src/views/system/application/index.vue

@@ -54,7 +54,7 @@
 			</el-table-column>
 		</el-table>
 		<pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
-		<EditForm ref="editFormRef"  :deptData="deptData" :roleData="roleData" @getList="getList()"></EditForm>
+		<EditForm ref="editFormRef"  :deptData="deptData" @getList="getList()"></EditForm>
 	</el-card>
 </template>
   
@@ -68,7 +68,6 @@ import { useSearch } from '/@/hooks/useCommon'
 
 const queryRef = ref()
 const deptData = ref([])
-const roleData = ref([])
 const editFormRef = ref()
 const { params, tableData, getList, loading } = useSearch<any[]>(api.getList, 'Data', { keyWord: '' })
 getList()
@@ -76,9 +75,6 @@ const initTableData = () => {
 	user.dept.getList({status:1}).then((res: any) => {
 		deptData.value = res;
 	});
-	user.role.getList({status:1}).then((res: any) => {
-		roleData.value = res;
-	});
 };
 initTableData();
 const onActionStatus = (item: any) => {

+ 1 - 1
src/views/system/assess/totalIndex/component/addItem.vue

@@ -39,7 +39,7 @@
 					</template>
 				</el-table-column>
 			</el-table>
-			<el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" class="mt15" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="tableData.param.pageNum" background v-model:page-size="tableData.param.pageSize"
+			<el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" class="mt15" :pager-count="5" :page-sizes="[10, 20, 30, 50, 100, 200, 300, 500]" v-model:current-page="tableData.param.pageNum" background v-model:page-size="tableData.param.pageSize"
 				layout="total, sizes, prev, pager, next, jumper" :total="tableData.total">
 			</el-pagination>
 

+ 1 - 2
src/views/system/assess/totalIndex/index.vue

@@ -114,9 +114,8 @@ export default defineComponent({
       state.tableData.loading = true
       api.getList().then((res: any) => {
         state.tableData.data = res;
-        state.tableData.loading = false
         // state.tableData.total = res.total;
-      });
+      }).finally(()  => state.tableData.loading = false)
     };
     // 打开新增用户弹窗
     const onOpenAddItem = () => {

+ 1 - 23
src/views/system/basicConfig/index.vue

@@ -10,12 +10,6 @@
 						<el-form-item label="系统版权" prop="keyWord">
 							<el-input v-model="state.info.copyright" placeholder="请输入系统版权" clearable size="default" />
 						</el-form-item>
-						<el-form-item label="开放接口AK" prop="keyWord">
-							<el-input v-model="state.info.accesskey" placeholder="请输入开放接口AK" clearable size="default" />
-						</el-form-item>
-						<el-form-item label="开放接口SK" prop="keyWord">
-							<el-input v-model="state.info.secretkey" placeholder="请输入开放接口SK" clearable size="default" />
-						</el-form-item>
 						<el-row>
 							<el-col :span="12">
 								<el-form-item label="系统LOGO" prop="keyWord">
@@ -90,17 +84,13 @@ const initBasicConfigInfo = () => {
 };
 const queryBasicConfigInfo = () => {
 	state.tableData.loading = true
-	api.basicConfig.getDetails().then((res: any) => {
+	api.basicConfig.getDetails({types: 0}).then((res: any) => {
 		state.data = res.data;
 		res.data.forEach((element: object) => {
 			if (element.configName == '系统名称') {
 				state.info.name = element.configValue
 			} else if (element.configName == '系统版权') {
 				state.info.copyright = element.configValue
-			} else if (element.configName == '开放接口AK') {
-				state.info.accesskey = element.configValue
-			} else if (element.configName == '开放接口SK') {
-				state.info.secretkey = element.configValue
 			} else if (element.configName == '系统LOGO') {
 				state.info.logo = element.configValue
 			} else if (element.configName == '系统LOGO(小图标)') {
@@ -121,14 +111,6 @@ const setDetails = () => {
 		ElMessage.error('请填写系统版权');
 		return;
 	}
-	if (!state.info.accesskey) {
-		ElMessage.error('请填写开放接口AK');
-		return;
-	}
-	if (!state.info.secretkey) {
-		ElMessage.error('请填写开放接口SK');
-		return;
-	}
 	if (!state.info.logo) {
 		ElMessage.error('请上传系统LOGO');
 		return;
@@ -146,10 +128,6 @@ const setDetails = () => {
 			element.configValue = state.info.name
 		} else if (element.configName == '系统版权') {
 			element.configValue = state.info.copyright
-		} else if (element.configName == '开放接口AK') {
-			element.configValue = state.info.accesskey
-		} else if (element.configName == '开放接口SK') {
-			element.configValue = state.info.secretkey
 		} else if (element.configName == '系统LOGO') {
 			element.configValue = state.info.logo
 		} else if (element.configName == '系统LOGO(小图标)') {

+ 1 - 1
src/views/system/dict/index.vue

@@ -47,7 +47,7 @@
         </el-form>
       </div>
       <!-- 字典切换 -->
-      <el-tabs v-model="tableData.param.moduleClassify" class="demo-tabs" @click="typeList">
+      <el-tabs v-model="tableData.param.moduleClassify" class="demo-tabs" @tab-change="typeList">
 				<el-tab-pane v-for="dict in tabDataList" :label="dict.dictLabel" :name="dict.dictValue">
           <el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="tableData.loading">
             <el-table-column type="selection" width="55" align="center" />

+ 7 - 11
src/views/system/manage/role/component/permission.vue

@@ -28,17 +28,7 @@
 			<el-step title="接口权限" />
 		</el-steps>
 		<div class="scroll-part mt-3">
-			<el-tree
-				ref="treeRef"
-				:data="treeData"
-				show-checkbox
-				default-expand-all
-				node-key="id"
-				highlight-current
-				:props="defaultProps"
-				check-on-click-node
-				:expand-on-click-node="false"
-			/>
+			<el-tree ref="treeRef" :data="treeData" show-checkbox default-expand-all node-key="id" highlight-current :props="defaultProps" check-on-click-node :expand-on-click-node="false" />
 		</div>
 	</el-dialog>
 </template>
@@ -138,9 +128,15 @@ const next = async () => {
 	const treeDataRes = await api.role.auth.getList(typeList[step.value], menuIds.value.concat(menuIdsHalf.value));
 	// 最外层是菜单,如果菜单下没有按钮,列表或者接口,就不显示这个菜单
 	// 菜单id和其他id可能会重复,所以最外层的菜单id变一下,避免重复
+	const itemsType = typeList[step.value]
 	const treeDateFilter = (treeDataRes || []).filter((item: any) => {
 		if (item.children?.length) {
 			item.id += '_memu';
+			if (itemsType === 'api') {
+				item.children.forEach((i: any) => {
+					i.title = i.method + '-' + i.title + (i.remark ? `(${i.remark})` : '')
+				});
+			}
 			return true;
 		}
 		return false;

+ 3 - 1
src/views/system/manage/user/component/editUser.vue

@@ -191,9 +191,11 @@ export default defineComponent({
 			if (row) {
 				api.user.detail(row.id).then((user: any) => {
 					state.ruleForm = user;
+					state.isShowDialog = true;
 				});
+			} else {
+				state.isShowDialog = true;
 			}
-			state.isShowDialog = true;
 		};
 		// 关闭弹窗
 		const closeDialog = () => {

+ 76 - 0
src/views/system/menu/component/api.vue

@@ -0,0 +1,76 @@
+<template>
+	<el-drawer v-model="drawer" :title="title" direction="rtl" size="700px">
+		<div class="p-3">
+			<el-button size="default" type="success" class="mr-3" @click="onAddRow">
+				<el-icon>
+					<ele-FolderAdd />
+				</el-icon>
+				绑定接口
+			</el-button>
+		</div>
+
+		<el-table :data="tableData" style="width: 100%" max-height="calc(100vh - 140px)" row-key="id" border :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
+			<el-table-column type="index" label="序号" width="60" align="center" />
+			<el-table-column prop="method" label="方法" width="70" align="center"></el-table-column>
+			<el-table-column prop="name" label="名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="address" label="地址" show-overflow-tooltip></el-table-column>
+			<el-table-column label="操作" width="100" align="center">
+				<template #default="scope">
+					<el-button size="small" text type="danger" @click="onDel(scope.row)">删除</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+	</el-drawer>
+	<!-- <listForm ref="listFormRef" :parent-data="tableData" @getList="getList"></listForm> -->
+	<listForm ref="listFormRef" @getList="getList"></listForm>
+	<BindVue ref="bindRef" @getList="getList"></BindVue>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import listForm from './list-form.vue';
+import { MenuListRow } from '/@/api/model/system/menu';
+import api from '/@/api/system';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import BindVue from './bind.vue'
+
+const title = ref('接口权限');
+const drawer = ref(false);
+const tableData = ref<MenuListRow[]>([]);
+const menuRow = ref();
+const bindRef = ref();
+const listFormRef = ref();
+const getList = async () => {
+	tableData.value = [];
+	let res = await api.menu.api.getList({ menuId: menuRow.value.id as number, status: -1 });
+	tableData.value = res || [];
+};
+
+const open = async (row: any) => {
+	title.value = '接口权限 - ' + row.title;
+	drawer.value = true;
+	menuRow.value = row;
+	getList();
+};
+
+const onAddRow = () => {
+	bindRef.value.open(tableData.value.map(item => item.id), menuRow.value.id)
+};
+
+const onDel = (row: MenuListRow) => {
+	ElMessageBox.confirm(`此操作将删除接口:“${row.name}”,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		await api.menu.api.add({
+			menuId: menuRow.value.id,
+			apiIds: tableData.value.map(item => item.id).filter(id => id !== row.id)
+		});
+		ElMessage.success('删除成功');
+		getList();
+	});
+};
+
+defineExpose({ open });
+</script>

+ 71 - 0
src/views/system/menu/component/bind.vue

@@ -0,0 +1,71 @@
+<template>
+	<el-dialog class="api-edit" v-model="showDialog" title="批量绑定接口" width="600px" :close-on-click-modal="false" :close-on-press-escape="false">
+		<el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="80px" @keyup.enter="onSubmit">
+			<el-form-item label="选择接口" prop="apiIds">
+				<el-cascader :options="menuData" :props="{ checkStrictly: false, multiple: true, emitPath: false, value: 'id', label: 'name' }" placeholder="请选择关联页面" filterable clearable class="w100" v-model="formData.apiIds"></el-cascader>
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<div class="dialog-footer">
+				<el-button @click="showDialog = false">取消</el-button>
+				<el-button type="primary" @click="onSubmit">确定</el-button>
+			</div>
+		</template>
+	</el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue';
+import api from '/@/api/system';
+import { ApiRow } from '/@/api/model/system/menu';
+import { ElMessage } from 'element-plus';
+
+const emit = defineEmits(['getList']);
+
+const showDialog = ref(false);
+const menuId = ref();
+const formRef = ref();
+const menuData = ref<any[]>([]);
+
+const baseForm: any = {
+	apiIds: [],
+};
+
+const formData = reactive<any>({
+	...baseForm,
+});
+
+const ruleForm = {};
+
+const onSubmit = async () => {
+	await formRef.value.validate();
+
+	await api.menu.api.add({
+		menuId: menuId.value,
+		apiIds: formData.apiIds
+	});
+
+	ElMessage.success('操作成功');
+	resetForm();
+	showDialog.value = false;
+	emit('getList');
+};
+
+const resetForm = async () => {
+	Object.assign(formData, { apiIds: [] });
+	formRef.value && formRef.value.resetFields();
+};
+
+const open = async (idsArr: any, id: number) => {
+	menuId.value = id
+	resetForm();
+	// 加载菜单列表
+	api.api.getList({ types: -1, status: -1 }).then((res: any) => {
+		menuData.value = res.Info || [];
+	});
+	formData.apiIds = idsArr;
+	showDialog.value = true;
+};
+
+defineExpose({ open });
+</script>

+ 1 - 0
src/views/system/menu/component/list.vue

@@ -18,6 +18,7 @@
 						<el-dropdown-item command="status-状态">状态</el-dropdown-item>
 						<el-dropdown-item command="createdAt-创建时间">创建时间</el-dropdown-item>
 						<el-dropdown-item command="handle-操作">操作</el-dropdown-item>
+						<el-dropdown-item command="selection-复选框">复选框</el-dropdown-item>
 					</el-dropdown-menu>
 				</template>
 			</el-dropdown>

+ 7 - 1
src/views/system/menu/index.vue

@@ -2,7 +2,7 @@
   <div class="system-user-container">
     <el-card shadow="hover">
       <div class="system-menu-search">
-        <el-form :model="state.queryParams" :inline="true" ref="queryRef">
+        <el-form :model="state.queryParams" :inline="true" ref="queryRef" @keyup.enter="handleQuery()">
           <el-form-item label="菜单名称" prop="title">
             <el-input v-model="state.queryParams.title" placeholder="请输入菜单名称" size="default" clearable class="w-50" />
           </el-form-item>
@@ -68,6 +68,7 @@
                 <el-dropdown-menu>
                   <el-dropdown-item @click.native="authOpen(scope.row, 'buttonAuthorizeList')" v-auth="'btn'">按钮权限</el-dropdown-item>
                   <el-dropdown-item @click.native="authOpen(scope.row, 'listAuthorizeList')" v-auth="'list'">列表权限</el-dropdown-item>
+                  <el-dropdown-item @click.native="authOpen(scope.row, 'apiAuthorizeList')" v-auth="'list'">接口权限</el-dropdown-item>
                 </el-dropdown-menu>
               </template>
             </el-dropdown>
@@ -89,10 +90,12 @@ import EditMenu from '/@/views/system/menu/component/editMenu.vue';
 import api from '/@/api/system';
 import ButtonAuthorizeListDrawer from './component/btn.vue';
 import ListAuthorizeListDrawer from './component/list.vue';
+import ApiAuthorizeListDrawer from './component/api.vue';
 const editMenuRef = ref();
 const queryRef = ref();
 const buttonAuthorizeList = ref();
 const listAuthorizeList = ref();
+const apiAuthorizeList = ref();
 const state = reactive({
   loading: false,
   queryParams: {
@@ -124,6 +127,9 @@ const authOpen = (row: any, key: string) => {
   if (key === 'listAuthorizeList') {
     return listAuthorizeList.value.open(row);
   }
+  if (key === 'apiAuthorizeList') {
+    return apiAuthorizeList.value.open(row);
+  }
 };
 
 // 删除当前行

+ 63 - 0
src/views/system/monitor/lastLinesLog/index.vue

@@ -0,0 +1,63 @@
+<template>
+	<div>
+	<el-card shadow="hover">
+
+		<el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
+			<el-table-column prop="name" label="文件名" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="size" label="大小" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="changeAt" label="修改时间" min-width="100" align="center"></el-table-column>
+			<el-table-column label="操作" width="200" align="center">
+				<template #default="scope">
+					<el-button size="small" text type="primary" v-if="!scope.row.folderName"
+						@click="view(scope.row)">详情</el-button>
+					<el-button size="small" text type="primary" v-auth="'del'" @click="down(scope.row)">下载</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+		<pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize"
+			@pagination="getList()" />
+	</el-card>
+
+	<el-dialog v-model="dialogVisible" title="查看详情">
+		<div v-for="line in topmsg" :key="line" class="error-line">{{ line }}</div>
+
+		<div v-for="line in errorMessage" :key="line" class="error-line">{{ line }}</div>
+	</el-dialog>
+
+</div>
+</template>
+
+<script lang="ts" setup>
+import api from '/@/api/system';
+import { useSearch } from '/@/hooks/useCommon';
+import getOrigin from '/@/utils/origin'
+import downloadFile from '/@/utils/download';
+import { ref } from 'vue';
+const { params, tableData, getList, loading } = useSearch<any[]>(api.lastLinesLog.getList, 'list', { keyWord: '' });
+getList();
+
+const dialogVisible=ref(false);
+const errorMessage=ref([]);
+const topmsg=ref([]);
+
+const view = (row: any) => {
+	const es = new EventSource(getOrigin(import.meta.env.VITE_SERVER_URL + "/subscribe/logInfo?name="+row.name));
+	es.addEventListener('log', ({ data }) => {
+		topmsg.value.unshift(data);
+	});
+	api.lastLinesLog.detail({name:row.name}).then((res: any) => {
+		errorMessage.value=res.list;
+		dialogVisible.value=true;
+    });
+};
+const down = (row: any) => {
+	// window.open(getOrigin(import.meta.env.VITE_SERVER_URL + "system/monitor/downloadLog?name="+row.name));
+	api.lastLinesLog.down({ name: row.name }).then((res: any) => downloadFile(res,row.name))
+};
+
+</script>
+<style scoped>
+.error-line {
+  white-space: pre-line; /* 保留换行符 */
+}
+</style>

+ 3 - 3
src/views/system/monitor/plugin/index.vue

@@ -47,13 +47,13 @@
 				</el-table-column>
 				<el-table-column label="操作" width="180" align="center" fixed="right" v-col="'handle'">
 					<template #default="scope">
-						<el-button :disabled="scope.row.status == 0" type="warning" size="small" link @click="changeStatus(scope.row, 0)" v-auth="'stop'"
+						<el-button v-if="scope.row.status == 1" type="warning" size="small" link @click="changeStatus(scope.row, 0)" v-auth="'stop'"
 							>停用</el-button
 						>
-						<el-button :disabled="scope.row.status == 1" size="small" type="success" link @click="changeStatus(scope.row, 1)" v-auth="'start'"
+						<el-button v-if="scope.row.status == 0" size="small" type="success" link @click="changeStatus(scope.row, 1)" v-auth="'start'"
 							>启用</el-button
 						>
-						<el-button :disabled="scope.row.status == 1" size="small" type="info" link @click="onDel(scope.row)" v-auth="'del'">删除</el-button>
+						<el-button v-if="scope.row.status == 0" size="small" type="info" link @click="onDel(scope.row)" v-auth="'del'">删除</el-button>
 						<el-button size="small" type="plain" link @click="addOrEdit(scope.row)">编辑</el-button>
 					</template>
 				</el-table-column>

+ 11 - 1
vite.config.ts

@@ -1,4 +1,5 @@
 import vue from '@vitejs/plugin-vue';
+import viteCompression from 'vite-plugin-compression'
 import { resolve } from 'path';
 import { defineConfig, loadEnv, ConfigEnv } from 'vite';
 
@@ -14,7 +15,16 @@ const alias: Record<string, string> = {
 const viteConfig = defineConfig((mode: ConfigEnv) => {
 	const env = loadEnv(mode.mode, process.cwd());
 	return {
-		plugins: [vue()],
+		plugins: [
+			vue(),
+			viteCompression({
+				threshold: 1024 * 20, // 对大于 20k 的文件进行压缩
+	      // filter: /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i, // 需要压缩的文件
+				algorithm: 'gzip', // 压缩方式
+				ext: 'gz', // 后缀名
+				deleteOriginFile: false, // 压缩后是否删除压缩源文件
+			})
+		],
 		root: process.cwd(),
 		resolve: { alias },
 		base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,

+ 43 - 6
yarn.lock

@@ -996,7 +996,7 @@ chalk@^1.1.1:
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
-chalk@^4.0.0:
+chalk@^4.0.0, chalk@^4.1.2:
   version "4.1.2"
   resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz"
   integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -1173,7 +1173,7 @@ dayjs@1.x, dayjs@^1.11.3, dayjs@^1.11.8:
   resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz"
   integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==
 
-debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
   version "4.3.4"
   resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz"
   integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -1749,6 +1749,15 @@ frac@~1.1.2:
   resolved "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz"
   integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
 
+fs-extra@^10.0.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
+  integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^6.0.1"
+    universalify "^2.0.0"
+
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz"
@@ -1858,6 +1867,11 @@ good-listener@^1.2.2:
   dependencies:
     delegate "^3.1.2"
 
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+  version "4.2.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
 grapheme-splitter@^1.0.4:
   version "1.0.4"
   resolved "https://registry.npmmirror.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz"
@@ -2099,6 +2113,15 @@ json2module@^0.0.3:
   dependencies:
     rw "^1.3.2"
 
+jsonfile@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+  integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+  dependencies:
+    universalify "^2.0.0"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
 jsplumb@^2.15.6:
   version "2.15.6"
   resolved "https://registry.npmmirror.com/jsplumb/-/jsplumb-2.15.6.tgz"
@@ -2831,6 +2854,11 @@ unbox-primitive@^1.0.2:
     has-symbols "^1.0.3"
     which-boxed-primitive "^1.0.2"
 
+universalify@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+  integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
 uri-js@^4.2.2:
   version "4.4.1"
   resolved "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz"
@@ -2858,10 +2886,19 @@ vform3-builds@^3.0.8:
   resolved "https://registry.npmjs.org/vform3-builds/-/vform3-builds-3.0.8.tgz"
   integrity sha512-ipfwAlFcZ87/asYmeYBqAtwXlq7pB4WRpwGsF2dW310GUmy8RrIm5sRklntTi4m6xuZCXwhkOFXkHokTOCkCpg==
 
-vite@^2.8.6:
-  version "2.9.15"
-  resolved "https://registry.npmmirror.com/vite/-/vite-2.9.15.tgz"
-  integrity sha512-fzMt2jK4vQ3yK56te3Kqpkaeq9DkcZfBbzHwYpobasvgYmP2SoAr6Aic05CsB4CzCZbsDv4sujX3pkEGhLabVQ==
+vite-plugin-compression@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz#a75b0d8f48357ebb377b65016da9f20885ef39b6"
+  integrity sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==
+  dependencies:
+    chalk "^4.1.2"
+    debug "^4.3.3"
+    fs-extra "^10.0.0"
+
+vite@^2.9.16:
+  version "2.9.16"
+  resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.16.tgz#daf7ba50f5cc37a7bf51b118ba06bc36e97898e9"
+  integrity sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==
   dependencies:
     esbuild "^0.14.27"
     postcss "^8.4.13"

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