feat: 新增多个业务组件与功能,优化页面样式与依赖

1. 新增QRCode二维码组件、ChecklistSelect选择器、DragResizePanel拖拽面板
2. 更新项目依赖,新增qrcode、html2canvas等工具库
3. 重构侧边栏Logo与导航栏样式,调整布局与视觉效果
4. 优化设备巡检日报页面,支持多日期范围查询与多维度统计
5. 修复迷你按钮样式,更新设备巡检记录页面功能与交互
6. 更新项目logo资源与部分样式变量
This commit is contained in:
2026-06-08 14:21:39 +08:00
parent 022312d02c
commit dd70c94dd7
12 changed files with 1920 additions and 1036 deletions

View File

@@ -0,0 +1,72 @@
<template>
<el-select
v-model="innerValue"
filterable
:loading="loading"
placeholder="请选择检验清单"
clearable
@change="handleChange"
style="width: 100%"
>
<el-option
v-for="item in options"
:key="item.checkId"
:label="`${item.checkNo || '无编号'} - ${item.partName || ''}`"
:value="item.checkId"
/>
</el-select>
</template>
<script>
import { listEquipmentChecklist } from "@/api/mill/equipmentChecklist";
export default {
name: "ChecklistSelect",
props: {
value: {
type: [String, Number],
default: ""
}
},
data() {
return {
loading: false,
options: [],
innerValue: this.value
};
},
watch: {
value(val) {
this.innerValue = val;
}
},
mounted() {
this.loadAll();
},
methods: {
async loadAll() {
this.loading = true;
try {
const res = await listEquipmentChecklist({ pageNum: 1, pageSize: 9999 });
if (res.code === 200) {
this.options = res.rows || [];
}
} catch (e) {
console.error("获取检验清单列表失败", e);
} finally {
this.loading = false;
}
},
handleChange(val) {
if (!val) {
this.$emit('input', "");
this.$emit('change', "", null);
} else {
this.$emit('input', val);
const row = this.options.find(o => o.checkId === val);
this.$emit('change', val, row || null);
}
}
}
};
</script>

View File

@@ -0,0 +1,159 @@
<template>
<div class="drag-resize-panel" :class="{ 'vertical': direction === 'vertical' }">
<div class="panel-a" :style="panelAstyle">
<slot name="panelA"></slot>
</div>
<div class="resizer" :class="{ 'vertical-resizer': direction === 'vertical' }" @mousedown="startResize"></div>
<div class="panel-b" :style="panelBstyle">
<slot name="panelB"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'DragResizePanel',
props: {
direction: {
type: String,
default: 'horizontal',
validator: function(value) {
return ['horizontal', 'vertical'].includes(value);
}
},
// 设置panelA的初始大小最大值和最小值默认值为300px10000px100px
// panelB占据剩余空间
initialSize: {
type: Number,
default: 300
},
minSize: {
type: Number,
default: 100
},
maxSize: {
type: Number,
default: 10000
}
},
data() {
return {
currentSize: this.initialSize,
isResizing: false,
startPosition: 0
};
},
computed: {
panelAstyle() {
if (this.direction === 'horizontal') {
return { width: this.currentSize + 'px' };
} else {
return { height: this.currentSize + 'px' };
}
},
panelBstyle() {
if (this.direction === 'horizontal') {
return { flex: 1 };
} else {
return { flex: 1 };
}
}
},
methods: {
startResize(e) {
e.preventDefault();
this.isResizing = true;
this.startPosition = this.direction === 'horizontal' ? e.clientX : e.clientY;
this.startSize = this.currentSize;
document.addEventListener('mousemove', this.handleResize);
document.addEventListener('mouseup', this.stopResize);
},
handleResize(e) {
if (!this.isResizing) return;
const delta = this.direction === 'horizontal' ? e.clientX - this.startPosition : e.clientY - this.startPosition;
const newSize = this.startSize + delta;
if (newSize >= this.minSize && newSize <= this.maxSize) {
this.currentSize = newSize;
}
},
stopResize() {
this.isResizing = false;
document.removeEventListener('mousemove', this.handleResize);
document.removeEventListener('mouseup', this.stopResize);
}
}
};
</script>
<style scoped>
.drag-resize-panel {
display: flex;
height: 100%;
overflow: hidden;
}
.drag-resize-panel.vertical {
flex-direction: column;
}
.panel-a {
position: relative;
overflow: hidden;
}
.panel-b {
position: relative;
overflow: auto;
}
.resizer {
width: 4px;
cursor: col-resize;
position: relative;
transition: background-color 0.3s;
height: 100%;
}
.resizer.vertical-resizer {
width: 100%;
height: 4px;
cursor: row-resize;
}
.resizer:hover {
background-color: #409eff;
}
.resizer::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 40px;
width: 2px;
background-color: #c0c4cc;
border-radius: 1px;
transition: background-color 0.3s;
}
.resizer.vertical-resizer::before {
width: 40px;
height: 2px;
}
.resizer:hover::before {
background-color: #409eff;
}
.resizer:active {
background-color: #409eff;
}
.resizer:active::before {
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,72 @@
<template>
<div class="qrcode-wrapper">
<canvas ref="qrcode"></canvas>
<p v-if="text" class="qrcode-text">{{ text }}</p>
</div>
</template>
<script>
import QRCode from 'qrcode';
export default {
name: 'QRCode',
props: {
content: {
type: String,
required: true
},
size: {
type: Number,
default: 90
},
text: {
type: String,
default: ''
}
},
data() {
return {
qrcode: null
}
},
watch: {
content: {
handler(newVal, oldVal) {
if (newVal && newVal !== oldVal) {
this.generateQRCode();
}
},
immediate: true
}
},
mounted() {
this.generateQRCode();
},
methods: {
generateQRCode() {
const el = this.$refs.qrcode;
const content = this.content;
if (!content || !el) return;
QRCode.toCanvas(el, content, {
width: this.size,
height: this.size,
margin: 0
});
}
}
}
</script>
<style scoped>
.qrcode-wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.qrcode-text {
margin-top: 8px;
font-size: 14px;
color: #303133;
text-align: center;
}
</style>