Merge branch 'dev' into dev-hub
This commit is contained in:
commit
97c6d391fb
|
@ -1,18 +1,27 @@
|
||||||
import server from '@/utils/request';
|
import server from '@/utils/request';
|
||||||
|
|
||||||
// 获取部门数据
|
// 获取部门数据
|
||||||
export const getTreeData_api = (data:object) => server.post(`/organization/_all/tree`, data);
|
export const getTreeData_api = (data: object) => server.post(`/organization/_all/tree`, data);
|
||||||
// 新增部门
|
// 新增部门
|
||||||
export const addDepartment_api = (data:object) => server.post(`/organization`, data);
|
export const addDepartment_api = (data: object) => server.post(`/organization`, data);
|
||||||
// 更新部门
|
// 更新部门
|
||||||
export const updateDepartment_api = (data:object) => server.patch(`/organization`, data);
|
export const updateDepartment_api = (data: object) => server.patch(`/organization`, data);
|
||||||
// 删除部门
|
// 删除部门
|
||||||
export const delDepartment_api = (id:string) => server.remove(`/organization/${id}`);
|
export const delDepartment_api = (id: string) => server.remove(`/organization/${id}`);
|
||||||
|
|
||||||
|
|
||||||
// 获取产品列表
|
// 获取产品列表
|
||||||
export const getDeviceOrProductList_api = (data:object) => server.post(`/device-product/_query`, data);
|
export const getDeviceOrProductList_api = (data: object) => server.post(`/device-product/_query`, data);
|
||||||
|
// 获取设备列表
|
||||||
|
export const getDeviceList_api = (data: object) => server.post(`/device/instance/_query`, data);
|
||||||
// 根据产品的id获取产品的权限
|
// 根据产品的id获取产品的权限
|
||||||
export const getPermission_api = (ids:object, id:string) => server.post(`/assets/bindings/product/org/${id}/_query`, ids);
|
export const getPermission_api = (type:'device' | 'product',ids: object, id: string) => server.post(`/assets/bindings/${type}/org/${id}/_query`, ids);
|
||||||
// 获取产品的权限字典
|
// 获取产品的权限字典
|
||||||
export const getPermissionDict_api = () => server.get(`/assets/bindings/product/permissions`);
|
export const getPermissionDict_api = () => server.get(`/assets/bindings/product/permissions`);
|
||||||
|
|
||||||
|
// 部门绑定产品
|
||||||
|
export const bindDeviceOrProductList_api = (type: 'device' | 'product', data: object) => server.post(`/assets/bind/${type}`, data);
|
||||||
|
// 批量解绑
|
||||||
|
export const unBindDeviceOrProduct_api = (type: 'device' | 'product', data: object) => server.post(`/assets/unbind/${type}`, data);
|
||||||
|
// 批量更新权限
|
||||||
|
export const updatePermission_api = (type: 'device' | 'product', parentId: string, data: object) => server.put(`/assets/permission/${type}/org/${parentId}/_batch`, data);
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<div style="width: 100%; height: 400px">
|
||||||
|
<el-amap
|
||||||
|
>
|
||||||
|
</el-amap>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { initAMapApiLoader } from '@vuemap/vue-amap';
|
||||||
|
import '@vuemap/vue-amap/dist/style.css';
|
||||||
|
initAMapApiLoader({
|
||||||
|
// key: '95fa72137f4263f8e64ae01f766ad09c',
|
||||||
|
key: 'a0415acfc35af15f10221bfa5a6850b4',
|
||||||
|
securityJsCode: 'cae6108ec3dd222f946d1a7237c78be0',
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,58 @@
|
||||||
|
<template>
|
||||||
|
<div class="home-title">
|
||||||
|
<div v-if="title">{{ title }}</div>
|
||||||
|
<div v-else>
|
||||||
|
<slot name="title"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="extra-text">
|
||||||
|
<slot name="extra"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="home-title-english">{{ english }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="Guide">
|
||||||
|
interface guideProps {
|
||||||
|
title?: string;
|
||||||
|
english?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<guideProps>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.home-title {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-left: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background-color: @primary-color;
|
||||||
|
border: 1px solid #b4c0da;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-title-english {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
color: rgba(0, 0, 0, 0.3);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,112 @@
|
||||||
|
<template>
|
||||||
|
<div class="chart" ref="chart"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
const { proxy } = <any>getCurrentInstance();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 图表数据
|
||||||
|
x: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
maxY:{
|
||||||
|
type:Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制图表
|
||||||
|
*/
|
||||||
|
const createChart = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
const myChart = echarts.init(proxy.$refs.chart);
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: props.x,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: '{b0}<br />{a0}: {c0}',
|
||||||
|
// formatter: '{b0}<br />{a0}: {c0}<br />{a1}: {c1}%'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: '2%',
|
||||||
|
bottom: '5%',
|
||||||
|
left: props.maxY > 100000 ? '90px' : '50px',
|
||||||
|
right: '50px',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '消息量',
|
||||||
|
data: props.y,
|
||||||
|
type: 'bar',
|
||||||
|
// type: 'line',
|
||||||
|
// smooth: true,
|
||||||
|
color: '#597EF7',
|
||||||
|
barWidth: '30%',
|
||||||
|
// areaStyle: {
|
||||||
|
// color: {
|
||||||
|
// type: 'linear',
|
||||||
|
// x: 0,
|
||||||
|
// y: 0,
|
||||||
|
// x2: 0,
|
||||||
|
// y2: 1,
|
||||||
|
// colorStops: [
|
||||||
|
// {
|
||||||
|
// offset: 0,
|
||||||
|
// color: '#685DEB', // 100% 处的颜色
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// offset: 1,
|
||||||
|
// color: '#FFFFFF', // 0% 处的颜色
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// global: false, // 缺省为 false
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '占比',
|
||||||
|
data: props.y,
|
||||||
|
// data: percentageY,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbolSize: 0, // 拐点大小
|
||||||
|
color: '#96ECE3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
myChart.setOption(options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.y,
|
||||||
|
() => createChart(),
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-radio-group
|
||||||
|
v-if="quickBtn"
|
||||||
|
default-value="today"
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="radioValue"
|
||||||
|
@change="(e) => handleBtnChange(e.target.value)"
|
||||||
|
>
|
||||||
|
<a-radio-button
|
||||||
|
v-for="item in quickBtnList"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
<a-range-picker
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="margin-left: 12px"
|
||||||
|
@change="rangeChange"
|
||||||
|
v-model:value="rangeVal"
|
||||||
|
:allowClear="false"
|
||||||
|
>
|
||||||
|
</a-range-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import moment from 'moment';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
interface BtnOptions {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EmitProps {
|
||||||
|
(e: 'change', data: Record<string, any>): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<EmitProps>();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 显示快捷按钮
|
||||||
|
quickBtn: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// 快捷按钮列表
|
||||||
|
quickBtnList: {
|
||||||
|
type: Array as PropType<BtnOptions[]>,
|
||||||
|
default: [
|
||||||
|
{ label: '今日', value: 'today' },
|
||||||
|
{ label: '近一周', value: 'week' },
|
||||||
|
{ label: '近一月', value: 'month' },
|
||||||
|
{ label: '近一年', value: 'year' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'today',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const radioValue = ref(props.type || 'week' || undefined);
|
||||||
|
const rangeVal = ref<[string, string]>();
|
||||||
|
|
||||||
|
const rangeChange = (val: any) => {
|
||||||
|
radioValue.value = undefined;
|
||||||
|
emit('change', {
|
||||||
|
start: moment(val[0]).valueOf(),
|
||||||
|
end: moment(val[1]).valueOf(),
|
||||||
|
type: undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTimeByType = (type: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'hour':
|
||||||
|
return moment().subtract(1, 'hours').valueOf();
|
||||||
|
case 'week':
|
||||||
|
return moment().subtract(6, 'days').valueOf();
|
||||||
|
case 'month':
|
||||||
|
return moment().subtract(29, 'days').valueOf();
|
||||||
|
case 'year':
|
||||||
|
return moment().subtract(365, 'days').valueOf();
|
||||||
|
default:
|
||||||
|
return moment().startOf('day').valueOf();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBtnChange = (val: string) => {
|
||||||
|
radioValue.value = val;
|
||||||
|
let endTime = moment(new Date()).valueOf();
|
||||||
|
let startTime = getTimeByType(val);
|
||||||
|
if (val === 'yesterday') {
|
||||||
|
startTime = moment().subtract(1, 'days').startOf('day').valueOf();
|
||||||
|
endTime = moment().subtract(1, 'days').endOf('day').valueOf();
|
||||||
|
}
|
||||||
|
rangeVal.value = [
|
||||||
|
moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
];
|
||||||
|
emit('change', {
|
||||||
|
start: startTime,
|
||||||
|
end: endTime,
|
||||||
|
type: val,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
handleBtnChange(radioValue.value);
|
||||||
|
watch(
|
||||||
|
() => radioValue.value,
|
||||||
|
{ deep: true, immediate: true },
|
||||||
|
);
|
||||||
|
</script>
|
|
@ -24,7 +24,10 @@
|
||||||
:footer="onlineFooter"
|
:footer="onlineFooter"
|
||||||
:value="onlineToday"
|
:value="onlineToday"
|
||||||
>
|
>
|
||||||
<BarChart :chartXData="barChartXData" :chartYData="barChartYData"></BarChart> </TopCard
|
<BarChart
|
||||||
|
:chartXData="barChartXData"
|
||||||
|
:chartYData="barChartYData"
|
||||||
|
></BarChart> </TopCard
|
||||||
></a-col>
|
></a-col>
|
||||||
<a-col :span="6"
|
<a-col :span="6"
|
||||||
><TopCard
|
><TopCard
|
||||||
|
@ -32,15 +35,50 @@
|
||||||
:footer="messageFooter"
|
:footer="messageFooter"
|
||||||
:value="dayMessage"
|
:value="dayMessage"
|
||||||
>
|
>
|
||||||
<LineChart :chartXData="lineChartXData" :chartYData="lineChartYData"></LineChart> </TopCard
|
<LineChart
|
||||||
|
:chartXData="lineChartXData"
|
||||||
|
:chartYData="lineChartYData"
|
||||||
|
></LineChart> </TopCard
|
||||||
></a-col>
|
></a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
<a-row :span="24">
|
||||||
|
<a-col :span="24">
|
||||||
|
<div class="message-card">
|
||||||
|
<Guide title="设备消息">
|
||||||
|
<template #extra>
|
||||||
|
<TimeSelect
|
||||||
|
key="flow-static"
|
||||||
|
:type="'week'"
|
||||||
|
:quickBtnList="quickBtnList"
|
||||||
|
@change="getEcharts"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Guide>
|
||||||
|
<div class="message-chart">
|
||||||
|
<MessageChart :x="messageChartXData" :y="messageChartYData" :maxY="messageMaxChartYData"></MessageChart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :span="24">
|
||||||
|
<a-col :span="24">
|
||||||
|
<div class="device-position">
|
||||||
|
<Guide title="设备分布"></Guide>
|
||||||
|
<div class="device-map">
|
||||||
|
<Amap></Amap>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</div>
|
</div>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import BarChart from './components/BarChart.vue';
|
import BarChart from './components/BarChart.vue';
|
||||||
import LineChart from './components/LineChart.vue';
|
import LineChart from './components/LineChart.vue';
|
||||||
|
import TimeSelect from './components/TimeSelect.vue';
|
||||||
|
import Guide from './components/Guide.vue';
|
||||||
|
import MessageChart from './components/messageChart.vue';
|
||||||
import {
|
import {
|
||||||
productCount,
|
productCount,
|
||||||
deviceCount,
|
deviceCount,
|
||||||
|
@ -51,6 +89,7 @@ import encodeQuery from '@/utils/encodeQuery';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import type { Footer } from '@/views/device/DashBoard/typings';
|
import type { Footer } from '@/views/device/DashBoard/typings';
|
||||||
import TopCard from '@/views/device/DashBoard/components/TopCard.vue';
|
import TopCard from '@/views/device/DashBoard/components/TopCard.vue';
|
||||||
|
import Amap from './components/Amap.vue'
|
||||||
let productTotal = ref(0);
|
let productTotal = ref(0);
|
||||||
let productFooter = ref<Footer[]>([
|
let productFooter = ref<Footer[]>([
|
||||||
{
|
{
|
||||||
|
@ -95,6 +134,15 @@ let lineChartYData = ref<any[]>([]);
|
||||||
let lineChartXData = ref<any[]>([]);
|
let lineChartXData = ref<any[]>([]);
|
||||||
let barChartXData = ref<any[]>([]);
|
let barChartXData = ref<any[]>([]);
|
||||||
let barChartYData = ref<any[]>([]);
|
let barChartYData = ref<any[]>([]);
|
||||||
|
let messageChartXData = ref<any[]>([]);
|
||||||
|
let messageChartYData = ref<any[]>([]);
|
||||||
|
let messageMaxChartYData = ref<number>();
|
||||||
|
const quickBtnList = [
|
||||||
|
{ label: '昨日', value: 'yesterday' },
|
||||||
|
{ label: '近一周', value: 'week' },
|
||||||
|
{ label: '近一月', value: 'month' },
|
||||||
|
{ label: '近一年', value: 'year' },
|
||||||
|
];
|
||||||
const getProductData = () => {
|
const getProductData = () => {
|
||||||
productCount().then((res) => {
|
productCount().then((res) => {
|
||||||
if (res.status == 200) {
|
if (res.status == 200) {
|
||||||
|
@ -224,19 +272,81 @@ const getDevice = () => {
|
||||||
const oneDay = res.result.find(
|
const oneDay = res.result.find(
|
||||||
(item: any) => item.group === 'oneday',
|
(item: any) => item.group === 'oneday',
|
||||||
)?.data.value;
|
)?.data.value;
|
||||||
dayMessage = oneDay;
|
dayMessage.value = oneDay;
|
||||||
messageFooter.value[0].value = thisMonth;
|
messageFooter.value[0].value = thisMonth;
|
||||||
const today = res.result.filter(
|
const today = res.result.filter(
|
||||||
(item: any) => item.group === 'today',
|
(item: any) => item.group === 'today',
|
||||||
);
|
);
|
||||||
const x = today.map((item: any) => item.data.timeString).reverse();
|
const x = today.map((item: any) => item.data.timeString).reverse();
|
||||||
const y = today.map((item: any) => item.data.value).reverse();
|
const y = today.map((item: any) => item.data.value).reverse();
|
||||||
lineChartXData.value = x ;
|
lineChartXData.value = x;
|
||||||
lineChartYData.value = y ;
|
lineChartYData.value = y;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
getDevice();
|
getDevice();
|
||||||
|
const getEcharts = (data: any) => {
|
||||||
|
let _time = '1h';
|
||||||
|
let format = 'HH';
|
||||||
|
let limit = 12;
|
||||||
|
const dt = data.end - data.start;
|
||||||
|
const hour = 60 * 60 * 1000;
|
||||||
|
const days = hour * 24;
|
||||||
|
const months = days * 30;
|
||||||
|
const year = 365 * days;
|
||||||
|
if (dt <= days) {
|
||||||
|
limit = Math.abs(Math.ceil(dt / hour));
|
||||||
|
} else if (dt > days && dt < year) {
|
||||||
|
limit = Math.abs(Math.ceil(dt / days)) + 1;
|
||||||
|
_time = '1d';
|
||||||
|
format = 'M月dd日';
|
||||||
|
} else if (dt >= year) {
|
||||||
|
limit = Math.abs(Math.floor(dt / months));
|
||||||
|
_time = '1M';
|
||||||
|
format = 'yyyy年-M月';
|
||||||
|
}
|
||||||
|
dashboard([
|
||||||
|
{
|
||||||
|
dashboard: 'device',
|
||||||
|
object: 'message',
|
||||||
|
measurement: 'quantity',
|
||||||
|
dimension: 'agg',
|
||||||
|
group: 'device_msg',
|
||||||
|
params: {
|
||||||
|
time: _time,
|
||||||
|
format: format,
|
||||||
|
limit: limit,
|
||||||
|
from: data.start,
|
||||||
|
to: data.end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]).then((res:any) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
messageChartXData.value = res.result
|
||||||
|
.map((item: any) =>
|
||||||
|
_time === '1h'
|
||||||
|
? `${item.data.timeString}时`
|
||||||
|
: item.data.timeString,
|
||||||
|
)
|
||||||
|
.reverse();
|
||||||
|
messageChartYData.value = res.result.map((item: any) => item.data.value).reverse();
|
||||||
|
messageMaxChartYData.value = Math.max.apply(null, messageChartYData.value.length ? messageChartYData.value : [0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.message-card,.device-position{
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.message-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
.amap-box{
|
||||||
|
height: 500px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -4,21 +4,29 @@
|
||||||
title="绑定"
|
title="绑定"
|
||||||
width="1440px"
|
width="1440px"
|
||||||
@ok="dialog.handleOk"
|
@ok="dialog.handleOk"
|
||||||
|
:confirmLoading="dialog.loading.value"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
v-model:visible="dialog.visible.value"
|
v-model:visible="dialog.visible.value"
|
||||||
|
destroyOnClose
|
||||||
>
|
>
|
||||||
<a-row>
|
<h5 class="row">
|
||||||
<exclamation-circle-outlined /> 只能分配有“共享”权限的资产数据
|
<exclamation-circle-outlined style="margin-right: 6px" />
|
||||||
</a-row>
|
只能分配有“共享”权限的资产数据
|
||||||
|
</h5>
|
||||||
|
|
||||||
<a-row>
|
<div class="row">
|
||||||
<span>批量配置</span>
|
<span style="margin-right: 8px">批量配置</span>
|
||||||
<a-switch v-model:checked="bulkBool" />
|
<a-switch
|
||||||
</a-row>
|
v-model:checked="bulkBool"
|
||||||
<a-row v-show="bulkBool">
|
checked-children="开"
|
||||||
|
un-checked-children="关"
|
||||||
|
style="width: 56px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-show="bulkBool">
|
||||||
<a-checkbox-group v-model:value="bulkList" :options="options" />
|
<a-checkbox-group v-model:value="bulkList" :options="options" />
|
||||||
</a-row>
|
</div>
|
||||||
|
|
||||||
<Search :columns="query.columns" @search="query.search" />
|
<Search :columns="query.columns" @search="query.search" />
|
||||||
|
|
||||||
|
@ -79,12 +87,14 @@
|
||||||
<div
|
<div
|
||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
class="card-item-content-value"
|
class="card-item-content-value"
|
||||||
|
@click="(e) => e.stopPropagation()"
|
||||||
>
|
>
|
||||||
{{
|
<a-checkbox-group
|
||||||
table.getPermissLabel(
|
v-model:value="
|
||||||
slotProps.permission,
|
slotProps.selectPermissions
|
||||||
)
|
"
|
||||||
}}
|
:options="slotProps.permissionList"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
@ -98,10 +108,15 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
|
import { uniq, intersection } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
getDeviceOrProductList_api,
|
getDeviceOrProductList_api,getDeviceList_api,
|
||||||
getPermission_api,
|
getPermission_api,
|
||||||
|
bindDeviceOrProductList_api,
|
||||||
} from '@/api/system/department';
|
} from '@/api/system/department';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const emits = defineEmits(['confirm']);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
parentId: string;
|
parentId: string;
|
||||||
allPermission: dictType;
|
allPermission: dictType;
|
||||||
|
@ -110,11 +125,31 @@ const props = defineProps<{
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const dialog = {
|
const dialog = {
|
||||||
visible: ref<boolean>(false),
|
visible: ref<boolean>(false),
|
||||||
|
loading: ref<boolean>(false),
|
||||||
handleOk: () => {
|
handleOk: () => {
|
||||||
// formRef.value?.validate().then(() => {
|
if (table.selectedRows.length < 1) {
|
||||||
// form.submit();
|
return message.warning('请先勾选数据');
|
||||||
// });
|
}
|
||||||
dialog.changeVisible();
|
|
||||||
|
const params = table.selectedRows.map((item: any) => ({
|
||||||
|
targetType: 'org',
|
||||||
|
targetId: props.parentId,
|
||||||
|
assetType: props.assetType,
|
||||||
|
assetIdList: [item.id],
|
||||||
|
permission: item.selectPermissions,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// console.log(params);
|
||||||
|
dialog.loading.value = true;
|
||||||
|
bindDeviceOrProductList_api(props.assetType, params)
|
||||||
|
.then(() => {
|
||||||
|
message.success('操作成功');
|
||||||
|
emits('confirm');
|
||||||
|
dialog.changeVisible();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
dialog.loading.value = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
// 控制弹窗的打开与关闭
|
// 控制弹窗的打开与关闭
|
||||||
changeVisible: () => {
|
changeVisible: () => {
|
||||||
|
@ -184,24 +219,77 @@ const query = {
|
||||||
query.params.value = params;
|
query.params.value = params;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const table = {
|
const table: any = {
|
||||||
_selectedRowKeys: ref<string[]>([]),
|
_selectedRowKeys: ref<string[]>([]), // 选中项的id
|
||||||
selectedRows: [] as any[],
|
backRowKeys: [] as string[], // 旧选中项的id
|
||||||
|
selectedRows: [] as any[], // 选中项
|
||||||
|
tableData: [] as any[], // 列表的浅拷贝
|
||||||
|
|
||||||
// 获取权限名称
|
init: () => {
|
||||||
getPermissLabel: (values: string[]) => {
|
watch(
|
||||||
const permissionList = props.allPermission;
|
[bulkBool, bulkList, () => table._selectedRowKeys],
|
||||||
if (permissionList.length < 1 || values.length < 1) return '';
|
(n) => {
|
||||||
const result = values.map(
|
const nValue = n[2].value;
|
||||||
(key) => permissionList.find((item) => item.id === key)?.name,
|
const oValue = table.backRowKeys;
|
||||||
|
|
||||||
|
table.selectedRows.forEach((item: any) => {
|
||||||
|
// 启用批量设置
|
||||||
|
if (bulkBool.value) {
|
||||||
|
// 将已勾选的权限和批量设置的权限进行合并,并与自己可选的权限进行比对,取交集作为当前选中的权限
|
||||||
|
let newPermission = uniq([
|
||||||
|
...item.selectPermissions,
|
||||||
|
...bulkList.value,
|
||||||
|
]);
|
||||||
|
const allPermissions = item.permissionList.map(
|
||||||
|
(item: any) => item.value,
|
||||||
|
);
|
||||||
|
newPermission = intersection(
|
||||||
|
newPermission,
|
||||||
|
allPermissions,
|
||||||
|
);
|
||||||
|
item.selectPermissions = newPermission;
|
||||||
|
// 禁用单独勾选
|
||||||
|
item.permissionList.forEach((permission: any) => {
|
||||||
|
permission.disabled = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 取消批量设置
|
||||||
|
// 放开自己权限的勾选限制,查看为必选
|
||||||
|
item.permissionList.forEach((permission: any) => {
|
||||||
|
permission.disabled = permission.value === 'read';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 取消勾选时触发
|
||||||
|
if (nValue && nValue.length < oValue.length) {
|
||||||
|
// 拿到取消选中的项的id
|
||||||
|
const removedKeys = oValue.filter(
|
||||||
|
(key: string) => !nValue.includes(key),
|
||||||
|
);
|
||||||
|
// 将取消勾选的项的权限重置
|
||||||
|
removedKeys.forEach((removedKey: string) => {
|
||||||
|
const removedItem = table.tableData.find(
|
||||||
|
(item: any) => item.id === removedKey,
|
||||||
|
);
|
||||||
|
removedItem.permissionList.forEach(
|
||||||
|
(permission: any) => (permission.disabled = true),
|
||||||
|
);
|
||||||
|
removedItem.selectPermissions = ['read'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
);
|
);
|
||||||
return result.join(',');
|
|
||||||
},
|
},
|
||||||
// 选中
|
// 选中
|
||||||
onSelectChange: (row: any) => {
|
onSelectChange: (row: any) => {
|
||||||
|
// 若该项的可选权限中没有分享权限,则不支持任何操作
|
||||||
|
if (!row.permissionList.find((item: any) => item.value === 'share'))
|
||||||
|
return;
|
||||||
const selectedRowKeys = table._selectedRowKeys.value;
|
const selectedRowKeys = table._selectedRowKeys.value;
|
||||||
const index = selectedRowKeys.indexOf(row.id);
|
const index = selectedRowKeys.indexOf(row.id);
|
||||||
|
|
||||||
|
table.backRowKeys = [...selectedRowKeys];
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
selectedRowKeys.push(row.id);
|
selectedRowKeys.push(row.id);
|
||||||
table.selectedRows.push(row);
|
table.selectedRows.push(row);
|
||||||
|
@ -212,13 +300,15 @@ const table = {
|
||||||
},
|
},
|
||||||
// 取消全选
|
// 取消全选
|
||||||
cancelSelect: () => {
|
cancelSelect: () => {
|
||||||
|
table.backRowKeys = [...table._selectedRowKeys.value];
|
||||||
table._selectedRowKeys.value = [];
|
table._selectedRowKeys.value = [];
|
||||||
table.selectedRows = [];
|
table.selectedRows = [];
|
||||||
},
|
},
|
||||||
// 获取并整理数据
|
// 获取并整理数据
|
||||||
getData: (params: object, parentId: string) =>
|
getData: (params: object, parentId: string) =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
getDeviceOrProductList_api(params).then((resp: any) => {
|
const api = props.assetType === 'product' ? getDeviceOrProductList_api: getDeviceList_api;
|
||||||
|
api(params).then((resp: any) => {
|
||||||
type resultType = {
|
type resultType = {
|
||||||
data: any[];
|
data: any[];
|
||||||
total: number;
|
total: number;
|
||||||
|
@ -228,20 +318,23 @@ const table = {
|
||||||
const { pageIndex, pageSize, total, data } =
|
const { pageIndex, pageSize, total, data } =
|
||||||
resp.result as resultType;
|
resp.result as resultType;
|
||||||
const ids = data.map((item) => item.id);
|
const ids = data.map((item) => item.id);
|
||||||
getPermission_api(ids, parentId).then((perResp: any) => {
|
getPermission_api(props.assetType,ids, parentId).then((perResp: any) => {
|
||||||
const permissionObj = {};
|
const permissionObj = {};
|
||||||
perResp.result.forEach((item: any) => {
|
perResp.result.forEach((item: any) => {
|
||||||
permissionObj[item.assetId] =
|
permissionObj[item.assetId] = props.allPermission
|
||||||
props.allPermission.filter((permission) =>
|
.filter((permission) =>
|
||||||
item.allPermissions.includes(
|
item.allPermissions.includes(permission.id),
|
||||||
(permissionId: string) =>
|
)
|
||||||
permissionId === permission.id,
|
.map((item) => ({
|
||||||
),
|
label: item.name,
|
||||||
);
|
value: item.id,
|
||||||
|
disabled: true,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
data.forEach((item) => {
|
||||||
|
item.permissionList = permissionObj[item.id];
|
||||||
|
item.selectPermissions = ['read'];
|
||||||
});
|
});
|
||||||
data.forEach(
|
|
||||||
(item) => (item.permission = permissionObj[item.id]),
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
code: 200,
|
code: 200,
|
||||||
|
@ -289,8 +382,7 @@ const table = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const resp: any = await table.getData(params, props.parentId);
|
const resp: any = await table.getData(params, props.parentId);
|
||||||
console.log(resp.result);
|
table.tableData = resp.result.data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: resp.status,
|
code: resp.status,
|
||||||
result: resp.result,
|
result: resp.result,
|
||||||
|
@ -310,6 +402,7 @@ const table = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
table.init();
|
||||||
|
|
||||||
// 将打开弹窗的操作暴露给父组件
|
// 将打开弹窗的操作暴露给父组件
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -320,8 +413,16 @@ defineExpose({
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.add-device-or-product-dialog-container {
|
.add-device-or-product-dialog-container {
|
||||||
.ant-spin-nested-loading {
|
.ant-spin-nested-loading {
|
||||||
height: calc(100vh - 440px);
|
height: calc(100vh - 400px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
h5 {
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -4,33 +4,72 @@
|
||||||
title="编辑"
|
title="编辑"
|
||||||
width="500px"
|
width="500px"
|
||||||
@ok="dialog.handleOk"
|
@ok="dialog.handleOk"
|
||||||
|
:confirmLoading="dialog.loading.value"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
v-model:visible="dialog.visible.value"
|
v-model:visible="dialog.visible.value"
|
||||||
>
|
>
|
||||||
|
<div>
|
||||||
|
<span>资产权限:</span>
|
||||||
|
<a-checkbox-group
|
||||||
|
v-model:value="form.permission"
|
||||||
|
:options="options"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps({
|
import { dictType, optionsType } from '../typing.d.ts';
|
||||||
parentId: String,
|
import { updatePermission_api } from '@/api/system/department';
|
||||||
});
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const emits = defineEmits(['confirm']);
|
||||||
|
const props = defineProps<{
|
||||||
|
parentId: string;
|
||||||
|
allPermission: dictType;
|
||||||
|
assetType: 'product' | 'device';
|
||||||
|
}>();
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const dialog = {
|
const dialog = {
|
||||||
|
loading: ref<boolean>(false),
|
||||||
visible: ref<boolean>(false),
|
visible: ref<boolean>(false),
|
||||||
handleOk: () => {
|
handleOk: () => {
|
||||||
// formRef.value?.validate().then(() => {
|
dialog.loading.value = true;
|
||||||
// form.submit();
|
updatePermission_api(props.assetType, props.parentId, form)
|
||||||
// });
|
.then(() => {
|
||||||
dialog.changeVisible()
|
message.success('操作成功');
|
||||||
|
emits('confirm');
|
||||||
|
dialog.visible.value = false;
|
||||||
|
})
|
||||||
|
.finally(() => (dialog.loading.value = false));
|
||||||
},
|
},
|
||||||
// 控制弹窗的打开与关闭
|
// 控制弹窗的打开与关闭
|
||||||
changeVisible: (ids?:string[], permissionList?:string[]) => {
|
changeVisible: (ids: string[], permissionList: string[]) => {
|
||||||
console.log(ids, permissionList);
|
console.log(ids, permissionList);
|
||||||
|
form.permission = [...permissionList];
|
||||||
|
form.assetIdList = ids;
|
||||||
|
options.value = setOptions(permissionList);
|
||||||
dialog.visible.value = !dialog.visible.value;
|
dialog.visible.value = !dialog.visible.value;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const form = reactive({
|
||||||
|
assetIdList: [] as string[],
|
||||||
|
permission: [] as string[],
|
||||||
|
});
|
||||||
|
const options = ref<optionsType>([]);
|
||||||
|
const setOptions = (havePermission: string[]): optionsType => {
|
||||||
|
const result: optionsType = [];
|
||||||
|
props.allPermission.forEach((item) => {
|
||||||
|
if (havePermission.includes(item.id))
|
||||||
|
result.push({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
disabled: item.id === 'read',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
// 将打开弹窗的操作暴露给父组件
|
// 将打开弹窗的操作暴露给父组件
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|
|
@ -1,13 +1,415 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="product-container">
|
||||||
设备
|
<Search :columns="query.columns" @search="query.search" />
|
||||||
|
<JTable
|
||||||
|
ref="tableRef"
|
||||||
|
:request="table.requestFun"
|
||||||
|
:gridColumn="2"
|
||||||
|
model="CARD"
|
||||||
|
:params="query.params.value"
|
||||||
|
:rowSelection="{
|
||||||
|
selectedRowKeys: table._selectedRowKeys.value,
|
||||||
|
}"
|
||||||
|
@cancelSelect="table.cancelSelect"
|
||||||
|
>
|
||||||
|
<template #headerTitle>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="table.clickAdd">
|
||||||
|
<plus-outlined />资产分配
|
||||||
|
</a-button>
|
||||||
|
<a-dropdown trigger="hover">
|
||||||
|
<a-button>批量操作</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item>
|
||||||
|
<a-popconfirm
|
||||||
|
title="是否批量解除绑定"
|
||||||
|
ok-text="确定"
|
||||||
|
cancel-text="取消"
|
||||||
|
@confirm="table.clickUnBind()"
|
||||||
|
>
|
||||||
|
<a-button>
|
||||||
|
<DisconnectOutlined /> 批量解绑
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item>
|
||||||
|
<a-button @click="table.clickEdit()">
|
||||||
|
<EditOutlined /> 批量编辑
|
||||||
|
</a-button>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #card="slotProps">
|
||||||
|
<CardBox
|
||||||
|
:value="slotProps"
|
||||||
|
:actions="[{ key: 1 }]"
|
||||||
|
v-bind="slotProps"
|
||||||
|
:active="
|
||||||
|
table._selectedRowKeys.value.includes(slotProps.id)
|
||||||
|
"
|
||||||
|
@click="table.onSelectChange"
|
||||||
|
:status="slotProps.state?.value"
|
||||||
|
:statusText="slotProps.state?.text"
|
||||||
|
:statusNames="{
|
||||||
|
online: 'success',
|
||||||
|
offline: 'error',
|
||||||
|
notActive: 'warning',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #img>
|
||||||
|
<slot name="img">
|
||||||
|
<img
|
||||||
|
:src="getImage('/device-product.png')"
|
||||||
|
style="cursor: pointer"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<h3 class="card-item-content-title">
|
||||||
|
{{ slotProps.name }}
|
||||||
|
</h3>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="card-item-content-text">ID</div>
|
||||||
|
<div
|
||||||
|
style="cursor: pointer"
|
||||||
|
class="card-item-content-value"
|
||||||
|
>
|
||||||
|
{{ slotProps.id }}
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
资产权限
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="cursor: pointer"
|
||||||
|
class="card-item-content-value"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
table.permissionList.value.length &&
|
||||||
|
table.getPermissLabel(
|
||||||
|
slotProps.permission,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<a-button
|
||||||
|
@click="table.clickEdit(slotProps)"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
>
|
||||||
|
<AIcon type="EditOutlined" />
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm
|
||||||
|
title="是否解除绑定"
|
||||||
|
ok-text="确定"
|
||||||
|
cancel-text="取消"
|
||||||
|
@confirm="table.clickUnBind(slotProps)"
|
||||||
|
><a-button>
|
||||||
|
<AIcon type="DisconnectOutlined" />
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
</CardBox>
|
||||||
|
</template>
|
||||||
|
</JTable>
|
||||||
|
|
||||||
|
<div class="dialogs">
|
||||||
|
<AddDeviceOrProductDialog
|
||||||
|
ref="addDialogRef"
|
||||||
|
:parent-id="props.parentId"
|
||||||
|
:all-permission="table.permissionList.value"
|
||||||
|
asset-type="device"
|
||||||
|
@confirm="table.refresh"
|
||||||
|
/>
|
||||||
|
<EditPermissionDialog
|
||||||
|
ref="editDialogRef"
|
||||||
|
:parent-id="props.parentId"
|
||||||
|
:all-permission="table.permissionList.value"
|
||||||
|
asset-type="device"
|
||||||
|
@confirm="table.refresh"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts" name="device">
|
||||||
|
import {
|
||||||
|
PlusOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
DisconnectOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
import AddDeviceOrProductDialog from '../components/AddDeviceOrProductDialog.vue';
|
||||||
|
import EditPermissionDialog from '../components/EditPermissionDialog.vue';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import {
|
||||||
|
getDeviceList_api,
|
||||||
|
getPermission_api,
|
||||||
|
getPermissionDict_api,
|
||||||
|
unBindDeviceOrProduct_api,
|
||||||
|
} from '@/api/system/department';
|
||||||
|
import { intersection } from 'lodash-es';
|
||||||
|
|
||||||
|
import { dictType } from '../typing.d.ts';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
parentId: string;
|
||||||
|
}>();
|
||||||
|
const query = {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '在线',
|
||||||
|
value: 'online',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '离线',
|
||||||
|
value: 'offline',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '禁用',
|
||||||
|
value: 'notActive',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
params: ref({}),
|
||||||
|
search: (params: any) => {
|
||||||
|
query.params.value = params;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableRef = ref();
|
||||||
|
const table = {
|
||||||
|
_selectedRowKeys: ref<string[]>([]),
|
||||||
|
selectedRows: [] as any[],
|
||||||
|
permissionList: ref<dictType>([]),
|
||||||
|
|
||||||
|
init: () => {
|
||||||
|
table.getPermissionDict();
|
||||||
|
watch(
|
||||||
|
() => props.parentId,
|
||||||
|
() => {
|
||||||
|
table.refresh();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取权限数据字典
|
||||||
|
getPermissionDict: () => {
|
||||||
|
getPermissionDict_api().then((resp: any) => {
|
||||||
|
table.permissionList.value = resp.result;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 获取权限名称
|
||||||
|
getPermissLabel: (values: string[]) => {
|
||||||
|
const permissionList = table.permissionList.value;
|
||||||
|
if (permissionList.length < 1 || values.length < 1) return '';
|
||||||
|
const result = values.map(
|
||||||
|
(key) => permissionList.find((item) => item.id === key)?.name,
|
||||||
|
);
|
||||||
|
return result.join(',');
|
||||||
|
},
|
||||||
|
// 选中
|
||||||
|
onSelectChange: (row: any) => {
|
||||||
|
const selectedRowKeys = table._selectedRowKeys.value;
|
||||||
|
const index = selectedRowKeys.indexOf(row.id);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
selectedRowKeys.push(row.id);
|
||||||
|
table.selectedRows.push(row);
|
||||||
|
} else {
|
||||||
|
selectedRowKeys.splice(index, 1);
|
||||||
|
table.selectedRows.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 取消全选
|
||||||
|
cancelSelect: () => {
|
||||||
|
table._selectedRowKeys.value = [];
|
||||||
|
table.selectedRows = [];
|
||||||
|
},
|
||||||
|
// 获取并整理数据
|
||||||
|
getData: (params: object, parentId: string) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
getDeviceList_api(params).then((resp) => {
|
||||||
|
type resultType = {
|
||||||
|
data: any[];
|
||||||
|
total: number;
|
||||||
|
pageSize: number;
|
||||||
|
pageIndex: number;
|
||||||
|
};
|
||||||
|
const { pageIndex, pageSize, total, data } =
|
||||||
|
resp.result as resultType;
|
||||||
|
const ids = data.map((item) => item.id);
|
||||||
|
getPermission_api('device',ids, parentId).then((perResp: any) => {
|
||||||
|
const permissionObj = {};
|
||||||
|
perResp.result.forEach((item: any) => {
|
||||||
|
permissionObj[item.assetId] = item.grantedPermissions;
|
||||||
|
});
|
||||||
|
data.forEach(
|
||||||
|
(item) => (item.permission = permissionObj[item.id]),
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
code: 200,
|
||||||
|
result: {
|
||||||
|
data: data,
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
total,
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
// 整理参数并获取数据
|
||||||
|
requestFun: async (oParams: any) => {
|
||||||
|
table._selectedRowKeys.value = [];
|
||||||
|
table.selectedRows = [];
|
||||||
|
if (props.parentId) {
|
||||||
|
const params = {
|
||||||
|
...oParams,
|
||||||
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
|
terms: [
|
||||||
|
...oParams.terms,
|
||||||
|
{
|
||||||
|
column: 'id',
|
||||||
|
termType: 'dim-assets',
|
||||||
|
value: {
|
||||||
|
assetType: 'device',
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
type: 'org',
|
||||||
|
id: props.parentId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const resp: any = await table.getData(params, props.parentId);
|
||||||
|
return {
|
||||||
|
code: resp.status,
|
||||||
|
result: resp.result,
|
||||||
|
status: resp.status,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
result: {
|
||||||
|
data: [],
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 0,
|
||||||
|
total: 0,
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickAdd: () => {
|
||||||
|
addDialogRef.value && addDialogRef.value.openDialog();
|
||||||
|
},
|
||||||
|
clickEdit: (row?: any) => {
|
||||||
|
const ids = row ? [row.id] : [...table._selectedRowKeys.value];
|
||||||
|
if (row || table.selectedRows.length === 1) {
|
||||||
|
const permissionList =
|
||||||
|
row?.permission || table.selectedRows[0].permission;
|
||||||
|
return (
|
||||||
|
editDialogRef.value &&
|
||||||
|
editDialogRef.value.openDialog(ids, permissionList)
|
||||||
|
);
|
||||||
|
} else if (table.selectedRows.length === 0) return;
|
||||||
|
const permissionList = table.selectedRows.map(
|
||||||
|
(item) => item.permission,
|
||||||
|
);
|
||||||
|
const mixPermissionList = intersection(...permissionList);
|
||||||
|
|
||||||
|
editDialogRef.value &&
|
||||||
|
editDialogRef.value.openDialog(ids, mixPermissionList);
|
||||||
|
},
|
||||||
|
clickUnBind: (row?: any) => {
|
||||||
|
const ids = row ? [row.id] : [...table._selectedRowKeys.value];
|
||||||
|
if (ids.length < 1) return message.warning('请勾选需要解绑的数据');
|
||||||
|
const params = [
|
||||||
|
{
|
||||||
|
targetType: 'org',
|
||||||
|
targetId: props.parentId,
|
||||||
|
assetType: 'device',
|
||||||
|
assetIdList: ids,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
unBindDeviceOrProduct_api('device', params).then(() => {
|
||||||
|
message.success('操作成功');
|
||||||
|
table.refresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
refresh: () => {
|
||||||
|
nextTick(() => {
|
||||||
|
tableRef.value.reload();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const addDialogRef = ref();
|
||||||
|
const editDialogRef = ref();
|
||||||
|
|
||||||
|
table.init();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="less" scoped>
|
||||||
|
.product-container {
|
||||||
</style>
|
.card {
|
||||||
|
.card-warp {
|
||||||
|
&.active {
|
||||||
|
.card-item-content-value {
|
||||||
|
color: #2f54eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-tools {
|
||||||
|
.ant-btn {
|
||||||
|
color: #252525;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="department-container">
|
<div class="department-container">
|
||||||
<a-card class="department-content">
|
<a-card class="department-content">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<LeftTree @change="id=>departmentId = id" />
|
<LeftTree @change="(id) => (departmentId = id)" />
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<a-tabs v-model:activeKey="activeKey">
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Product :parentId="departmentId" />
|
<Product :parentId="departmentId" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="device" tab="设备">
|
<a-tab-pane key="device" tab="设备">
|
||||||
<Device />
|
<Device :parentId="departmentId" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="user" tab="用户">
|
<a-tab-pane key="user" tab="用户">
|
||||||
<User />
|
<User />
|
||||||
|
@ -29,7 +29,7 @@ import User from './user/index.vue';
|
||||||
|
|
||||||
const activeKey = ref<'product' | 'device' | 'user'>('product');
|
const activeKey = ref<'product' | 'device' | 'user'>('product');
|
||||||
|
|
||||||
const departmentId = ref<string>('')
|
const departmentId = ref<string>('');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -44,9 +44,11 @@ const departmentId = ref<string>('')
|
||||||
}
|
}
|
||||||
.right {
|
.right {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
.ant-tabs-nav {
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -112,7 +112,7 @@
|
||||||
title="是否解除绑定"
|
title="是否解除绑定"
|
||||||
ok-text="确定"
|
ok-text="确定"
|
||||||
cancel-text="取消"
|
cancel-text="取消"
|
||||||
@confirm="table.clickUnBind"
|
@confirm="table.clickUnBind(slotProps)"
|
||||||
><a-button>
|
><a-button>
|
||||||
<AIcon type="DisconnectOutlined" />
|
<AIcon type="DisconnectOutlined" />
|
||||||
</a-button>
|
</a-button>
|
||||||
|
@ -128,8 +128,15 @@
|
||||||
:parent-id="props.parentId"
|
:parent-id="props.parentId"
|
||||||
:all-permission="table.permissionList.value"
|
:all-permission="table.permissionList.value"
|
||||||
asset-type="product"
|
asset-type="product"
|
||||||
|
@confirm="table.refresh"
|
||||||
|
/>
|
||||||
|
<EditPermissionDialog
|
||||||
|
ref="editDialogRef"
|
||||||
|
:parent-id="props.parentId"
|
||||||
|
:all-permission="table.permissionList.value"
|
||||||
|
asset-type="product"
|
||||||
|
@confirm="table.refresh"
|
||||||
/>
|
/>
|
||||||
<EditPermissionDialog ref="editDialogRef" :parent-id="props.parentId" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -147,12 +154,15 @@ import {
|
||||||
getDeviceOrProductList_api,
|
getDeviceOrProductList_api,
|
||||||
getPermission_api,
|
getPermission_api,
|
||||||
getPermissionDict_api,
|
getPermissionDict_api,
|
||||||
|
unBindDeviceOrProduct_api,
|
||||||
} from '@/api/system/department';
|
} from '@/api/system/department';
|
||||||
|
import { intersection } from 'lodash-es';
|
||||||
|
|
||||||
import {dictType} from '../typing.d.ts'
|
import { dictType } from '../typing.d.ts';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
parentId:string
|
parentId: string;
|
||||||
}>();
|
}>();
|
||||||
const query = {
|
const query = {
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -218,9 +228,7 @@ const table = {
|
||||||
watch(
|
watch(
|
||||||
() => props.parentId,
|
() => props.parentId,
|
||||||
() => {
|
() => {
|
||||||
nextTick(() => {
|
table.refresh();
|
||||||
tableRef.value.reload();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -271,7 +279,7 @@ const table = {
|
||||||
const { pageIndex, pageSize, total, data } =
|
const { pageIndex, pageSize, total, data } =
|
||||||
resp.result as resultType;
|
resp.result as resultType;
|
||||||
const ids = data.map((item) => item.id);
|
const ids = data.map((item) => item.id);
|
||||||
getPermission_api(ids, parentId).then((perResp: any) => {
|
getPermission_api('product', ids, parentId).then((perResp: any) => {
|
||||||
const permissionObj = {};
|
const permissionObj = {};
|
||||||
perResp.result.forEach((item: any) => {
|
perResp.result.forEach((item: any) => {
|
||||||
permissionObj[item.assetId] = item.grantedPermissions;
|
permissionObj[item.assetId] = item.grantedPermissions;
|
||||||
|
@ -341,7 +349,7 @@ const table = {
|
||||||
addDialogRef.value && addDialogRef.value.openDialog();
|
addDialogRef.value && addDialogRef.value.openDialog();
|
||||||
},
|
},
|
||||||
clickEdit: (row?: any) => {
|
clickEdit: (row?: any) => {
|
||||||
const ids = row ? row.id : [...table._selectedRowKeys.value];
|
const ids = row ? [row.id] : [...table._selectedRowKeys.value];
|
||||||
if (row || table.selectedRows.length === 1) {
|
if (row || table.selectedRows.length === 1) {
|
||||||
const permissionList =
|
const permissionList =
|
||||||
row?.permission || table.selectedRows[0].permission;
|
row?.permission || table.selectedRows[0].permission;
|
||||||
|
@ -353,29 +361,31 @@ const table = {
|
||||||
const permissionList = table.selectedRows.map(
|
const permissionList = table.selectedRows.map(
|
||||||
(item) => item.permission,
|
(item) => item.permission,
|
||||||
);
|
);
|
||||||
const mixPermissionList = table.getMixed(permissionList);
|
const mixPermissionList = intersection(...permissionList);
|
||||||
|
|
||||||
editDialogRef.value &&
|
editDialogRef.value &&
|
||||||
editDialogRef.value.openDialog(ids, mixPermissionList);
|
editDialogRef.value.openDialog(ids, mixPermissionList);
|
||||||
},
|
},
|
||||||
clickUnBind: (row?: any) => {},
|
clickUnBind: (row?: any) => {
|
||||||
|
const ids = row ? [row.id] : [...table._selectedRowKeys.value];
|
||||||
/**
|
if (ids.length < 1) return message.warning('请勾选需要解绑的数据');
|
||||||
* 获取多个数组的交集
|
const params = [
|
||||||
* @param permissionList
|
{
|
||||||
*/
|
targetType: 'org',
|
||||||
getMixed: (permissionList: string[][]) => {
|
targetId: props.parentId,
|
||||||
if (permissionList.length === 1) return permissionList[0];
|
assetType: 'product',
|
||||||
const obj = {};
|
assetIdList: ids,
|
||||||
const result: string[] = [];
|
},
|
||||||
permissionList.forEach((items) => {
|
];
|
||||||
items.forEach((item) => {
|
unBindDeviceOrProduct_api('product', params).then(() => {
|
||||||
if (obj[item]) obj[item]++;
|
message.success('操作成功');
|
||||||
else obj[item] = 1;
|
table.refresh();
|
||||||
if (obj[item] === permissionList.length) result.push(item);
|
});
|
||||||
});
|
},
|
||||||
|
refresh: () => {
|
||||||
|
nextTick(() => {
|
||||||
|
tableRef.value.reload();
|
||||||
});
|
});
|
||||||
return result;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -383,7 +393,6 @@ const addDialogRef = ref();
|
||||||
const editDialogRef = ref();
|
const editDialogRef = ref();
|
||||||
|
|
||||||
table.init();
|
table.init();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
type dictType = {
|
type dictType = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
type optionsType = {
|
||||||
|
label: string,
|
||||||
|
value: string;
|
||||||
|
disabled?:boolean
|
||||||
|
}[]
|
Loading…
Reference in New Issue