This commit is contained in:
GYYM
2024-10-23 20:52:21 +08:00
35 changed files with 2262 additions and 414 deletions

View File

@@ -1,6 +1,8 @@
{
"cSpell.words": [
"daterange",
"errordata",
"logdata",
"resourcedata",
"resourcename",
"rmark"

View File

@@ -0,0 +1,36 @@
import type {ErrorSystem} from "./interface"
const errordata = ref<ErrorSystem.ErrorSystemList[]>([
{
'id': '1',
'name': 'Q/GDW 1650.2- 2016',
'year':'2016',
'level':'A级',
'details': [
{ measured: '详细1-1', deviceLevel: '详细1-2', condition: '详细1-3', maxErrorValue: '详细1-4' },
{ measured: '详细1-5', deviceLevel: '详细1-6', condition: '详细1-7', maxErrorValue: '详细1-8' },
],
},
{
'id': '2',
'name': 'Q/GDW 10650.2 - 2021',
'year':'2021',
'level':'A级',
'details': [
{ measured: '详细1-1', deviceLevel: '详细1-2', condition: '详细1-3', maxErrorValue: '详细1-4' },
{ measured: '详细1-5', deviceLevel: '详细1-6', condition: '详细1-7', maxErrorValue: '详细1-8' },
],
},
{
'id': '3',
'name': 'GBT 19862 - 2016',
'year':'2016',
'level':'A级',
'details': [
{ measured: '详细1-1', deviceLevel: '详细1-2', condition: '详细1-3', maxErrorValue: '详细1-4' },
{ measured: '详细1-5', deviceLevel: '详细1-6', condition: '详细1-7', maxErrorValue: '详细1-8' },
],
},
])
export default errordata

View File

@@ -0,0 +1,17 @@
// 误差体系模块
export namespace ErrorSystem {
// 误差体系列表
export interface ErrorSystemList {
id: string;//误差体系表Id
name: string;//误差体系名称
year:string;//标准实施年份
level:string;//使用设备等级
details?: Array<{
measured: string;//被测量
deviceLevel: string;//检测装置级别
condition: string;//测量条件
maxErrorValue: string;//最大误差
}>; // 详细信息
}
}

View File

@@ -0,0 +1,13 @@
// 日志管理模块
export namespace Log {
// 日志列表
export interface LogList {
id: string;//日志表Id
content: string;//日志内容
user:string;//操作用户
record_Time:string;//记录时间
type:string;//日志类型
level:string;//日志等级
}
}

View File

@@ -0,0 +1,62 @@
import type {Log} from "./interface"
const logdata = ref<Log.LogList[]>([
{
'id': '1',
'content': 'Admin用户09:35:47,790 登录系统',
'user':'Admin',
'record_Time':'2024-10-16 12:13:14',
'type':'操作日志',
'level':'/',
},
{
'id': '2',
'content': 'Admin用户09:35:47,891 新建设备 “模拟式装置1”',
'user':'Admin',
'record_Time':'2024-10-16 12:13:14',
'type':'操作日志',
'level':'/',
},
{
'id': '3',
'content': 'User用户 09:35:56,391 新建设备 “模拟式装置2”',
'user':'User',
'record_Time':'2024-10-16 12:13:14',
'type':'操作日志',
'level':'/',
},
{
'id': '4',
'content': 'Admin用户09:35:59,977 对 “模拟式装置1”开始进行自动检测',
'user':'Admin',
'record_Time':'2024-10-16 12:13:14',
'type':'操作日志',
'level':'/',
},
{
'id': '5',
'content': 'User用户09:36:02,229 对 “模拟式装置2”开始进行自动检测',
'user':'User',
'record_Time':'2024-10-16 12:13:14',
'type':'操作日志',
'level':'/',
},
{
'id': '6',
'content': 'DKLN源回复报文异常已丢弃。报文长度为XX内容为“XXXXXXX”',
'user':'Admin',
'record_Time':'2024-10-16 12:13:14',
'type':'告警日志',
'level':'WARN警告',
},
{
'id': '7',
'content': 'IP192.168.1.205 的装置第1通道触发QVVR01报告失败',
'user':'AA',
'record_Time':'2024-10-16 12:13:14',
'type':'告警日志',
'level':'ERROR一般错误',
},
])
export default logdata

View File

@@ -244,6 +244,7 @@ const resourcedata = ref<Resource.ResResourceList[]>([
}
]
},
])
export default resourcedata

View File

@@ -0,0 +1,139 @@
import { type Dict } from "@/api/system/dict/interface";
export const dictTypeList: Dict.ResDictType[] = [
{
id: "1",
name: "用户性别",
code: "EXAMPLE_CODE1",
sort: 1,
openLevel: 1,
openDescribe: 1,
remark: "示例描述1221",
state: 1,
createBy: "admin",
createTime: "2023-10-01 12:00:00",
updateBy: "admin",
updateTime: "2023-10-12 14:40:00",
},
{
id: "231",
name: "菜单状态",
code: "EXAMPLE_CODE2",
sort: 1,
openLevel: 1,
openDescribe: 1,
remark: "示例描述11133",
state: 0,
createBy: "admin",
createTime: "2023-10-01 12:00:00",
updateBy: "admin",
updateTime: "2023-10-10 12:00:00",
},
{
id: "33",
name: "系统开关",
code: "EXAMPLE_CODE3",
sort: 1,
openLevel: 1,
openDescribe: 1,
remark: "示例描述111344",
state: 1,
createBy: "admin",
createTime: "2023-10-01 12:00:00",
updateBy: "admin",
updateTime: "2023-10-10 12:00:00",
},
];
export const dictDataList: Dict.ResDictData[] = [
{
id: "210",
typeId: "101",
name: "字典项1",
code: "EXAMPLE_00111",
sort: 1,
level: 1,
algoDescribe: null,
value: "字典值1",
state: 1,
createBy: "admin",
createTime: "2023-10-01 12:00:00",
updateBy: "admin",
updateTime: "2023-10-01 14:43:03",
},
{
id: "2210",
typeId: "101",
name: "字典项2",
code: "EXAMPLE_00121",
sort: 1,
level: 2,
algoDescribe: null,
value: "字典值2",
state: 1,
createBy: "admin",
createTime: "2023-10-03 12:00:00",
updateBy: "admin",
updateTime: "2023-10-05 14:43:03",
},
{
id: "212",
typeId: "101",
name: "字典项3",
code: "EXAMPLE_00141",
sort: 1,
level: 3,
algoDescribe: null,
value: "字典值3",
state: 0,
createBy: "admin",
createTime: "2023-10-01 11:00:00",
updateBy: "admin",
updateTime: "2023-10-05 16:43:03",
},
{
id: "210",
typeId: "101",
name: "字典项4",
code: "EXAMPLE_00311",
sort: 1,
level: null,
algoDescribe: null,
value: "字典值4",
state: 0,
createBy: "admin",
createTime: "2023-10-03 12:40:00",
updateBy: "admin",
updateTime: "2023-10-04 14:43:03",
},
{
id: "217",
typeId: "101",
name: "字典项5",
code: "EXAMPLE_00341",
sort: 1,
level: undefined,
algoDescribe: null,
value: "字典值5",
state: 1,
createBy: "admin",
createTime: "2023-10-06 12:45:03",
updateBy: "admin",
updateTime: "2023-10-07 19:37:03",
},
{
id: "237",
typeId: "101",
name: "字典项6",
code: "EXAMPLE_00318",
sort: 1,
level: 0,
algoDescribe: null,
value: "字典值6",
state: 1,
createBy: "admin",
createTime: "2023-10-16 10:45:03",
updateBy: "admin",
updateTime: "2023-10-17 19:37:03",
},
];

View File

@@ -0,0 +1,70 @@
import http from "@/api";
import { ADMIN as rePrefix } from "@/api/config/serviceName";
import { type Dict } from "@/api/system/dict/interface";
//获取字典类型
export const getDictTypeList = (params: Dict.ReqDictTypeParams) => {
return http.post(`${rePrefix}/dict/list`, params);
};
//添加字典类型
export const addDictType = (params: Dict.ReqDictTypeParams) => {
return http.post(`${rePrefix}/dict/add`,params);
};
//删除字典类型
export const deleteDictType = (params: { id: string[] }) => {
return http.post(`${rePrefix}/dict/delete`, { data: params });
};
//编辑字典类型
export const updateDictType = (params: Dict.ReqDictTypeParams) => {
return http.post(`${rePrefix}/dict/update`, params);
};
//导出字典类型
export const exportDictType = (params: Dict.ResDictType) => {
return http.download(`${rePrefix}/dict/export`, params);
};
//批量添加字典类型
export const batchAddDictType = (params: FormData) => {
return http.post(`${rePrefix}/dict/import`, params);
};
//获取字典数据
export const getDictDataList = (params: Dict.ReqDictDataParams) => {
return http.post(`${rePrefix}/dict-data/list`, params);
};
//添加字典数据
export const addDictData = (params: Dict.ReqDictDataParams) => {
return http.post(`${rePrefix}/dict-data/add`, params);
};
//删除字典数据
export const deleteDictData = (params: { id: string[] }) => {
return http.post(`${rePrefix}/dict-data/delete`, { data: params });
};
//编辑字典数据
export const updateDictData = (params: Dict.ReqDictDataParams) => {
return http.post(`${rePrefix}/dict-data/update`, params);
};
//导出字典数据
export const exportDictData = (params: Dict.ResDictData) => {
return http.download(`${rePrefix}/dict-data/export`, params);
};
//批量添加字典数据
export const batchAddDictData = (params: FormData) => {
return http.post(`${rePrefix}/dict-data/import`, params);
};

View File

@@ -0,0 +1,43 @@
import type { ReqPage } from "@/api/interface";
export namespace Dict {
export interface ResDictType {
id: string; // 字典类型表Id
name: string; // 名称
code: string; // 编码
sort: number; // 排序
openLevel: number; // 开启等级0-不开启1-开启,默认不开启
openDescribe: number; // 开启描述0-不开启1-开启,默认不开启
remark?: string | null; // 描述
state: number; // 状态0-删除 1-正常
createBy?: string | null; // 创建用户
createTime?: string | null; // 创建时间
updateBy?: string | null; // 更新用户
updateTime?: string | null; // 更新时间
}
export interface ReqDictTypeParams extends ReqPage{
}
export interface ResDictData {
id: string; // 字典数据表Id
typeId: string; // 字典类型表Id
name: string; // 名称
code: string; // 编码
sort: number; // 排序
level?: number | null; // 事件等级0-普通1-中等2-严重 (默认为0)
algoDescribe?: number | null; // 与高级算法内部Id描述对应
value?: string | null; // 字典针对电压等级
state: number; // 状态0-删除 1-正常
createBy?: string | null; // 创建用户
createTime?: string | null; // 创建时间
updateBy?: string | null; // 更新用户
updateTime?: string | null; // 更新时间
}
export interface ReqDictDataParams extends ReqPage{
}
}

View File

