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> <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"> <template v-if="row.status === 2">
<el-dropdown trigger="hover" @command="command => handleMoreCommand(command, row)"> <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-menu slot="dropdown">
<el-dropdown-item command="edit">编辑</el-dropdown-item> <el-dropdown-item command="edit">编辑</el-dropdown-item>
<el-dropdown-item command="delete">删除</el-dropdown-item> <el-dropdown-item command="delete">删除</el-dropdown-item>
@ -165,6 +165,12 @@
@on-clear="handleDateClear" @on-clear="handleDateClear"
/> />
</FormItem> </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"> <FormItem label="备注" prop="remark">
<Input v-model="planModal.form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" /> <Input v-model="planModal.form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
</FormItem> </FormItem>
@ -341,7 +347,7 @@
<template slot="photos" slot-scope="{ row }"> <template slot="photos" slot-scope="{ row }">
<div v-if="row.files && row.files.length" class="photo-list"> <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"> <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>
<div v-if="row.files.length > 3" class="photo-more"> <div v-if="row.files.length > 3" class="photo-more">
+{{ row.files.length - 3 }} +{{ row.files.length - 3 }}
@ -370,7 +376,7 @@
<!-- 预览弹窗 --> <!-- 预览弹窗 -->
<div v-if="previewUrl" class="image-preview-modal" @click="closePreview"> <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> </div>
<!-- Plan Detail Modal --> <!-- Plan Detail Modal -->
@ -426,7 +432,7 @@
<template slot="photos" slot-scope="{ row }"> <template slot="photos" slot-scope="{ row }">
<div v-if="row.files && row.files.length" class="photo-list"> <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"> <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>
<div v-if="row.files.length > 3" class="photo-more"> <div v-if="row.files.length > 3" class="photo-more">
+{{ row.files.length - 3 }} +{{ row.files.length - 3 }}
@ -454,7 +460,11 @@
</Modal> </Modal>
<!-- 盘点小结 Modal --> <!-- 盘点小结 Modal -->
<Modal v-model="summaryModal.visible" title="盘点小结" width="800"> <Modal
v-model="summaryModal.visible"
title="盘点小结"
width="800"
>
<div> <div>
<div id="print-table"> <div id="print-table">
<div style="text-align: center; font-size: 20px; font-weight: bold; margin-bottom: 12px;">盘点小结</div> <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> <td>{{ summaryModal.data ? summaryModal.data.actualCount : '' }}</td>
</tr> </tr>
<tr> <tr>
<td>签字区</td> <td class="sign-cell">签字区</td>
<td class="sign-cell"></td> <td style="text-align: center">
</td>
</tr> </tr>
</table> </table>
</div> </div>
<div style="margin: 24px 0 8px 0;"> <div style="margin: 24px 0 8px 0;">
<span style="font-weight: bold;">签字图片上传</span> <span style="font-weight: bold;">签字图片上传</span>
<div v-if="summaryModal.signImage" style="margin-top: 8px;"> <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>
<div v-else style="margin-top: 8px;"> <div v-else style="margin-top: 8px;">
<Upload <Upload
:before-upload="beforeSignUpload" :before-upload="beforeSignUpload"
:on-success="handleSignUpload" :on-success="handleSignUpload"
:show-upload-list="false" :show-upload-list="false"
:action="'/api/admin/upload-file'" :action="baseUrl + 'api/admin/upload-file'"
> >
<Button type="primary" size="small">上传签字图片</Button> <Button type="primary" size="small">上传签字图片</Button>
</Upload> </Upload>
@ -512,7 +538,7 @@
</div> </div>
<template slot="footer"> <template slot="footer">
<Button v-print="'#print-table'"></Button> <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> </template>
</Modal> </Modal>
</div> </div>
@ -529,6 +555,7 @@ import request from '@/utils/request'
export default { export default {
data() { data() {
return { return {
baseUrl: process.env.VUE_APP_BASE_API || window.location.origin + '/',
planSearch: { planSearch: {
keyword: '', keyword: '',
dateRange: [], dateRange: [],
@ -542,6 +569,17 @@ export default {
planColumns: [ planColumns: [
{ title: '计划编号', key: 'no' }, { title: '计划编号', key: 'no' },
{ title: '计划名称', key: 'name' }, { 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: 'start_date' },
{ title: '结束日期', key: 'end_date' }, { title: '结束日期', key: 'end_date' },
{ {
@ -617,6 +655,7 @@ export default {
id: '', id: '',
name: '', name: '',
dateRange: [], dateRange: [],
type: 1, //
remark: '' remark: ''
}, },
rules: { rules: {
@ -796,7 +835,9 @@ export default {
summaryModal: { summaryModal: {
visible: false, visible: false,
data: null, data: null,
signImage: null signImageId: null,
signImage: null,
type: 1 //
} }
} }
}, },
@ -939,6 +980,7 @@ export default {
id: plan.id, id: plan.id,
name: plan.name, name: plan.name,
dateRange: [plan.start_date, plan.end_date], dateRange: [plan.start_date, plan.end_date],
type: plan.type,
remark: plan.remark remark: plan.remark
} }
this.planModal.visible = true this.planModal.visible = true
@ -947,6 +989,7 @@ export default {
id: '', id: '',
name: '', name: '',
dateRange: [], dateRange: [],
type: 1, //
remark: '' remark: ''
} }
this.planModal.visible = true this.planModal.visible = true
@ -978,7 +1021,7 @@ export default {
return return
} }
const { id, name, dateRange, remark } = this.planModal.form const { id, name, dateRange,type, remark } = this.planModal.form
const formatDate = (date) => { const formatDate = (date) => {
const d = new Date(date) const d = new Date(date)
const year = d.getFullYear() const year = d.getFullYear()
@ -995,6 +1038,7 @@ export default {
formData.append('name', name) formData.append('name', name)
formData.append('start_date', formatDate(dateRange[0])) formData.append('start_date', formatDate(dateRange[0]))
formData.append('end_date', formatDate(dateRange[1])) formData.append('end_date', formatDate(dateRange[1]))
formData.append('type', type)
if (remark) formData.append('remark', remark) if (remark) formData.append('remark', remark)
if (id) formData.append('id', id) if (id) formData.append('id', id)
@ -1486,17 +1530,25 @@ export default {
this.loadPlanDetailData() this.loadPlanDetailData()
}, },
showSummaryModal(row) { showSummaryModal(row) {
// row / //
const planData = this.planList.find(plan => plan.id === row.id)
if (!planData) {
this.$Message.error('未找到计划数据')
return
}
this.summaryModal.data = { this.summaryModal.data = {
name: row.name, id: planData.id,
planStart: row.start_date, name: planData.name,
planEnd: row.end_date, planStart: planData.start_date,
actualStart: row.actual_start_date || '-', // planEnd: planData.end_date,
actualEnd: row.actual_end_date || '-', actualStart: planData.act_start_date || '-',
planCount: row.plan_count || '-', // actualEnd: planData.act_end_date || '-',
actualCount: row.actual_count || '-' 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 this.summaryModal.visible = true
}, },
beforeSignUpload(file) { beforeSignUpload(file) {
@ -1505,6 +1557,7 @@ export default {
}, },
handleSignUpload(response) { handleSignUpload(response) {
// response.url // response.url
this.summaryModal.signImageId = response.id
this.summaryModal.signImage = response.url this.summaryModal.signImage = response.url
}, },
handleMoreCommand(command, row) { handleMoreCommand(command, row) {
@ -1516,14 +1569,25 @@ export default {
this.viewPlanDetail(row) this.viewPlanDetail(row)
} }
}, },
handleSummarySubmit() { async handleSummarySubmit() {
if (!this.summaryModal.signImage) { if (!this.summaryModal.signImageId) {
this.$Message.warning('请上传签字图片') this.$Message.warning('请上传签字图片')
return return
} }
// TODO:
this.$Message.success('提交成功') try {
this.summaryModal.visible = false 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>
<!-- 查看详情 Modal --> <!-- 查看详情 Modal -->
<div class="modal" :class="{ show: showViewModal }"> <Modal
<div class="modal-dialog"> v-model="showViewModal"
<div class="modal-content"> title="维护记录详情"
<div class="modal-header"> width="900"
<h3 class="modal-title">维护记录详情</h3> :mask-closable="true"
<button class="close-button" @click="closeViewModal">&times;</button> >
</div> <div class="modal-body">
<div class="modal-body"> <div class="form-group">
<div class="form-group"> <label>记录编号:</label>
<label>记录编号:</label> <div class="form-value">{{ currentRecord.no }}</div>
<div class="form-value">{{ currentRecord.no }}</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label>维护物资:</label>
<label>维护物资:</label> <div class="form-value">{{ currentRecord.stocks_item_name }}</div>
<div class="form-value">{{ currentRecord.stocks_item_name }}</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label>计划维护日期:</label>
<label>计划维护日期:</label> <div class="form-value">{{ currentRecord.planned_maintenance_date }}</div>
<div class="form-value">{{ currentRecord.planned_maintenance_date }}</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label>实际维护日期:</label>
<label>实际维护日期:</label> <div class="form-value">{{ currentRecord.maintenance_date || '-' }}</div>
<div class="form-value">{{ currentRecord.maintenance_date || '-' }}</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label>负责人:</label>
<label>负责人:</label> <div class="form-value">{{ currentRecord.responsible_admin_name }}</div>
<div class="form-value">{{ currentRecord.responsible_admin_name }}</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label>状态:</label>
<label>状态:</label> <div class="form-value">{{ getStatusText(currentRecord.status) }}</div>
<div class="form-value">{{ getStatusText(currentRecord.status) }}</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label>维护备注:</label>
<label>维护备注:</label> <div class="form-value">{{ currentRecord.maintenance_notes || '-' }}</div>
<div class="form-value">{{ currentRecord.maintenance_notes || '-' }}</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label>维护照片:</label>
<label>维护照片:</label> <div v-if="currentRecord.files && currentRecord.files.length" class="photo-gallery">
<div v-if="currentRecord.files && currentRecord.files.length" class="photo-gallery"> <img
<img v-for="(file, idx) in currentRecord.files"
v-for="(file, idx) in currentRecord.files" :key="'file-' + idx"
:key="'file-' + idx" :src="file.url"
:src="file.url" class="photo-preview"
class="photo-preview" style="cursor:pointer;"
style="cursor:pointer;" alt="维护图片"
alt="维护图片" @click="previewImage(file.url)"
@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>
</div> </div>
<div class="modal-footer" style="justify-content: center;"> <div v-else class="form-value">-</div>
<Button @click="closeViewModal"></Button> </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>
<div v-else class="form-value">-</div>
</div> </div>
</div> </div>
</div> <template slot="footer">
<Button @click="closeViewModal"></Button>
</template>
</Modal>
<!-- 预览弹窗 --> <!-- 预览弹窗 -->
<div v-if="previewUrl" class="image-preview-modal" @click="closePreview"> <div v-if="previewUrl" class="image-preview-modal" @click="closePreview">
@ -993,7 +990,7 @@ export default {
} }
</script> </script>
<style scoped> <style lang="scss" scoped>
.table-page-container { .table-page-container {
padding: 20px; padding: 20px;
background-color: #f0f2f5; background-color: #f0f2f5;
@ -1203,12 +1200,10 @@ export default {
} }
.modal-body { .modal-body {
background: #fcfcfd;
padding: 20px 15px 15px 15px;
border-radius: 0 0 12px 12px;
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 16px 32px; gap: 16px 32px;
padding: 0;
} }
.form-group { .form-group {
@ -1230,95 +1225,45 @@ export default {
font-weight: 500; font-weight: 500;
} }
.form-group input[type="date"], .form-value {
.form-group textarea, color: #333;
.form-group input[type="file"] { font-size: 14px;
display: block;
width: 100%;
padding: 5px 7.5px;
font-size: 16px;
font-weight: 400;
line-height: 1.5; line-height: 1.5;
color: #495057; word-break: break-word;
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;
} }
.form-text { .photo-gallery {
font-size: 16px; display: grid;
color: #6c757d; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 8px;
margin-top: 8px;
} }
.signature-area { .photo-preview {
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;
width: 100%; width: 100%;
height: 120px; height: 120px;
cursor: crosshair; object-fit: cover;
display: block; border-radius: 4px;
box-sizing: border-box; border: 1px solid #eaeaea;
margin: 0; cursor: pointer;
padding: 0; transition: transform 0.2s;
min-width: 0;
}
.form-value {
color: #222;
font-size: 12px;
font-weight: 500;
background: transparent;
border: none;
min-height: 32px;
padding: 0;
} }
.photo-gallery { .photo-preview:hover {
display: flex; transform: scale(1.05);
flex-wrap: wrap;
gap: 10px;
background: #f8f9fa;
border-radius: 6px;
padding: 8px;
margin-top: 4px;
} }
.photo-preview { .sign-preview {
width: 64px; max-width: 200px;
height: 64px; margin-top: 8px;
object-fit: cover;
border-radius: 4px; border-radius: 4px;
border: 1.5px solid #eaeaea; border: 1px solid #eaeaea;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
cursor: pointer;
transition: transform 0.15s;
} }
.photo-preview:hover { @media (max-width: 900px) {
transform: scale(1.08); .modal-body {
box-shadow: 0 4px 16px rgba(0,0,0,0.13); grid-template-columns: 1fr;
}
} }
.pagination-container { .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 { .image-preview-modal {
position: fixed; position: fixed;
z-index: 2000; z-index: 2000;
@ -1409,19 +1332,6 @@ export default {
background: #fff; 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 { hr {
border: none; border: none;
@ -1480,12 +1390,26 @@ hr {
z-index: 9998 !important; z-index: 9998 !important;
} }
.ivu-modal-body { :deep(.ivu-modal-body) {
min-height: 0 !important; padding: 20px;
height: auto !important; max-height: 70vh;
max-height: 60vh;
overflow-y: auto; 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> </style>

Loading…
Cancel
Save