字典、配置、websocket
This commit is contained in:
parent
aa8920f487
commit
05dd125357
16
src/App.vue
16
src/App.vue
@ -27,24 +27,29 @@
|
|||||||
:y-offset="60"
|
:y-offset="60"
|
||||||
:rotate="-15"
|
:rotate="-15"
|
||||||
/>
|
/>
|
||||||
|
<global-websocket :uri="'/api/websocket/' + userInfo.id" @rollback="rollback" />
|
||||||
</NConfigProvider>
|
</NConfigProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted } from 'vue';
|
import { computed, onMounted, onUnmounted, defineAsyncComponent, h } from 'vue';
|
||||||
import { darkTheme, dateZhCN, zhCN } from 'naive-ui';
|
import { darkTheme, dateZhCN, zhCN } from 'naive-ui';
|
||||||
import { LockScreen } from '@/components/Lockscreen';
|
import { LockScreen } from '@/components/Lockscreen';
|
||||||
import { AppProvider } from '@/components/Application';
|
import { AppProvider } from '@/components/Application';
|
||||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
||||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
|
import { initWebSocket, sendWebSocket } from '@/components/Websocket/index';
|
||||||
|
const GlobalWebsocket = defineAsyncComponent(() => import('@/components/Websocket/index.vue'));
|
||||||
import { lighten } from '@/utils';
|
import { lighten } from '@/utils';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const useLockscreen = useLockscreenStore();
|
const useLockscreen = useLockscreenStore();
|
||||||
const designStore = useDesignSettingStore();
|
const designStore = useDesignSettingStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const userInfo: object = userStore.getUserInfo || {};
|
||||||
const isLock = computed(() => useLockscreen.isLock);
|
const isLock = computed(() => useLockscreen.isLock);
|
||||||
const lockTime = computed(() => useLockscreen.lockTime);
|
const lockTime = computed(() => useLockscreen.lockTime);
|
||||||
|
|
||||||
@ -95,6 +100,13 @@
|
|||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
const rollback = (msg) => {
|
||||||
|
$notification.info({
|
||||||
|
title: '通知',
|
||||||
|
content: () => h('div', msg),
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// document.addEventListener('mousedown', timekeeping);
|
// document.addEventListener('mousedown', timekeeping);
|
||||||
|
|||||||
155
src/components/Websocket/index.vue
Normal file
155
src/components/Websocket/index.vue
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="global-websocket">
|
||||||
|
import { reactive, ref, computed,onMounted,onUnmounted } from 'vue';
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
|
||||||
|
const emit = defineEmits(['rollback']);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
uri: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
webSocket: ref(), // webSocket实例
|
||||||
|
lockReconnect: false, // 重连锁,避免多次重连
|
||||||
|
maxReconnect: 6, // 最大重连次数, -1 标识无限重连
|
||||||
|
reconnectTime: 0, // 重连尝试次数
|
||||||
|
heartbeat: {
|
||||||
|
interval: 30 * 1000, // 心跳间隔时间
|
||||||
|
timeout: 10 * 1000, // 响应超时时间
|
||||||
|
pingTimeoutObj: ref(), // 延时发送心跳的定时器
|
||||||
|
pongTimeoutObj: ref(), // 接收心跳响应的定时器
|
||||||
|
pingMessage: JSON.stringify({ type: 'ping' }), // 心跳请求信息
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = computed(() => {
|
||||||
|
return useUserStore().getToken;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tenant = computed(() => {
|
||||||
|
return Session.getTenant();
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initWebSocket();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
state.webSocket.close();
|
||||||
|
clearTimeoutObj(state.heartbeat);
|
||||||
|
});
|
||||||
|
|
||||||
|
const initWebSocket = () => {
|
||||||
|
// ws地址
|
||||||
|
let host = window.location.host;
|
||||||
|
let wsUri =`${location.protocol === 'https:' ? 'wss' : 'ws'}://${host}${props.uri}`;
|
||||||
|
|
||||||
|
// 建立连接
|
||||||
|
state.webSocket = new WebSocket(wsUri);
|
||||||
|
// 连接成功
|
||||||
|
state.webSocket.onopen = onOpen;
|
||||||
|
// 连接错误
|
||||||
|
state.webSocket.onerror = onError;
|
||||||
|
// 接收信息
|
||||||
|
state.webSocket.onmessage = onMessage;
|
||||||
|
// 连接关闭
|
||||||
|
state.webSocket.onclose = onClose;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reconnect = () => {
|
||||||
|
if (!token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.lockReconnect || (state.maxReconnect !== -1 && state.reconnectTime > state.maxReconnect)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.lockReconnect = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
state.reconnectTime++;
|
||||||
|
// 建立新连接
|
||||||
|
initWebSocket();
|
||||||
|
state.lockReconnect = false;
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 清空定时器
|
||||||
|
*/
|
||||||
|
const clearTimeoutObj = (heartbeat: any) => {
|
||||||
|
heartbeat.pingTimeoutObj && clearTimeout(heartbeat.pingTimeoutObj);
|
||||||
|
heartbeat.pongTimeoutObj && clearTimeout(heartbeat.pongTimeoutObj);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 开启心跳
|
||||||
|
*/
|
||||||
|
const startHeartbeat = () => {
|
||||||
|
const webSocket = state.webSocket;
|
||||||
|
const heartbeat = state.heartbeat;
|
||||||
|
// 清空定时器
|
||||||
|
clearTimeoutObj(heartbeat);
|
||||||
|
// 延时发送下一次心跳
|
||||||
|
heartbeat.pingTimeoutObj = setTimeout(() => {
|
||||||
|
// 如果连接正常
|
||||||
|
if (webSocket.readyState === 1) {
|
||||||
|
//这里发送一个心跳,后端收到后,返回一个心跳消息,
|
||||||
|
webSocket.send(heartbeat.pingMessage);
|
||||||
|
// 心跳发送后,如果服务器超时未响应则断开,如果响应了会被重置心跳定时器
|
||||||
|
heartbeat.pongTimeoutObj = setTimeout(() => {
|
||||||
|
webSocket.close();
|
||||||
|
}, heartbeat.timeout);
|
||||||
|
} else {
|
||||||
|
// 否则重连
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
|
}, heartbeat.interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接成功事件
|
||||||
|
*/
|
||||||
|
const onOpen = () => {
|
||||||
|
//开启心跳
|
||||||
|
startHeartbeat();
|
||||||
|
state.reconnectTime = 0;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 连接失败事件
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
const onError = () => {
|
||||||
|
//重连
|
||||||
|
reconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接关闭事件
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
const onClose = () => {
|
||||||
|
//重连
|
||||||
|
reconnect();
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 接收服务器推送的信息
|
||||||
|
* @param msgEvent
|
||||||
|
*/
|
||||||
|
const onMessage = (msgEvent: any) => {
|
||||||
|
//收到服务器信息,心跳重置并发送
|
||||||
|
startHeartbeat();
|
||||||
|
// if (msgEvent.data.indexOf('pong') > 0) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// let text =''
|
||||||
|
// try{
|
||||||
|
// text = JSON.parse(msgEvent.data);
|
||||||
|
// }catch(e){
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
emit('rollback', msgEvent.data);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -6,12 +6,6 @@ export const columns = [
|
|||||||
width: 50,
|
width: 50,
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'ID',
|
|
||||||
key: 'id',
|
|
||||||
fixed: 'left',
|
|
||||||
width: 50,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '配置名称',
|
title: '配置名称',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<n-grid x-gap="12" cols="1 s:1 m:1 l:24 xl:24 2xl:24" responsive="screen">
|
<n-grid x-gap="12" cols="1 s:1 m:1 l:24 xl:24 2xl:24" responsive="screen">
|
||||||
<n-grid-item span="7">
|
<n-grid-item span="8">
|
||||||
<n-card shadow="hover" class="border-0">
|
<n-card shadow="hover" class="border-0">
|
||||||
<template #header>
|
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-input
|
<n-input
|
||||||
type="text"
|
type="text"
|
||||||
@ -11,13 +10,7 @@
|
|||||||
placeholder="请输入配置名称"
|
placeholder="请输入配置名称"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
<n-button
|
<n-button type="primary" @click="reloadTable">
|
||||||
type="primary"
|
|
||||||
@click="
|
|
||||||
pager.page = 1;
|
|
||||||
loadDataTable();
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon>
|
<n-icon>
|
||||||
<SearchOutlined />
|
<SearchOutlined />
|
||||||
@ -35,14 +28,12 @@
|
|||||||
</template>
|
</template>
|
||||||
新建
|
新建
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button type="warning" @click="handleEdit" v-perm="['sys:config:edit']">
|
<n-button
|
||||||
<template #icon>
|
type="error"
|
||||||
<n-icon>
|
@click="handleDelete()"
|
||||||
<FormOutlined />
|
v-perm="['sys:config:delete']"
|
||||||
</n-icon> </template
|
:disabled="!selectionData.length"
|
||||||
>编辑
|
>
|
||||||
</n-button>
|
|
||||||
<n-button type="error" @click="handleDelete()" v-perm="['sys:config:delete']">
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon>
|
<n-icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@ -51,31 +42,23 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<BasicTable
|
||||||
<template #default>
|
:columns="columns"
|
||||||
<div :style="{ height: fwbHeight + 'px' }" class="dict-list-box">
|
:actionColumn="actionColumn"
|
||||||
<div
|
:request="loadDataTable"
|
||||||
v-for="(item, index) in configDataList"
|
:row-key="(row) => row.id"
|
||||||
:key="index"
|
ref="tableRef"
|
||||||
@click="onCheckedRow(item)"
|
:showTableSetting="false"
|
||||||
class="dict-item"
|
@update:checked-row-keys="onSelectionChange"
|
||||||
:class="item.id == configId ? 'active' : ''"
|
:pagination="{ showQuickJumper: false, showSizePicker: false }"
|
||||||
|
:row-props="rowProps"
|
||||||
|
:row-class-name="getRowClassName"
|
||||||
|
:autoScrollX="true"
|
||||||
>
|
>
|
||||||
<span class="t1"
|
</BasicTable>
|
||||||
>{{ item.name }}<span class="t2">({{ item.code }})</span></span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<pagination
|
|
||||||
style="justify-content: flex-end"
|
|
||||||
class="mt-10 flex"
|
|
||||||
@change="loadDataTable"
|
|
||||||
v-model="pager"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item span="17">
|
<n-grid-item span="16">
|
||||||
<n-card shadow="hover" class="mb-4 border-0 proCard">
|
<n-card shadow="hover" class="mb-4 border-0 proCard">
|
||||||
<configItem :configId="configId" v-if="configItemShow" />
|
<configItem :configId="configId" v-if="configItemShow" />
|
||||||
</n-card>
|
</n-card>
|
||||||
@ -86,23 +69,27 @@
|
|||||||
:configId="configId"
|
:configId="configId"
|
||||||
v-model:visible="editVisible"
|
v-model:visible="editVisible"
|
||||||
ref="createModalRef"
|
ref="createModalRef"
|
||||||
@success="loadDataTable()"
|
@success="reloadTable('noRefresh')"
|
||||||
/>
|
/>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, nextTick, defineAsyncComponent, onMounted } from 'vue';
|
import { ref, nextTick, defineAsyncComponent, onMounted, reactive, h } from 'vue';
|
||||||
import { getConfigList, configDelete } from '@/api/data/config';
|
import { getConfigList, configDelete } from '@/api/data/config';
|
||||||
import { PlusOutlined, FormOutlined, DeleteOutlined, SearchOutlined } from '@vicons/antd';
|
import { PlusOutlined, FormOutlined, DeleteOutlined, SearchOutlined } from '@vicons/antd';
|
||||||
import editDialog from './edit.vue';
|
import editDialog from './edit.vue';
|
||||||
import configItem from './configItem.vue';
|
import configItem from './configItem.vue';
|
||||||
|
import { TableAction } from '@/components/Table';
|
||||||
|
import { columns } from './columns';
|
||||||
|
import { renderIcon } from '@/utils';
|
||||||
import { useMessage, useDialog } from 'naive-ui';
|
import { useMessage, useDialog } from 'naive-ui';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义参数变量
|
* 定义参数变量
|
||||||
*/
|
*/
|
||||||
const configId = ref(0);
|
const configId = ref(0);
|
||||||
|
const tableRef = ref();
|
||||||
const createModalRef = ref();
|
const createModalRef = ref();
|
||||||
const configItemShow = ref(false);
|
const configItemShow = ref(false);
|
||||||
const editVisible = ref(false);
|
const editVisible = ref(false);
|
||||||
@ -115,18 +102,47 @@
|
|||||||
const params = ref({
|
const params = ref({
|
||||||
name: '',
|
name: '',
|
||||||
});
|
});
|
||||||
const configDataList = ref([]);
|
const selectionData = ref([]);
|
||||||
|
|
||||||
|
const actionColumn = reactive({
|
||||||
|
width: 200,
|
||||||
|
title: '操作',
|
||||||
|
align: 'center',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
render(record) {
|
||||||
|
return h(TableAction as any, {
|
||||||
|
style: 'button',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
icon: renderIcon(FormOutlined),
|
||||||
|
type: 'warning',
|
||||||
|
auth: ['sys:config:update'],
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
icon: renderIcon(DeleteOutlined),
|
||||||
|
type: 'error',
|
||||||
|
auth: ['sys:config:delete'],
|
||||||
|
onClick: handleDelete.bind(null, record),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
select: (key) => {
|
||||||
|
message.info(`您点击了,${key} 按钮`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义分页参数
|
* 刷新配置项值列表
|
||||||
|
* @param noRefresh 参数
|
||||||
*/
|
*/
|
||||||
const pager = ref({
|
function reloadTable(noRefresh = '') {
|
||||||
page: 1,
|
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||||
size: 10,
|
}
|
||||||
count: configDataList.value.length,
|
|
||||||
});
|
|
||||||
const fwbHeight = document.body.clientHeight - 335;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行添加
|
* 执行添加
|
||||||
*/
|
*/
|
||||||
@ -140,52 +156,58 @@
|
|||||||
/**
|
/**
|
||||||
* 执行编辑
|
* 执行编辑
|
||||||
*/
|
*/
|
||||||
const handleEdit = async () => {
|
const handleEdit = async (row) => {
|
||||||
|
configId.value = row.id;
|
||||||
editVisible.value = true;
|
editVisible.value = true;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
createModalRef.value.openModal();
|
createModalRef.value.openModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据行选中事件
|
* 选项发生变化
|
||||||
* @param row 参数
|
* @param value 参数
|
||||||
*/
|
*/
|
||||||
function onCheckedRow(row) {
|
function onSelectionChange(value) {
|
||||||
configId.value = row.id;
|
selectionData.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载数据列表
|
* 加载数据列表
|
||||||
*/
|
*/
|
||||||
const loadDataTable = async () => {
|
const loadDataTable = async (res) => {
|
||||||
let result = await getConfigList({
|
const result = await getConfigList({ ...params.value, ...res });
|
||||||
...params.value,
|
|
||||||
pageNo: pager.value.page,
|
|
||||||
pageSize: pager.value.size,
|
|
||||||
});
|
|
||||||
configId.value = result?.records[0]?.id;
|
configId.value = result?.records[0]?.id;
|
||||||
configItemShow.value = true;
|
configItemShow.value = true;
|
||||||
configDataList.value = result.records;
|
return result;
|
||||||
pager.value.count = result.total;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行删除
|
* 执行删除
|
||||||
*/
|
*/
|
||||||
async function handleDelete() {
|
async function handleDelete(row) {
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确定要删除?',
|
content: '确定要删除?',
|
||||||
positiveText: '确定',
|
positiveText: '确定',
|
||||||
negativeText: '取消',
|
negativeText: '取消',
|
||||||
onPositiveClick: async () => {
|
onPositiveClick: async () => {
|
||||||
configDelete(configId.value);
|
row ? await configDelete(row.id) : await configBatchDelete(selectionData.value);
|
||||||
message.success('删除成功');
|
message.success('删除成功');
|
||||||
pager.value.page = 1;
|
reloadTable();
|
||||||
loadDataTable();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const getRowClassName = (row) => {
|
||||||
|
return configId.value === row.id ? 'clickRowStyle' : '';
|
||||||
|
};
|
||||||
|
const rowProps = (row) => {
|
||||||
|
return {
|
||||||
|
style: 'cursor: pointer;',
|
||||||
|
onClick: () => {
|
||||||
|
configId.value = row.id;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 钩子函数
|
* 钩子函数
|
||||||
@ -243,3 +265,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<style lang="less">
|
||||||
|
.n-data-table-tbody {
|
||||||
|
.n-data-table-tr.clickRowStyle,
|
||||||
|
.n-data-table-tr.clickRowStyle {
|
||||||
|
td {
|
||||||
|
background-color: #e7eeff !important;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
td {
|
||||||
|
background-color: #e7eeff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -6,16 +6,14 @@ export const columns = [
|
|||||||
width: 50,
|
width: 50,
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'ID',
|
|
||||||
key: 'id',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '字典名称',
|
title: '字典名称',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '字典编码',
|
title: '字典编码',
|
||||||
key: 'code',
|
key: 'code',
|
||||||
|
width: 100,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<n-grid x-gap="12" cols="1 s:1 m:1 l:24 xl:24 2xl:24" responsive="screen">
|
<n-grid x-gap="12" cols="1 s:1 m:1 l:24 xl:24 2xl:24" responsive="screen">
|
||||||
<n-grid-item span="7">
|
<n-grid-item span="8">
|
||||||
<n-card shadow="hover" class="border-0" size="small">
|
<n-card shadow="hover" class="border-0" size="small">
|
||||||
<template #header>
|
|
||||||
<n-space :size="4">
|
<n-space :size="4">
|
||||||
<n-input
|
<n-input
|
||||||
type="text"
|
type="text"
|
||||||
@ -11,25 +10,19 @@
|
|||||||
placeholder="请输入字典名称"
|
placeholder="请输入字典名称"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
<n-button
|
<n-button type="primary" @click="reloadTable">
|
||||||
type="primary"
|
|
||||||
@click="
|
|
||||||
pager.page = 1;
|
|
||||||
loadDataTable();
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon>
|
<n-icon>
|
||||||
<SearchOutlined />
|
<SearchOutlined />
|
||||||
</n-icon> </template
|
</n-icon> </template
|
||||||
>查询
|
>查询
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button type="primary" @click="dictRefresh" v-perm="['sys:dict:cache']">
|
|
||||||
<template #icon> <RedoOutlined /> </template>刷新缓存</n-button
|
|
||||||
>
|
|
||||||
</n-space>
|
</n-space>
|
||||||
<div style="margin-top: 15px">
|
<div style="margin-top: 15px">
|
||||||
<n-space>
|
<n-space>
|
||||||
|
<n-button type="primary" @click="dictRefresh" v-perm="['sys:dict:cache']">
|
||||||
|
<template #icon> <RedoOutlined /> </template>刷新缓存</n-button
|
||||||
|
>
|
||||||
<n-button type="primary" @click="handleAdd" v-perm="['sys:dict:add']">
|
<n-button type="primary" @click="handleAdd" v-perm="['sys:dict:add']">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon>
|
<n-icon>
|
||||||
@ -38,14 +31,12 @@
|
|||||||
</template>
|
</template>
|
||||||
新建
|
新建
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button type="warning" @click="handleEdit" v-perm="['sys:dict:edit']">
|
<n-button
|
||||||
<template #icon>
|
type="error"
|
||||||
<n-icon>
|
@click="handleDelete()"
|
||||||
<FormOutlined />
|
v-perm="['sys:dict:delete']"
|
||||||
</n-icon> </template
|
:disabled="!selectionData.length"
|
||||||
>编辑
|
>
|
||||||
</n-button>
|
|
||||||
<n-button type="error" @click="handleDelete()" v-perm="['sys:dict:delete']">
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon>
|
<n-icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@ -54,31 +45,23 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<BasicTable
|
||||||
<template #default>
|
:columns="columns"
|
||||||
<div :style="{ height: fwbHeight + 'px' }" class="dict-list-box">
|
:actionColumn="actionColumn"
|
||||||
<div
|
:request="loadDataTable"
|
||||||
v-for="(item, index) in dictDataList"
|
:row-key="(row) => row.id"
|
||||||
:key="index"
|
ref="tableRef"
|
||||||
@click="onCheckedRow(item)"
|
:showTableSetting="false"
|
||||||
class="dict-item"
|
@update:checked-row-keys="onSelectionChange"
|
||||||
:class="item.id == dictId ? 'active' : ''"
|
:pagination="{ showQuickJumper: false, showSizePicker: false }"
|
||||||
|
:row-props="rowProps"
|
||||||
|
:row-class-name="getRowClassName"
|
||||||
|
:autoScrollX="true"
|
||||||
>
|
>
|
||||||
<span class="t1"
|
</BasicTable>
|
||||||
>{{ item.name }}<span class="t2">({{ item.code }})</span></span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<pagination
|
|
||||||
style="justify-content: flex-end"
|
|
||||||
class="mt-10 flex"
|
|
||||||
@change="loadDataTable"
|
|
||||||
v-model="pager"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item span="17">
|
<n-grid-item span="16">
|
||||||
<n-card shadow="hover" class="border-0 proCard" size="small">
|
<n-card shadow="hover" class="border-0 proCard" size="small">
|
||||||
<dictItem :dictId="dictId" v-if="dictItemShow" />
|
<dictItem :dictId="dictId" v-if="dictItemShow" />
|
||||||
</n-card>
|
</n-card>
|
||||||
@ -89,13 +72,13 @@
|
|||||||
:dictId="dictId"
|
:dictId="dictId"
|
||||||
v-model:visible="editVisible"
|
v-model:visible="editVisible"
|
||||||
ref="createModalRef"
|
ref="createModalRef"
|
||||||
@success="loadDataTable()"
|
@success="reloadTable('noRefresh')"
|
||||||
/>
|
/>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, nextTick, defineAsyncComponent, onMounted } from 'vue';
|
import { ref, nextTick, defineAsyncComponent, onMounted, reactive, h } from 'vue';
|
||||||
import { getDictList, refreshCache, dictDelete } from '@/api/data/dictionary';
|
import { getDictList, refreshCache, dictDelete } from '@/api/data/dictionary';
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
@ -104,37 +87,71 @@
|
|||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
RedoOutlined,
|
RedoOutlined,
|
||||||
} from '@vicons/antd';
|
} from '@vicons/antd';
|
||||||
|
import { TableAction } from '@/components/Table';
|
||||||
|
import { columns } from './columns';
|
||||||
import editDialog from './edit.vue';
|
import editDialog from './edit.vue';
|
||||||
import dictItem from './dictItem.vue';
|
import dictItem from './dictItem.vue';
|
||||||
|
import { renderIcon } from '@/utils';
|
||||||
import { useMessage, useDialog } from 'naive-ui';
|
import { useMessage, useDialog } from 'naive-ui';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义参数变量
|
* 定义参数变量
|
||||||
*/
|
*/
|
||||||
const dictId = ref(0);
|
const dictId = ref(0);
|
||||||
|
const tableRef = ref();
|
||||||
const createModalRef = ref();
|
const createModalRef = ref();
|
||||||
const dictItemShow = ref(false);
|
const dictItemShow = ref(false);
|
||||||
const editVisible = ref(false);
|
const editVisible = ref(false);
|
||||||
|
const selectionData = ref([]);
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
|
|
||||||
|
const actionColumn = reactive({
|
||||||
|
width: 200,
|
||||||
|
title: '操作',
|
||||||
|
align: 'center',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
render(record) {
|
||||||
|
return h(TableAction as any, {
|
||||||
|
style: 'button',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
icon: renderIcon(FormOutlined),
|
||||||
|
type: 'warning',
|
||||||
|
auth: ['sys:dict:update'],
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
icon: renderIcon(DeleteOutlined),
|
||||||
|
type: 'error',
|
||||||
|
auth: ['sys:dict:delete'],
|
||||||
|
onClick: handleDelete.bind(null, record),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
select: (key) => {
|
||||||
|
message.info(`您点击了,${key} 按钮`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义查询参数
|
* 定义查询参数
|
||||||
*/
|
*/
|
||||||
const params = ref({
|
const params = ref({
|
||||||
name: '',
|
name: '',
|
||||||
});
|
});
|
||||||
const dictDataList = ref([]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义分页参数
|
* 刷新配置项值列表
|
||||||
|
* @param noRefresh 参数
|
||||||
*/
|
*/
|
||||||
const pager = ref({
|
function reloadTable(noRefresh = '') {
|
||||||
page: 1,
|
tableRef.value.reload(noRefresh ? {} : { pageNo: 1 });
|
||||||
size: 10,
|
}
|
||||||
count: dictDataList.value.length,
|
|
||||||
});
|
|
||||||
const fwbHeight = document.body.clientHeight - 335;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行添加
|
* 执行添加
|
||||||
@ -149,7 +166,8 @@
|
|||||||
/**
|
/**
|
||||||
* 执行编辑
|
* 执行编辑
|
||||||
*/
|
*/
|
||||||
const handleEdit = async () => {
|
const handleEdit = async (row) => {
|
||||||
|
dictId.value = row.id;
|
||||||
editVisible.value = true;
|
editVisible.value = true;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
createModalRef.value.openModal();
|
createModalRef.value.openModal();
|
||||||
@ -163,46 +181,50 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据行选中事件
|
* 选项发生变化
|
||||||
* @param row 参数
|
* @param value 参数
|
||||||
*/
|
*/
|
||||||
function onCheckedRow(row) {
|
function onSelectionChange(value) {
|
||||||
dictId.value = row.id;
|
selectionData.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载数据列表
|
* 加载数据列表
|
||||||
*/
|
*/
|
||||||
const loadDataTable = async () => {
|
const loadDataTable = async (res) => {
|
||||||
let result = await getDictList({
|
const result = await getDictList({ ...params.value, ...res });
|
||||||
...params.value,
|
|
||||||
pageNo: pager.value.page,
|
|
||||||
pageSize: pager.value.size,
|
|
||||||
});
|
|
||||||
dictId.value = result?.records[0]?.id;
|
dictId.value = result?.records[0]?.id;
|
||||||
dictItemShow.value = true;
|
dictItemShow.value = true;
|
||||||
dictDataList.value = result.records;
|
return result;
|
||||||
pager.value.count = result.total;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行删除
|
* 执行删除
|
||||||
*/
|
*/
|
||||||
async function handleDelete() {
|
async function handleDelete(row) {
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确定要删除?',
|
content: '确定要删除?',
|
||||||
positiveText: '确定',
|
positiveText: '确定',
|
||||||
negativeText: '取消',
|
negativeText: '取消',
|
||||||
onPositiveClick: async () => {
|
onPositiveClick: async () => {
|
||||||
dictDelete(dictId.value);
|
row ? await dictDelete(row.id) : await dictBatchDelete(selectionData.value);
|
||||||
message.success('删除成功');
|
message.success('删除成功');
|
||||||
pager.value.page = 1;
|
reloadTable();
|
||||||
loadDataTable();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const getRowClassName = (row) => {
|
||||||
|
return dictId.value === row.id ? 'clickRowStyle' : '';
|
||||||
|
};
|
||||||
|
const rowProps = (row) => {
|
||||||
|
return {
|
||||||
|
style: 'cursor: pointer;',
|
||||||
|
onClick: () => {
|
||||||
|
dictId.value = row.id;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 钩子函数
|
* 钩子函数
|
||||||
*/
|
*/
|
||||||
@ -259,3 +281,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<style lang="less">
|
||||||
|
.n-data-table-tbody {
|
||||||
|
.n-data-table-tr.clickRowStyle,
|
||||||
|
.n-data-table-tr.clickRowStyle {
|
||||||
|
td {
|
||||||
|
background-color: #e7eeff !important;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
td {
|
||||||
|
background-color: #e7eeff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user