@@ -1,11 +1,10 @@
<template>
<div v-show="isShow" :style="style">
<div v-show='isShow' :style='style'>
<slot></slot>
</div>
</template>
<script setup lang="ts" name="GridItem">
import { computed, inject, Ref, ref, useAttrs, watch } from "vue";
import { BreakPoint, Responsive } from "../interface/index";
<script setup lang='ts' name='GridItem'>
import { BreakPoint, Responsive } from '../interface/index'
type Props = {
offset?: number;
@@ -26,43 +25,43 @@ const props = withDefaults(defineProps<Props>(), {
sm: undefined,
md: undefined,
lg: undefined,
xl: undefined
});
xl: undefined,
})
const attrs = useAttrs() as { index: string };
const isShow = ref(true);
const attrs = useAttrs() as { index: string }
const isShow = ref(true)
// 注入断点
const breakPoint = inject<Ref<BreakPoint>>("breakPoint", ref("xl"));
const shouldHiddenIndex = inject<Ref<number>>("shouldHiddenIndex", ref(-1));
const breakPoint = inject<Ref<BreakPoint>>('breakPoint', ref('xl'))
const shouldHiddenIndex = inject<Ref<number>>('shouldHiddenIndex', ref(-1))
watch(
() => [shouldHiddenIndex.value, breakPoint.value],
n => {
if (!!attrs.index) {
isShow.value = !(n[0] !== -1 && parseInt(attrs.index) >= Number(n[0]));
}
},
{ immediate: true }
);
() => [shouldHiddenIndex.value, breakPoint.value],
n => {
if (!!attrs.index) {
isShow.value = !(n[0] !== -1 && parseInt(attrs.index) >= Number(n[0]))
}
},
{ immediate: true },
)
const gap = inject("gap", 0);
const cols = inject("cols", ref(4));
const gap = inject('gap', 0)
const cols = inject('cols', ref(4))
const style = computed(() => {
let span = props[breakPoint.value]?.span ?? props.span;
let offset = props[breakPoint.value]?.offset ?? props.offset;
let span = props[breakPoint.value]?.span ?? props.span
let offset = props[breakPoint.value]?.offset ?? props.offset
if (props.suffix) {
return {
gridColumnStart: cols.value - span - offset + 2,
gridColumnStart: cols.value - span - offset + 1,
gridColumnEnd: `span ${span + offset}`,
marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : "unset"
};
marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : 'unset',
}
} else {
return {
gridColumn: `span ${span + offset > cols.value ? cols.value : span + offset }/span ${
span + offset > cols.value ? cols.value : span + offset
gridColumn: `span ${span + offset > cols.value ? cols.value : span + offset}/span ${
span + offset > cols.value ? cols.value : span + offset
}`,
marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : "unset"
};
marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : 'unset',
}
}
});
})
</script>

View File

@@ -1,25 +1,11 @@
<template>
<div :style="style">
<div :style='style'>
<slot></slot>
</div>
</template>
<script setup lang="ts" name="Grid">
import {
ref,
watch,
useSlots,
computed,
provide,
onBeforeMount,
onMounted,
onUnmounted,
onDeactivated,
onActivated,
VNodeArrayChildren,
VNode
} from "vue";
import type { BreakPoint } from "./interface/index";
<script setup lang='ts' name='Grid'>
import type { BreakPoint } from './interface/index'
type Props = {
cols?: number | Record<BreakPoint, number>;
@@ -32,136 +18,136 @@ const props = withDefaults(defineProps<Props>(), {
cols: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
collapsed: false,
collapsedRows: 1,
gap: 0
});
gap: 0,
})
onBeforeMount(() => props.collapsed && findIndex());
onBeforeMount(() => props.collapsed && findIndex())
onMounted(() => {
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
window.addEventListener("resize", resize);
});
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent)
window.addEventListener('resize', resize)
})
onActivated(() => {
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
window.addEventListener("resize", resize);
});
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent)
window.addEventListener('resize', resize)
})
onUnmounted(() => {
window.removeEventListener("resize", resize);
});
window.removeEventListener('resize', resize)
})
onDeactivated(() => {
window.removeEventListener("resize", resize);
});
window.removeEventListener('resize', resize)
})
// 监听屏幕变化
const resize = (e: UIEvent) => {
let width = (e.target as Window).innerWidth;
let width = (e.target as Window).innerWidth
switch (!!width) {
case width < 768:
breakPoint.value = "xs";
break;
breakPoint.value = 'xs'
break
case width >= 768 && width < 992:
breakPoint.value = "sm";
break;
breakPoint.value = 'sm'
break
case width >= 992 && width < 1200:
breakPoint.value = "md";
break;
breakPoint.value = 'md'
break
case width >= 1200 && width < 1920:
breakPoint.value = "lg";
break;
breakPoint.value = 'lg'
break
case width >= 1920:
breakPoint.value = "xl";
break;
breakPoint.value = 'xl'
break
}
};
}
// 注入 gap 间距
provide("gap", Array.isArray(props.gap) ? props.gap[0] : props.gap);
provide('gap', Array.isArray(props.gap) ? props.gap[0] : props.gap)
// 注入响应式断点
let breakPoint = ref<BreakPoint>("xl");
provide("breakPoint", breakPoint);
let breakPoint = ref<BreakPoint>('xl')
provide('breakPoint', breakPoint)
// 注入要开始折叠的 index
const hiddenIndex = ref(-1);
provide("shouldHiddenIndex", hiddenIndex);
const hiddenIndex = ref(-1)
provide('shouldHiddenIndex', hiddenIndex)
// 注入 cols
const gridCols = computed(() => {
if (typeof props.cols === "object") return props.cols[breakPoint.value] ?? props.cols;
return props.cols;
});
provide("cols", gridCols);
if (typeof props.cols === 'object') return props.cols[breakPoint.value] ?? props.cols
return props.cols
})
provide('cols', gridCols)
// 寻找需要开始折叠的字段 index
const slots = useSlots().default!();
const slots = useSlots().default!()
const findIndex = () => {
let fields: VNodeArrayChildren = [];
let suffix: VNode | null = null;
let fields: VNodeArrayChildren = []
let suffix: VNode | null = null
slots.forEach((slot: any) => {
// suffix
if (typeof slot.type === "object" && slot.type.name === "GridItem" && slot.props?.suffix !== undefined) suffix = slot;
if (typeof slot.type === 'object' && slot.type.name === 'GridItem' && slot.props?.suffix !== undefined) suffix = slot
// slot children
if (typeof slot.type === "symbol" && Array.isArray(slot.children)) fields.push(...slot.children);
});
if (typeof slot.type === 'symbol' && Array.isArray(slot.children)) fields.push(...slot.children)
})
// 计算 suffix 所占用的列
let suffixCols = 0;
let suffixCols = 0
if (suffix) {
suffixCols =
((suffix as VNode).props![breakPoint.value]?.span ?? (suffix as VNode).props?.span ?? 1) +
((suffix as VNode).props![breakPoint.value]?.offset ?? (suffix as VNode).props?.offset ?? 0);
((suffix as VNode).props![breakPoint.value]?.span ?? (suffix as VNode).props?.span ?? 1) +
((suffix as VNode).props![breakPoint.value]?.offset ?? (suffix as VNode).props?.offset ?? 0)
}
try {
let find = false;
let find = false
fields.reduce((prev = 0, current, index) => {
prev +=
((current as VNode)!.props![breakPoint.value]?.span ?? (current as VNode)!.props?.span ?? 1) +
((current as VNode)!.props![breakPoint.value]?.offset ?? (current as VNode)!.props?.offset ?? 0);
((current as VNode)!.props![breakPoint.value]?.span ?? (current as VNode)!.props?.span ?? 1) +
((current as VNode)!.props![breakPoint.value]?.offset ?? (current as VNode)!.props?.offset ?? 0)
if (Number(prev) > props.collapsedRows * gridCols.value - suffixCols) {
hiddenIndex.value = index;
find = true;
throw "find it";
hiddenIndex.value = index
find = true
throw 'find it'
}
return prev;
}, 0);
if (!find) hiddenIndex.value = -1;
return prev
}, 0)
if (!find) hiddenIndex.value = -1
} catch (e) {
// console.warn(e);
}
};
}
// 断点变化时执行 findIndex
watch(
() => breakPoint.value,
() => {
if (props.collapsed) findIndex();
}
);
() => breakPoint.value,
() => {
if (props.collapsed) findIndex()
},
)
// 监听 collapsed
watch(
() => props.collapsed,
value => {
if (value) return findIndex();
hiddenIndex.value = -1;
}
);
() => props.collapsed,
value => {
if (value) return findIndex()
hiddenIndex.value = -1
},
)
// 设置间距
const gridGap = computed(() => {
if (typeof props.gap === "number") return `${props.gap}px`;
if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`;
return "unset";
});
if (typeof props.gap === 'number') return `${props.gap}px`
if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`
return 'unset'
})
// 设置 style
const style = computed(() => {
return {
display: "grid",
display: 'grid',
gridGap: gridGap.value,
gridTemplateColumns: `repeat(${gridCols.value}, minmax(0, 1fr))`
};
});
gridTemplateColumns: `repeat(${gridCols.value}, minmax(0, 1fr))`,
}
})
defineExpose({ breakPoint });
defineExpose({ breakPoint })
</script>

View File

@@ -1,119 +1,122 @@
<template>
<!-- 查询表单 -->
<SearchForm
v-show="isShowSearch"
:search="_search"
:reset="_reset"
:columns="searchColumns"
:search-param="searchParam"
:search-col="searchCol"
v-show='isShowSearch'
:search='_search'
:reset='_reset'
:columns='searchColumns'
:search-param='searchParam'
:search-col='searchCol'
/>
<!-- 表格主体 -->
<div class="card table-main">
<div class='card table-main'>
<!-- 表格头部 操作按钮 -->
<div class="table-header">
<div class="header-button-lf">
<slot name="tableHeader" :selected-list="selectedList" :selected-list-ids="selectedListIds" :is-selected="isSelected" />
<div class='table-header'>
<div class='header-button-lf'>
<slot name='tableHeader' :selected-list='selectedList' :selected-list-ids='selectedListIds'
:is-selected='isSelected' />
</div>
<div v-if="toolButton" class="header-button-ri">
<slot name="toolButton">
<el-button v-if="showToolButton('refresh')" :icon="Refresh" circle @click="getTableList" />
<el-button v-if="showToolButton('setting') && columns.length" :icon="Operation" circle @click="openColSetting" />
<div v-if='toolButton' class='header-button-ri'>
<slot name='toolButton'>
<el-button v-if="showToolButton('refresh')" :icon='Refresh' circle @click='getTableList' />
<el-button v-if="showToolButton('setting') && columns.length" :icon='Operation' circle
@click='openColSetting' />
<el-button
v-if="showToolButton('search') && searchColumns?.length"
:icon="Search"
:icon='Search'
circle
@click="isShowSearch = !isShowSearch"
@click='isShowSearch = !isShowSearch'
/>
</slot>
</div>
</div>
<!-- 表格主体 -->
<el-table
ref="tableRef"
v-bind="$attrs"
:id="uuid"
:data="processTableData"
:border="border"
:row-key="rowKey"
@selection-change="selectionChange"
ref='tableRef'
v-bind='$attrs'
:id='uuid'
:data='processTableData'
:border='border'
:row-key='rowKey'
@selection-change='selectionChange'
>
<!-- 默认插槽 -->
<slot />
<template v-for="item in tableColumns" :key="item">
<template v-for='item in tableColumns' :key='item'>
<!-- selection || radio || index || expand || sort -->
<el-table-column
v-if="item.type && columnTypes.includes(item.type)"
v-bind="item"
v-if='item.type && columnTypes.includes(item.type)'
v-bind='item'
:align="item.align ?? 'center'"
:reserve-selection="item.type == 'selection'"
>
<template #default="scope">
<template #default='scope'>
<!-- expand -->
<template v-if="item.type == 'expand'">
<component :is="item.render" v-bind="scope" v-if="item.render" />
<slot v-else :name="item.type" v-bind="scope" />
<component :is='item.render' v-bind='scope' v-if='item.render' />
<slot v-else :name='item.type' v-bind='scope' />
</template>
<!-- radio -->
<el-radio v-if="item.type == 'radio'" v-model="radio" :label="scope.row[rowKey]">
<el-radio v-if="item.type == 'radio'" v-model='radio' :label='scope.row[rowKey]'>
<i></i>
</el-radio>
<!-- sort -->
<el-tag v-if="item.type == 'sort'" class="move">
<el-icon> <DCaret /></el-icon>
<el-tag v-if="item.type == 'sort'" class='move'>
<el-icon>
<DCaret />
</el-icon>
</el-tag>
</template>
</el-table-column>
<!-- other -->
<TableColumn v-else :column="item">
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
<slot :name="slot" v-bind="scope" />
<TableColumn v-else :column='item'>
<template v-for='slot in Object.keys($slots)' #[slot]='scope'>
<slot :name='slot' v-bind='scope' />
</template>
</TableColumn>
</template>
<!-- 插入表格最后一行之后的插槽 -->
<template #append>
<slot name="append" />
<slot name='append' />
</template>
<!-- 无数据 -->
<template #empty>
<div class="table-empty">
<slot name="empty">
<img src="@/assets/images/notData.png" alt="notData" />
<div class='table-empty'>
<slot name='empty'>
<img src='@/assets/images/notData.png' alt='notData' />
<div>暂无数据</div>
</slot>
</div>
</template>
</el-table>
<!-- 分页组件 -->
<slot name="pagination">
<slot name='pagination'>
<Pagination
v-if="pagination"
:pageable="pageable"
:handle-size-change="handleSizeChange"
:handle-current-change="handleCurrentChange"
v-if='pagination'
:pageable='pageable'
:handle-size-change='handleSizeChange'
:handle-current-change='handleCurrentChange'
/>
</slot>
</div>
<!-- 列设置 -->
<ColSetting v-if="toolButton" ref="colRef" v-model:col-setting="colSetting" />
<ColSetting v-if='toolButton' ref='colRef' v-model:col-setting='colSetting' />
</template>
<script setup lang="ts" name="ProTable">
import { ref, watch, provide, onMounted, unref, computed, reactive } from "vue";
import { ElTable } from "element-plus";
import { useTable } from "@/hooks/useTable";
import { useSelection } from "@/hooks/useSelection";
import { BreakPoint } from "@/components/Grid/interface";
import { ColumnProps, TypeProps } from "@/components/ProTable/interface";
import { Refresh, Operation, Search } from "@element-plus/icons-vue";
import { generateUUID, handleProp } from "@/utils";
import SearchForm from "@/components/SearchForm/index.vue";
import Pagination from "./components/Pagination.vue";
import ColSetting from "./components/ColSetting.vue";
import TableColumn from "./components/TableColumn.vue";
import Sortable from "sortablejs";
<script setup lang='ts' name='ProTable'>
import { ElTable } from 'element-plus'
import { useTable } from '@/hooks/useTable'
import { useSelection } from '@/hooks/useSelection'
import { BreakPoint } from '@/components/Grid/interface'
import { ColumnProps, TypeProps } from '@/components/ProTable/interface'
import { Refresh, Operation, Search } from '@element-plus/icons-vue'
import { generateUUID, handleProp } from '@/utils'
import SearchForm from '@/components/SearchForm/index.vue'
import Pagination from './components/Pagination.vue'
import ColSetting from './components/ColSetting.vue'
import TableColumn from './components/TableColumn.vue'
import Sortable from 'sortablejs'
export interface ProTableProps {
columns: ColumnProps[]; // 列配置项 ==> 必传
@@ -126,7 +129,7 @@ export interface ProTableProps {
pagination?: boolean; // 是否需要分页组件 ==> 非必传默认为true
initParam?: any; // 初始化请求参数 ==> 非必传(默认为{}
border?: boolean; // 是否带有纵向边框 ==> 非必传默认为true
toolButton?: ("refresh" | "setting" | "search")[] | boolean; // 是否显示表格功能按钮 ==> 非必传默认为true
toolButton?: ('refresh' | 'setting' | 'search')[] | boolean; // 是否显示表格功能按钮 ==> 非必传默认为true
rowKey?: string; // 行数据的 Key用来优化 Table 的渲染,当表格数据多选时,所指定的 id ==> 非必传(默认为 id
searchCol?: number | Record<BreakPoint, number>; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
}
@@ -139,161 +142,171 @@ const props = withDefaults(defineProps<ProTableProps>(), {
initParam: {},
border: true,
toolButton: true,
rowKey: "id",
searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 })
});
rowKey: 'id',
searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
})
// table 实例
const tableRef = ref<InstanceType<typeof ElTable>>();
const tableRef = ref<InstanceType<typeof ElTable>>()
// 生成组件唯一id
const uuid = ref("id-" + generateUUID());
const uuid = ref('id-' + generateUUID())
// column 列类型
const columnTypes: TypeProps[] = ["selection", "radio", "index", "expand", "sort"];
const columnTypes: TypeProps[] = ['selection', 'radio', 'index', 'expand', 'sort']
// 是否显示搜索模块
const isShowSearch = ref(true);
const isShowSearch = ref(true)
// 控制 ToolButton 显示
const showToolButton = (key: "refresh" | "setting" | "search") => {
return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton;
};
const showToolButton = (key: 'refresh' | 'setting' | 'search') => {
return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton
}
// 单选值
const radio = ref("");
const radio = ref('')
// 表格多选 Hooks
const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey);
const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey)
// 表格操作 Hooks
const { tableData, pageable, searchParam, searchInitParam, getTableList, search, reset, handleSizeChange, handleCurrentChange } =
useTable(props.requestApi, props.initParam, props.pagination, props.dataCallback, props.requestError);
const {
tableData,
pageable,
searchParam,
searchInitParam,
getTableList,
search,
reset,
handleSizeChange,
handleCurrentChange,
} =
useTable(props.requestApi, props.initParam, props.pagination, props.dataCallback, props.requestError)
// 清空选中数据列表
const clearSelection = () => tableRef.value!.clearSelection();
const clearSelection = () => tableRef.value!.clearSelection()
// 初始化表格数据 && 拖拽排序
onMounted(() => {
dragSort();
props.requestAuto && getTableList();
props.data && (pageable.value.total = props.data.length);
});
dragSort()
props.requestAuto && getTableList()
props.data && (pageable.value.total = props.data.length)
})
// 处理表格数据
const processTableData = computed(() => {
if (!props.data) return tableData.value;
if (!props.pagination) return props.data;
if (!props.data) return tableData.value
if (!props.pagination) return props.data
return props.data.slice(
(pageable.value.pageNum - 1) * pageable.value.pageSize,
pageable.value.pageSize * pageable.value.pageNum
);
});
pageable.value.pageSize * pageable.value.pageNum,
)
})
// 监听页面 initParam 改化,重新获取表格数据
watch(() => props.initParam, getTableList, { deep: true });
watch(() => props.initParam, getTableList, { deep: true })
// 接收 columns 并设置为响应式
const tableColumns = reactive<ColumnProps[]>(props.columns);
const tableColumns = reactive<ColumnProps[]>(props.columns)
// 扁平化 columns
const flatColumns = computed(() => flatColumnsFunc(tableColumns));
const flatColumns = computed(() => flatColumnsFunc(tableColumns))
// 定义 enumMap 存储 enum 值(避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择)
const enumMap = ref(new Map<string, { [key: string]: any }[]>());
const enumMap = ref(new Map<string, { [key: string]: any }[]>())
const setEnumMap = async ({ prop, enum: enumValue }: ColumnProps) => {
if (!enumValue) return;
if (!enumValue) return
// 如果当前 enumMap 存在相同的值 return
if (enumMap.value.has(prop!) && (typeof enumValue === "function" || enumMap.value.get(prop!) === enumValue)) return;
if (enumMap.value.has(prop!) && (typeof enumValue === 'function' || enumMap.value.get(prop!) === enumValue)) return
// 当前 enum 为静态数据,则直接存储到 enumMap
if (typeof enumValue !== "function") return enumMap.value.set(prop!, unref(enumValue!));
if (typeof enumValue !== 'function') return enumMap.value.set(prop!, unref(enumValue!))
// 为了防止接口执行慢,而存储慢,导致重复请求,所以预先存储为[],接口返回后再二次存储
enumMap.value.set(prop!, []);
enumMap.value.set(prop!, [])
// 当前 enum 为后台数据需要请求数据,则调用该请求接口,并存储到 enumMap
const { data } = await enumValue();
enumMap.value.set(prop!, data);
};
const { data } = await enumValue()
enumMap.value.set(prop!, data)
}
// 注入 enumMap
provide("enumMap", enumMap);
provide('enumMap', enumMap)
// 扁平化 columns 的方法
const flatColumnsFunc = (columns: ColumnProps[], flatArr: ColumnProps[] = []) => {
columns.forEach(async col => {
if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
flatArr.push(col);
if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children))
flatArr.push(col)
// column 添加默认 isShow && isSetting && isFilterEnum 属性值
col.isShow = col.isShow ?? true;
col.isSetting = col.isSetting ?? true;
col.isFilterEnum = col.isFilterEnum ?? true;
col.isShow = col.isShow ?? true
col.isSetting = col.isSetting ?? true
col.isFilterEnum = col.isFilterEnum ?? true
// 设置 enumMap
await setEnumMap(col);
});
return flatArr.filter(item => !item._children?.length);
};
await setEnumMap(col)
})
return flatArr.filter(item => !item._children?.length)
}
// 过滤需要搜索的配置项 && 排序
const searchColumns = computed(() => {
return flatColumns.value
?.filter(item => item.search?.el || item.search?.render)
.sort((a, b) => a.search!.order! - b.search!.order!);
});
.sort((a, b) => a.search!.order! - b.search!.order!)
})
// 设置 搜索表单默认排序 && 搜索表单项的默认值
searchColumns.value?.forEach((column, index) => {
column.search!.order = column.search?.order ?? index + 2;
const key = column.search?.key ?? handleProp(column.prop!);
const defaultValue = column.search?.defaultValue;
column.search!.order = column.search?.order ?? index + 2
const key = column.search?.key ?? handleProp(column.prop!)
const defaultValue = column.search?.defaultValue
if (defaultValue !== undefined && defaultValue !== null) {
searchParam.value[key] = defaultValue;
searchInitParam.value[key] = defaultValue;
searchParam.value[key] = defaultValue
searchInitParam.value[key] = defaultValue
}
});
})
// 列设置 ==> 需要过滤掉不需要设置的列
const colRef = ref();
const colRef = ref()
const colSetting = tableColumns!.filter(item => {
const { type, prop, isSetting } = item;
return !columnTypes.includes(type!) && prop !== "operation" && isSetting;
});
const openColSetting = () => colRef.value.openColSetting();
const { type, prop, isSetting } = item
return !columnTypes.includes(type!) && prop !== 'operation' && isSetting
})
const openColSetting = () => colRef.value.openColSetting()
// 定义 emit 事件
const emit = defineEmits<{
search: [];
reset: [];
dragSort: [{ newIndex?: number; oldIndex?: number }];
}>();
}>()
const _search = () => {
search();
emit("search");
};
search()
emit('search')
}
const _reset = () => {
reset();
emit("reset");
};
reset()
emit('reset')
}
// 表格拖拽排序
const dragSort = () => {
const tbody = document.querySelector(`#${uuid.value} tbody`) as HTMLElement;
const tbody = document.querySelector(`#${uuid.value} tbody`) as HTMLElement
Sortable.create(tbody, {
handle: ".move",
handle: '.move',
animation: 300,
onEnd({ newIndex, oldIndex }) {
const [removedItem] = processTableData.value.splice(oldIndex!, 1);
processTableData.value.splice(newIndex!, 0, removedItem);
emit("dragSort", { newIndex, oldIndex });
}
});
};
const [removedItem] = processTableData.value.splice(oldIndex!, 1)
processTableData.value.splice(newIndex!, 0, removedItem)
emit('dragSort', { newIndex, oldIndex })
},
})
}
// 暴露给父组件的参数和方法 (外部需要什么,都可以从这里暴露出去)
defineExpose({
@@ -314,6 +327,6 @@ defineExpose({
handleSizeChange,
handleCurrentChange,
clearSelection,
enumMap
});
enumMap,
})
</script>

View File

@@ -1,29 +1,29 @@
<template>
<div v-if="columns.length" class="card table-search">
<el-form ref="formRef" :model="searchParam">
<Grid ref="gridRef" :collapsed="collapsed" :gap="[20, 0]" :cols="searchCol">
<GridItem v-for="(item, index) in columns" :key="item.prop" v-bind="getResponsive(item)" :index="index">
<div v-if='columns.length' class='card table-search'>
<el-form ref='formRef' :model='searchParam'>
<Grid ref='gridRef' :collapsed='collapsed' :gap='[20, 0]' :cols='searchCol'>
<GridItem v-for='(item, index) in columns' :key='item.prop' v-bind='getResponsive(item)' :index='index + 1'>
<el-form-item>
<template #label>
<el-space :size="4">
<el-space :size='4'>
<span>{{ `${item.search?.label ?? item.label}` }}</span>
<el-tooltip v-if="item.search?.tooltip" effect="dark" :content="item.search?.tooltip" placement="top">
<el-tooltip v-if='item.search?.tooltip' effect='dark' :content='item.search?.tooltip' placement='top'>
<i :class="'iconfont icon-yiwen'"></i>
</el-tooltip>
</el-space>
<span>&nbsp;:</span>
</template>
<SearchFormItem :column="item" :search-param="searchParam" />
<SearchFormItem :column='item' :search-param='searchParam' />
</el-form-item>
</GridItem>
<GridItem suffix>
<div class="operation">
<el-button type="primary" :icon="Search" @click="search"> 搜索 </el-button>
<el-button :icon="Delete" @click="reset"> 重置 </el-button>
<el-button v-if="showCollapse" type="primary" link class="search-isOpen" @click="collapsed = !collapsed">
{{ collapsed ? "展开" : "合并" }}
<el-icon class="el-icon--right">
<component :is="collapsed ? ArrowDown : ArrowUp"></component>
<div class='operation'>
<el-button type='primary' :icon='Search' @click='search'> 搜索</el-button>
<el-button :icon='Delete' @click='reset'> 重置</el-button>
<el-button v-if='showCollapse' type='primary' link class='search-isOpen' @click='collapsed = !collapsed'>
{{ collapsed ? '展开' : '合并' }}
<el-icon class='el-icon--right'>
<component :is='collapsed ? ArrowDown : ArrowUp'></component>
</el-icon>
</el-button>
</div>
@@ -32,14 +32,15 @@
</el-form>
</div>
</template>
<script setup lang="ts" name="SearchForm">
import { computed, ref } from "vue";
import { ColumnProps } from "@/components/ProTable/interface";
import { BreakPoint } from "@/components/Grid/interface";
import { Delete, Search, ArrowDown, ArrowUp } from "@element-plus/icons-vue";
import SearchFormItem from "./components/SearchFormItem.vue";
import Grid from "@/components/Grid/index.vue";
import GridItem from "@/components/Grid/components/GridItem.vue";
<script setup lang='ts' name='SearchForm'>
import { ColumnProps } from '@/components/ProTable/interface'
import { BreakPoint } from '@/components/Grid/interface'
import { Delete, Search, ArrowDown, ArrowUp } from '@element-plus/icons-vue'
import SearchFormItem from './components/SearchFormItem.vue'
import Grid from '@/components/Grid/index.vue'
import GridItem from '@/components/Grid/components/GridItem.vue'
interface ProTableProps {
columns?: ColumnProps[]; // 搜索配置列
@@ -52,8 +53,12 @@ interface ProTableProps {
// 默认值
const props = withDefaults(defineProps<ProTableProps>(), {
columns: () => [],
searchParam: () => ({})
});
searchParam: () => ({}),
})
onMounted(() => {
console.log(props.columns)
})
// 获取响应式设置
const getResponsive = (item: ColumnProps) => {
@@ -64,31 +69,31 @@ const getResponsive = (item: ColumnProps) => {
sm: item.search?.sm,
md: item.search?.md,
lg: item.search?.lg,
xl: item.search?.xl
};
};
xl: item.search?.xl,
}
}
// 是否默认折叠搜索项
const collapsed = ref(true);
const collapsed = ref(true)
// 获取响应式断点
const gridRef = ref();
const breakPoint = computed<BreakPoint>(() => gridRef.value?.breakPoint);
const gridRef = ref()
const breakPoint = computed<BreakPoint>(() => gridRef.value?.breakPoint)
// 判断是否显示 展开/合并 按钮
const showCollapse = computed(() => {
let show = false;
let show = false
props.columns.reduce((prev, current) => {
prev +=
(current.search![breakPoint.value]?.span ?? current.search?.span ?? 1) +
(current.search![breakPoint.value]?.offset ?? current.search?.offset ?? 0);
if (typeof props.searchCol !== "number") {
if (prev >= props.searchCol[breakPoint.value]) show = true;
(current.search![breakPoint.value]?.offset ?? current.search?.offset ?? 0)
if (typeof props.searchCol !== 'number') {
if (prev >= props.searchCol[breakPoint.value]) show = true
} else {
if (prev >= props.searchCol) show = true;
if (prev >= props.searchCol) show = true
}
return prev;
}, 0);
return show;
});
return prev
}, 0)
return show
})
</script>

