feat: 新增多个业务组件与功能,优化页面样式与依赖
1. 新增QRCode二维码组件、ChecklistSelect选择器、DragResizePanel拖拽面板 2. 更新项目依赖,新增qrcode、html2canvas等工具库 3. 重构侧边栏Logo与导航栏样式,调整布局与视觉效果 4. 优化设备巡检日报页面,支持多日期范围查询与多维度统计 5. 修复迷你按钮样式,更新设备巡检记录页面功能与交互 6. 更新项目logo资源与部分样式变量
This commit is contained in:
72
ruoyi-ui/src/components/ChecklistSelect/index.vue
Normal file
72
ruoyi-ui/src/components/ChecklistSelect/index.vue
Normal 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>
|
||||
159
ruoyi-ui/src/components/DragResizePanel/index.vue
Normal file
159
ruoyi-ui/src/components/DragResizePanel/index.vue
Normal 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的初始大小,最大值和最小值,默认值为300px,10000px,100px
|
||||
// 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>
|
||||
72
ruoyi-ui/src/components/QRCode/index.vue
Normal file
72
ruoyi-ui/src/components/QRCode/index.vue
Normal 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>
|
||||
Reference in New Issue
Block a user