master
lynn 6 months ago
parent 0a2e7ccc8e
commit 60e14c3180

@ -45,7 +45,7 @@
<Button v-if="row.status === 2" type="success" size="small" style="border-radius: 6px;" ghost @click="showSummaryModal(row)"></Button>
<template v-if="row.status === 2">
<el-dropdown trigger="hover" @command="command => handleMoreCommand(command, row)">
<Button type="info" size="small" style="border-radius: 6px;" ghost>更多<i class="el-icon-arrow-down el-icon--right"></i></Button>
<Button type="info" size="small" style="border-radius: 6px;" ghost>更多<i class="el-icon-arrow-down el-icon--right" /></Button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="edit">编辑</el-dropdown-item>
<el-dropdown-item command="delete">删除</el-dropdown-item>
@ -165,6 +165,12 @@
@on-clear="handleDateClear"
/>
</FormItem>
<FormItem label="盘点类型" prop="type">
<Select v-model="planModal.form.type" style="width: 200px">
<Option :value="1">日常检查</Option>
<Option :value="2">年度盘点</Option>
</Select>
</FormItem>
<FormItem label="备注" prop="remark">
<Input v-model="planModal.form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
</FormItem>
@ -341,7 +347,7 @@
<template slot="photos" slot-scope="{ row }">
<div v-if="row.files && row.files.length" class="photo-list">
<div v-for="(file, index) in row.files.slice(0, 3)" :key="index" class="photo-item">
<img :src="file.url" @click="previewImage(file.url)" />
<img :src="file.url" @click="previewImage(file.url)">
</div>
<div v-if="row.files.length > 3" class="photo-more">
+{{ row.files.length - 3 }}
@ -370,7 +376,7 @@
<!-- 预览弹窗 -->
<div v-if="previewUrl" class="image-preview-modal" @click="closePreview">
<img :src="previewUrl" class="image-preview-large" />
<img :src="previewUrl" class="image-preview-large">
</div>
<!-- Plan Detail Modal -->
@ -426,7 +432,7 @@
<template slot="photos" slot-scope="{ row }">
<div v-if="row.files && row.files.length" class="photo-list">
<div v-for="(file, index) in row.files.slice(0, 3)" :key="index" class="photo-item">
<img :src="file.url" @click="previewImage(file.url)" />
<img :src="file.url" @click="previewImage(file.url)">
</div>
<div v-if="row.files.length > 3" class="photo-more">
+{{ row.files.length - 3 }}
@ -454,7 +460,11 @@
</Modal>
<!-- 盘点小结 Modal -->
<Modal v-model="summaryModal.visible" title="盘点小结" width="800">
<Modal
v-model="summaryModal.visible"
title="盘点小结"
width="800"
>
<div>
<div id="print-table">
<div style="text-align: center; font-size: 20px; font-weight: bold; margin-bottom: 12px;">盘点小结</div>
@ -488,22 +498,38 @@
<td>{{ summaryModal.data ? summaryModal.data.actualCount : '' }}</td>
</tr>
<tr>
<td>签字区</td>
<td class="sign-cell"></td>
<td class="sign-cell">签字区</td>
<td style="text-align: center">
</td>
</tr>
</table>
</div>
<div style="margin: 24px 0 8px 0;">
<span style="font-weight: bold;">签字图片上传</span>
<div v-if="summaryModal.signImage" style="margin-top: 8px;">
<img :src="summaryModal.signImage" style="max-width: 200px; max-height: 100px; border: 1px solid #eee;" />
<div style="position: relative; display: inline-block;">
<img
:src="summaryModal.signImage"
style="max-width: 200px; max-height: 100px; border: 1px solid #eee; cursor: pointer;"
@click="previewImage(summaryModal.signImage)"
>
<Button
type="error"
size="small"
style="position: absolute; top: 5px; right: 5px;"
@click.stop="summaryModal.signImage = null; summaryModal.signImageId = null"
>
重新上传
</Button>
</div>
</div>
<div v-else style="margin-top: 8px;">
<Upload
:before-upload="beforeSignUpload"
:on-success="handleSignUpload"
:show-upload-list="false"
:action="'/api/admin/upload-file'"
:action="baseUrl + 'api/admin/upload-file'"
>
<Button type="primary" size="small">上传签字图片</Button>
</Upload>
@ -512,7 +538,7 @@
</div>
<template slot="footer">
<Button v-print="'#print-table'"></Button>
<Button type="primary" @click="handleSummarySubmit" style="margin-left: 8px;"></Button>
<Button type="primary" style="margin-left: 8px;" @click="handleSummarySubmit"></Button>
</template>
</Modal>
</div>
@ -529,6 +555,7 @@ import request from '@/utils/request'
export default {
data() {
return {
baseUrl: process.env.VUE_APP_BASE_API || window.location.origin + '/',
planSearch: {
keyword: '',
dateRange: [],
@ -542,6 +569,17 @@ export default {
planColumns: [
{ title: '计划编号', key: 'no' },
{ title: '计划名称', key: 'name' },
{
title: '盘点类型',
key: 'type',
render: (h, params) => {
const typeMap = {
1: '日常检查',
2: '年度盘点'
}
return h('span', typeMap[params.row.type] || '-')
}
},
{ title: '开始日期', key: 'start_date' },
{ title: '结束日期', key: 'end_date' },
{
@ -617,6 +655,7 @@ export default {
id: '',
name: '',
dateRange: [],
type: 1, //
remark: ''
},
rules: {
@ -796,7 +835,9 @@ export default {
summaryModal: {
visible: false,
data: null,
signImage: null
signImageId: null,
signImage: null,
type: 1 //
}
}
},
@ -939,6 +980,7 @@ export default {
id: plan.id,
name: plan.name,
dateRange: [plan.start_date, plan.end_date],
type: plan.type,
remark: plan.remark
}
this.planModal.visible = true
@ -947,6 +989,7 @@ export default {
id: '',
name: '',
dateRange: [],
type: 1, //
remark: ''
}
this.planModal.visible = true
@ -978,7 +1021,7 @@ export default {
return
}
const { id, name, dateRange, remark } = this.planModal.form
const { id, name, dateRange,type, remark } = this.planModal.form
const formatDate = (date) => {
const d = new Date(date)
const year = d.getFullYear()
@ -995,6 +1038,7 @@ export default {
formData.append('name', name)
formData.append('start_date', formatDate(dateRange[0]))
formData.append('end_date', formatDate(dateRange[1]))
formData.append('type', type)
if (remark) formData.append('remark', remark)
if (id) formData.append('id', id)
@ -1486,17 +1530,25 @@ export default {
this.loadPlanDetailData()
},
showSummaryModal(row) {
// row /
//
const planData = this.planList.find(plan => plan.id === row.id)
if (!planData) {
this.$Message.error('未找到计划数据')
return
}
this.summaryModal.data = {
name: row.name,
planStart: row.start_date,
planEnd: row.end_date,
actualStart: row.actual_start_date || '-', //
actualEnd: row.actual_end_date || '-',
planCount: row.plan_count || '-', //
actualCount: row.actual_count || '-'
id: planData.id,
name: planData.name,
planStart: planData.start_date,
planEnd: planData.end_date,
actualStart: planData.act_start_date || '-',
actualEnd: planData.act_end_date || '-',
planCount: planData.chart_total || '-',
actualCount: planData.chart_done || '-'
}
this.summaryModal.signImage = null
this.summaryModal.signImageId = planData.sign_id || null
this.summaryModal.signImage = planData.sign.url || null
this.summaryModal.visible = true
},
beforeSignUpload(file) {
@ -1505,6 +1557,7 @@ export default {
},
handleSignUpload(response) {
// response.url
this.summaryModal.signImageId = response.id
this.summaryModal.signImage = response.url
},
handleMoreCommand(command, row) {
@ -1516,14 +1569,25 @@ export default {
this.viewPlanDetail(row)
}
},
handleSummarySubmit() {
if (!this.summaryModal.signImage) {
async handleSummarySubmit() {
if (!this.summaryModal.signImageId) {
this.$Message.warning('请上传签字图片')
return
}
// TODO:
this.$Message.success('提交成功')
this.summaryModal.visible = false
try {
const params = {
id: this.summaryModal.data.id,
sign_id: this.summaryModal.signImageId,
}
await saveStocktakingPlan(params)
this.$Message.success('提交成功')
this.summaryModal.visible = false
this.searchPlans() //
} catch (error) {
this.$Message.error('提交失败:' + (error.message || '未知错误'))
}
}
}
}

@ -192,77 +192,74 @@
</Modal>
<!-- 查看详情 Modal -->
<div class="modal" :class="{ show: showViewModal }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">维护记录详情</h3>
<button class="close-button" @click="closeViewModal">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>记录编号:</label>
<div class="form-value">{{ currentRecord.no }}</div>
</div>
<div class="form-group">
<label>维护物资:</label>
<div class="form-value">{{ currentRecord.stocks_item_name }}</div>
</div>
<div class="form-group">
<label>计划维护日期:</label>
<div class="form-value">{{ currentRecord.planned_maintenance_date }}</div>
</div>
<div class="form-group">
<label>实际维护日期:</label>
<div class="form-value">{{ currentRecord.maintenance_date || '-' }}</div>
</div>
<div class="form-group">
<label>负责人:</label>
<div class="form-value">{{ currentRecord.responsible_admin_name }}</div>
</div>
<div class="form-group">
<label>状态:</label>
<div class="form-value">{{ getStatusText(currentRecord.status) }}</div>
</div>
<div class="form-group">
<label>维护备注:</label>
<div class="form-value">{{ currentRecord.maintenance_notes || '-' }}</div>
</div>
<div class="form-group">
<label>维护照片:</label>
<div v-if="currentRecord.files && currentRecord.files.length" class="photo-gallery">
<img
v-for="(file, idx) in currentRecord.files"
:key="'file-' + idx"
:src="file.url"
class="photo-preview"
style="cursor:pointer;"
alt="维护图片"
@click="previewImage(file.url)"
>
</div>
<div v-else class="form-value">-</div>
</div>
<div class="form-group">
<label>签名照片:</label>
<div v-if="currentRecord.sign && currentRecord.sign.url" class="photo-gallery">
<img
:src="currentRecord.sign.url"
class="sign-preview"
style="cursor:pointer;"
alt="签名图片"
@click="previewImage(currentRecord.sign.url)"
>
</div>
<div v-else class="form-value">-</div>
</div>
<Modal
v-model="showViewModal"
title="维护记录详情"
width="900"
:mask-closable="true"
>
<div class="modal-body">
<div class="form-group">
<label>记录编号:</label>
<div class="form-value">{{ currentRecord.no }}</div>
</div>
<div class="form-group">
<label>维护物资:</label>
<div class="form-value">{{ currentRecord.stocks_item_name }}</div>
</div>
<div class="form-group">
<label>计划维护日期:</label>
<div class="form-value">{{ currentRecord.planned_maintenance_date }}</div>
</div>
<div class="form-group">
<label>实际维护日期:</label>
<div class="form-value">{{ currentRecord.maintenance_date || '-' }}</div>
</div>
<div class="form-group">
<label>负责人:</label>
<div class="form-value">{{ currentRecord.responsible_admin_name }}</div>
</div>
<div class="form-group">
<label>状态:</label>
<div class="form-value">{{ getStatusText(currentRecord.status) }}</div>
</div>
<div class="form-group">
<label>维护备注:</label>
<div class="form-value">{{ currentRecord.maintenance_notes || '-' }}</div>
</div>
<div class="form-group">
<label>维护照片:</label>
<div v-if="currentRecord.files && currentRecord.files.length" class="photo-gallery">
<img
v-for="(file, idx) in currentRecord.files"
:key="'file-' + idx"
:src="file.url"
class="photo-preview"
style="cursor:pointer;"
alt="维护图片"
@click="previewImage(file.url)"
>
</div>
<div class="modal-footer" style="justify-content: center;">
<Button @click="closeViewModal"></Button>
<div v-else class="form-value">-</div>
</div>
<div class="form-group">
<label>签名照片:</label>
<div v-if="currentRecord.sign && currentRecord.sign.url" class="photo-gallery">
<img
:src="currentRecord.sign.url"
class="sign-preview"
style="cursor:pointer;"
alt="签名图片"
@click="previewImage(currentRecord.sign.url)"
>
</div>
<div v-else class="form-value">-</div>
</div>
</div>
</div>
<template slot="footer">
<Button @click="closeViewModal"></Button>
</template>
</Modal>
<!-- 预览弹窗 -->
<div v-if="previewUrl" class="image-preview-modal" @click="closePreview">
@ -993,7 +990,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
.table-page-container {
padding: 20px;
background-color: #f0f2f5;
@ -1203,12 +1200,10 @@ export default {
}
.modal-body {
background: #fcfcfd;
padding: 20px 15px 15px 15px;
border-radius: 0 0 12px 12px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px 32px;
padding: 0;
}
.form-group {
@ -1230,95 +1225,45 @@ export default {
font-weight: 500;
}
.form-group input[type="date"],
.form-group textarea,
.form-group input[type="file"] {
display: block;
width: 100%;
padding: 5px 7.5px;
font-size: 16px;
font-weight: 400;
.form-value {
color: #333;
font-size: 14px;
line-height: 1.5;
color: #495057;
background-color: #fff !important;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.form-group textarea {
resize: vertical;
}
.form-group input[type="file"] {
padding: 3px 7.5px;
word-break: break-word;
}
.form-text {
font-size: 16px;
color: #6c757d;
.photo-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 8px;
margin-top: 8px;
}
.signature-area {
width: 100%;
min-width: 0;
}
.signature-pad-wrapper {
width: 100%;
min-width: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.signature-canvas {
border: 1px dashed #ced4da;
background: #fff;
border-radius: 4px;
.photo-preview {
width: 100%;
height: 120px;
cursor: crosshair;
display: block;
box-sizing: border-box;
margin: 0;
padding: 0;
min-width: 0;
}
.form-value {
color: #222;
font-size: 12px;
font-weight: 500;
background: transparent;
border: none;
min-height: 32px;
padding: 0;
object-fit: cover;
border-radius: 4px;
border: 1px solid #eaeaea;
cursor: pointer;
transition: transform 0.2s;
}
.photo-gallery {
display: flex;
flex-wrap: wrap;
gap: 10px;
background: #f8f9fa;
border-radius: 6px;
padding: 8px;
margin-top: 4px;
.photo-preview:hover {
transform: scale(1.05);
}
.photo-preview {
width: 64px;
height: 64px;
object-fit: cover;
.sign-preview {
max-width: 200px;
margin-top: 8px;
border-radius: 4px;
border: 1.5px solid #eaeaea;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
cursor: pointer;
transition: transform 0.15s;
border: 1px solid #eaeaea;
}
.photo-preview:hover {
transform: scale(1.08);
box-shadow: 0 4px 16px rgba(0,0,0,0.13);
@media (max-width: 900px) {
.modal-body {
grid-template-columns: 1fr;
}
}
.pagination-container {
@ -1370,28 +1315,6 @@ export default {
}
}
@media (max-width: 900px) {
.modal-dialog {
max-width: 98vw;
}
.modal-body {
padding: 10px 5px;
grid-template-columns: 1fr;
}
}
.modal-footer {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
padding: 7.5px;
border-top: 1px solid #dee2e6;
border-bottom-right-radius: 12px;
border-bottom-left-radius: 12px;
gap: 0.5rem;
}
.image-preview-modal {
position: fixed;
z-index: 2000;
@ -1409,19 +1332,6 @@ export default {
background: #fff;
}
.sign-preview {
width: 100%;
max-width: 100%;
height: auto;
object-fit: contain;
border-radius: 4px;
border: 1.5px solid #eaeaea;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
margin-top: 4px;
cursor: pointer;
background: #fff;
}
/* 分割线 */
hr {
border: none;
@ -1480,12 +1390,26 @@ hr {
z-index: 9998 !important;
}
.ivu-modal-body {
min-height: 0 !important;
height: auto !important;
max-height: 60vh;
:deep(.ivu-modal-body) {
padding: 20px;
max-height: 70vh;
overflow-y: auto;
padding-bottom: 0 !important;
margin-bottom: 0 !important;
}
:deep(.ivu-modal-header) {
padding: 16px;
border-bottom: 1px solid #eaeaea;
}
:deep(.ivu-modal-header-inner) {
font-size: 16px;
font-weight: 600;
color: #222;
}
:deep(.ivu-modal-footer) {
padding: 16px;
border-top: 1px solid #eaeaea;
text-align: center;
}
</style>

Loading…
Cancel
Save