View File

@@ -0,0 +1,42 @@
/* 添加样式 */
.time-control {
display: flex;
align-items: center;
}
.select {
margin-right: 10px; /* 下拉框右侧间距 */
width: 90px; /* 下拉框宽度 */
}
.date-display {
display: flex;
align-items: center;
margin-right: 10px; /* 日期选择器右侧间距 */
width: 320px;
}
.date-picker {
margin-right: 10px; /* 日期选择器之间的间距 */
width: 100%;
}
.triangle-button {
margin: 0 2px; /* 设置左右间距 */
}
.left_triangle {
width: 0;
height: 0;
border-top: 10px solid transparent; /* 上边透明 */
border-bottom: 10px solid transparent; /* 下边透明 */
border-right: 15px solid white; /* 左边为白色 */
}
.right_triangle {
width: 0;
height: 0;
border-top: 10px solid transparent; /* 上边透明 */
border-bottom: 10px solid transparent; /* 下边透明 */
border-left: 15px solid white; /* 左边为白色 */
}

View File

@@ -0,0 +1,220 @@
<template>
<div class='time-control'>
<el-select
class='select'
v-model='timeUnit'
placeholder='选择时间单位'
@change='handleChange'
>
<!-- 采用 v-for 动态渲染 -->
<el-option
v-for="unit in timeUnits"
:key="unit.value"
:label="unit.label"
:value="unit.value"
></el-option>
</el-select>
<!-- 禁用时间选择器 -->
<div class='date-display'>
<el-date-picker
class='date-picker'
v-model='startDate'
type='date'
placeholder='起始时间'
:disabled-date="disableStartDate"
:readonly="timeUnit != '自定义'"
></el-date-picker>
<el-text>~</el-text>
<el-date-picker
class='date-picker'
v-model='endDate'
type='date'
placeholder='结束时间'
:disabled-date="disableEndDate"
:readonly="timeUnit !== '自定义'"
></el-date-picker>
</div>
<div class='date-display' v-if="timeUnit !== '自定义'">
<el-button
style='width: 10px;'
class='triangle-button'
type='primary'
@click='prevPeriod'
>
<div class='left_triangle'></div>
</el-button>
<el-button class='triangle-button' type='primary' @click='goToCurrent'>
{{ timeUnit }}
</el-button>
<el-button
style='width: 10px;'
class='triangle-button'
type='primary'
@click='nextPeriod'
:disabled='isNextDisabled'
>
<div class='right_triangle'></div>
</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { watch,computed, ref,reactive ,onMounted, defineProps, defineEmits } from 'vue';
const timeUnit = ref<string>('日'); // 默认选择按周
const startDate = ref<Date>(new Date()); // 起始日期
const endDate = ref<Date>(new Date()); // 结束日期
const isNextDisabled = ref<boolean>(false); // 控制下一周期按钮的禁用状态
const today = ref<Date>(new Date()); // 当前日期
const timeUnits = [
{ label: '日', value: '日' },
{ label: '周', value: '周' },
{ label: '月', value: '月' },
{ label: '季度', value: '季度' },
{ label: '年', value: '年' },
{ label: '自定义', value: '自定义' },
];
// 在组件挂载时更新日期范围
onMounted(() => {
updateDateRange();
});
const handleChange = (unit: string)=> {
// 根据选择的时间单位处理日期变化
if (unit !== '自定义') {
updateDateRange()
} else {
// 自定义选项逻辑
startDate.value = new Date(new Date().setDate(new Date().getDate() - 1))
endDate.value = new Date()
}
updateNextButtonStatus()
}
const updateDateRange = () => {
// 根据选择的时间单位计算起始和结束日期
if (timeUnit.value === '日') {
startDate.value = today.value
endDate.value = today.value
} else if (timeUnit.value === '周') {
startDate.value = getStartOfWeek(today.value)
endDate.value = getEndOfWeek(today.value)
} else if (timeUnit.value === '月') {
startDate.value = new Date(today.value.getFullYear(), today.value.getMonth(), 1)
endDate.value = new Date(today.value.getFullYear(), today.value.getMonth() + 1, 0)
} else if (timeUnit.value === '季度') {
const quarter = Math.floor(today.value.getMonth() / 3)
startDate.value = new Date(today.value.getFullYear(), quarter * 3, 1)
endDate.value = new Date(today.value.getFullYear(), quarter * 3 + 3, 0)
} else if (timeUnit.value === '年') {
startDate.value = new Date(today.value.getFullYear(), 0, 1)
endDate.value = new Date(today.value.getFullYear(), 11, 31)
}
updateNextButtonStatus()
}
const getStartOfWeek =(date:Date)=> {
const startOfWeek = new Date(date)
const day = startOfWeek.getDay()
const diff = day === 0 ? -6 : 1 - day // 星期天的情况
startOfWeek.setDate(startOfWeek.getDate() + diff)
return startOfWeek
}
const getEndOfWeek =(date:Date) =>{
const endOfWeek = new Date(date)
const day = endOfWeek.getDay()
const diff = day === 0 ? 0 : 7 - day // 星期天的情况
endOfWeek.setDate(endOfWeek.getDate() + diff)
return endOfWeek
}
const prevPeriod =()=> {
const prevStartDate = new Date(startDate.value)
const prevEndDate = new Date(endDate.value)
if (timeUnit.value === '日') {
prevStartDate.setDate(prevStartDate.getDate() - 1)
prevEndDate.setDate(prevEndDate.getDate() - 1)
} else if (timeUnit.value === '周') {
prevStartDate.setDate(prevStartDate.getDate() - 7)
prevEndDate.setDate(prevEndDate.getDate() - 7)
} else if (timeUnit.value === '月') {
prevStartDate.setMonth(prevStartDate.getMonth() - 1)
prevEndDate.setMonth(prevEndDate.getMonth() - 1)
} else if (timeUnit.value === '季度') {
prevStartDate.setMonth(prevStartDate.getMonth() - 3)
prevEndDate.setMonth(prevEndDate.getMonth() - 3)
} else if (timeUnit.value === '年') {
prevStartDate.setFullYear(prevStartDate.getFullYear() - 1)
prevEndDate.setFullYear(prevEndDate.getFullYear() - 1)
}
startDate.value = prevStartDate
endDate.value = prevEndDate
updateNextButtonStatus()
}
const goToCurrent =()=> {
if (timeUnit.value !== '自定义') {
updateDateRange() // 更新为当前选择时间单位的时间范围
}
}
const nextPeriod = ()=> {
const nextStartDate = new Date(startDate.value)
const nextEndDate = new Date(endDate.value)
if (timeUnit.value === '日') {
nextStartDate.setDate(nextStartDate.getDate() + 1)
nextEndDate.setDate(nextEndDate.getDate() + 1)
} else if (timeUnit.value === '周') {
nextStartDate.setDate(nextStartDate.getDate() + 7)
nextEndDate.setDate(nextEndDate.getDate() + 7)
} else if (timeUnit.value === '月') {
nextStartDate.setMonth(nextStartDate.getMonth() + 1)
nextEndDate.setMonth(nextEndDate.getMonth() + 1)
} else if (timeUnit.value === '季度') {
nextStartDate.setMonth(nextStartDate.getMonth() + 3)
nextEndDate.setMonth(nextStartDate.getMonth() + 3)
} else if (timeUnit.value === '年') {
nextStartDate.setFullYear(nextStartDate.getFullYear() + 1)
nextEndDate.setFullYear(nextEndDate.getFullYear() + 1)
}
startDate.value = nextStartDate
endDate.value = nextEndDate
updateNextButtonStatus()
}
const updateNextButtonStatus =()=> {
// 更新下一个按钮的禁用状态
const maxDate = new Date() // 假设最新日期为今天
// 将 maxDate 设置为当天的开始时间
maxDate.setHours(0, 0, 0, 0);
// 将 endDate 设置为当天的开始时间并进行比较
const endDateAdjusted = new Date(endDate.value);
endDateAdjusted.setHours(0, 0, 0, 0);
// 仅比较日期部分
isNextDisabled.value = endDateAdjusted >= maxDate;
}
// 限制开始日期不能选择超过当前日期
const disableStartDate = (date:Date)=> {
return date > today.value;
}
// 限制结束日期不能超过当前日期且必须大于开始日期
const disableEndDate = (date:Date)=> {
if (timeUnit.value !== '自定义') return false; // 如果不是自定义时间单位,则不限制
const start = new Date(startDate.value);
return date > today.value || (start && date <= start);
}
defineExpose({
startDate,
endDate,
})
</script>
<style scoped lang='scss'>
@import "./index.scss";
</style>

View File

@@ -1,11 +1,12 @@
<template>
<div class="tabs-box">
<div class="tabs-menu">
<el-tabs v-model="tabsMenuValue" type="card" @tab-click="tabClick" @tab-remove="tabRemove">
<el-tab-pane v-for="item in tabsMenuList" :key="item.path" :label="item.title" :name="item.path" :closable="item.close">
<div class='tabs-box'>
<div class='tabs-menu'>
<el-tabs v-model='tabsMenuValue' type='card' @tab-click='tabClick' @tab-remove='tabRemove'>
<el-tab-pane v-for='item in tabsMenuList' :key='item.path' :label='item.title' :name='item.path'
:closable='item.close'>
<template #label>
<el-icon v-show="item.icon && tabsIcon" class="tabs-icon">
<component :is="item.icon"></component>
<el-icon v-show='item.icon && tabsIcon' class='tabs-icon'>
<component :is='item.icon'></component>
</el-icon>
{{ item.title }}
</template>
@@ -16,49 +17,54 @@
</div>
</template>
<script setup lang="ts">
import Sortable from "sortablejs";
import { ref, computed, watch, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useGlobalStore } from "@/stores/modules/global";
import { useTabsStore } from "@/stores/modules/tabs";
import { useAuthStore } from "@/stores/modules/auth";
import { TabsPaneContext, TabPaneName } from "element-plus";
import MoreButton from "./components/MoreButton.vue";
<script setup lang='ts'>
import Sortable from 'sortablejs'
import { ref, computed, watch, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useGlobalStore } from '@/stores/modules/global'
import { useTabsStore } from '@/stores/modules/tabs'
import { useAuthStore } from '@/stores/modules/auth'
import { TabsPaneContext, TabPaneName } from 'element-plus'
import MoreButton from './components/MoreButton.vue'
const route = useRoute();
const router = useRouter();
const tabStore = useTabsStore();
const authStore = useAuthStore();
const globalStore = useGlobalStore();
const route = useRoute()
const router = useRouter()
const tabStore = useTabsStore()
const authStore = useAuthStore()
const globalStore = useGlobalStore()
const tabsMenuValue = ref(route.fullPath);
const tabsMenuList = computed(() => tabStore.tabsMenuList);
const tabsIcon = computed(() => globalStore.tabsIcon);
const tabsMenuValue = ref(route.fullPath)
const tabsMenuList = computed(() => tabStore.tabsMenuList)
const tabsIcon = computed(() => globalStore.tabsIcon)
onMounted(() => {
tabsDrop();
initTabs();
});
tabsDrop()
initTabs()
})
// 监听路由的变化(防止浏览器后退/前进不变化 tabsMenuValue
watch(
() => route.fullPath,
() => {
if (route.meta.isFull) return;
tabsMenuValue.value = route.fullPath;
const tabsParams = {
icon: route.meta.icon as string,
title: route.meta.title as string,
path: route.fullPath,
name: route.name as string,
close: !route.meta.isAffix,
isKeepAlive: route.meta.isKeepAlive as boolean
};
tabStore.addTabs(tabsParams);
if (route.meta.isFull) return
if (route.meta.hideTab){
tabsMenuValue.value = route.meta.parentPath as string
}else{
tabsMenuValue.value = route.fullPath
const tabsParams = {
icon: route.meta.icon as string,
title: route.meta.title as string,
path: route.fullPath,
name: route.name as string,
close: !route.meta.isAffix,
isKeepAlive: route.meta.isKeepAlive as boolean,
}
tabStore.addTabs(tabsParams)
}
},
{ immediate: true }
);
{ immediate: true },
)
// 初始化需要固定的 tabs
const initTabs = () => {
@@ -70,39 +76,39 @@ const initTabs = () => {
path: item.path,
name: item.name,
close: !item.meta.isAffix,
isKeepAlive: item.meta.isKeepAlive
};
tabStore.addTabs(tabsParams);
isKeepAlive: item.meta.isKeepAlive,
}
tabStore.addTabs(tabsParams)
}
});
};
})
}
// tabs 拖拽排序
const tabsDrop = () => {
Sortable.create(document.querySelector(".el-tabs__nav") as HTMLElement, {
draggable: ".el-tabs__item",
Sortable.create(document.querySelector('.el-tabs__nav') as HTMLElement, {
draggable: '.el-tabs__item',
animation: 300,
onEnd({ newIndex, oldIndex }) {
const tabsList = [...tabStore.tabsMenuList];
const currRow = tabsList.splice(oldIndex as number, 1)[0];
tabsList.splice(newIndex as number, 0, currRow);
tabStore.setTabs(tabsList);
}
});
};
const tabsList = [...tabStore.tabsMenuList]
const currRow = tabsList.splice(oldIndex as number, 1)[0]
tabsList.splice(newIndex as number, 0, currRow)
tabStore.setTabs(tabsList)
},
})
}
// Tab Click
const tabClick = (tabItem: TabsPaneContext) => {
const fullPath = tabItem.props.name as string;
router.push(fullPath);
};
const fullPath = tabItem.props.name as string
router.push(fullPath)
}
// Remove Tab
const tabRemove = (fullPath: TabPaneName) => {
tabStore.removeTabs(fullPath as string, fullPath == route.fullPath);
};
tabStore.removeTabs(fullPath as string, fullPath == route.fullPath)
}
</script>
<style scoped lang="scss">
<style scoped lang='scss'>
@import "./index.scss";
</style>

View File

