Compare commits
No commits in common. "master" and "3.1.2026072315" have entirely different histories.
master
...
3.1.202607
|
|
@ -61,8 +61,6 @@
|
|||
:default-viewport="{ zoom: 0.9 }"
|
||||
:min-zoom="0.2"
|
||||
:max-zoom="2"
|
||||
:connection-radius="8"
|
||||
:connect-on-click="false"
|
||||
:nodes-draggable="true"
|
||||
:nodes-connectable="!readonly"
|
||||
:elements-selectable="true"
|
||||
|
|
@ -89,14 +87,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 复用现有单步表单:新增/编辑一条 Route;key 随每次打开递增强制重建实例,预填走 prop 在挂载时即确定 -->
|
||||
<!-- 复用现有单步表单:新增/编辑一条 Route;key 随每次打开递增,强制重建实例杜绝上次内容残留 -->
|
||||
<route-form
|
||||
v-if="dialogVisible"
|
||||
:key="dialogSeq"
|
||||
ref="routeForm"
|
||||
:count="count"
|
||||
:routepack="routepack"
|
||||
:prefill="formPrefill"
|
||||
@success="onFormSuccess"
|
||||
@closed="dialogVisible = false"
|
||||
></route-form>
|
||||
|
|
@ -146,7 +143,6 @@ export default {
|
|||
count: 0,
|
||||
dialogVisible: false,
|
||||
dialogSeq: 0,
|
||||
formPrefill: null,
|
||||
// 左侧物料面板
|
||||
matType: "30",
|
||||
matKeyword: "",
|
||||
|
|
@ -157,7 +153,6 @@ export default {
|
|||
// 连线手势内部状态
|
||||
connectSource: null,
|
||||
connectMade: false,
|
||||
connectStartPos: null,
|
||||
// 仅初次加载自动适配一次,避免编辑时视图跳动
|
||||
hasFitted: false,
|
||||
};
|
||||
|
|
@ -289,12 +284,9 @@ export default {
|
|||
if (pid) routeNodeIds.add(pid);
|
||||
this.seedNodes = this.seedNodes.filter((s) => !routeNodeIds.has(s.id));
|
||||
|
||||
const prevCount = this.nodes.length;
|
||||
this.edges = edges;
|
||||
this.nodes = layoutGraph(routeNodes.concat(this.seedNodes), edges);
|
||||
this.syncStore();
|
||||
// 节点增减后 dagre 会整体重排,重新适配视野,避免“看着是空白其实有节点”导致拉线误吸附
|
||||
if (this.hasFitted && this.nodes.length !== prevCount) this.doFit();
|
||||
},
|
||||
|
||||
/** 把本地 nodes/edges 命令式同步到 Vue Flow store(先节点后边,避免边找不到端点) */
|
||||
|
|
@ -384,18 +376,11 @@ export default {
|
|||
onConnectStart(params) {
|
||||
this.connectSource = (params && (params.nodeId || params.source)) || null;
|
||||
this.connectMade = false;
|
||||
const oe = params && params.event;
|
||||
this.connectStartPos = oe && oe.clientX != null ? { x: oe.clientX, y: oe.clientY } : null;
|
||||
},
|
||||
/** 连到已有节点:新建一条 Route,预填输入/输出物料 */
|
||||
onConnect(conn) {
|
||||
this.connectMade = true;
|
||||
if (this.readonly) return;
|
||||
// 误吸附保护:吸回起点自身或目标无效时,按“拉到空白”处理,只带输入
|
||||
if (!conn.target || conn.target === conn.source) {
|
||||
this.openAddFrom(conn.source);
|
||||
return;
|
||||
}
|
||||
const src = this.nodes.find((n) => n.id === conn.source);
|
||||
const tgt = this.nodes.find((n) => n.id === conn.target);
|
||||
// 注意:id 为雪花大整数,必须保持字符串,切勿 Number() 否则精度丢失
|
||||
|
|
@ -407,28 +392,16 @@ export default {
|
|||
});
|
||||
},
|
||||
/** 拉线到空白处:新建工序,输出物料留空由后端自动生成下一节点 */
|
||||
onConnectEnd(evt) {
|
||||
onConnectEnd() {
|
||||
const src = this.connectSource;
|
||||
const made = this.connectMade;
|
||||
const startPos = this.connectStartPos;
|
||||
this.connectSource = null;
|
||||
this.connectMade = false;
|
||||
this.connectStartPos = null;
|
||||
if (this.readonly || made || !src) return;
|
||||
// 单纯点击手柄(几乎无位移)不算拉线,不弹新增表单
|
||||
const oe = evt && evt.clientX != null ? evt : evt && evt.event;
|
||||
if (startPos && oe && oe.clientX != null) {
|
||||
const dist = Math.hypot(oe.clientX - startPos.x, oe.clientY - startPos.y);
|
||||
if (dist < 8) return;
|
||||
}
|
||||
this.openAddFrom(src);
|
||||
},
|
||||
/** 以某节点为输入新建工序:只带输入物料,其余字段一律默认值 */
|
||||
openAddFrom(srcId) {
|
||||
const node = this.nodes.find((n) => n.id === srcId);
|
||||
const node = this.nodes.find((n) => n.id === src);
|
||||
// id 为雪花大整数,保持字符串
|
||||
this.openForm("add", {
|
||||
material_in: srcId,
|
||||
material_in: src,
|
||||
material_in_name: node ? node.data.label : "",
|
||||
});
|
||||
},
|
||||
|
|
@ -506,13 +479,13 @@ export default {
|
|||
this.openForm("add", null);
|
||||
},
|
||||
|
||||
/** 打开单步表单;prefill 为 null 表示纯新增。预填经 prop 传入,在表单挂载 open 时一次性应用 */
|
||||
/** 打开单步表单;prefill 为 null 表示纯新增 */
|
||||
openForm(mode, prefill) {
|
||||
this.dialogSeq += 1;
|
||||
this.formPrefill = prefill || null;
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.routeForm.open(mode);
|
||||
const inst = this.$refs.routeForm.open(mode);
|
||||
if (prefill) inst.setData(prefill);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -305,8 +305,6 @@ export default {
|
|||
props: {
|
||||
count: { type: Number, default : 0 },
|
||||
routepack: { type: String, default: "" },
|
||||
// 打开时的预填数据(画布拉线传入):挂载即确定,避免事后setData在任何时序下带入脏数据
|
||||
prefill: { type: Object, default: null },
|
||||
},
|
||||
emits: ["success", "closed"],
|
||||
data() {
|
||||
|
|
@ -505,7 +503,6 @@ export default {
|
|||
let that = this;
|
||||
that.mode = mode;
|
||||
that.resetForm();
|
||||
if (that.prefill) that.setData(that.prefill);
|
||||
that.visible = true;
|
||||
that.$nextTick(() => {
|
||||
// 仅新增时按现有数量预填排序;编辑时保留 setData 带入的原 sort
|
||||
|
|
|
|||
|
|
@ -107,7 +107,13 @@
|
|||
</el-table-column>
|
||||
<el-table-column label="总数">
|
||||
<template #default="scope">
|
||||
<span @click="handleClick(scope.row)" style="color:#0052d9;">{{ scope.row.总切片数 }}</span>
|
||||
<span v-if="scope.row.总切片数" @click="handleClick(scope.row)" style="color:#0052d9;">{{ scope.row.总切片数 }}</span>
|
||||
<span v-else @click="handleClick(scope.row)" style="color:#0052d9;">{{ scope.row.总生产数 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合格率" prop="合格率">
|
||||
<template #default="scope">
|
||||
<span>{{((scope.row.总切片合格数/scope.row.总切片数)*100).toFixed(2) }}%</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</scTable>
|
||||
|
|
@ -131,8 +137,11 @@
|
|||
<el-table-column label="物料名" prop="物料名" :filters="nameFilters" :filter-method="filterName" filter-placement="bottom-end">
|
||||
</el-table-column>
|
||||
<el-table-column label="总数" prop="总切片数">
|
||||
</el-table-column>
|
||||
<el-table-column label="合格率" prop="合格率">
|
||||
<template #default="scope">
|
||||
<span @click="handleClick(scope.row)" style="color:#0052d9;">{{ scope.row.总切片数 }}</span>
|
||||
<span v-if="scope.row.总切片数>0">{{((scope.row.总切片合格数/scope.row.总切片数)*100).toFixed(2) }}%</span>
|
||||
<span v-else>0%</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</scTable>
|
||||
|
|
@ -140,86 +149,13 @@
|
|||
</el-container>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-drawer
|
||||
v-model="boardVisible"
|
||||
:title="boardTitle"
|
||||
size="80%"
|
||||
direction="rtl"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="board-drawer-body">
|
||||
<div class="board-drawer-table">
|
||||
<el-table :data="boardList" border stripe height="100%" style="width: 100%;">
|
||||
<el-table-column type="index" label="序号" width="60" />
|
||||
<el-table-column label="板段号" prop="板段号" />
|
||||
<el-table-column label="像位移" prop="像位移" />
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="board-drawer-chart">
|
||||
<div class="board-chart-scroll">
|
||||
<scEcharts
|
||||
:option="chartOption"
|
||||
:style="{ width: chartWidth + 'px', height: '100%' }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import scEcharts from "@/components/scEcharts";
|
||||
export default {
|
||||
name: "workerTimes",
|
||||
components: { scEcharts },
|
||||
computed: {
|
||||
chartWidth() {
|
||||
const perPoint = 22;
|
||||
const minWidth = 600;
|
||||
return Math.max(minWidth, (this.boardList.length || 1) * perPoint);
|
||||
},
|
||||
chartOption() {
|
||||
const xs = this.boardList.map(i => i.板段号);
|
||||
const ys = this.boardList.map(i => (i.像位移 == null ? null : Number(i.像位移)));
|
||||
return {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 50, right: 20, top: 30, bottom: 100 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xs,
|
||||
axisLabel: { rotate: 85, interval: 0, fontSize: 11 }
|
||||
},
|
||||
yAxis: { type: 'value', name: '像位移' },
|
||||
series: [{
|
||||
name: '像位移',
|
||||
type: 'line',
|
||||
data: ys,
|
||||
connectNulls: false,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
markLine: {
|
||||
symbol: 'none',
|
||||
silent: true,
|
||||
data: [
|
||||
{
|
||||
yAxis: 100,
|
||||
lineStyle: { color: '#f5a623', width: 1.5, type: 'dashed' },
|
||||
label: { formatter: '标准 100', position: 'insideEndTop', color: '#f5a623' }
|
||||
},
|
||||
{
|
||||
yAxis: 200,
|
||||
lineStyle: { color: '#f5222d', width: 1.5, type: 'dashed' },
|
||||
label: { formatter: '标准 200', position: 'insideEndTop', color: '#f5222d' }
|
||||
}
|
||||
]
|
||||
}
|
||||
}]
|
||||
};
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
query:{
|
||||
|
|
@ -239,9 +175,6 @@ export default {
|
|||
filterNames:[],
|
||||
visible:false,
|
||||
userName:'',
|
||||
boardVisible:false,
|
||||
boardTitle:'',
|
||||
boardList:[],
|
||||
code:'lineDay_p',
|
||||
activeName:'shengchan',
|
||||
apiObj: this.$API.system.user.list,
|
||||
|
|
@ -377,16 +310,6 @@ export default {
|
|||
deptChange(){
|
||||
this.getData();
|
||||
},
|
||||
handleClick(row){
|
||||
let that = this;
|
||||
let detail = row.板段明细;
|
||||
if(typeof detail === 'string'){
|
||||
try{ detail = JSON.parse(detail); }catch(e){ detail = []; }
|
||||
}
|
||||
that.boardList = Array.isArray(detail) ? detail : [];
|
||||
that.boardTitle = (row.工装编号 || row.开台人员姓名 || '') + ' 板段列表';
|
||||
that.boardVisible = true;
|
||||
},
|
||||
handleQuery(){
|
||||
let that = this;
|
||||
that.getData();
|
||||
|
|
@ -396,26 +319,4 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.board-drawer-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
.board-drawer-table {
|
||||
flex: 0 0 60%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.board-drawer-chart {
|
||||
flex: 0 0 40%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.board-chart-scroll {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
<!--
|
||||
物料仓库合格库存统计
|
||||
数据来源: BI 数据集 code=materialCount2 (POST api/bi/dataset/{code}/exec)
|
||||
抽自 factory_web/src/views/inm/good.vue 的"物料仓库合格库存统计"抽屉内容
|
||||
-->
|
||||
<template>
|
||||
<el-container v-loading="loading">
|
||||
<el-header style="display: flex; align-items: center;">
|
||||
<span style="font-weight: 600;">物料仓库合格库存统计</span>
|
||||
<el-text style="margin-left: 16px;">仓库总数:{{ totalCount }}</el-text>
|
||||
<el-button type="primary" icon="el-icon-refresh" @click="getProductCount" :loading="loading" style="margin-left: auto;">刷新</el-button>
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-header style="height: 40%;">
|
||||
<scTable :data="tableData" hideDo hidePagination row-key="material_name" style="width: 100%;">
|
||||
<el-table-column label="物料名" prop="material_name"></el-table-column>
|
||||
<el-table-column label="数量" prop="count"></el-table-column>
|
||||
</scTable>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<scEcharts :option="echartsOptions" height="100%"></scEcharts>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import scEcharts from "@/components/scEcharts/index.vue";
|
||||
|
||||
export default {
|
||||
name: "goodStatistic",
|
||||
components: { scEcharts },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
totalCount: 0,
|
||||
echartsOptions: {},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getProductCount();
|
||||
},
|
||||
methods: {
|
||||
getProductCount() {
|
||||
let that = this;
|
||||
let query = {
|
||||
material_types: "10",
|
||||
select_material: "material.name as material_name",
|
||||
groupby_material: "material.name",
|
||||
};
|
||||
that.loading = true;
|
||||
that.$API.bi.dataset.exec
|
||||
.req("materialCount2", { query: query })
|
||||
.then((res) => {
|
||||
let data = (that.tableData = res.data2.ds0 || []);
|
||||
let totalCount = 0;
|
||||
data.forEach((item) => {
|
||||
totalCount += Number(item.count) || 0;
|
||||
});
|
||||
that.totalCount = totalCount;
|
||||
if (res.echart_options) {
|
||||
that.echartsOptions = JSON.parse(res.echart_options);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
that.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
物料保障车间-仓库数量汇总表
|
||||
数据来源: BI 数据集 code=wh_material_summary (POST api/bi/dataset/{code}/exec)
|
||||
返回 res.data2.ds0~ds4, 分组维度为 material.model(型号)
|
||||
返回 res.data2.ds0~ds3, 分组维度为 material.model(型号)
|
||||
菜单/路由由后端下发, component 填 statistics/material_wh_summary
|
||||
-->
|
||||
<template>
|
||||
|
|
@ -25,13 +25,15 @@
|
|||
|
||||
<el-main id="elMain" v-loading="loading">
|
||||
<!-- KPI 概览 -->
|
||||
<div class="kpi-grid">
|
||||
<el-card v-for="k in kpiList" :key="k.label" shadow="hover" class="kpi-card" :style="{ borderLeft: '4px solid ' + k.color }">
|
||||
<div class="kpi-label">{{ k.label }} · 现有库存合计</div>
|
||||
<div class="kpi-value" :style="{ color: k.color }">{{ fmtNum(k.total) }}</div>
|
||||
<div class="kpi-sub">覆盖 {{ k.count }} 个型号</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<el-row :gutter="15" style="margin-bottom: 15px;">
|
||||
<el-col :md="6" :sm="12" v-for="k in kpiList" :key="k.label">
|
||||
<el-card shadow="hover" class="kpi-card" :style="{ borderLeft: '4px solid ' + k.color }">
|
||||
<div class="kpi-label">{{ k.label }} · 现有库存合计</div>
|
||||
<div class="kpi-value" :style="{ color: k.color }">{{ fmtNum(k.total) }}</div>
|
||||
<div class="kpi-sub">覆盖 {{ k.count }} 个型号</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 库存量对比图 -->
|
||||
<el-card shadow="never" style="margin-bottom: 15px;">
|
||||
|
|
@ -101,18 +103,6 @@
|
|||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="12">
|
||||
<el-card shadow="never" class="rpt-card">
|
||||
<template #header><b>半成品库周数据</b><span class="hd-tip">(入库=采购入库+生产入库 / 出库=生产领料)</span></template>
|
||||
<el-table :data="ds4" border stripe size="small" show-summary :summary-method="sumRow">
|
||||
<el-table-column label="型号" prop="型号" width="110" />
|
||||
<el-table-column label="入库数据" prop="入库数据" align="right" :formatter="numCell" />
|
||||
<el-table-column label="出库数据" prop="出库数据" align="right" :formatter="numCell" />
|
||||
<el-table-column label="现有库存" prop="现有库存" align="right" :formatter="numCell" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 设置安全库存上下限 -->
|
||||
|
|
@ -146,7 +136,6 @@ import scEcharts from "@/components/scEcharts/index.vue";
|
|||
const BLOCKS = [
|
||||
{ key: "原料", color: "#409EFF" },
|
||||
{ key: "成品", color: "#67C23A" },
|
||||
{ key: "半成品", color: "#B37FEB" },
|
||||
{ key: "白玻", color: "#E6A23C" },
|
||||
{ key: "黑玻", color: "#909399" },
|
||||
];
|
||||
|
|
@ -162,7 +151,6 @@ export default {
|
|||
ds1: [],
|
||||
ds2: [],
|
||||
ds3: [],
|
||||
ds4: [],
|
||||
safeDialog: {
|
||||
visible: false,
|
||||
saving: false,
|
||||
|
|
@ -279,7 +267,6 @@ export default {
|
|||
this.ds1 = d.ds1 || [];
|
||||
this.ds2 = d.ds2 || [];
|
||||
this.ds3 = d.ds3 || [];
|
||||
this.ds4 = d.ds4 || [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
|
|
@ -348,19 +335,8 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.kpi-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@media (max-width: 992px) {
|
||||
.kpi-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
.kpi-card {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.kpi-label {
|
||||
color: #909399;
|
||||
|
|
@ -915,7 +915,6 @@ export default {
|
|||
this.imputDisable = true;
|
||||
await this.getInputText(data);
|
||||
this.imputDisable = false;
|
||||
await this.$nextTick();
|
||||
this.$refs.codeInput.focus();
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -87,21 +87,12 @@
|
|||
>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.submit_time!==null"
|
||||
link size="small"
|
||||
@click="check_revert(scope.row)"
|
||||
v-auth="'ftestwork.create'"
|
||||
type="primary"
|
||||
>撤回
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
link size="small"
|
||||
@click="check_delete(scope.row)"
|
||||
v-auth="'ftestwork.create'"
|
||||
type="danger"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</scTable>
|
||||
|
|
@ -168,17 +159,7 @@ export default {
|
|||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
check_delete(row){
|
||||
let that = this;
|
||||
this.$confirm(`确定删除该检验吗?`, "提示", {
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
that.$API.qm.ftestwork.delete.req(row.id).then((res) => {
|
||||
this.$refs.table.refresh();
|
||||
})
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
handleQuery() {
|
||||
this.$refs.table.queryData(this.query);
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue