feat: 新增钢卷成本信息展示与能耗辅料成本报表页面
1. 在钢卷详情页新增成本信息模块,展示产线类型、投入产出量、报表信息和总成本 2. 新增成本数据服务类,支持按日期和产线获取完整成本明细与计算数据 3. 新增能耗和辅料两类成本报表页面,支持按产线筛选查看报表 4. 优化岗位管理页面,替换vis.js为ECharts实现岗位树图,新增职责弹窗查看功能 5. 优化综合成本页面,隐藏部分反填按钮和操作入口
This commit is contained in:
@@ -3,100 +3,41 @@
|
||||
<!-- 顶部搜索与操作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="岗位名称" prop="postName">
|
||||
<el-input
|
||||
v-model="queryParams.postName"
|
||||
placeholder="请输入岗位名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.postName" placeholder="请输入岗位名称" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增岗位</el-button>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
>新增岗位</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 左右主体区域 -->
|
||||
<el-row :gutter="16" class="post-content" v-loading="loading">
|
||||
<!-- 左侧:ECharts 岗位树 + 岗位信息 -->
|
||||
<el-col :span="18">
|
||||
<div class="tree-panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">岗位结构树</span>
|
||||
<span v-if="!currentPost" class="panel-hint">点击节点查看详情</span>
|
||||
</div>
|
||||
<div ref="treeChart" class="chart-container"></div>
|
||||
<!-- 岗位信息及操作 -->
|
||||
<div v-if="currentPost" class="post-info-bar">
|
||||
<span class="post-name">{{ currentPost.postName }}</span>
|
||||
<div class="post-actions">
|
||||
<el-button type="primary" size="mini" icon="el-icon-edit" @click="handleUpdate(currentPost)">编辑</el-button>
|
||||
<el-button type="success" size="mini" icon="el-icon-plus" @click="handleAdd(currentPost)">添加子级</el-button>
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="handleDelete(currentPost)">删除</el-button>
|
||||
</div>
|
||||
<div class="tree-panel" v-loading="loading">
|
||||
<div class="panel-header">
|
||||
<span v-if="!currentPost" class="panel-hint">单击节点查看详情,双击查看岗位职责</span>
|
||||
<span v-else class="panel-hint">双击「{{ currentPost.postName }}」查看岗位职责</span>
|
||||
<div v-if="currentPost">
|
||||
<div class="post-actions">
|
||||
<el-button type="primary" size="mini" icon="el-icon-edit" @click="handleUpdate(currentPost)">编辑</el-button>
|
||||
<el-button type="success" size="mini" icon="el-icon-plus" @click="handleAdd(currentPost)">添加子级</el-button>
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="handleDelete(currentPost)">删除</el-button>
|
||||
<el-button type="warning" size="mini" icon="el-icon-document"
|
||||
@click="handleShowDuty(currentPost)">查看职责</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</div>
|
||||
<div ref="treeChart" class="chart-container"></div>
|
||||
<!-- 岗位信息及操作 -->
|
||||
|
||||
<!-- 右侧:岗位职责管理 -->
|
||||
<el-col :span="6">
|
||||
<div class="duty-panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">岗位职责</span>
|
||||
<span v-if="currentPost" class="panel-sub-title">{{ currentPost.postName }}</span>
|
||||
<span v-else class="panel-hint">请点击左侧岗位节点</span>
|
||||
</div>
|
||||
|
||||
<!-- 自定义职责列表 -->
|
||||
<div v-if="currentPost" class="duty-list-wrap">
|
||||
<div class="duty-toolbar">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAddDuty">新增职责</el-button>
|
||||
</div>
|
||||
<div v-loading="dutyLoading" class="duty-list">
|
||||
<div v-for="item in dutyList" :key="item.dutyId" class="duty-item">
|
||||
<div class="duty-item-header">
|
||||
<span class="duty-name">{{ item.dutyName }}</span>
|
||||
<!-- <span class="duty-sort">排序: {{ item.sortOrder }}</span> -->
|
||||
</div>
|
||||
<div class="duty-content">{{ item.dutyContent }}</div>
|
||||
<div v-if="item.remark" class="duty-remark">备注:{{ item.remark }}</div>
|
||||
<div class="duty-item-actions">
|
||||
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleUpdateDuty(item)">修改</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-delete" style="color: #F56C6C" @click="handleDeleteDuty(item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!dutyLoading && dutyList.length === 0" description="暂无职责" :image-size="60" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 未选择岗位时的提示 -->
|
||||
<div v-else class="empty-hint">
|
||||
<i class="el-icon-info"></i>
|
||||
<p>请点击左侧树图中的岗位节点以查看和管理其岗位职责</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 添加或修改岗位对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="上级岗位" prop="parentId">
|
||||
<treeselect v-model="form.parentId" :options="postOptions" :normalizer="normalizer" placeholder="请选择上级岗位(留空为顶级)" />
|
||||
<treeselect v-model="form.parentId" :options="postOptions" :normalizer="normalizer"
|
||||
placeholder="请选择上级岗位(留空为顶级)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位名称" prop="postName">
|
||||
<el-input v-model="form.postName" placeholder="请输入岗位名称" />
|
||||
@@ -167,15 +108,39 @@
|
||||
<el-button @click="cancelDuty">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 岗位职责查看对话框(双击节点打开) -->
|
||||
<el-dialog :title="dutyDialogTitle" :visible.sync="dutyDialogVisible" width="700px" append-to-body>
|
||||
<div class="duty-dialog-wrap">
|
||||
<div class="duty-dialog-toolbar">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAddDuty">新增职责</el-button>
|
||||
</div>
|
||||
<div v-loading="dutyLoading" class="duty-dialog-list">
|
||||
<div v-for="item in dutyList" :key="item.dutyId" class="duty-item">
|
||||
<div class="duty-item-header">
|
||||
<span class="duty-name">{{ item.dutyName }}</span>
|
||||
</div>
|
||||
<div class="duty-content">{{ item.dutyContent }}</div>
|
||||
<div v-if="item.remark" class="duty-remark">备注:{{ item.remark }}</div>
|
||||
<div class="duty-item-actions">
|
||||
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleUpdateDuty(item)">修改</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-delete" style="color: #F56C6C"
|
||||
@click="handleDeleteDuty(item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!dutyLoading && dutyList.length === 0" description="暂无职责" :image-size="60" />
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPost, getPost, addPost, updatePost, delPost } from "@/api/wms/post";
|
||||
import { listPostDuty, getPostDuty, addPostDuty, updatePostDuty, delPostDuty } from "@/api/wms/postDuty";
|
||||
import * as echarts from 'echarts';
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
import { Network } from 'vis-network/peer/umd/vis-network.min.js';
|
||||
|
||||
export default {
|
||||
name: "WmsPost",
|
||||
@@ -213,6 +178,9 @@ export default {
|
||||
dutyOpen: false,
|
||||
dutyButtonLoading: false,
|
||||
dutyForm: {},
|
||||
// 职责查看弹窗
|
||||
dutyDialogVisible: false,
|
||||
dutyDialogTitle: "",
|
||||
dutyRules: {
|
||||
dutyName: [
|
||||
{ required: true, message: "职责名称不能为空", trigger: "blur" }
|
||||
@@ -222,8 +190,6 @@ export default {
|
||||
]
|
||||
},
|
||||
|
||||
// vis.js 实例
|
||||
network: null,
|
||||
// 岗位类型颜色映射
|
||||
postTypeColors: {
|
||||
'PRODUCTION': '#409EFF',
|
||||
@@ -231,20 +197,19 @@ export default {
|
||||
'MAINTENANCE': '#E6A23C',
|
||||
'TECHNICAL': '#909399',
|
||||
'MANAGEMENT': '#F56C6C'
|
||||
}
|
||||
},
|
||||
// ECharts 相关
|
||||
chart: null,
|
||||
lastClickInfo: { nodeId: null, time: 0 }
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this._resizeHandler) {
|
||||
window.removeEventListener('resize', this._resizeHandler);
|
||||
this._resizeHandler = null;
|
||||
}
|
||||
if (this.network) {
|
||||
this.network.destroy();
|
||||
this.network = null;
|
||||
if (this.chart) {
|
||||
this.chart.dispose();
|
||||
this.chart = null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -260,123 +225,229 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** 初始化 vis.js 树图 */
|
||||
/** 初始化 ECharts 图 */
|
||||
initChart() {
|
||||
if (!this.$refs.treeChart) return;
|
||||
if (this.network) {
|
||||
this.network.destroy();
|
||||
if (this.chart) {
|
||||
this.chart.dispose();
|
||||
}
|
||||
|
||||
// 构建扁平化的 nodes 和 edges
|
||||
const nodesData = [];
|
||||
const edgesData = [];
|
||||
this.buildVisData(this.postList, null, nodesData, edgesData);
|
||||
const chartDom = this.$refs.treeChart;
|
||||
this.chart = echarts.init(chartDom, null, { renderer: 'canvas' });
|
||||
|
||||
if (nodesData.length === 0) {
|
||||
nodesData.push({ id: 0, label: '暂无岗位', font: { color: '#909399' }, shape: 'box' });
|
||||
if (this.postList.length === 0) {
|
||||
this.chart.setOption({
|
||||
title: {
|
||||
text: '暂无岗位数据',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
textStyle: { color: '#909399', fontSize: 14, fontWeight: 400 }
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const that = this;
|
||||
const options = {
|
||||
layout: {
|
||||
hierarchical: {
|
||||
enabled: true,
|
||||
direction: 'UD',
|
||||
sortMethod: 'directed',
|
||||
levelSeparation: 100,
|
||||
nodeSpacing: 150,
|
||||
treeSpacing: 200
|
||||
const { nodes, edges } = this.buildGraphData(this.postList);
|
||||
const { width, height } = this.calculateNodePositions(nodes);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
triggerOn: 'mousemove',
|
||||
formatter: params => {
|
||||
const data = params.data;
|
||||
if (!data || !data.postId) return '';
|
||||
const typeMap = { 'PRODUCTION': '生产岗', 'QUALITY': '质检岗', 'MAINTENANCE': '维修岗', 'TECHNICAL': '技术岗', 'MANAGEMENT': '管理岗' };
|
||||
const typeLabel = typeMap[data.postType] || '未分类';
|
||||
return `<div style="font-size:13px;font-weight:600;margin-bottom:4px">${data.name}</div>
|
||||
<div style="font-size:12px;color:#909399">类型:${typeLabel}</div>`;
|
||||
}
|
||||
},
|
||||
physics: {
|
||||
enabled: false
|
||||
},
|
||||
interaction: {
|
||||
hover: false,
|
||||
hoverConnectedEdges: false,
|
||||
selectable: false,
|
||||
dragNodes: false,
|
||||
zoomView: true,
|
||||
dragView: true
|
||||
},
|
||||
edges: {
|
||||
smooth: { type: 'curvedCW', roundness: 0.2 },
|
||||
color: '#bbb',
|
||||
width: 2,
|
||||
arrows: { to: { enabled: true, scaleFactor: 0.5 } }
|
||||
},
|
||||
nodes: {
|
||||
shape: 'box',
|
||||
size: 40,
|
||||
font: {
|
||||
color: '#fff',
|
||||
size: 12,
|
||||
face: 'Microsoft YaHei',
|
||||
multi: false
|
||||
series: [{
|
||||
type: 'graph',
|
||||
layout: 'none',
|
||||
roam: true,
|
||||
draggable: false,
|
||||
emphasis: {
|
||||
focus: 'none',
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(64,158,255,0.3)',
|
||||
shadowOffsetY: 3
|
||||
}
|
||||
},
|
||||
borderWidth: 0,
|
||||
margin: {
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
left: 14,
|
||||
right: 14
|
||||
data: nodes,
|
||||
links: edges,
|
||||
lineStyle: {
|
||||
color: '#C0C4CC',
|
||||
width: 2,
|
||||
curveness: 0
|
||||
},
|
||||
shadow: false
|
||||
}
|
||||
animationDuration: 600,
|
||||
animationEasing: 'cubicOut',
|
||||
animationDelay: (idx) => idx * 40
|
||||
}]
|
||||
};
|
||||
|
||||
const container = this.$refs.treeChart;
|
||||
this.network = new Network(container, { nodes: nodesData, edges: edgesData }, options);
|
||||
this.chart.setOption(option);
|
||||
this.chart.resize({ width: width, height: height });
|
||||
|
||||
// 节点点击事件
|
||||
this.network.on('click', function(params) {
|
||||
const nodeId = that.network.getNodeAt(params.pointer.DOM);
|
||||
if (nodeId) {
|
||||
const post = that.findPostById(that.postList, nodeId);
|
||||
if (post) {
|
||||
that.handleTreeNodeClick(post);
|
||||
}
|
||||
this.chart.off('click');
|
||||
this.chart.on('click', params => {
|
||||
const data = params.data;
|
||||
if (!data || !data.postId) return;
|
||||
|
||||
const now = Date.now();
|
||||
if (this.lastClickInfo.nodeId === data.postId && now - this.lastClickInfo.time < 300) {
|
||||
this.lastClickInfo = { nodeId: null, time: 0 };
|
||||
this.handleNodeDblClick(data);
|
||||
return;
|
||||
}
|
||||
this.lastClickInfo = { nodeId: data.postId, time: now };
|
||||
this.handleTreeNodeClick(data);
|
||||
});
|
||||
|
||||
// 窗口大小变化自适应
|
||||
const resizeHandler = () => {
|
||||
if (that.network) {
|
||||
that.network.fit({ animation: false });
|
||||
if (this._resizeHandler) {
|
||||
window.removeEventListener('resize', this._resizeHandler);
|
||||
}
|
||||
this._resizeHandler = () => {
|
||||
if (this.chart) {
|
||||
this.chart.resize();
|
||||
}
|
||||
};
|
||||
window.addEventListener('resize', resizeHandler);
|
||||
this._resizeHandler = resizeHandler;
|
||||
window.addEventListener('resize', this._resizeHandler);
|
||||
},
|
||||
|
||||
/** 构建 vis.js 扁平化节点和边数据 */
|
||||
buildVisData(nodes, parentId, nodesData, edgesData) {
|
||||
if (!nodes) return;
|
||||
/** 计算节点位置 */
|
||||
calculateNodePositions(nodes) {
|
||||
const depthNodes = {};
|
||||
nodes.forEach(node => {
|
||||
const color = this.postTypeColors[node.postType] || '#409EFF';
|
||||
nodesData.push({
|
||||
id: node.postId,
|
||||
label: node.postName || '未命名',
|
||||
color: { background: color, border: color }
|
||||
if (!depthNodes[node.depth]) {
|
||||
depthNodes[node.depth] = [];
|
||||
}
|
||||
depthNodes[node.depth].push(node);
|
||||
});
|
||||
|
||||
const maxDepth = Math.max(...Object.keys(depthNodes).map(Number));
|
||||
const maxNodesPerDepth = Math.max(...Object.values(depthNodes).map(nodes => nodes.length));
|
||||
|
||||
const horizontalGap = 30;
|
||||
const verticalGap = 140;
|
||||
|
||||
const avgNodeWidth = nodes.reduce((sum, node) => sum + node.symbolSize[0], 0) / nodes.length;
|
||||
const avgNodeHeight = nodes.reduce((sum, node) => sum + node.symbolSize[1], 0) / nodes.length;
|
||||
|
||||
const totalWidth = Math.max(1200, maxNodesPerDepth * (avgNodeWidth + horizontalGap) + 200);
|
||||
const totalHeight = Math.max(800, (maxDepth + 1) * verticalGap + avgNodeHeight + 100);
|
||||
const topPadding = 80;
|
||||
|
||||
Object.keys(depthNodes).forEach(depth => {
|
||||
const depthNum = parseInt(depth);
|
||||
const nodesAtDepth = depthNodes[depthNum];
|
||||
const y = topPadding + depthNum * verticalGap;
|
||||
|
||||
const totalNodeWidth = nodesAtDepth.reduce((sum, node) => {
|
||||
return sum + node.symbolSize[0];
|
||||
}, 0) + (nodesAtDepth.length - 1) * horizontalGap;
|
||||
|
||||
const startX = Math.max(60, (totalWidth - totalNodeWidth) / 2);
|
||||
|
||||
let currentX = startX;
|
||||
nodesAtDepth.forEach(node => {
|
||||
node.x = currentX;
|
||||
node.y = y;
|
||||
currentX += node.symbolSize[0] + horizontalGap;
|
||||
});
|
||||
});
|
||||
|
||||
return { width: totalWidth, height: totalHeight };
|
||||
},
|
||||
|
||||
/** 构建 Graph 数据 */
|
||||
buildGraphData(nodes, parentId = null, depth = 0) {
|
||||
const nodesResult = [];
|
||||
const edgesResult = [];
|
||||
|
||||
nodes.forEach(node => {
|
||||
const bgColor = depth === 0 ? '#1A365D' : depth === 1 ? '#2C5282' : depth === 2 ? '#3182CE' : '#EBF8FF';
|
||||
const labelColor = depth <= 2 ? '#FFFFFF' : '#303133';
|
||||
const borderColor = depth > 2 ? '#3182CE' : 'transparent';
|
||||
const borderWidth = depth > 2 ? 1 : 0;
|
||||
|
||||
const fontSize = depth === 0 ? 16 : depth === 1 ? 14 : depth === 2 ? 13 : 12;
|
||||
const nodeHeight = depth === 0 ? 56 : depth === 1 ? 52 : depth === 2 ? 48 : 44;
|
||||
const textLength = node.postName.length;
|
||||
const nodeWidth = Math.max(160, textLength * fontSize * 0.65 + 50);
|
||||
|
||||
nodesResult.push({
|
||||
id: node.postId,
|
||||
name: node.postName,
|
||||
postName: node.postName,
|
||||
postId: node.postId,
|
||||
postType: node.postType,
|
||||
depth: depth,
|
||||
symbol: 'rect',
|
||||
symbolSize: [nodeWidth, nodeHeight],
|
||||
itemStyle: {
|
||||
color: bgColor,
|
||||
borderColor: borderColor,
|
||||
borderWidth: borderWidth,
|
||||
borderRadius: 4,
|
||||
shadowBlur: 8,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.12)',
|
||||
shadowOffsetY: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inside',
|
||||
color: labelColor,
|
||||
fontSize: fontSize,
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Microsoft YaHei',
|
||||
letterSpacing: 0.5
|
||||
}
|
||||
});
|
||||
|
||||
if (parentId !== null) {
|
||||
edgesData.push({
|
||||
from: parentId,
|
||||
to: node.postId
|
||||
edgesResult.push({
|
||||
source: parentId,
|
||||
target: node.postId,
|
||||
lineStyle: {
|
||||
color: '#C0C4CC',
|
||||
width: 2,
|
||||
curveness: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (node.children && node.children.length > 0) {
|
||||
this.buildVisData(node.children, node.postId, nodesData, edgesData);
|
||||
const childResult = this.buildGraphData(node.children, node.postId, depth + 1);
|
||||
nodesResult.push(...childResult.nodes);
|
||||
edgesResult.push(...childResult.edges);
|
||||
}
|
||||
});
|
||||
|
||||
return { nodes: nodesResult, edges: edgesResult };
|
||||
},
|
||||
|
||||
/** 树节点点击 */
|
||||
/** 树节点单击 */
|
||||
handleTreeNodeClick(post) {
|
||||
this.currentPost = post;
|
||||
},
|
||||
|
||||
/** 树节点双击 - 打开职责弹窗 */
|
||||
handleNodeDblClick(post) {
|
||||
this.currentPost = post;
|
||||
this.dutyDialogTitle = `岗位职责 - ${post.postName}`;
|
||||
this.dutyDialogVisible = true;
|
||||
this.loadDutyList(post.postId);
|
||||
},
|
||||
|
||||
/** 通过按钮查看职责 */
|
||||
handleShowDuty(post) {
|
||||
this.handleNodeDblClick(post);
|
||||
},
|
||||
|
||||
/** 递归查找岗位 */
|
||||
findPostById(nodes, postId) {
|
||||
for (let node of nodes) {
|
||||
@@ -592,40 +663,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.post-content {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.tree-panel,
|
||||
.duty-panel {
|
||||
border: 1px solid #EBEEF5;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.tree-panel {
|
||||
min-height: 600px;
|
||||
}
|
||||
|
||||
.duty-panel {
|
||||
min-height: 600px;
|
||||
min-height: calc(100vh - 140px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
background: #FAFAFA;
|
||||
padding: 12px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
}
|
||||
|
||||
.panel-hint {
|
||||
@@ -633,60 +683,28 @@ export default {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.panel-sub-title {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
max-width: 140px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ---- ECharts 树图容器 ---- */
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
min-height: 600px;
|
||||
width: 100%;
|
||||
height: 540px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.post-info-bar {
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-top: 1px solid #EBEEF5;
|
||||
background: #FAFAFA;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.post-info-bar .post-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.post-info-bar .post-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* 自定义职责列表样式 */
|
||||
.duty-list-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.duty-toolbar {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
}
|
||||
|
||||
.duty-list {
|
||||
flex: 1;
|
||||
/* ---- 职责弹窗列表样式 ---- */
|
||||
.duty-dialog-wrap {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
padding: 8px 14px 14px;
|
||||
}
|
||||
|
||||
.duty-dialog-toolbar {
|
||||
padding: 0 0 12px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.duty-dialog-list {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.duty-item {
|
||||
@@ -714,11 +732,6 @@ export default {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.duty-sort {
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.duty-content {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
@@ -740,24 +753,4 @@ export default {
|
||||
padding-top: 6px;
|
||||
border-top: 1px dashed #EBEEF5;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
color: #C0C4CC;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.empty-hint i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.empty-hint p {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user