@@ -25,12 +25,12 @@ export const staticRouter: RouteRecordRaw[] = [
{
path: "/plan",
name: "plan",
redirect:"/plan/planList",
redirect: "/plan/planList",
children: [
{
path: "/plan/planList",
name: "planList",
component:()=> import("@/views/plan/planList/index.vue"),
component: () => import("@/views/plan/planList/index.vue"),
meta: {
title: "检测计划列表",
icon: "List",
@@ -44,7 +44,7 @@ export const staticRouter: RouteRecordRaw[] = [
{
path: "/plan/singlePlanList",
name: "singlePlanList",
component:()=> import("@/views/plan/singlePlanList/index.vue"),
component: () => import("@/views/plan/singlePlanList/index.vue"),
meta: {
title: "单个计划列表",
icon: "List",
@@ -58,7 +58,7 @@ export const staticRouter: RouteRecordRaw[] = [
{
path: "/plan/preTest",
name: "preTest",
component:()=> import("@/views/plan/preTest/index.vue"),
component: () => import("@/views/plan/preTest/index.vue"),
meta: {
title: "预检测",
icon: "List",
@@ -72,11 +72,13 @@ export const staticRouter: RouteRecordRaw[] = [
{
path: "/plan/autoTest",
name: "autoTest",
component:()=> import("@/views/plan/autoTest/index.vue"),
component: () => import("@/views/plan/autoTest/index.vue"),
meta: {
title: "自动检测",
icon: "List",
isLink: "",
hideTab:true,
parentPath:'/system/proTable',
isHide: false,
isFull: false,
isAffix: false,
@@ -115,6 +117,20 @@ export const staticRouter: RouteRecordRaw[] = [
},
],
},
{
path: "/system/dict-data/:code/:id(\\d+)",
name: "dictData",
component: () => import("@/views/system/dict/data.vue"),
meta: {
title: "字典数据",
icon: "List",
isLink: "",
isHide: false,
isFull: false,
isAffix: false,
isKeepAlive: false,
},
}
],
},
];

View File

@@ -4,6 +4,11 @@
align-items: center;
justify-content: center;
}
.flx-flex-start {
display: flex;
align-items: center;
justify-content: flex-start;
}
.flx-justify-between {
display: flex;
align-items: center;

View File

@@ -331,7 +331,7 @@
.el-dialog__body {
// height: 200px;
// max-height: 60vh;
min-height: 200px;
//min-height: 200px;
overflow-y: auto;
padding: 10px 15px;
@@ -436,7 +436,7 @@
.dialog-small{
.el-dialog__body{
max-height: 240px;
max-height: 260px;
}
}
.dialog-middle{

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog :title="dialogTitle" :model-value="visible" @close="handleCancel" width="500">
<el-dialog :title="dialogTitle" :model-value="visible" @close="handleCancel" v-bind="dialogSmall">
<el-form :model="formData" :rules="rules">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="formData.name" />
@@ -33,9 +33,9 @@
</el-dialog>
</template>
<script lang="ts" setup>
<script lang="ts" setup name="ResourceDialog">
import { defineProps, defineEmits, reactive,watch } from 'vue';
import { type Resource } from '@/api/resource/interface'
import { dialogSmall } from '@/utils/elementBind'
const props = defineProps<{
visible: boolean;
dialogTitle: string;

View File

@@ -39,13 +39,12 @@
import { defineComponent,ref ,reactive} from 'vue'
import { type Resource } from '@/api/resource/interface'
import ProTable from '@/components/ProTable/index.vue'
import ResourceDialog from '@/components/ResourceDialog/index.vue'; // 导入子组件
import {Operation, CirclePlus, Delete, EditPen, Download, Upload, View, Refresh} from '@element-plus/icons-vue'
import ResourceDialog from "@/views/authority/resource/components/ResourceDialog.vue"; // 导入子组件
import {CirclePlus, Delete, EditPen} from '@element-plus/icons-vue'
import resourceDataList from '@/api/resource/resourceData'
import { useDictStore } from '@/stores/modules/dict'
import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface'
import { ElMessage, ElMessageBox, inputEmits } from 'element-plus';
let multipleSelection = ref<string[]>([]);
import { ElMessage, ElMessageBox, inputEmits } from 'element-plus';
let multipleSelection = ref<string[]>([]);
const resourceData = resourceDataList
const dialogFormVisible = ref(false)
const isEditMode = ref(false);
@@ -70,17 +69,18 @@ import { ElMessage, ElMessageBox, inputEmits } from 'element-plus';
{
prop: 'name',
label: '名称',
width: 120,
width: 150,
search: { el: 'input', tooltip: '我是搜索提示' },
},
{
prop: 'path',
label: '路径',
width: 180,
width: 300,
},
{
prop: 'sort',
label: '排序',
width: 100,
search: {
// 自定义 search 显示内容
render: ({ searchParam }) => {
@@ -90,6 +90,7 @@ import { ElMessage, ElMessageBox, inputEmits } from 'element-plus';
<span class='mr10 ml10'>-</span>
<el-input vModel_trim={searchParam.maxAge} placeholder='最大排序' />
</div>
)
},
},
@@ -97,13 +98,31 @@ import { ElMessage, ElMessageBox, inputEmits } from 'element-plus';
{
prop: 'type',
label: '资源类型',
width: 150,
// 字典数据(本地数据)
//enum: dictStore.getDictData('type'),
search: { el: 'select', props: { filterable: true } },
//search: { el: 'select', props: { filterable: true } },
//fieldNames: { label: 'label', value: 'resourceType' },
search: {
// 自定义 search 显示内容
render: ({ searchParam }) => {
return (
<div class='flx-center'>
<el-select >
<el-option ></el-option>
<el-option label="未检测"></el-option>
<el-option label="检测中"></el-option>
<el-option label="检测完成"></el-option>
</el-select>
</div>
)
},
},
},
{
prop: 'route_Name',
width: 200,
label: '路由名称',
search: { el: 'input' },
},
@@ -121,11 +140,7 @@ import { ElMessage, ElMessageBox, inputEmits } from 'element-plus';
prop: 'update_Time',
label: '更新时间',
width: 180,
search: {
el: 'date-picker',
span: 1,
props: { type: 'daterange', valueFormat: 'YYYY-MM-DD'},
defaultValue: ['2024-11-12', '2024-12-12'],}
},
{ prop: 'operation', label: '操作', fixed: 'right', width: 200 },
])
@@ -166,7 +181,7 @@ const submitForm = () => {
const openEditDialog = (resource: Resource.ResResourceList) => {
dialogForm.value = { ...resource }; // 复制资源数据以便编辑
isEditMode.value = true; // 设置为编辑模式
dialogTitle.value = '编辑资源';
dialogTitle.value = '编辑菜单';
dialogFormVisible.value = true; // 显示对话框
};
//选中
@@ -177,9 +192,21 @@ const openEditDialog = (resource: Resource.ResResourceList) => {
};
//批量删除
const handleDelList =()=>{
ElMessageBox.confirm(
`是否删除所选菜单信息?`, // 使用模板字符串来插值
'温馨提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
resourceData.value = resourceData.value.filter(item => !multipleSelection.value.includes(item.id));
multipleSelection.value = []; // Clear selected rows
ElMessage.success("批量删除成功");
})
}
// 删除资源
const deleteResource = (params: Resource.ResResourceList) => {

View File

@@ -1,6 +1,6 @@
<!--双列-->
<template>
<el-dialog v-model='dialogVisible' :title='title' v-bind='dialogMiddle'>
<el-dialog v-model='dialogVisible' :title='title' v-bind='dialogMiddle' >
<el-scrollbar>
<el-form :inline="false" label-width="auto" ref="formRef" class='form-two'>
<el-form-item label="姓名" prop="username" >

View File

@@ -1,9 +1,9 @@
<template>
<div class='table-box'>
<ProTable
ref='proTable'
:columns='columns'
:data='userData'
ref='proTable'
:columns='columns'
:data='userData'
>
<!-- 表格 header 按钮 -->
<template #tableHeader='scope'>
@@ -26,11 +26,11 @@
</template>
</ProTable>
</div>
<single-column ref='singleColumn' />
<double-column ref='doubleColumn' />
<single-column ref='singleColumn' />
<double-column ref='doubleColumn' />
</template>
<script setup lang='tsx' name='useProTable'>
import { ref ,reactive} from 'vue'
import { ref, reactive } from 'vue'
import { User } from '@/api/user/interface'
import { useHandleData } from '@/hooks/useHandleData'
import { useDownload } from '@/hooks/useDownload'
@@ -43,6 +43,7 @@ import userDataList from '@/api/user/userData'
import { useDictStore } from '@/stores/modules/dict'
import SingleColumn from '@/views/demo/proTable/singleColumn.vue'
import DoubleColumn from '@/views/demo/proTable/doubleColumn.vue'
const dictStore = useDictStore()
import {
getUserList,
@@ -54,6 +55,8 @@ import {
getUserStatus,
} from '@/api/user/user'
import { ElMessageBox } from 'element-plus'
import router from '@/routers'
const userData = userDataList
const singleColumn = ref()
const doubleColumn = ref()
@@ -88,7 +91,7 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
prop: 'username',
label: '姓名',
width: 120,
search: { el: 'input'},
search: { el: 'input' },
},
{
prop: 'gender',
@@ -105,11 +108,11 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
// 自定义 search 显示内容
render: ({ searchParam }) => {
return (
<div class='flx-center'>
<el-input vModel_trim={searchParam.minAge} placeholder='最小年龄' />
<span class='mr10 ml10'>-</span>
<el-input vModel_trim={searchParam.maxAge} placeholder='最大年龄' />
</div>
<div class='flx-center'>
<el-input vModel_trim={searchParam.minAge} placeholder='最小年龄' />
<span class='mr10 ml10'>-</span>
<el-input vModel_trim={searchParam.maxAge} placeholder='最大年龄' />
</div>
)
},
},
@@ -125,19 +128,19 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
fieldNames: { label: 'userLabel', value: 'userStatus' },
render: scope => {
return (
<>
{BUTTONS.value.status ? (
<el-switch
model-value={scope.row.status}
active-text={scope.row.status ? '启用' : '禁用'}
active-value={1}
inactive-value={0}
onClick={() => changeStatus(scope.row)}
/>
) : (
<el-tag type={scope.row.status ? 'success' : 'danger'}>{scope.row.status ? '启用' : '禁用'}</el-tag>
)}
</>
<>
{BUTTONS.value.status ? (
<el-switch
model-value={scope.row.status}
active-text={scope.row.status ? '启用' : '禁用'}
active-value={1}
inactive-value={0}
onClick={() => changeStatus(scope.row)}
/>
) : (
<el-tag type={scope.row.status ? 'success' : 'danger'}>{scope.row.status ? '启用' : '禁用'}</el-tag>
)}
</>
)
},
},
@@ -156,8 +159,9 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
])
// 删除用户信息
const deleteAccount = async (params: User.ResUserList) => {
await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`)
proTable.value?.getTableList()
// await useHandleData(deleteUser, { id: [params.id] }, `删除【${params.username}】用户`)
// proTable.value?.getTableList()
router.push('/plan/autoTest')
}
// 批量删除用户信息
const batchDelete = async (id: string[]) => {
@@ -169,7 +173,7 @@ const batchDelete = async (id: string[]) => {
const resetPass = async (params: User.ResUserList) => {
// await useHandleData(resetUserPassWord, { id: params.id }, `重置【${params.username}】用户密码`)
// proTable.value?.getTableList()
doubleColumn.value.open("双列弹出框")
doubleColumn.value.open('双列弹出框')
}
// 切换用户状态
const changeStatus = async (row: User.ResUserList) => {
@@ -182,7 +186,7 @@ const changeStatus = async (row: User.ResUserList) => {
// 导出用户列表
const downloadFile = async () => {
ElMessageBox.confirm('确认导出用户数据?', '温馨提示', { type: 'warning' }).then(() =>
useDownload(exportUserInfo, '用户列表', proTable.value?.searchParam),
useDownload(exportUserInfo, '用户列表', proTable.value?.searchParam),
)
}
// 批量添加用户
@@ -198,6 +202,6 @@ const batchAdd = () => {
}
// 打开 drawer(新增、查看、编辑)
const openDrawer = (title: string, row: Partial<User.ResUserList> = {}) => {
singleColumn.value.open("单列弹出框")
singleColumn.value.open('单列弹出框')
}
</script>

View File

@@ -1,6 +1,6 @@
<!--单列-->
<template>
<el-dialog v-model='dialogVisible' :title='title' v-bind='dialogSmall'>
<el-dialog v-model='dialogVisible' :title='title' v-bind='dialogSmall' >
<el-scrollbar>
<el-form :inline="false" label-width="auto" ref="formRef">
<el-form-item label="姓名" prop="username">

View File

@@ -0,0 +1,93 @@
<template>
<div class='table-box'>
<ProTable
ref='proTable'
:columns='columns'
:data='logData'
@selection-change='handleSelectionChange'
type='selection'
>
<!-- 表格 header 按钮 -->
<template #tableHeader>
<el-button type='primary' :icon='DataAnalysis'>分析</el-button>
<el-button type='primary' :icon='Upload'>导出csv</el-button>
</template>
</ProTable>
</div>
</template>
<script setup lang='tsx' name='useProTable'>
// 根据实际路径调整
import TimeControl from '@/components/TimeControl/index.vue'
import { type Log } from '@/api/log/interface'
import ProTable from '@/components/ProTable/index.vue'
import { Upload ,DataAnalysis} from '@element-plus/icons-vue'
import logDataList from '@/api/log/logData'
import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface'
import { reactive,ref } from 'vue'
let multipleSelection = ref<string[]>([])
const logData = logDataList
// 定义包含和排除的单位
const includedUnits = ['日', '周', '月', '季度']; // 可以根据需要包含的单位
const excludedUnits = ['年']; // 要排除的单位
// ProTable 实例
const proTable = ref<ProTableInstance>()
// 表格配置项
const columns = reactive<ColumnProps<Log.LogList>[]>([
{ type: 'selection', fixed: 'left', width: 50 },
{
prop: 'content',
label: '内容',
width: 600,
},
{
prop: 'record_Time',
label: '记录时间',
width: 180,
},
{
prop: 'user',
label: '操作用户',
search: { el: 'select', props: { filterable: true } },
},
{
prop: 'type',
label: '日志类型',
search: { el: 'select', props: { filterable: true } },
},
{
prop: 'level',
label: '日志等级',
search: { el: 'select', props: { filterable: true } },
},
{
prop: 'record_Time',
label: '记录时间',
isShow: false,
search: {
span: 2,
render: ({ searchParam }) => {
return (
<div class='flx-flex-start'>
<TimeControl/>
</div>
)
},
},
},
])
//选中
// 处理选择变化
const handleSelectionChange = (selection: Log.LogList[]) => {
multipleSelection.value = selection.map(row => row.id) // 更新选中的行
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,91 @@
<template >
<el-dialog :title="dialogTitle" :model-value="visible" @close="handleCancel" v-bind="dialogSmall" >
<el-form-item label="指标类型" prop="type">
<el-select
class='select'
placeholder='选择时间单位'
>
<!-- 采用 v-for 动态渲染 -->
<el-option
v-for="unit in typeList"
:key="unit.value"
:label="unit.label"
:value="unit.value"
></el-option>
</el-select>
</el-form-item>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel"> </el-button>
<el-button type="primary" @click="handleSubmit">新增</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup name="IndicatorTypeDialog">
import { defineProps, defineEmits, reactive,watch } from 'vue';
import { dialogSmall} from '@/utils/elementBind'
const props = defineProps<{
visible: boolean;
dialogTitle: string;
formData: {
id:string;
name: string;
path: string;
sort: number;
type: string;
remark: string;
route_Name: string;
};
}>();
const typeList = [
{ label: '电压', value: '电压' },
{ label: '频率', value: '频率' },
{ label: '电压三相不平衡度', value: '电压三相不平衡度' },
{ label: '电流三相不平衡度', value: '电流三相不平衡度' },
{ label: '电压波动', value: '电压波动' },
{ label: '短时闪变', value: '短时闪变' },
{ label: '谐波电压', value: '谐波电压' },
{ label: '谐波电流', value: '谐波电流' },
{ label: '谐波相角', value: '谐波相角' },
{ label: '谐波功率', value: '谐波功率' },
{ label: '间谐波电压', value: '间谐波电压' },
{ label: '间谐波电流', value: '间谐波电流' },
{ label: '功率', value: '功率' },
{ label: '电流', value: '电流' },
{ label: '暂态电压幅值', value: '暂态电压幅值' },
{ label: '暂态持续时间', value: '暂态持续时间' },
];
const rules = {
name :[
{require:true,trigger:"blur",message:"请填写菜单名称"}
]
}
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'submit', data: any): void;
}>();
const handleCancel = () => {
emit('update:visible', false); // 关闭对话框
};
const handleSubmit = () => {
emit('submit', props.formData); // 提交表单数据
emit('update:visible', false); // 提交后关闭对话框
};
// 当 props.visible 改变时,更新 formData
watch(() => props.visible, (newVal) => {
if (!newVal) {
// 这里可以重置表单数据,如果需要的话
}
});
</script>

View File

@@ -0,0 +1,294 @@
<template>
<el-dialog :title="dialogTitle" :model-value="visible" @close="handleCancel" v-bind="dialogBig">
<el-tabs type="border-card">
<el-tab-pane label="基础信息">
<div class="form-grid">
<el-form :model="formData" >
<el-row :gutter="20" >
<el-col :span="10">
<el-form-item label="误差体系名称" prop="name">
<el-input placeholder="标准号+年份+设备等级"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="发布时间" prop="publishTime">
<el-input />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="上传误差体系截图" prop="screenshot">
<el-button :icon="FolderOpened" type="primary"></el-button>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" >
<el-col :span="10">
<el-form-item label="适用设备等级" prop="type">
<el-select placeholder="请选择设备等级">
<el-option label="A级" value="A级" />
<el-option label="S级" value="S级" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="实施时间" prop="publishTime">
<el-input />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="状态" prop="type">
<el-select placeholder="请选择状态">
<el-option label="启用" value="启用" />
<el-option label="停用" value="停用" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</el-tab-pane>
</el-tabs>
<div class="dialog-footer">
<el-button :icon='CirclePlus' type="primary" @click="openAddDialog">新增</el-button>
<el-button :icon='Delete' type="danger" >批量删除</el-button>
</div>
<el-table :data="tableData" style="width: 100%;text-align: center" >
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="序号" width="60" />
<el-table-column prop="type" label="电能质量检测指标类型" width="200"/>
<el-table-column label="起止范围">
<el-table-column label="起始">
<template #default="{ row }">
<el-row type="flex" align="middle">
<el-col :span="16">
<el-select v-model="row.startSelect" placeholder="选择起始值" style="width: 70px;">
<el-option
v-for="option in errorStartOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-col>
<el-col :span="8">
<span>{{ row.startRange }}</span>
</el-col>
</el-row>
</template>
</el-table-column>
<el-table-column label="结束">
<template #default="{ row }">
<el-row type="flex" align="middle">
<el-col :span="16">
<el-select v-model="row.endSelect" placeholder="选择结束值" style="width: 70px;">
<el-option
v-for="option in errorEndOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-col>
<el-col :span="8">
<span>{{ row.endRange }}</span>
</el-col>
</el-row>
</template>
</el-table-column>
<el-table-column label="单位" width="120">
<template #default="{ row }">
<el-select v-model="row.unit" placeholder="选择单位">
<el-option
v-for="option in errorUnitOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="最大误差">
<el-table-column prop="maxErrorValue" label="最大误差值" width="100"/>
<el-table-column label="误差类型">
<template #default="{ row }">
<el-select v-model="row.errorType" placeholder="选择误差类型">
<el-option
v-for="option in errorTypeOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="操作" width="150">
<template #default>
<el-button type="primary" link :icon='CopyDocument'>复制</el-button>
<el-button type='primary' link :icon='Delete' >删除</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel"> </el-button>
<el-button type="primary" @click="handleSubmit">保存</el-button>
</div>
</template>
</el-dialog>
<!-- 新增/编辑资源对话框 -->
<IndicatorTypeDialog
:visible="dialogFormVisible"
:formData="dialogForm"
:dialogTitle="dialogTitle"
@update:visible="dialogFormVisible = $event"
/>
</template>
<script lang="ts" setup name="ErrorSystemDialog">
import { defineProps, defineEmits, reactive,watch,ref } from 'vue';
import { dialogBig,dialogMiddle} from '@/utils/elementBind'
import IndicatorTypeDialog from "@/views/machine/errorSystem/components/IndicatorTypeDialog.vue"; // 导入子组件
import {CirclePlus, Delete, EditPen,FolderOpened,CopyDocument} from '@element-plus/icons-vue'
const props = defineProps<{
visible: boolean;
dialogTitle: string;
formData: {
id:string;
name: string;
path: string;
sort: number;
type: string;
remark: string;
route_Name: string;
};
}>();
const dialogFormVisible = ref(false)
const dialogTitle = ref('新增检测指标误差项')
const errorTypeOptions = [
{ label: '绝对值-标称值', value: 'type1' },
{ label: '相对值-I类', value: 'type2' },
{ label: '相对值-II类', value: 'type3' },
{ label: '绝对值-值类型', value: 'type4' },
];
const errorUnitOptions = [
{ label: '标称值', value: 'type1' },
{ label: '值', value: 'type2' },
{ label: '无', value: 'type3' },
];
const errorStartOptions = [
{ label: '>', value: 'type1' },
{ label: '>=', value: 'type2' },
{ label: '无', value: 'type3' },
];
const errorEndOptions = [
{ label: '<', value: 'type1' },
{ label: '<=', value: 'type2' },
{ label: '无', value: 'type3' },
];
const tableData = [
{
id: '1',
type: '电压',
startSelect: 'type1',
startRange: '0.1',
endSelect: 'type2',
endRange: '1.5',
unit:'type1',
maxErrorValue:'0.001',
errorType:'type1'
},
{
id: '2',
type: '电流',
startSelect: 'type2',
startRange: '0.01',
endSelect: 'type1',
endRange: '0.05',
unit:'type1',
maxErrorValue:'0.005',
errorType:'type1'
},
{
id: '3',
type: '频率',
startSelect: 'type2',
startRange: '42.5',
endSelect: 'type2',
endRange: '57.5',
unit:'type1',
maxErrorValue:'0.01',
errorType:'type4'
},
]
const rules = {
name :[
{require:true,trigger:"blur",message:"请填写菜单名称"}
]
}
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'submit', data: any): void;
}>();
const handleCancel = () => {
emit('update:visible', false); // 关闭对话框
};
const handleSubmit = () => {
emit('submit', props.formData); // 提交表单数据
emit('update:visible', false); // 提交后关闭对话框
};
// 当 props.visible 改变时,更新 formData
watch(() => props.visible, (newVal) => {
if (!newVal) {
// 这里可以重置表单数据,如果需要的话
}
});
const openAddDialog = () => {
dialogFormVisible.value = true; // 打开对话框
};
</script>
<style scoped>
.form-grid {
display: flex;
flex-direction: row; /* 横向排列 */
flex-wrap: wrap; /* 允许换行 */
}
.form-grid .el-form-item {
flex: 1 1 30%; /* 控件宽度 */
margin-right: 20px; /* 控件间距 */
}
.form-grid .el-form-item:last-child {
margin-right: 0; /* 最后一个控件不需要右边距 */
}
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-bottom: 10px; /* 调整这里的值以增加或减少间距 */
}
.el-tabs {
margin-bottom: 20px; /* 添加底部边距 */
}
.el-table th, .el-table td {
text-align: center; /* 所有单元格文字居中 */
}
</style>

View File

@@ -0,0 +1,91 @@
<template >
<el-dialog :title="dialogTitle" :model-value="visible" @close="handleCancel" v-bind="dialogSmall" >
<el-form-item label="指标类型" prop="type">
<el-select
class='select'
placeholder='选择时间单位'
>
<!-- 采用 v-for 动态渲染 -->
<el-option
v-for="unit in typeList"
:key="unit.value"
:label="unit.label"
:value="unit.value"
></el-option>
</el-select>
</el-form-item>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel"> </el-button>
<el-button type="primary" @click="handleSubmit">新增</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup name="IndicatorTypeDialog">
import { defineProps, defineEmits, reactive,watch } from 'vue';
import { dialogSmall} from '@/utils/elementBind'
const props = defineProps<{
visible: boolean;
dialogTitle: string;
formData: {
id:string;
name: string;
path: string;
sort: number;
type: string;
remark: string;
route_Name: string;
};
}>();
const typeList = [
{ label: '电压', value: '电压' },
{ label: '频率', value: '频率' },
{ label: '电压三相不平衡度', value: '电压三相不平衡度' },
{ label: '电流三相不平衡度', value: '电流三相不平衡度' },
{ label: '电压波动', value: '电压波动' },
{ label: '短时闪变', value: '短时闪变' },
{ label: '谐波电压', value: '谐波电压' },
{ label: '谐波电流', value: '谐波电流' },
{ label: '谐波相角', value: '谐波相角' },
{ label: '谐波功率', value: '谐波功率' },
{ label: '间谐波电压', value: '间谐波电压' },
{ label: '间谐波电流', value: '间谐波电流' },
{ label: '功率', value: '功率' },
{ label: '电流', value: '电流' },
{ label: '暂态电压幅值', value: '暂态电压幅值' },
{ label: '暂态持续时间', value: '暂态持续时间' },
];
const rules = {
name :[
{require:true,trigger:"blur",message:"请填写菜单名称"}
]
}
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'submit', data: any): void;
}>();
const handleCancel = () => {
emit('update:visible', false); // 关闭对话框
};
const handleSubmit = () => {
emit('submit', props.formData); // 提交表单数据
emit('update:visible', false); // 提交后关闭对话框
};
// 当 props.visible 改变时,更新 formData
watch(() => props.visible, (newVal) => {
if (!newVal) {
// 这里可以重置表单数据,如果需要的话
}
});
</script>

View File

@@ -0,0 +1,104 @@
<template>
<div class='table-box'>
<ProTable
ref='proTable'
:columns='columns'
:data='errorData'
type='selection'
>
<!-- 表格 header 按钮 -->
<template #tableHeader>
<el-button type='primary' :icon='CirclePlus' @click="openAddDialog">新增误差体系</el-button>
<el-button type='danger' :icon='Delete' plain
>
批量删除误差体系
</el-button>
</template>
<!-- 表格操作 -->
<template #operation='scope'>
<el-button type='primary' link :icon='View' @row-click="handleRowClick">查看</el-button>
<el-button type='primary' link :icon='EditPen' @click="openEditDialog(scope.row)">编辑</el-button>
<el-button type='primary' link :icon='Delete' >删除</el-button>
</template>
</ProTable>
<!-- 新增/编辑资源对话框 -->
<ResourceDialog
:visible="dialogFormVisible"
:formData="dialogForm"
:dialogTitle="dialogTitle"
@update:visible="dialogFormVisible = $event"
/>
</div>
</template>
<script setup lang="ts" name='useProTable'>
import ProTable from '@/components/ProTable/index.vue'
import type { ColumnProps } from '@/components/ProTable/interface'
import { CirclePlus, Delete,EditPen,View} from '@element-plus/icons-vue'
import errorDataList from '@/api/error/errorData'
import { reactive,ref } from 'vue'
import type { ErrorSystem } from '@/api/error/interface'
import ResourceDialog from "@/views/machine/errorSystem/components/ErrorSystemDialog.vue"; // 导入子组件
let multipleSelection = ref<string[]>([])
const errorData = errorDataList
const dialogFormVisible = ref(false)
const dialogTitle = ref('新增误差体系')
const dialogForm = ref<ErrorSystem.ErrorSystemList>({
id: '',
name: '',
year:'',
level:'',
});
// 表格配置项
const columns = reactive<ColumnProps<ErrorSystem.ErrorSystemList>[]>([
{ type: 'selection', fixed: 'left', width: 50 },
{
prop: 'id',
label: '序号',
width: 100,
},
{
prop: 'name',
label: '误差体系名称',
width: 300,
},
{
prop: 'year',
label: '标准实施年份',
search: { el: 'input' },
},
{
prop: 'level',
label: '适用设备等级',
search: { el: 'select', props: { filterable: true } },
},
{ prop: 'operation', label: '操作', fixed: 'right' },
])
// 打开编辑对话框
const openEditDialog = (resource: ErrorSystem.ErrorSystemList) => {
};
const openAddDialog = () => {
dialogForm.value = {
id: '',
name: '',
year: '',
level: '',
};
dialogFormVisible.value = true; // 打开对话框
};
const handleRowClick = (row: ErrorSystem.ErrorSystemList) =>{
}
</script>
<style scoped>
</style>

View File

@@ -51,8 +51,6 @@ const getTreeData = (val: any) => {
defaultChecked.value = [];
data.value = val;
defaultChecked.value.push(data.value[0].children[0].children[0].id);
console.log(defaultChecked.value, "+++++++++++++");
console.log(treeList.value);
treeRef.value.setCurrentKey(defaultChecked.value);
};
const filterText = ref("");

View File

@@ -271,7 +271,6 @@ const dataCallback = (data: any) => {
total: data.length || data.total, //total
};
};
console.log(proTable.value, "proTable.value?proTable.value?proTable.value?");
// 如果你想在请求之前对当前请求参数做一些操作可以自定义如下函数params 为当前所有的请求参数(包括分页),最后返回请求列表接口
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
const getTableList = (params: any) => {

View File

@@ -240,7 +240,6 @@ const dataCallback = (data: any) => {
total: data.length || data.total,
};
};
console.log(proTable.value, "proTable.value?proTable.value?proTable.value?");
// 如果你想在请求之前对当前请求参数做一些操作可以自定义如下函数params 为当前所有的请求参数(包括分页),最后返回请求列表接口
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
const getTableList = (params: any) => {

View File

@@ -240,7 +240,6 @@ const dataCallback = (data: any) => {
total: data.length || data.total,
};
};
console.log(proTable.value, "proTable.value?proTable.value?proTable.value?");
// 如果你想在请求之前对当前请求参数做一些操作可以自定义如下函数params 为当前所有的请求参数(包括分页),最后返回请求列表接口
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
const getTableList = (params: any) => {

View File

@@ -0,0 +1,250 @@
<template>
<div class="table-box">
<ProTable :columns="columns" :data="data">
<template #tableHeader="scope">
<el-button type="primary" :icon="CirclePlus" @click="openDrawer('新增')">新增</el-button>
<el-button type="primary" :icon="Upload" plain @click="batchAdd">批量添加</el-button>
<el-button type="primary" :icon="Download" plain @click="downloadFile">导出</el-button>
<el-button type="danger" :icon="Delete" plain :disabled="!scope.isSelected"
@click="batchDelete(scope.selectedListIds)">
批量删除
</el-button>
</template>
<template #operation="scope">
<el-button type="primary" link :icon="EditPen" @click="openDrawer('编辑', scope.row)">编辑</el-button>
<el-button type="primary" link :icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</ProTable>
</div>
<el-dialog v-model="dialogVisible" :title="dialogType === '新增' ? '新增字典数据' : '编辑字典数据'" v-bind="dialogSmall">
<div>
<el-form :model="dialogForm">
<el-form-item label="字典类型编码" :label-width="100">
<el-input :value="route.params.code" disabled />
</el-form-item>
<el-form-item label="编码" :label-width="100">
<el-input v-model="dialogForm.code" placeholder="请输入" autocomplete="off" />
</el-form-item>
<el-form-item label="名称" :label-width="100">
<el-input v-model="dialogForm.name" placeholder="请输入" autocomplete="off" />
</el-form-item>
<el-form-item label="值" :label-width="100">
<el-input v-model="dialogForm.value" placeholder="请输入" autocomplete="off" />
</el-form-item>
<el-form-item label="显示排序" :label-width="100">
<el-input-number v-model="dialogForm.sort" :min="1" :max="100" />
</el-form-item>
<el-form-item label="状态" :label-width="100">
<el-radio-group v-model.number="dialogForm.state">
<el-radio v-for="item in dictStore.getDictData('status')" :key="item.id" :value="item.code">{{
item.label
}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="close()">取消</el-button>
<el-button type="primary" @click="save()">
保存
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="tsx" name="dictData">
import { useRoute } from 'vue-router'
import { CirclePlus, Delete, EditPen, Download, Upload } from '@element-plus/icons-vue'
import { Dict } from '@/api/system/dict/interface'
import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
import ImportExcel from '@/components/ImportExcel/index.vue'
import { dialogSmall } from '@/utils/elementBind'
import { useDictStore } from '@/stores/modules/dict'
import { useHandleData } from '@/hooks/useHandleData'
import { useDownload } from '@/hooks/useDownload'
import { dictDataList } from '@/api/system/dict/dictExample'
import {
updateDictData,
addDictData,
batchAddDictData,
exportDictData,
deleteDictData
} from '@/api/system/dict'
const route = useRoute()
const dictStore = useDictStore()
const data = dictDataList
const proTable = ref<ProTableInstance>()
const columns = reactive<ColumnProps<Dict.ResDictData>[]>([
{ type: 'selection', fixed: 'left', width: 70 },
{ type: 'index', fixed: 'left', width: 70, label: '序号' },
{
prop: 'name',
label: '名称',
width: 160,
search: {
el: 'input',
props: {
placeholder: '请输入名称'
}
}
},
{
prop: 'code',
label: '编码',
width: 160,
search: {
el: 'input',
props: {
placeholder: '请输入编码'
}
}
},
{
prop: 'value',
label: '值',
width: 160
},
{
prop: 'level',
label: '事件等级',
width: 100,
render: scope => {
return (
<>
{scope.row.level === 0 || scope.row.level === null || scope.row.level === undefined ?
(<span></span>) :
(<el-tag type={scope.row.level === 1 ? 'info' : scope.row.level === 2 ? 'warning' : 'danger'}>
{scope.row.level === 1 ? '普通' : scope.row.level === 2 ? '警告' : '危险'}
</el-tag>)
}
</>
)
}
},
{
prop: 'state',
label: '状态',
enum: dictStore.getDictData('status'),
search: {
el: 'tree-select',
props: { filterable: true }
},
fieldNames: { label: 'label', value: 'code' },
render: scope => {
return (
<>
{
<el-tag type={scope.row.state ? 'success' : 'danger'} > {scope.row.state ? '正常' : '禁用'} </el-tag>
}
</>
)
},
},
{
prop: 'createTime',
label: '创建时间',
width: 180,
search: {
el: 'date-picker',
props: { type: 'daterange', valueFormat: 'YYYY-MM-DD' }
}
},
{
prop: 'operation',
label: '操作',
fixed: 'right',
width: 330
},
])
const { dialogVisible, dialogType, dialogForm } = useCount();
function useCount() {
const dialogVisible = ref(false)
const dialogType = ref('新增')
const dialogForm = ref({
id: "",
name: "",
code: "",
value: "",
sort: 1,
state: 1, // 状态0-删除 1-正常
})
return { dialogVisible, dialogType, dialogForm };
}
// 打开 drawer(新增、查看、编辑)
const openDrawer = (title: string, row: Partial<Dict.ResDictType> = {}) => {
dialogVisible.value = true
dialogType.value = title
if (title === '编辑') {
row && (dialogForm.value = row as { id: string; name: string; code: string; value: string; sort: number; state: number; });
}
}
// 批量添加字典数据
const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null)
const batchAdd = () => {
const params = {
title: '字典数据',
tempApi: exportDictData,
importApi: batchAddDictData,
getTableList: proTable.value?.getTableList,
}
dialogRef.value?.acceptParams(params)
}
// 导出字典数据
const downloadFile = async () => {
ElMessageBox.confirm('确认导出字典数据?', '温馨提示', { type: 'warning' }).then(() =>
useDownload(exportDictData, '字典数据列表', proTable.value?.searchParam),
)
}
// 批量删除字典数据
const batchDelete = async (id: string[]) => {
await useHandleData(deleteDictData, { id }, '删除所选字典数据')
proTable.value?.clearSelection()
proTable.value?.getTableList()
}
// 删除字典数据
const handleDelete = async (params: Dict.ResDictType) => {
await useHandleData(deleteDictData, { id: [params.id] }, `删除【${params.name}】字典数据`)
proTable.value?.getTableList()
}
const close = () => {
dialogVisible.value = false
//清空dialogForm中的值
dialogForm.value = {
id: "",
name: "",
code: "",
value: "",
sort: 1,
state: 1,
}
}
const save = async () => {
if (dialogType.value === '新增') {
await useHandleData(addDictData, dialogForm.value, '新增字典数据')
} else {
await useHandleData(updateDictData, dialogForm.value, '编辑字典数据')
}
close()
proTable.value?.getTableList()
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,224 @@
<template>
<div class="table-box">
<ProTable :columns="columns" :data="data" :request-api="getDictTypeList">
<template #tableHeader="scope">
<el-button type="primary" :icon="CirclePlus" @click="openDrawer('新增')">新增</el-button>
<el-button type="primary" :icon="Download" plain @click="downloadFile">导出</el-button>
<el-button type="danger" :icon="Delete" plain :disabled="!scope.isSelected"
@click="batchDelete(scope.selectedListIds)">
批量删除
</el-button>
</template>
<template #operation="scope">
<el-button type="primary" link :icon="View" @click="toDictData(scope.row)">查看</el-button>
<el-button type="primary" link :icon="EditPen" @click="openDrawer('编辑', scope.row)">编辑</el-button>
<el-button type="primary" link :icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</ProTable>
</div>
<el-dialog v-model="dialogVisible" :title="dialogType === '新增' ? '新增字典类型' : '编辑字典类型'" v-bind="dialogSmall">
<div>
<el-form :model="dialogForm">
<el-form-item label="类型名称" :label-width="100">
<el-input v-model="dialogForm.name" placeholder="请输入" autocomplete="off" />
</el-form-item>
<el-form-item label="类型编码" :label-width="100">
<el-input v-model="dialogForm.code" placeholder="请输入" autocomplete="off" />
</el-form-item>
<el-form-item label="显示排序" :label-width="100">
<el-input-number v-model="dialogForm.sort" :min="1" :max="100" />
</el-form-item>
<el-form-item label="状态" :label-width="100">
<el-radio-group v-model.number="dialogForm.state">
<el-radio v-for="item in dictStore.getDictData('status')" :key="item.id" :value="item.code">{{ item.label
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" :label-width="100">
<el-input v-model="dialogForm.remark" placeholder="请输入备注" autocomplete="off" />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="close()">取消</el-button>
<el-button type="primary" @click="save()">
保存
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="tsx" name="dict">
import { CirclePlus, Delete, EditPen, Download, View } from '@element-plus/icons-vue'
import { Dict } from '@/api/system/dict/interface'
import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
// import ImportExcel from '@/components/ImportExcel/index.vue'
import { dialogSmall } from '@/utils/elementBind'
import { useDictStore } from '@/stores/modules/dict'
import { useHandleData } from '@/hooks/useHandleData'
// import { useAuthButtons } from '@/hooks/useAuthButtons'
import { useDownload } from '@/hooks/useDownload'
import { useRouter } from 'vue-router'
import { dictTypeList } from '@/api/system/dict/dictExample'
import {
getDictTypeList,
addDictType,
updateDictType,
exportDictType,
deleteDictType
} from '@/api/system/dict'
const dictStore = useDictStore()
// const BUTTONS = useAuthButtons()
const router = useRouter()
const data = dictTypeList
const proTable = ref<ProTableInstance>()
const columns = reactive<ColumnProps<Dict.ResDictType>[]>([
{ type: 'selection', fixed: 'left', width: 70 },
{ type: 'index', fixed: 'left', width: 70, label: '序号' },
{
prop: 'name',
label: '类型名称',
width: 160,
search: {
el: 'input',
props: {
placeholder: '请输入字典名称'
}
}
},
{
prop: 'code',
label: '类型编码',
width: 160,
search: {
el: 'input',
props: {
placeholder: '请输入字典编码'
}
}
},
{
prop: 'state',
label: '状态',
enum: dictStore.getDictData('status'),
search: {
el: 'tree-select',
props: { filterable: true }
},
fieldNames: { label: 'label', value: 'code' },
render: scope => {
return (
<>
{
<el-tag type={scope.row.state ? 'success' : 'danger'} > {scope.row.state ? '正常' : '禁用'} </el-tag>
}
</>
)
},
},
{
prop: 'remark',
label: '描述',
width: 200,
},
{
prop: 'createTime',
label: '创建时间',
width: 180,
search: {
el: 'date-picker',
props: { type: 'daterange', valueFormat: 'YYYY-MM-DD' }
}
},
{
prop: 'operation',
label: '操作',
fixed: 'right',
width: 330
},
])
const { dialogVisible, dialogType, dialogForm } = useCount();
function useCount() {
const dialogVisible = ref(false)
const dialogType = ref('新增')
const dialogForm = ref({
id: "",
name: "",
code: "",
sort: 1,
state: 1, // 状态0-删除 1-正常
remark: ""
})
return { dialogVisible, dialogType, dialogForm };
}
// 打开 drawer(新增、查看、编辑)
const openDrawer = (title: string, row: Partial<Dict.ResDictType> = {}) => {
dialogVisible.value = true
dialogType.value = title
if (title === '编辑') {
row && (dialogForm.value = row as { id: string; name: string; code: string; sort: number; state: number; remark: string; });
}
}
// 导出字典类型
const downloadFile = async () => {
ElMessageBox.confirm('确认导出字典类型数据?', '温馨提示', { type: 'warning' }).then(() =>
useDownload(exportDictType, '字典类型列表', proTable.value?.searchParam),
)
}
// 批量删除字典类型
const batchDelete = async (id: string[]) => {
await useHandleData(deleteDictType, { id }, '删除所选字典类型')
proTable.value?.clearSelection()
proTable.value?.getTableList()
}
// 删除字典类型
const handleDelete = async (params: Dict.ResDictType) => {
await useHandleData(deleteDictType, { id: [params.id] }, `删除【${params.name}】字典类型`)
proTable.value?.getTableList()
}
//查看字典类型包含的字典数据
const toDictData = (row: Dict.ResDictType) => {
router.push({ name: 'dictData', params: { id: row.id, code: row.code } })
}
const close = () => {
dialogVisible.value = false
//清空dialogForm中的值
dialogForm.value = {
id: "",
name: "",
code: "",
state: 1,
sort: 1,
remark: ""
}
}
const save =async () => {
if (dialogType.value === '新增') {
await useHandleData(addDictType, dialogForm.value, '新增字典类型')
} else {
await useHandleData(updateDictType, dialogForm.value, '编辑字典类型')
}
close()
proTable.value?.getTableList()
}
</script>
<style lang="scss" scoped></style>