Explorar el Código

自定义指令文件整理,列表新增导出功能

zhangchong hace 2 años
padre
commit
a76d730960

+ 12 - 0
.eslintrc.js

@@ -13,6 +13,14 @@ module.exports = {
 	},
 	extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
 	plugins: ['vue', '@typescript-eslint'],
+	overrides: [
+		{
+			files: ['*.ts', '*.tsx', '*.vue'],
+			rules: {
+				'no-undef': 'off',
+			},
+		},
+	],
 	rules: {
 		// http://eslint.cn/docs/rules/
 		// https://eslint.vuejs.org/rules/
@@ -26,6 +34,9 @@ module.exports = {
 		'@type-eslint/ban-types': 'off',
 		'@type-eslint/no-non-null-assertion': 'off',
 		'@type-eslint/explicit-module-boundary-types': 'off',
+		'@typescript-eslint/no-redeclare': 'error',
+		'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
+		'@typescript-eslint/no-unused-vars': [2],
 		'vue/custom-event-name-casing': 'off',
 		'vue/attributes-order': 'off',
 		'vue/one-component-per-file': 'off',
@@ -59,5 +70,6 @@ module.exports = {
 		'no-v-model-argument': 'off',
 		'no-case-declarations': 'off',
 		'no-console': 0,
+		'no-redeclare': 'off',
 	},
 };

+ 2 - 11
README.md

@@ -2,7 +2,7 @@
 
 | Edge      | Firefox      | Chrome      | Safari      |
 | --------- | ------------ | ----------- | ----------- |
-| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 |
+| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 72 | Safari ≥ 12 |
 
 > 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。
 
@@ -19,13 +19,4 @@ npm run dev
 
 # 打包发布
 npm run build
-```
-
-#### 🍉 git 命令
-
-- 在本地新建一个分支:`git branch newBranch`
-- 切换到你的新分支:`git checkout newBranch`
-- 将新分支发布在 github、gitee 上:`git push origin newBranch`
-- 在本地删除一个分支:`git branch -d newBranch`
-- 在 github 远程端删除一个分支:`git push origin :newBranch (分支名前的冒号代表删除)`
-- 注意删除远程分支后,如果有对应的本地分支,本地分支并不会同步删除!
+```

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 205 - 382
package-lock.json


+ 2 - 3
package.json

@@ -25,6 +25,7 @@
 		"element-plus": "^2.2.22",
 		"file-saver": "^2.0.5",
 		"js-cookie": "^3.0.1",
+		"js-table2excel": "^1.0.3",
 		"mitt": "^3.0.0",
 		"nprogress": "^0.2.0",
 		"pinia": "^2.0.25",
@@ -36,9 +37,7 @@
 		"vue": "^3.2.45",
 		"vue-clipboard3": "^2.0.0",
 		"vue-router": "^4.1.6",
-		"vue3-seamless-scroll": "^2.0.1",
-		"xlsx": "^0.18.5",
-		"xlsx-js-style": "^1.2.0"
+		"vue3-seamless-scroll": "^2.0.1"
 	},
 	"devDependencies": {
 		"@types/node": "^18.11.9",

+ 11 - 6
src/components/Empty/index.vue

@@ -2,37 +2,37 @@
   <div class="empty-container h100 w100">
     <!-- 无数据 -->
     <template v-if="type==='data'">
-        <el-empty :description="descriptionData" :image="getImageUrl('public/empty.png')">
+        <el-empty :description="description ? description : descriptionData" :image="getImageUrl('public/empty.png')">
             <slot></slot>
         </el-empty>
     </template>
     <!-- 无文件夹 -->
     <template v-else-if="type==='file'">
-        <el-empty :description="descriptionFile" :image="getImageUrl('public/fileEmpty.png')">
+        <el-empty :description="description ? description : descriptionFile" :image="getImageUrl('public/fileEmpty.png')">
             <slot></slot>
         </el-empty>
     </template>
     <!-- 知识库为空 -->
     <template v-else-if="type==='knowledge'">
-        <el-empty :description="descriptionKnowledge" :image="getImageUrl('public/NoKnowledge.png')">
+        <el-empty :description="description ? description : descriptionKnowledge" :image="getImageUrl('public/NoKnowledge.png')">
             <slot></slot>
         </el-empty>
     </template>
     <!-- 无搜索结果 -->
     <template v-else-if="type==='search'">
-        <el-empty :description="descriptionSearch" :image="getImageUrl('public/noSearch.png')">
+        <el-empty :description="description ? description : descriptionSearch" :image="getImageUrl('public/noSearch.png')">
             <slot></slot>
         </el-empty>
     </template>
     <!-- 无搜记录 -->
     <template v-else-if="type==='record'">
-        <el-empty :description="descriptionRecord" :image="getImageUrl('public/noReocrd.png')">
+        <el-empty :description="description ? description : descriptionRecord" :image="getImageUrl('public/noReocrd.png')">
             <slot></slot>
         </el-empty>
     </template>
     <!-- 无信件 -->
     <template v-else-if="type==='letter'">
-        <el-empty :description="descriptionLetter" :image="getImageUrl('public/leeter.png')">
+        <el-empty :description="description ? description : descriptionLetter" :image="getImageUrl('public/leeter.png')">
             <slot></slot>
         </el-empty>
     </template>
@@ -51,6 +51,11 @@ export default {
         type: String,
         default:'data'
     },
+    description:{
+      required: false,
+      type: String,
+      default:'暂无数据'
+    },
     descriptionData: {
       required: false,
       type: String,

+ 1 - 2
src/components/LogicFlow/index.vue

@@ -283,7 +283,7 @@ const initEvent = () => {
   })
   // 节点点击事件
   eventCenter.on('node:click', (args) => {
-    console.log(args, 'args')
+    // console.log(args, 'args')
     currentOpId.value = args.data.id
     // propertSettingRef.value.show({
     //   ...args.data.properties,
@@ -376,7 +376,6 @@ defineExpose({
 
 .lf-control {
   background-color: var(--el-color-white) !important;
-  opacity: .8;
 }
 
 .custom-minimap {

+ 2 - 2
src/utils/authDirective.ts → src/directive/authDirective.ts

@@ -13,7 +13,7 @@ export function authDirective(app: App) {
 	app.directive('auth', {
 		mounted(el, binding) {
 			const stores = useUserInfo();
-			if (!stores.userInfos.authBtnList.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
+			if (!stores.userInfos.authBtnList?.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
 		},
 	});
 	// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
@@ -21,7 +21,7 @@ export function authDirective(app: App) {
 		mounted(el, binding) {
 			let flag = false;
 			const stores = useUserInfo();
-			stores.userInfos.authBtnList.map((val: string) => {
+			stores.userInfos.authBtnList?.map((val: string) => {
 				binding.value.map((v: string) => {
 					if (val === v) flag = true;
 				});

+ 0 - 0
src/utils/customDirective.ts → src/directive/customDirective.ts


+ 2 - 2
src/utils/directive.ts → src/directive/index.ts

@@ -1,6 +1,6 @@
 import type { App } from 'vue';
-import { authDirective } from '/@/utils/authDirective';
-import { wavesDirective, dragDirective, lazyImgDirective } from '/@/utils/customDirective';
+import { authDirective } from '/@/directive/authDirective';
+import { wavesDirective, dragDirective, lazyImgDirective } from '/@/directive/customDirective';
 
 /**
  * 导出指令方法:v-xxx

+ 1 - 1
src/layout/main/classic.vue

@@ -36,7 +36,7 @@ export default defineComponent({
 		});
 		// 重置滚动条高度,更新子级 scrollbar
 		const updateScrollbar = () => {
-			layoutMainRef.value.layoutMainScrollbarRef.update();
+			layoutMainRef.value.layoutMainScrollbarRef?.update();
 		};
 		// 重置滚动条高度,由于组件是异步引入的
 		const initScrollBarHeight = () => {

+ 1 - 1
src/layout/main/defaults.vue

@@ -36,7 +36,7 @@ export default defineComponent({
 			// 更新父级 scrollbar
 			layoutScrollbarRef.value.update();
 			// 更新子级 scrollbar
-			layoutMainRef.value.layoutMainScrollbarRef.update();
+			layoutMainRef.value.layoutMainScrollbarRef?.update();
 		};
 		// 重置滚动条高度,由于组件是异步引入的
 		const initScrollBarHeight = () => {

+ 1 - 1
src/layout/main/transverse.vue

@@ -24,7 +24,7 @@ export default defineComponent({
 		const route = useRoute();
 		// 重置滚动条高度,更新子级 scrollbar
 		const updateScrollbar = () => {
-			layoutMainRef.value.layoutMainScrollbarRef.update();
+			layoutMainRef.value.layoutMainScrollbarRef?.update();
 		};
 		// 重置滚动条高度,由于组件是异步引入的
 		const initScrollBarHeight = () => {

+ 1 - 9
src/main.ts

@@ -1,16 +1,8 @@
-/*
- * @Author: zc
- * @Description: 
- * @version: 
- * @Date: 2022-11-17 17:01:50
- * @LastEditors: Please set LastEditors
- * @LastEditTime: 2022-11-23 14:51:24
- */
 import { createApp } from 'vue';
 import pinia from '/@/stores/index';
 import App from './App.vue';
 import router from './router';
-import { directive } from '/@/utils/directive';
+import { directive } from '/@/directive';
 import other from '/@/utils/other';
 //  引入element
 import ElementPlus from 'element-plus';

+ 2 - 2
src/router/backEnd.ts

@@ -25,8 +25,8 @@ const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
 const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules });
 
 // 菜单格式化
-function formatRouter(arr:Array<any>){
-	if (!arr.length){
+function formatRouter(arr:any){
+	if (!arr.length || !Array.isArray(arr)){
 		ElNotification({
 			type:'error',
 			title:"错误",

+ 16 - 0
src/router/route.ts

@@ -1,6 +1,8 @@
 import { RouteRecordRaw } from 'vue-router';
 
 /**
+ *  * 建议:路由 path 路径与文件夹名称相同,找文件可浏览器地址找,方便定位文件位置
+ *
  * 路由meta对象参数说明
  * meta: {
  *      title:          菜单栏及 tagsView 栏、菜单搜索名称(国际化)
@@ -14,6 +16,20 @@ import { RouteRecordRaw } from 'vue-router';
  * }
  */
 
+// 扩展 RouteMeta 接口
+declare module 'vue-router' {
+	interface RouteMeta {
+		title?: string;
+		isLink?: string;
+		isHide?: boolean;
+		isKeepAlive?: boolean;
+		isAffix?: boolean;
+		isIframe?: boolean;
+		roles?: string[];
+		icon?: string;
+	}
+}
+
 /**
  * 定义动态路由
  * 前端添加路由,请在顶级节点的 `children 数组` 里添加

+ 4 - 0
src/theme/app.scss

@@ -409,4 +409,8 @@ ul,li{
 		width: 20px;
 		height: 20px;
 	}
+}
+.flexEnd{
+	display: flex;
+	justify-content: flex-end;
 }

+ 13 - 0
src/types/axios.d.ts

@@ -0,0 +1,13 @@
+/* eslint-disable */
+import * as axios from 'axios';
+
+// 扩展 axios 数据返回类型,可自行扩展
+declare module 'axios' {
+	export interface AxiosResponse<T = any> {
+		code: number;
+		data: T;
+		message: string;
+		type?: string;
+		[key: string]: T;
+	}
+}

+ 60 - 0
src/types/global.d.ts

@@ -0,0 +1,60 @@
+// 申明外部 npm 插件模块
+declare module 'vue-grid-layout';
+declare module 'qrcodejs2-fixes';
+declare module 'splitpanes';
+declare module 'js-cookie';
+declare module '@wangeditor/editor-for-vue';
+declare module 'js-table2excel';
+
+// 声明一个模块,防止引入文件时报错
+declare module '*.json';
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.scss';
+declare module '*.ts';
+declare module '*.js';
+
+// 声明文件,*.vue 后缀的文件交给 vue 模块来处理
+declare module '*.vue' {
+	import type { DefineComponent } from 'vue';
+	const component: DefineComponent<{}, {}, any>;
+	export default component;
+}
+
+// 声明文件,定义全局变量
+/* eslint-disable */
+declare interface Window {
+	nextLoading: boolean;
+}
+
+// 声明路由当前项类型
+declare type RouteItem<T = any> = {
+	path: string;
+	name?: string | symbol | undefined | null;
+	redirect?: string;
+	k?: T;
+	meta?: {
+		title?: string;
+		isLink?: string;
+		isHide?: boolean;
+		isKeepAlive?: boolean;
+		isAffix?: boolean;
+		isIframe?: boolean;
+		roles?: string[];
+		icon?: string;
+		isDynamic?: boolean;
+		isDynamicPath?: string;
+		isIframeOpen?: string;
+		loading?: boolean;
+	};
+	children: T[];
+	query?: { [key: string]: T };
+	params?: { [key: string]: T };
+	contextMenuClickId?: string | number;
+	commonUrl?: string;
+	isFnClick?: boolean;
+	url?: string;
+	transUrl?: string;
+	title?: string;
+	id?: string | number;
+};

+ 59 - 0
src/types/layout.d.ts

@@ -0,0 +1,59 @@
+// aside
+declare type AsideState = {
+	menuList: RouteRecordRaw[];
+	clientWidth: number;
+};
+
+// columnsAside
+declare type ColumnsAsideState<T = any> = {
+	columnsAsideList: T[];
+	liIndex: number;
+	liOldIndex: null | number;
+	liHoverIndex: null | number;
+	liOldPath: null | string;
+	difference: number;
+	routeSplit: string[];
+};
+
+// navBars breadcrumb
+declare type BreadcrumbState<T = any> = {
+	breadcrumbList: T[];
+	routeSplit: string[];
+	routeSplitFirst: string;
+	routeSplitIndex: number;
+};
+
+// navBars search
+declare type SearchState<T = any> = {
+	isShowSearch: boolean;
+	menuQuery: string;
+	tagsViewList: T[];
+};
+
+// navBars tagsView
+declare type TagsViewState<T = any> = {
+	routeActive: string | T;
+	routePath: string | unknown;
+	dropdown: {
+		x: string | number;
+		y: string | number;
+	};
+	sortable: T;
+	tagsRefsIndex: number;
+	tagsViewList: T[];
+	tagsViewRoutesList: T[];
+};
+
+// navBars parent
+declare type ParentViewState<T = any> = {
+	refreshRouterViewKey: string;
+	iframeRefreshKey: string;
+	keepAliveNameList: string[];
+	iframeList: T[];
+};
+
+// navBars link
+declare type LinkViewState = {
+	title: string;
+	isLink: string;
+};

+ 11 - 0
src/types/mitt.ts → src/types/mitt.d.ts

@@ -12,3 +12,14 @@ export type MittType = {
 	onCurrentContextmenuClick?: any; // tagsview 右键菜单每项点击时
 	scrollTopEmit?:object; //点击分页跳转滚动到顶部
 };
+// mitt 参数类型定义
+declare type LayoutMobileResize = {
+	layout: string;
+	clientWidth: number;
+};
+
+// mitt 参数菜单类型
+declare type MittMenu = {
+	children: RouteRecordRaw[];
+	item?: RouteItem;
+};

+ 90 - 0
src/types/pinia.d.ts

@@ -0,0 +1,90 @@
+/**
+ * pinia 类型定义
+ */
+
+// 用户信息
+declare interface UserInfosState<T = any> {
+	userInfos: {
+		authBtnList: string[];
+		photo: string;
+		roles: string[];
+		time: number;
+		userName: string;
+		[key: string]: T;
+	};
+}
+
+// 路由缓存列表
+declare interface KeepAliveNamesState {
+	keepAliveNames: string[];
+	cachedViews: string[];
+}
+
+// 后端返回原始路由(未处理时)
+declare interface RequestOldRoutesState {
+	requestOldRoutes: string[];
+}
+
+// TagsView 路由列表
+declare interface TagsViewRoutesState<T = any> {
+	tagsViewRoutes: T[];
+	isTagsViewCurrenFull: Boolean;
+}
+
+// 路由列表
+declare interface RoutesListState<T = any> {
+	routesList: T[];
+	isColumnsMenuHover: Boolean;
+	isColumnsNavHover: Boolean;
+}
+
+// 布局配置
+declare interface ThemeConfigState {
+	themeConfig: {
+		isDrawer: boolean;
+		primary: string;
+		topBar: string;
+		topBarColor: string;
+		isTopBarColorGradual: boolean;
+		menuBar: string;
+		menuBarColor: string;
+		isMenuBarColorGradual: boolean;
+		columnsMenuBar: string;
+		columnsMenuBarColor: string;
+		isColumnsMenuBarColorGradual: boolean;
+		isColumnsMenuHoverPreload: boolean;
+		isCollapse: boolean;
+		isUniqueOpened: boolean;
+		isFixedHeader: boolean;
+		isFixedHeaderChange: boolean;
+		isClassicSplitMenu: boolean;
+		isLockScreen: boolean;
+		lockScreenTime: number;
+		isShowLogo: boolean;
+		isShowLogoChange: boolean;
+		isBreadcrumb: boolean;
+		isTagsview: boolean;
+		isBreadcrumbIcon: boolean;
+		isTagsviewIcon: boolean;
+		isCacheTagsView: boolean;
+		isSortableTagsView: boolean;
+		isShareTagsView: boolean;
+		isFooter: boolean;
+		isGrayscale: boolean;
+		isInvert: boolean;
+		isIsDark: boolean;
+		isWartermark: boolean;
+		wartermarkText: string;
+		tagsStyle: string;
+		animation: string;
+		columnsAsideStyle: string;
+		columnsAsideLayout: string;
+		layout: string;
+		isRequestRoutes: boolean;
+		globalTitle: string;
+		globalViceTitle: string;
+		globalViceTitleMsg: string;
+		globalI18n: string;
+		globalComponentSize: string;
+	};
+}

+ 0 - 0
src/types/views.d.ts


+ 2 - 2
src/utils/authFunction.ts

@@ -8,7 +8,7 @@ import { judementSameArr } from '/@/utils/arrayOperation';
  */
 export function auth(value: string): boolean {
 	const stores = useUserInfo();
-	return stores.userInfos.authBtnList.some((v: string) => v === value);
+	return stores.userInfos.authBtnList?.some((v: string) => v === value);
 }
 
 /**
@@ -19,7 +19,7 @@ export function auth(value: string): boolean {
 export function auths(value: Array<string>): boolean {
 	let flag = false;
 	const stores = useUserInfo();
-	stores.userInfos.authBtnList.map((val: string) => {
+	stores.userInfos.authBtnList?.map((val: string) => {
 		value.map((v: string) => {
 			if (val === v) flag = true;
 		});

+ 1 - 9
src/utils/mitt.ts

@@ -1,14 +1,6 @@
-/*
- * @Author: zc
- * @Description: 
- * @version: 
- * @Date: 2022-11-17 10:31:27
- * @LastEditors: 
- * @LastEditTime: 2022-11-17 10:31:30
- */
 // https://www.npmjs.com/package/mitt
 import mitt, { Emitter } from 'mitt';
-import { MittType } from '/@/types/mitt';
+import { MittType } from '../types/mitt';
 
 // 类型
 const emitter: Emitter<MittType> = mitt<MittType>();

+ 38 - 10
src/views/deviceManagement/ivrCategroy/index.vue

@@ -3,14 +3,18 @@
         <div class="layout-padding-auto layout-padding-view pd20">
             <div class="flex-center-between mb20">
                 <p class="table-title">信息列表</p>
-                <div v-auth="'300302'">
-                    <el-button type="primary" @click="onAddCategory" v-waves>
+                <div>
+                    <el-button type="primary" @click="onAddCategory" v-waves v-auth="'300302'">
                         <SvgIcon name="ele-Plus" class="mr5" />新增分类
                     </el-button>
+                    <el-button type="primary" v-waves @click="onImportTable">
+                        <SvgIcon name="iconfont icon-daochu" class="mr5"/>导出
+                    </el-button>
                 </div>
             </div>
             <!-- 表格 -->
-            <el-table :data="list" v-loading="loading">
+            <el-table :data="tableData" v-loading="loading" row-key="id" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55" :reserve-selection="true"/>
                 <el-table-column prop="name" label="分类名称" show-overflow-tooltip></el-table-column>
                 <el-table-column prop="remark" label="备注" show-overflow-tooltip></el-table-column>
                 <el-table-column prop="creationTime" label="创建时间" show-overflow-tooltip width="170">
@@ -59,16 +63,26 @@ export default {
 }
 </script>
 <script lang="ts" setup name="ivrCategroy">
-import { defineAsyncComponent, onMounted, ref } from "vue";
+import { defineAsyncComponent, onMounted, ref,reactive, toRefs } from "vue";
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { getIvrCategories, deleteIvrCategroies } from "/@/api/deviceManagement/ivr";
 import { formatDate } from '/@/utils/formatTime';
-
+import table2excel from 'js-table2excel';
 const AddCategroy = defineAsyncComponent(() => import('/@/views/deviceManagement/ivrCategroy/component/addCategroy.vue'))
 const EditCategroy = defineAsyncComponent(() => import('/@/views/deviceManagement/ivrCategroy/component/editCatehroy.vue'))
+
+interface ivrCategroyState{
+    loading:boolean;
+    tableData:Array<any>; //列表数据
+    multipleSelection:Array<any>; //多选列表
+}
 // import { useRouter } from "vue-router";
-const loading = ref(false);
-const list = ref<any>([]);
+const state = reactive<ivrCategroyState>({
+    loading:false,
+    tableData:[],
+    multipleSelection:[]
+})
+const {loading,tableData} = toRefs(state)
 
 const addCategroyRef = ref();
 const editCategroyRef = ref();
@@ -93,11 +107,11 @@ const onDelCategroy = (row: any) => { //删除分类
     }).catch(() => { });
 }
 const handleQuery = () => { //查询列表
-    loading.value = true;
+    state.loading = true;
     getIvrCategories().then((res: any) => {
-        list.value = res?.result ?? [];
+        state.tableData = res?.result ?? [];
         setTimeout(() => {
-            loading.value = false;
+            state.loading = false;
         }, 300);
     })
 }
@@ -111,6 +125,20 @@ const handleQuery = () => { //查询列表
 //         }
 //     })
 // }
+// 表格多选
+const handleSelectionChange = (val: any) => {
+	state.multipleSelection = val;
+}
+// 导出表格
+const onImportTable = ()=>{
+	if (state.multipleSelection.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	const tabeHeader = [
+		{ key: 'name', colWidth: '', title: '分类名称', type: 'text', isCheck: true },
+        { key: 'remark', colWidth: '', title: '备注', type: 'text', isCheck: true },
+		{ key: 'creationTime', colWidth: '', title: '创建时间', type: 'text', isCheck: true },
+	]
+	table2excel(tabeHeader, state.multipleSelection, `ivr分类管理 ${formatDate(new Date(),'YYYY-mm-dd HH-MM')}`);
+}
 onMounted(() => {
     handleQuery();
 })

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 486 - 485
src/views/deviceManagement/ivrList/index.vue


+ 40 - 22
src/views/deviceManagement/tels/index.vue

@@ -1,24 +1,20 @@
-<!--
- * @Author: zc
- * @Description: 
- * @version: 
- * @Date: 2022-11-22 15:21:26
- * @LastEditors: 
- * @LastEditTime: 2022-11-25 13:57:06
--->
 <template>
     <div class="deviceManagement-tels-container layout-padding">
         <div class="layout-padding-auto layout-padding-view pd20">
             <div class="flex-center-between mb20">
                 <p class="table-title">信息列表</p>
-                <div v-auth="'300101'">
-                    <el-button type="primary" @click="asyncDevice" v-waves>
+                <div>
+                    <el-button type="primary" @click="asyncDevice" v-waves v-auth="'300101'">
                         <SvgIcon name="ele-Refresh" class="mr5" />同步
                     </el-button>
+                    <el-button type="primary" v-waves @click="onImportTable">
+                        <SvgIcon name="iconfont icon-daochu" class="mr5"/>导出
+                    </el-button>
                 </div>
             </div>
             <!-- 表格 -->
-            <el-table :data="list" v-loading="loading"  style="width: 100%">
+            <el-table :data="tableData" v-loading="loading" row-key="id" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55" :reserve-selection="true"/>
                 <el-table-column prop="no" label="分机编号" show-overflow-tooltip></el-table-column>
                 <el-table-column prop="groupNames" label="所属分机组" show-overflow-tooltip></el-table-column>
                 <el-table-column prop="registerIP" label="注册IP" show-overflow-tooltip></el-table-column>
@@ -37,29 +33,35 @@
 </template>
 
 <script lang="ts" name="tels" setup>
-import { ref, onMounted } from "vue";
+import {  onMounted, reactive,toRefs } from "vue";
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { formatDate } from '/@/utils/formatTime';
+import table2excel from 'js-table2excel';
 // 引入api
 import { getTelsList, syncTel } from '/@/api/deviceManagement/tels';
-const loading = ref(false);
-const list = ref<any>([]);
-
+interface telsState {
+    loading:boolean;
+    tableData:Array<any>;
+    multipleSelection:Array<any>
+}
+const state = reactive<telsState>({
+    loading:false,
+    tableData:[],
+    multipleSelection:[]
+})
+const {loading,tableData} = toRefs(state)
 /** 获取分机列表 */
 const getList = () => {
-    loading.value = true;
+    state.loading = true;
     getTelsList()
         .then((response: any) => {
-            list.value = response.result ?? [];
-            for (let i of list.value) {
-                i.creationTime = formatDate(new Date(i.creationTime), 'YYYY-mm-dd HH:MM:SS');
-            }
+            state.tableData = response.result ?? [];
             setTimeout(() => {
-                loading.value = false;
+                state.loading = false;
             }, 300);
         })
         .catch(() => {
-            loading.value = false;
+            state.loading = false;
         });
 };
 // 同步
@@ -79,6 +81,22 @@ const asyncDevice = () => {
         })
     }).catch(() => { });
 }
+// 表格多选
+const handleSelectionChange = (val: any) => {
+	state.multipleSelection = val;
+}
+// 导出表格
+const onImportTable = ()=>{
+	if (state.multipleSelection.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	const tabeHeader = [
+		{ key: 'no', colWidth: '', title: '分机编号', type: 'text', isCheck: true },
+		{ key: 'groupNames', colWidth: '', title: '所属分机组', type: 'text', isCheck: true },
+		{ key: 'registerIP', colWidth: '', title: '注册IP', type: 'text', isCheck: true },
+		{ key: '分机状态', colWidth: '', title: '分机状态', type: 'text', isCheck: true },
+		{ key: '创建时间', colWidth: '', title: '创建时间', type: 'text', isCheck: true },
+	]
+	table2excel(tabeHeader, state.multipleSelection, `话机管理 ${formatDate(new Date(),'YYYY-mm-dd HH-MM')}`);
+}
 onMounted(() => {
     getList();
 })

+ 80 - 38
src/views/deviceManagement/telsGroup/index.vue

@@ -3,13 +3,17 @@
         <div class="layout-padding-auto layout-padding-view pd20">
             <div class="flex-center-between mb20">
                 <p class="table-title">信息列表</p>
-                <div v-auth="'300202'">
-                    <el-button type="primary" @click="onAddTelsGroup" v-waves>
+                <div>
+                    <el-button type="primary" @click="onAddTelsGroup" v-waves v-auth="'300202'">
                         <SvgIcon name="ele-Plus" class="mr5" />新增分机组
                     </el-button>
+                    <el-button type="primary" v-waves @click="onImportTable">
+                        <SvgIcon name="iconfont icon-daochu" class="mr5"/>导出
+                    </el-button>
                 </div>
             </div>
-            <el-table :data="list" v-loading="loading" height="100%">
+            <el-table :data="tableData" v-loading="loading" row-key="id" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55" :reserve-selection="true"/>
                 <el-table-column prop="no" label="编号" show-overflow-tooltip></el-table-column>
                 <el-table-column prop="name" label="分机组名称" show-overflow-tooltip></el-table-column>
                 <el-table-column prop="distributionText" label="呼叫分配方式" show-overflow-tooltip></el-table-column>
@@ -77,7 +81,7 @@
                         <el-form-item label="选择分机" prop="telNos"
                             :rules="[{ required: true, message: '请选择分机', trigger: 'change' }]">
                             <el-transfer v-model="ruleForm.telNos" :titles="['所有分机', '已选分机']" :props="{ key: 'no' }"
-                                :data="data" :filterable="true">
+                                :data="telsList" :filterable="true">
                                 <template #default="{ option }">
                                     <span>分机 - {{ option.no }}</span>
                                 </template>
@@ -121,47 +125,68 @@ import { ElMessage, ElNotification } from 'element-plus';
 import { storeToRefs } from 'pinia';
 import { formatDate } from '/@/utils/formatTime';
 import { useUserInfo } from '/@/stores/userInfo';
+import table2excel from 'js-table2excel';
 // 引入需要的api
 import { getTelsGroupList, addTelsGroup, updateTelsGroup, baseInfoTelsGroup } from '/@/api/deviceManagement/telsGroup';
 import { getTelsList } from '/@/api/deviceManagement/tels';
 import { voicequerylist } from '/@/api/deviceManagement/ivr';
 
-const loading = ref(false);
-const list = ref<any>([]);
-const voiceData = ref<any>([]); // 音频文件
+interface telsGroupState {
+    ruleForm:{
+        no: string; // 分机组编号
+        name: string; // 分机组名称
+        remark: string; // 分机组备注
+        telNos: Array<any>; //选择的分机
+        voiceList: Array<any>; //语音列表
+        voice: string; //格式化之后的语音列表
+        distribution:object; //呼叫分配方式
+        isDefault: boolean;  //是否默认分组
+    }
+    distributions:Array<any>; //呼叫分配方式列表
+    loading:boolean;
+    tableData:Array<any>; //列表数据
+    voiceData:Array<any>; //音频文件
+    isShowDialog:boolean;
+    telsList:Array<any>; //分机列表
+    multipleSelection:Array<any>; //多选列表
+}
 const dialogTitle = ref('配置分机组')
-const isShowDialog = ref(false);
-const data = ref<any>([]); //分机列表
-const state = reactive({
+const state = reactive<telsGroupState>({
     ruleForm: {
         no: '', // 分机组编号
         name: '', // 分机组名称
         remark: '', // 分机组备注
-        telNos: [] as unknown as any, //选择的分机
-        voiceList: <any>[],
+        telNos: [],
+        voiceList: [],
         voice: "",
-        distribution: null as unknown as number,
+        distribution:{},
         isDefault: false
     },
-    distributions: [] as unknown as any
+    distributions: [], 
+    loading:false,
+    tableData:[],
+    voiceData:[],
+    isShowDialog:false,
+    telsList:[],
+    multipleSelection:[]
 })
 const storesUserInfo = useUserInfo();
 const { userInfos } = storeToRefs(storesUserInfo);
-const { ruleForm, distributions } = toRefs(state);
+const { ruleForm, distributions,loading,tableData,voiceData,isShowDialog,telsList } = toRefs(state);
 /** 获取所有语音文件列表 */
 const getAudioList = () => {
     voicequerylist().then((response: any) => {
-        voiceData.value = response.result.map((item: any) => {
+        state.voiceData = response.result.map((item: any) => {
             return {
                 key: item,
                 label: item
             }
         })
         setTimeout(() => {
-            loading.value = false;
+            state.loading = false;
         }, 300);
     }).catch(() => {
-        loading.value = false;
+        state.loading = false;
     });
     if (userInfos.value.authBtnList.includes('300201')) { // 校验是否有权限 页面基础信息
         baseInfoTelsGroup().then((res: any) => {// 获取页面基础信息
@@ -178,18 +203,18 @@ const getAudioList = () => {
 };
 /** 获取分机列表 */
 const getList = () => {
-    loading.value = true;
+    state.loading = true;
     getTelsGroupList().then((response: any) => {
-        list.value = response?.result ?? [];
-        for (let i of list.value) {
+        state.tableData = response?.result ?? [];
+        for (let i of state.tableData) {
             i.voiceList = i.voice ? i.voice.split('+') : [];
             i.telNos = i.tels;
         }
         setTimeout(() => {
-            loading.value = false;
+            state.loading = false;
         }, 300);
     }).catch(() => {
-        loading.value = false;
+        state.loading = false;
     });
 };
 const configure = (row: any) => { //配置分机
@@ -204,7 +229,7 @@ const configure = (row: any) => { //配置分机
     } else {
         state.ruleForm.telNos = <any>[]
     }
-    isShowDialog.value = true;
+    state.isShowDialog = true;
 }
 const ruleFormRef = ref();
 const onAddTelsGroup = () => {//新增分机组
@@ -213,26 +238,26 @@ const onAddTelsGroup = () => {//新增分机组
         ruleFormRef.value.clearValidate();
         ruleFormRef.value.resetFields();
     }
-    if (list.value.length) {
+    if (state.tableData.length) {
         state.ruleForm = {
-            no: String(Number(list.value.length + 1)), // ivr编号
+            no: String(Number(state.tableData.length + 1)), // ivr编号
             name: '', // 分机组名称
             remark: '', // 分机组备注
-            telNos: [] as unknown as any, //选择的分机
-            voiceList: <any>[],
+            telNos: [],
+            voiceList: [],
             voice: "",
-            distribution: null as unknown as number,
+            distribution:{},
             isDefault: false
         }
     } else {
         state.ruleForm.no = '1';
     }
-    isShowDialog.value = true;
+    state.isShowDialog = true;
 }
 /** 获取分机列表 */
 const getTelList = () => {
     getTelsList().then((response: any) => {
-        data.value = response?.result ?? [];
+        state.telsList = response?.result ?? [];
     }).catch(() => {
 
     });
@@ -243,17 +268,17 @@ const save = () => { // 保存分机组配置
             if (state.ruleForm.voiceList.length) {
                 state.ruleForm.voice = state.ruleForm.voiceList.join("+");
             }
-            loading.value = true;
+            state.loading = true;
             if (dialogTitle.value == '新增分机组') {
                 addTelsGroup(state.ruleForm).then(() => {
                     ElMessage.success("操作成功");
                     setTimeout(() => {
                         getList();
                     }, 1000);
-                    isShowDialog.value = false;
-                    loading.value = false;
+                    state.isShowDialog = false;
+                    state.loading = false;
                 }).catch(() => {
-                    loading.value = false;
+                    state.loading = false;
                 });
             } else if (dialogTitle.value == '配置分机组') {
                 updateTelsGroup(state.ruleForm).then(() => {
@@ -261,10 +286,10 @@ const save = () => { // 保存分机组配置
                     setTimeout(() => {
                         getList();
                     }, 1000);
-                    isShowDialog.value = false;
-                    loading.value = false;
+                    state.isShowDialog = false;
+                    state.loading = false;
                 }).catch(() => {
-                    loading.value = false;
+                    state.loading = false;
                 });
             }
 
@@ -273,6 +298,23 @@ const save = () => { // 保存分机组配置
         }
     });
 }
+// 表格多选
+const handleSelectionChange = (val: any) => {
+	state.multipleSelection = val;
+}
+// 导出表格
+const onImportTable = ()=>{
+	if (state.multipleSelection.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	const tabeHeader = [
+		{ key: 'no', colWidth: '', title: '编号', type: 'text', isCheck: true },
+		{ key: 'name', colWidth: '', title: '分机组名称', type: 'text', isCheck: true },
+		{ key: 'distributionText', colWidth: '', title: '呼叫分配方式', type: 'text', isCheck: true },
+		{ key: 'isDefault', colWidth: '', title: '是否默认分组', type: 'text', isCheck: true },
+		{ key: 'remark', colWidth: '', title: '备注', type: 'text', isCheck: true },
+		{ key: 'creationTime', colWidth: '', title: '创建时间', type: 'text', isCheck: true },
+	]
+	table2excel(tabeHeader, state.multipleSelection, `分机组管理 ${formatDate(new Date(),'YYYY-mm-dd HH-MM')}`);
+}
 // 页面加载时
 onMounted(() => {
     getTelList(); //获取分机列表

+ 1 - 1
src/views/error/401.vue

@@ -13,7 +13,7 @@
 					</div>
 				</div>
 				<div class="right">
-					<img :src="getImageUrl('/public/jurisdiction.png')" alt="" />
+					<img :src="getImageUrl('public/jurisdiction.png')" alt="" />
 				</div>
 			</div>
 		</div>

+ 1 - 1
src/views/error/404.vue

@@ -13,7 +13,7 @@
 					</div>
 				</div>
 				<div class="right">
-					<img :src="getImageUrl('/public/404.png')" alt="" />
+					<img :src="getImageUrl('public/404.png')" alt="" />
 				</div>
 			</div>
 		</div>

+ 30 - 8
src/views/system/institutionalUsers/roles/index.vue

@@ -17,13 +17,17 @@
     <el-card shadow="never">
       <div class="flex-center-between mb20">
         <p class="table-title">信息列表</p>
-        <div v-auth="'100201'">
-          <el-button type="primary"  @click="onOpenAddRole" v-waves>
+        <div>
+          <el-button type="primary"  @click="onOpenAddRole" v-waves  v-auth="'100201'">
               <SvgIcon name="ele-Plus" class="mr5"/>新增角色
           </el-button>
+          <el-button type="primary" v-waves @click="onImportTable">
+              <SvgIcon name="iconfont icon-daochu" class="mr5"/>导出
+          </el-button>
         </div>
       </div>
-      <el-table :data="state.tableData.data"  v-loading="loading" style="width: 100%">
+      <el-table :data="state.tableData.data" v-loading="loading" row-key="id" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" :reserve-selection="true"/>
         <el-table-column prop="name" label="角色名称" show-overflow-tooltip></el-table-column>
         <el-table-column prop="displayName" label="角色别名" show-overflow-tooltip></el-table-column>
         <el-table-column prop="status" label="角色状态" show-overflow-tooltip>
@@ -86,7 +90,10 @@ import type { FormInstance } from 'element-plus';
 import { throttle } from '/@/utils/tools';
 import { formatDate } from '/@/utils/formatTime';
 import { getRoleListPaged,delRole } from '/@/api/system/roles';
-
+import table2excel from 'js-table2excel';
+const AddRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/roles/component/addRole.vue'))
+const EditRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/roles/component/editRole.vue'))
+const PermissionRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/roles/component/permission.vue'))
 // 定义接口来定义对象的类型
 interface TableDataState {
   tableData: {
@@ -94,6 +101,7 @@ interface TableDataState {
     total: number;
   };
   loading: boolean;
+  multipleSelection:Array<any>
 }
 interface queryState {
   queryParams: {
@@ -102,9 +110,6 @@ interface queryState {
     SearchText?: string,
   }
 }
-const AddRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/roles/component/addRole.vue'))
-const EditRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/roles/component/editRole.vue'))
-const PermissionRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/roles/component/permission.vue'))
 const addRoleRef = ref();
 const editRoleRef = ref();
 const PermissionRoleRef = ref();
@@ -114,7 +119,8 @@ const state = reactive<TableDataState>({
     data: [],
     total: 0,
   },
-  loading: false
+  loading: false,
+  multipleSelection:[]
 });
 const data = reactive<queryState>({
   queryParams: {
@@ -161,6 +167,10 @@ const onOpenEditRole = (row: Object) => {
 const onPermissions = (row: Object)=>{
   PermissionRoleRef.value.openDialog(row)
 }
+// 表格多选
+const handleSelectionChange = (val: any) => {
+  state.multipleSelection = val;
+}
 // 删除角色
 const onRowDel = (row: any) => {
   ElMessageBox.confirm(`此操作将永久删除角色名称:“${row.name}”,是否继续?`, '提示', {
@@ -177,6 +187,18 @@ const onRowDel = (row: any) => {
     })
     .catch(() => { });
 };
+// 导出表格
+const onImportTable = ()=>{
+	if (state.multipleSelection.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	const tabeHeader = [
+		{ key: 'name', colWidth: '', title: '账号', type: 'text', isCheck: true },
+		{ key: 'phoneNo', colWidth: '', title: '手机号', type: 'text', isCheck: true },
+		{ key: 'staffNo', colWidth: '', title: '工号', type: 'text', isCheck: true },
+		{ key: 'defaultTelNo', colWidth: '', title: '默认分机', type: 'text', isCheck: true },
+		{ key: 'creationTime', colWidth: '', title: '创建时间', type: 'text', isCheck: true },
+	]
+	table2excel(tabeHeader, state.multipleSelection, `角色信息 ${formatDate(new Date(),'YYYY-mm-dd HH-MM')}`);
+}
 // 页面加载时
 onMounted(() => {
   handleQuery();

+ 45 - 44
src/views/system/institutionalUsers/user/index.vue

@@ -20,16 +20,18 @@
     <el-card shadow="never">
       <div class="flex-center-between mb20">
         <p class="table-title">信息列表</p>
-        <div v-auth="'100102'">
-          <el-button type="primary" @click="onOpenAddUser" v-waves>
+        <div>
+          <el-button type="primary" @click="onOpenAddUser" v-waves  v-auth="'100102'">
             <SvgIcon name="ele-Plus" class="mr5" />新增用户
           </el-button>
+          <el-button type="primary" v-waves @click="onImportTable">
+              <SvgIcon name="iconfont icon-daochu" class="mr5"/>导出
+          </el-button>
         </div>
       </div>
       <!-- 表格 -->
-      <el-table :data="list" v-loading="loading" row-key="id" ref="multipleTableRef"
-        @selection-change="handleSelectionChange" id="table">
-        <!-- <el-table-column type="selection" width="55" :reserve-selection="true"/> -->
+      <el-table :data="tableData" v-loading="loading" row-key="id" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" :reserve-selection="true"/>
         <el-table-column prop="name" label="账号" show-overflow-tooltip></el-table-column>
         <el-table-column prop="phoneNo" label="手机号码" show-overflow-tooltip></el-table-column>
         <el-table-column prop="staffNo" label="工号" show-overflow-tooltip></el-table-column>
@@ -96,8 +98,10 @@ import { throttle } from '/@/utils/tools';
 import type { FormInstance } from 'element-plus';
 import { formatDate } from '/@/utils/formatTime';
 import { delUser, getUserListPaged, restPwd } from '/@/api/system/user';
-// import FileSaver from 'file-saver';
-// import * as XLSX from 'xlsx'//这是vue3导入XLSX的方法
+import table2excel from 'js-table2excel';
+const AddUer = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/user/component/addUser.vue'))
+const EditUser = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/user/component/editUser.vue'))
+const SetRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/user/component/setRole.vue'))
 interface queryState {
   queryParams: {
     PageIndex: number,
@@ -105,9 +109,13 @@ interface queryState {
     PhoneNo?: string,
     Name?: string,
     NickName?: string
-  }
+  };
+  tableData:Array<any>;
+  total:number;
+  loading:boolean;
+  multipleSelection:Array<any>
 }
-const data = reactive<queryState>({
+const state = reactive<queryState>({
   queryParams: {
     PageIndex: 1,
     pageSize: 10,
@@ -115,33 +123,32 @@ const data = reactive<queryState>({
     Name: '',
     NickName: '',
   },
+  tableData:[],
+  total:0,
+  loading:false,
+  multipleSelection:[]
 });
-const AddUer = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/user/component/addUser.vue'))
-const EditUser = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/user/component/editUser.vue'))
-const SetRole = defineAsyncComponent(() => import('/@/views/system/institutionalUsers/user/component/setRole.vue'))
-const loading = ref(false);
-const total = ref(0);
-const list = ref<any>([]);
+const { queryParams,tableData,total,loading } = toRefs(state);
 const ruleFormRef = ref<FormInstance>();
-const addUserRef = ref();
-const editUserRef = ref();
-const setRoleRef = ref();
+const addUserRef = ref(); //新增用户
+const editUserRef = ref(); //修改用户信息
+const setRoleRef = ref(); //设置角色
 /** 搜索按钮操作 节流操作 */
 const handleQuery = throttle(() => {
-  data.queryParams.PageIndex = 1;
+  state.queryParams.PageIndex = 1;
   getList();
 }, 1000);
 /** 获取用户列表 */
 const getList = () => {
-  loading.value = true;
-  getUserListPaged(data.queryParams).then((response: any) => {
-    list.value = response?.result.items ?? [];
-    total.value = response?.result.total;
+  state.loading = true;
+  getUserListPaged(state.queryParams).then((response: any) => {
+    state.tableData = response?.result.items ?? [];
+    state.total = response?.result.total;
     setTimeout(() => {
-      loading.value = false;
+      state.loading  = false;
     }, 300);
   }).catch(() => {
-    loading.value = false;
+    state.loading  = false;
   });
 };
 // 打开新增用户弹窗
@@ -152,9 +159,8 @@ const onOpenAddUser = () => {
 const onSetRole = (row: any) => {
   setRoleRef.value.openDialog(row);
 };
-const multipleSelection = ref<any>([]) //多选
 const handleSelectionChange = (val: any) => {
-  multipleSelection.value = val;
+  state.multipleSelection = val;
 }
 /** 重置按钮操作 */
 const resetQuery = throttle((formEl: FormInstance | undefined) => {
@@ -196,25 +202,20 @@ const onRowDel = (row: any) => {
     });
   }).catch(() => { });
 };
-const { queryParams } = toRefs(data);
+// 导出表格
+const onImportTable = ()=>{
+	if (state.multipleSelection.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	const tabeHeader = [
+		{ key: 'name', colWidth: '', title: '账号', type: 'text', isCheck: true },
+		{ key: 'phoneNo', colWidth: '', title: '手机号', type: 'text', isCheck: true },
+		{ key: 'staffNo', colWidth: '', title: '工号', type: 'text', isCheck: true },
+		{ key: 'defaultTelNo', colWidth: '', title: '默认分机', type: 'text', isCheck: true },
+		{ key: 'creationTime', colWidth: '', title: '创建时间', type: 'text', isCheck: true },
+	]
+	table2excel(tabeHeader, state.multipleSelection, `用户信息 ${formatDate(new Date(),'YYYY-mm-dd HH-MM')}`);
+}
 onMounted(() => {
   getList();
 });
 
-// // 全部導出
-// const allExport = async (name:string, id:string) => {
-//   ElMessageBox.confirm('確定導出所有會計報表?', '導出提示', {
-//     confirmButtonText: '確定',
-//     cancelButtonText: '取消',
-//     type: 'warning'
-//   }).then(() => {
-//       var wb = XLSX.utils.table_to_book(document.querySelector('#' + id),{ raw: true })
-//       var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: true, type: 'array' })
-//       try {
-//         FileSaver.saveAs(new Blob([wbout], { type: 'application/octet-stream' }), name + '.xlsx')
-//       } catch (e) { if (typeof console !== 'undefined') console.log(e, wbout) }
-//       return wbout
-//     })
-//     .catch(() => {})
-// }
 </script>

+ 44 - 33
src/views/system/systemParameter/index.vue

@@ -1,11 +1,12 @@
 <template>
 	<div class="system-systemParameter-container layout-padding">
-		<div  class="layout-padding-auto layout-padding-view pd20">
-			<template v-if="list.length">
+		<div  class="layout-padding-auto layout-padding-view pd100">
+			<template v-if="data.length">
+				<el-divider content-position="center"><h4 class="table-title">系统参数设置</h4></el-divider>
 				<el-form :model="ruleForm" label-width="200px" ref="ruleFormRef" v-loading="loading" class="form">
 					<el-row :gutter="35">
-						<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-for="(items) in list" :key="items.id">
-							<el-divider content-position="left">{{ items.groupName }}</el-divider>
+						<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-for="(items) in data" :key="items.id">
+							<!-- <el-divider content-position="left">{{ items.groupName }}</el-divider> -->
 							<div v-for="(item) in items.systemSettings" :key="item.id">
 								<el-form-item :label="item.settingName">
 									<!-- 单选框 -->
@@ -17,7 +18,7 @@
 										</el-radio-group>
 									</template>
 									<!-- 多选框 -->
-									<template v-else-if="item.valueTeType === 1">
+									<template v-else-if="item.valueType === 1">
 										<el-checkbox-group v-model="item.settingValue"
 											@change="changeVal($event, item.id)">
 											<el-checkbox v-for="item1 in item.presetValue" :key="item1.value"
@@ -26,46 +27,48 @@
 									</template>
 									<!-- 文本 -->
 									<template v-else-if="item.valueType === 2">
-										<el-input v-model="item.settingValue" :placeholder="'请输入' + item.settingName"
+										<el-input v-model="item.settingValue" :placeholder="'请输入' + item.settingName" class="maxwidth400"
 											@input="changeVal($event, item.id)" />
 									</template>
 									<!-- 数字 -->
 									<template v-else-if="item.valueType === 3">
 										<el-input-number v-model="item.settingValue"
-											:placeholder="'请输入' + item.settingName" class="w100"
+											:placeholder="'请输入' + item.settingName"
 											@input="changeVal($event, item.id)" />
 									</template>
 									<!-- 下拉框 -->
 									<template v-else-if="item.valueType === 4">
 										<el-select v-model="item.settingValue" :placeholder="'请选择' + item.settingName"
-											@change="changeVal($event, item.id)" class="w100">
+											@change="changeVal($event, item.id)">
 											<el-option v-for="item1 in item.presetValue" :key="item1.value"
 												:label="item1.key" :value="item1.value" />
 										</el-select>
 									</template>
 									<!-- 开关 -->
 									<template v-else-if="item.valueType === 5">
-										<el-switch v-model="item.settingValue" class="w100"
+										<el-switch v-model="item.settingValue"
 											@change="changeVal($event, item.id)" />
 									</template>
 									<!-- 时间 -->
 									<template v-else-if="item.valueType === 6">
 										<el-time-select v-model="item.settingValue"
-											:placeholder="'请选择' + item.settingName" class="w100"
+											:placeholder="'请选择' + item.settingName"
 											@change="changeVal($event, item.id)" />
 									</template>
 									<!-- 日期 -->
 									<template v-else-if="item.valueType === 7">
 										<el-date-picker v-model="item.settingValue" value-format="YYYY-MM-DD"
-											type="date" :placeholder="'请选择' + item.settingName" class="w100"
+											type="date" :placeholder="'请选择' + item.settingName"
 											@change="changeVal($event, item.id)" />
 									</template>
 									<!-- 日期区间 -->
 									<template v-else-if="item.valueType === 8">
-										<el-date-picker v-model="item.settingValue" value-format="YYYY-MM-DD"
-											type="daterange" :placeholder="'请选择' + item.settingName" class="w100"
+										<div>
+											<el-date-picker v-model="item.settingValue" value-format="YYYY-MM-DD"
+											type="daterange" :placeholder="'请选择' + item.settingName"
 											@change="changeVal($event, item.id)" range-separator="至"
 											start-placeholder="开始日期" end-placeholder="结束日期" />
+										</div>
 									</template>
 									<!-- 时间区间 -->
 									<template v-else-if="item.valueType === 9">
@@ -73,7 +76,7 @@
 											v-model="item.settingValue"
 											type="daterange"
 											is-range
-											:placeholder="'请选择'+item.settingName" class="w100" @change="changeVal($event,item.id)"
+											:placeholder="'请选择'+item.settingName" @change="changeVal($event,item.id)"
 											range-separator="至"
 											start-placeholder="开始时间"
 											end-placeholder="结束时间"
@@ -90,8 +93,8 @@
 
 						</el-col>
 					</el-row>
-					<el-form-item>
-						<el-button type="primary" @click="submitForm" :loading="loading" v-auth="'100301'">保 存
+					<el-form-item v-auth="'100301'">
+						<el-button type="primary" @click="submitForm" :loading="loading">保 存
 						</el-button>
 						<el-button @click="resetForm" :loading="loading">重 置</el-button>
 					</el-form-item>
@@ -101,10 +104,6 @@
 				<Empty />
 			</template>
 		</div>
-		<!-- <div class="pd20 scorll">
-			
-		</div> -->
-
 	</div>
 </template>
 
@@ -112,23 +111,29 @@
 import { reactive, ref, toRefs, onMounted } from "vue";
 import { getsyssettings, modifysettings } from "/@/api/system/systemParameter";
 import { ElMessage } from 'element-plus';
-const list = ref<any>([]);
-const arr = ref<any>([]);
-const loading = ref(false);
-const state = reactive({
-	ruleForm: {
+interface systemState{
+	ruleForm:object;
+	data:Array<any>;
+	formateData:Array<any>;
+	loading:boolean;
 
-	}
+}
+const state = reactive<systemState>({
+	ruleForm: {},
+	data:[],
+	formateData:[],
+	loading:false
 })
+const { ruleForm,data,loading } = toRefs(state);
 let timeRageRef = ref<any>(['', '']);
 const getSetList = () => {//获取参数列表
 	loading.value = true;
-	arr.value = [];
+	state.formateData = [];
 	getsyssettings().then((res: any) => {
-		list.value = res?.result ?? [];
-		for (let j of list.value) {
+		state.data  = res?.result ?? [];
+		for (let j of state.data) {
 			for (let i of j.systemSettings) {
-				arr.value.push({
+				state.formateData.push({
 					id: i.id,
 					value: i.settingValue ? i.settingValue : i.valueType == 5 ? 'false' : null,
 					valueType: i.valueType
@@ -162,7 +167,7 @@ const getSetList = () => {//获取参数列表
 	})
 }
 const changeVal = (val: any, id: string | number, startOrEnd?: string) => { // 处理提交数据
-	arr.value.forEach((item: any) => {
+	state.formateData.forEach((item: any) => {
 		if (item.id == id) {
 			item.value = val;
 			if (item.valueType == 1 || item.valueType == 8) { // 多选 日期区间
@@ -184,7 +189,7 @@ const changeVal = (val: any, id: string | number, startOrEnd?: string) => { // 
 	})
 }
 const submitForm = () => {
-	modifysettings({ list: arr.value }).then(() => {
+	modifysettings({ list: state.formateData }).then(() => {
 		ElMessage.success("配置成功");
 		getSetList();
 	}).catch(() => {
@@ -194,7 +199,6 @@ const submitForm = () => {
 const resetForm = () => {
 	getSetList();
 }
-const { ruleForm } = toRefs(state);
 // 页面加载时
 onMounted(() => {
 	getSetList();
@@ -204,10 +208,17 @@ onMounted(() => {
 
 <style lang="scss" scoped>
 .system-systemParameter-container{
+	.pd100{
+		padding: 0 100px;
+	}
+	.maxwidth400{
+			max-width: 214px;
+		}
 	.form{
 		flex: 1;
 		overflow-y: auto;
 		overflow-x: hidden;
+		
 	}
 }
 </style>

+ 38 - 18
src/views/telslog/blacklist/index.vue

@@ -15,14 +15,18 @@
 		<el-card shadow="never">
 			<div class="flex-center-between mb20">
 				<p class="table-title">信息列表</p>
-                <div v-auth="'200201'">
-                    <el-button type="primary"  @click="onAddBacklist" v-waves>
+                <div>
+                    <el-button type="primary"  @click="onAddBacklist" v-waves v-auth="'200201'">
                         <SvgIcon name="ele-Plus" class="mr5"/>添加黑名单
+                    </el-button>
+					<el-button type="primary" v-waves @click="onImportTable">
+                        <SvgIcon name="iconfont icon-daochu" class="mr5"/>导出
                     </el-button>
                 </div>
 			</div>
 			<!-- 表格 -->
-			<el-table :data="list" v-loading="loading" row-key="id" ref="multipleTableRef" @selection-change="handleSelectionChange" id="table">
+			<el-table :data="tableData" v-loading="loading" row-key="id" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" :reserve-selection="true"/>
 				<el-table-column prop="phoneNo" label="电话号码" show-overflow-tooltip></el-table-column>
 				<el-table-column prop="duration" label="黑名单时长(秒)" show-overflow-tooltip></el-table-column>
 				<el-table-column label="创建时间" show-overflow-tooltip  width="170">
@@ -61,6 +65,7 @@ import type { FormInstance } from 'element-plus';
 import { formatDate } from '/@/utils/formatTime';
 import { blacklistPaged, balcklistDelete} from '/@/api/telslog/blacklist';
 import { throttle } from '/@/utils/tools';
+import table2excel from 'js-table2excel';
 
 const AddBlacklist = defineAsyncComponent(() => import('/@/views/telslog/blacklist/component/addBlacklist.vue'))
 interface queryState{
@@ -69,39 +74,46 @@ interface queryState{
 		pageSize:number,
 		PhoneNum?:string,
 	}
+	loading:boolean;
+	total:number;
+	tableData:Array<any>;
+	multipleSelection:Array<any>;
 }
-const data = reactive<queryState>({
+const state = reactive<queryState>({
 	queryParams: {
 		PageIndex: 1,
 		pageSize: 10,
 		PhoneNum: '',
 	},
+	loading:false,
+	total:0,
+	tableData:[],
+	multipleSelection:[]
 });
-const loading = ref(false);
-const total = ref(0);
-const list = ref<any>([]);
 const ruleFormRef = ref<FormInstance>();
+const addBlacklistRef = ref();
+const { queryParams,loading,total,tableData } = toRefs(state);
 /** 搜索按钮操作 节流操作 */
 const handleQuery = throttle(()=> {
-	queryParams.value.PageIndex = 1;
+	state.queryParams.PageIndex = 1;
 	getList();
 },1000);
 /** 获取用户列表 */
 const getList = () => {
-	loading.value = true;
-	blacklistPaged(queryParams.value).then((response: any) => {
-		list.value = response?.result.items;
-		total.value = response?.result.total;
+	state.loading = true;
+	blacklistPaged(state.queryParams).then((response: any) => {
+		state.tableData = response?.result.items;
+		state.total = response?.result.total;
 		setTimeout(() => {
-			loading.value = false;
+			state.loading = false;
 		}, 300);
 	}).catch(() => {
-		loading.value = false;
+		state.loading = false;
 	});
 };
-const multipleSelection = ref<any>([]) //多选
+
 const handleSelectionChange = (val: any) => {
-	multipleSelection.value = val;
+	state.multipleSelection = val;
 }
 /** 重置按钮操作 */
 const resetQuery = throttle((formEl: FormInstance | undefined) => {
@@ -109,11 +121,19 @@ const resetQuery = throttle((formEl: FormInstance | undefined) => {
 	formEl.resetFields();
 	handleQuery();
 },1000);
-const { queryParams } = toRefs(data);
-const addBlacklistRef = ref();
 const onAddBacklist = ()=>{// 新增黑名单
 	addBlacklistRef.value.openDialog();
 }
+// 导出表格
+const onImportTable = ()=>{
+	if (state.multipleSelection.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	const tabeHeader = [
+		{ key: 'phoneNo', colWidth: '', title: '电话号码', type: 'text', isCheck: true },
+		{ key: 'duration', colWidth: '', title: '黑名单时长(秒)', type: 'text', isCheck: true },
+		{ key: 'creationTime', colWidth: '', title: '创建时间', type: 'text', isCheck: true },
+	]
+	table2excel(tabeHeader, state.multipleSelection, `${formatDate(new Date(),'YYYY-mm-dd HH-MM')}`);
+}
 // 删除黑名单
 const onRowDel = (row: any) => {
 	ElMessageBox.confirm(`此操作将永久删除:“${row.phoneNo}”,是否继续?`, '提示', {

+ 48 - 21
src/views/telslog/callRecord/index.vue

@@ -41,14 +41,14 @@
 			<div class="flex-center-between mb20">
 				<p class="table-title">信息列表</p>
 				<div>
-                    <el-button type="primary" v-waves>
+                    <el-button type="primary" v-waves @click="onImportTable">
                         <SvgIcon name="iconfont icon-daochu" class="mr5"/>导出
                     </el-button>
                 </div>
 			</div>
 			<!-- 表格 -->
-			<el-table :data="list" v-loading="loading" row-key="id" ref="multipleTableRef" @selection-change="handleSelectionChange" id="table">
-				<!-- <el-table-column type="selection" width="55" :reserve-selection="true"/> -->
+			<el-table :data="tableList" v-loading="loading" row-key="id" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" :reserve-selection="true"/>
 				<el-table-column prop="userName" label="用户名称" show-overflow-tooltip></el-table-column>
 				<el-table-column prop="fromNo" label="主叫号码" show-overflow-tooltip></el-table-column>
 				<el-table-column prop="toNo" label="被叫号码" show-overflow-tooltip></el-table-column>
@@ -77,31 +77,42 @@
 <script lang="ts" setup name="callRecord">
 import { ref, reactive, toRefs, onMounted} from 'vue';
 import type { FormInstance } from 'element-plus';
+import { ElMessage } from 'element-plus';
 import { formatDate } from '/@/utils/formatTime';// 引入api
 import { callPaged } from '/@/api/telslog/callRecord';
+import table2excel from 'js-table2excel';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
 // 引入节流
 import { throttle } from '/@/utils/tools';
 
 interface queryState{
+	activeName:string;
 	queryParams:{
 		PageIndex:number,
 		pageSize:number,
 		PhoneNum?:string,
-	}
+	};
+	tableList:Array<any>;
+	loading:boolean;
+	total:number;
+	multipleSelection:Array<any>
 }
-const data = reactive<queryState>({
-	queryParams: {
+const state = reactive(<queryState>{
+	activeName:"first",
+	queryParams:{
 		PageIndex: 1,
 		pageSize: 10,
 		PhoneNum: '',
 	},
-});
-const state = reactive({
-	activeName:"first"
+	tableList:[],
+	loading:false,
+	total:0,
+	multipleSelection:[]
 })
-const loading = ref(false);
-const total = ref(0);
-const list = ref<any>([]);
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { queryParams,tableList,loading,total,activeName } = toRefs(state);
 const ruleFormRef = ref<FormInstance>();
 /** 搜索按钮操作 节流操作 */
 const handleQuery = throttle(()=> {
@@ -110,20 +121,19 @@ const handleQuery = throttle(()=> {
 },1000);
 /** 获取用户列表 */
 const getList = () => {
-	loading.value = true;
+	state.loading = true;
 	callPaged(queryParams.value).then((response: any) => {
-		list.value = response?.result.items ?? [];
-		total.value = response?.result.total;
+		state.tableList = response?.result.items ?? [];
+		state.total = response?.result.total;
 		setTimeout(() => {
-			loading.value = false;
+			state.loading = false;
 		}, 300);
 	}).catch(() => {
-		loading.value = false;
+		state.loading = false;
 	});
 };
-const multipleSelection = ref<any>([]) //多选
 const handleSelectionChange = (val: any) => {
-	multipleSelection.value = val;
+	state.multipleSelection = val;
 }
 /** 重置按钮操作 */
 const resetQuery = throttle((formEl: FormInstance | undefined) => {
@@ -134,8 +144,25 @@ const resetQuery = throttle((formEl: FormInstance | undefined) => {
 const handleClick = ()=>{// 切换tab
 
 }
-const { queryParams } = toRefs(data);
-const { activeName } = toRefs(state);
+// 导出表格
+const onImportTable = ()=>{
+	if (state.multipleSelection.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	const tabeHeader = [
+		{ key: 'userName', colWidth: '', title: '用户名称', type: 'text', isCheck: true },
+		{ key: 'fromNo', colWidth: '', title: '主叫号码', type: 'text', isCheck: true },
+		{ key: 'toNo', colWidth: '', title: '被叫号码', type: 'text', isCheck: true },
+		{ key: 'callTypeText', colWidth: '', title: '通话类型', type: 'text', isCheck: true },
+		{ key: 'callStatusText', colWidth: '', title: '通话状态', type: 'text', isCheck: true },
+		{ key: 'callDirectionText', colWidth: '', title: '呼叫方向', type: 'text', isCheck: true },
+		{ key: 'endByText', colWidth: '', title: '通话结束方', type: 'text', isCheck: true },
+		{ key: 'phoneIspText', colWidth: '', title: '运营商', type: 'text', isCheck: true },
+		{ key: 'ringOffTypeText', colWidth: '', title: '挂断类型', type: 'text', isCheck: true },
+		{ key: 'attribution', colWidth: '', title: '归属地', type: 'text', isCheck: true },
+		{ key: 'creationTime', colWidth: '', title: '创建时间', type: 'text', isCheck: true },
+		// { key: 'image', colWidth: '', width: '70', height: '40', title: '图片描述', type: 'image', isCheck: true },
+	]
+	table2excel(tabeHeader, state.multipleSelection, `${themeConfig.value.globalTitle} ${formatDate(new Date(),'YYYY-mm-dd HH-MM')}`);
+}
 onMounted(() => {
 	getList();
 });

+ 6 - 3
tsconfig.json

@@ -22,7 +22,7 @@
 		// "noEmit": true,                        /* Do not emit outputs. */
 		// "importHelpers": true /* Import emit helpers from 'tslib'. */,
 		// "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */,
-		// "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
+		"isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
 
 		/* Strict Type-Checking Options */
 		"strict": true /* Enable all strict type-checking options. */,
@@ -67,6 +67,9 @@
 
 		/* Advanced Options */
 		"skipLibCheck": true /* Skip type checking of declaration files. */,
-		"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
-	}
+		"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
+		"suppressImplicitAnyIndexErrors": true
+	},
+	"include": ["src/**/*.ts", "src/**/*.vue", "src/**/*.tsx", "src/**/*.d.ts","src/**/*.js"], // **Represents any directory, and * represents any file. Indicates that all files in the src directory will be compiled
+	"exclude": ["node_modules", "dist"] // 
 }

+ 1 - 8
vite.config.ts

@@ -1,11 +1,3 @@
-/*
- * @Author: zc
- * @Description: 
- * @version: 
- * @Date: 2022-10-21 09:01:23
- * @LastEditors: Please set LastEditors
- * @LastEditTime: 2022-11-23 13:46:56
- */
 import vue from '@vitejs/plugin-vue';
 import { resolve } from 'path';
 import { defineConfig, loadEnv, ConfigEnv } from 'vite';
@@ -27,6 +19,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
 		root: process.cwd(),
 		resolve: { alias },
 		base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
+		hmr: true,
 		server: {
 			host: '0.0.0.0',
 			port: env.VITE_PORT as unknown as number,

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio