This commit is contained in:
磷叶 2024-12-27 13:54:58 +08:00
parent b0cef59523
commit 4e1cc94bb1
7 changed files with 330 additions and 79 deletions
package.json
src
components/ImageUpload
views
bst
order
prodProcess/components
index.vue

View File

@ -57,6 +57,7 @@
"vue": "2.6.12", "vue": "2.6.12",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-cropper": "0.5.5", "vue-cropper": "0.5.5",
"vue-masonry-css": "^1.0.3",
"vue-meta": "2.4.0", "vue-meta": "2.4.0",
"vue-router": "3.4.9", "vue-router": "3.4.9",
"vuedraggable": "2.24.3", "vuedraggable": "2.24.3",

View File

@ -1,5 +1,9 @@
<template> <template>
<div class="component-upload-image"> <div
class="component-upload-image"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<el-upload <el-upload
multiple multiple
:action="uploadImgUrl" :action="uploadImgUrl"
@ -18,6 +22,7 @@
:class="{hide: this.fileList.length >= this.limit}" :class="{hide: this.fileList.length >= this.limit}"
class="image-upload" class="image-upload"
:style="cssVars" :style="cssVars"
drag
> >
<i class="el-icon-plus"></i> <i class="el-icon-plus"></i>
</el-upload> </el-upload>
@ -46,6 +51,7 @@
<script> <script>
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import axios from 'axios'
export default { export default {
props: { props: {
@ -91,7 +97,8 @@ export default {
headers: { headers: {
Authorization: "Bearer " + getToken(), Authorization: "Bearer " + getToken(),
}, },
fileList: [] fileList: [],
isListening: false //
}; };
}, },
watch: { watch: {
@ -133,6 +140,71 @@ export default {
} }
}, },
methods: { methods: {
//
handleMouseEnter() {
if (!this.isListening) {
document.addEventListener('paste', this.handlePaste);
this.isListening = true;
}
},
//
handleMouseLeave() {
if (this.isListening) {
document.removeEventListener('paste', this.handlePaste);
this.isListening = false;
}
},
//
handlePaste(event) {
//
event.preventDefault();
const items = event.clipboardData?.items;
if (!items) {
this.$modal.msgError('当前浏览器不支持粘贴上传');
return;
}
let file = null;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
file = items[i].getAsFile();
break;
}
}
if (!file) {
this.$modal.msgError('粘贴内容中不包含图片');
return;
}
//
if (this.fileList.length >= this.limit) {
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
return;
}
// 使 handleBeforeUpload
const valid = this.handleBeforeUpload(file);
if (valid !== false) {
//
const formData = new FormData();
formData.append('file', file);
//
axios.post(this.uploadImgUrl, formData, {
headers: {
...this.headers,
'Content-Type': 'multipart/form-data'
}
}).then(res => {
this.handleUploadSuccess(res.data, file);
}).catch(() => {
this.handleUploadError();
});
}
},
// loading // loading
handleBeforeUpload(file) { handleBeforeUpload(file) {
let isImg = false; let isImg = false;
@ -248,6 +320,14 @@ export default {
height: var(--height); height: var(--height);
} }
::v-deep .el-upload-dragger {
width: var(--width);
height: var(--height);
display: flex;
justify-content: center;
align-items: center;
}
/* 图片操作按钮容器 */ /* 图片操作按钮容器 */
::v-deep .el-upload-list__item-actions { ::v-deep .el-upload-list__item-actions {
display: flex; display: flex;

View File

@ -1,59 +1,53 @@
<template> <template>
<div class="order-flow" v-loading="loading"> <div class="order-flow" v-loading="loading">
<el-row :gutter="20" v-if="!isEmpty(list)"> <div class="waterfall-container" v-if="!isEmpty(list)" ref="container">
<el-col :span="6" v-for="order in list" :key="order.id"> <div
v-for="(order, index) in list"
:key="order.id"
class="waterfall-item"
:style="itemPositions[index]"
>
<el-card class="order-card" shadow="hover"> <el-card class="order-card" shadow="hover">
<div class="order-header">
<!-- 主图区域 --> <!-- 主图区域 -->
<div class="order-main-image"> <div class="order-main-image">
<image-preview :src="order.picture" :width="200" :height="200" /> <image-preview :src="order.picture" :width="100" :height="100" />
</div> </div>
<!-- 订单信息区域 --> <!-- 订单信息区域 -->
<div class="order-info"> <div @click="handleOrderClick(order)">
<div class="info-row"> <el-descriptions :column="2" size="small">
<span class="label">订单号:</span> <el-descriptions-item label="订单" :span="2">{{ order.orderNo | dv}}</el-descriptions-item>
<span class="value">{{ order.orderNo }}</span> <el-descriptions-item label="客户" :span="2">{{ order.customer | dv}}</el-descriptions-item>
<el-descriptions-item label="特殊要求" :span="2">{{ order.remark | dv}}</el-descriptions-item>
<el-descriptions-item label="数量">{{ order.num | dv}}</el-descriptions-item>
<el-descriptions-item label="装量">{{ order.storeNum | dv}}</el-descriptions-item>
</el-descriptions>
</div> </div>
<div class="info-row">
<span class="label">客户名称:</span>
<span class="value">{{ order.customer }}</span>
</div>
<div class="info-row">
<span class="label">数量:</span>
<span class="value">{{ order.num }}</span>
</div>
<div class="info-row">
<span class="label">装量:</span>
<span class="value">{{ order.reportNum }}/{{ order.num }}</span>
</div>
<div class="info-row">
<span class="label">特殊要求:</span>
<span class="value">{{ order.remark }}</span>
</div> </div>
<div class="progress-container">
<el-progress :percentage="order.progress" :color="ProgressColors" :format="ProgressFormat" style="width: 100%;" :stroke-width="10"/>
</div> </div>
<!-- 子产品列表 --> <!-- 子产品列表 -->
<div class="sub-products"> <div class="sub-products">
<div <div class="sub-product" v-for="(prod, index) in order.prodList" :key="index">
class="sub-product" <image-preview class="prod-image" :src="prod.picture" :width="28" :height="28" />
v-for="(prod, index) in order.orderProdList" <span class="prod-name">{{ prod.name | dv }}</span>
:key="index" <dict-tag class="prod-type" :value="prod.workType" :options="dict.type.order_prod_work_type" size="mini"/>
>
<el-checkbox :value="prod.source === 1" disabled
>子产品{{ index + 1 }}</el-checkbox
>
<div class="prod-progress"> <div class="prod-progress">
<el-progress <el-progress
:percentage="prod.progress || 0" :percentage="prod.progress || 0"
:color="ProgressColors" :color="ProgressColors"
:format="ProgressFormat" :format="ProgressFormat"
:stroke-width="6"
/> />
</div> </div>
</div> </div>
</div> </div>
</el-card> </el-card>
</el-col> </div>
</el-row> </div>
<el-empty v-else description="暂无进行中的订单" /> <el-empty v-else description="暂无进行中的订单" />
</div> </div>
</template> </template>
@ -66,6 +60,7 @@ import { isEmpty } from '@/utils/index'
export default { export default {
name: 'OrderFlowList', name: 'OrderFlowList',
dicts: ['order_prod_work_type'],
props: { props: {
}, },
data() { data() {
@ -73,28 +68,127 @@ export default {
ProgressColors, ProgressColors,
list: [], list: [],
queryParams: { queryParams: {
pageSize: 10, pageSize: 12,
pageNum: 1, pageNum: 1,
status: OrderStatus.RELEASED, status: OrderStatus.RELEASED,
needProd: true,
needProdProgress: true,
orderByColumn: "createTime",
isAsc: "desc"
}, },
loading: false, loading: false,
total: 0, total: 0,
itemPositions: [],
columnWidth: 300,
columnCount: 4,
columnHeights: [],
resizeObserver: null
} }
}, },
created() { created() {
this.getList() this.getList()
}, },
mounted() {
this.initWaterfall()
this.resizeObserver = new ResizeObserver(this.handleResize)
if (this.$refs.container) {
this.resizeObserver.observe(this.$refs.container)
}
},
beforeDestroy() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
},
methods: { methods: {
handleOrderClick(order) {
this.$router.push({ path: `/view/order/${order.id}` })
},
ProgressFormat, ProgressFormat,
isEmpty, isEmpty,
getList() { getList() {
this.loading = true; this.loading = true
listOrder(this.queryParams).then(res => { listOrder(this.queryParams).then(res => {
this.list = res.rows; this.list = res.rows
this.total = res.total; this.total = res.total
}).finally(() => { this.$nextTick(() => {
this.loading = false; this.initWaterfall()
}) })
}).finally(() => {
this.loading = false
})
},
initWaterfall() {
this.$nextTick(() => {
const container = this.$refs.container
if (!container) {
return
}
if (this.resizeObserver && !this.isObserving) {
this.resizeObserver.observe(container)
this.isObserving = true
}
// 4
const containerWidth = container.clientWidth
const minColumnWidth = 300 //
this.columnCount = Math.min(4, Math.max(1, Math.floor(containerWidth / minColumnWidth)))
//
this.columnWidth = (containerWidth - (this.columnCount + 1) * 8) / this.columnCount // 8pxpadding
//
this.columnHeights = new Array(this.columnCount).fill(0)
//
this.layoutItems()
})
},
layoutItems() {
const items = this.$refs.container?.getElementsByClassName('waterfall-item')
if (!items) return
this.columnHeights = new Array(this.columnCount).fill(0)
this.itemPositions = []
Array.from(items).forEach((item, index) => {
const minHeight = Math.min(...this.columnHeights)
const column = this.columnHeights.indexOf(minHeight)
// xpadding
const left = column * this.columnWidth + (column + 1) * 8
const top = minHeight
this.itemPositions[index] = {
transform: `translate3d(${left}px, ${top}px, 0)`,
width: `${this.columnWidth}px`
}
this.columnHeights[column] = minHeight + item.offsetHeight + 8
})
const containerHeight = Math.max(...this.columnHeights)
this.$refs.container.style.height = `${containerHeight}px`
},
handleResize() {
if (this.resizeTimer) {
clearTimeout(this.resizeTimer)
}
this.resizeTimer = setTimeout(() => {
this.initWaterfall()
}, 100)
}
},
watch: {
list: {
handler() {
this.$nextTick(() => {
this.initWaterfall()
})
},
deep: true
} }
} }
} }
@ -102,12 +196,35 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.order-flow { .order-flow {
.waterfall-container {
position: relative;
margin: 0 auto;
box-sizing: border-box;
}
.waterfall-item {
position: absolute;
transition: all 0.3s ease;
box-sizing: border-box;
}
.order-card { .order-card {
margin-bottom: 20px; margin-bottom: 0;
z-index: 1;
cursor: pointer;
}
.order-header {
display: flex;
}
.progress-container {
margin-bottom: 16px;
}
.order-main-image { .order-main-image {
text-align: center; text-align: center;
margin-bottom: 15px; margin-right: 16px;
} }
.order-info { .order-info {
@ -131,16 +248,52 @@ export default {
} }
.sub-products { .sub-products {
margin-top: 15px;
.sub-title {
font-size: 14px;
color: #606266;
margin-bottom: 12px;
font-weight: 500;
}
.sub-product { .sub-product {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 10px; padding: 4px;
margin-bottom: 4px;
border-radius: 4px;
transition: all 0.3s ease;
background: #f8f9fb;
&:hover {
background: #f0f2f5;
}
.prod-image {
margin-right: 12px;
border-radius: 4px;
overflow: hidden;
}
.prod-name {
flex: 1;
margin-right: 12px;
color: #303133;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.prod-type {
margin-right: 12px;
}
.prod-progress { .prod-progress {
flex: 1; width: 160px;
margin-left: 10px;
:deep(.el-progress-bar__outer) {
background-color: rgba(0,0,0,0.04);
} }
} }
} }

View File

@ -1,8 +1,9 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<edit-header :title="title"> <edit-header :title="title">
<el-button plain @click="cancel" icon="el-icon-close" size="small">取消</el-button>
<el-button type="primary" plain @click="submitForm(false)" icon="el-icon-check" size="small">保存</el-button> <el-button type="primary" plain @click="submitForm(false)" icon="el-icon-check" size="small">保存</el-button>
<el-button type="primary" @click="submitForm(true)" icon="el-icon-s-check" size="small" v-if="OrderStatus.canRelease().includes(form.status)">保存并发布</el-button> <el-button type="primary" @click="submitForm(true)" icon="el-icon-s-check" size="small" v-if="form.id == null || OrderStatus.canRelease().includes(form.status)">保存并发布</el-button>
</edit-header> </edit-header>
<div class="app-container"> <div class="app-container">
@ -10,7 +11,7 @@
<div class="edit-title">基础信息</div> <div class="edit-title">基础信息</div>
<el-row> <el-row>
<form-col :span="span" label="主图" prop="picture"> <form-col :span="span" label="主图" prop="picture">
<image-upload v-model="form.picture" :limit="1"/> <image-upload v-model="form.picture" :limit="1" />
</form-col> </form-col>
<form-col :span="span" label="订单编号" prop="orderNo"> <form-col :span="span" label="订单编号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入订单编号"/> <el-input v-model="form.orderNo" placeholder="请输入订单编号"/>
@ -160,6 +161,9 @@ export default {
} }
}); });
}, },
cancel() {
this.$tab.closeBack();
}
} }
} }
</script> </script>

View File

@ -308,11 +308,11 @@ export default {
}, },
/** 新增按钮操作 */ /** 新增按钮操作 */
handleAdd() { handleAdd() {
this.$router.push("/edit/order"); this.$tab.closeOpenPage("/edit/order")
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
this.$router.push(`/edit/order/${row.id}`) this.$tab.closeOpenPage(`/edit/order/${row.id}`)
}, },
// //
handleFinish(row) { handleFinish(row) {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="process-card-list"> <div class="process-card-list" v-if="!isEmpty(data)">
<el-card <el-card
v-for="(item, index) in data" v-for="(item, index) in data"
:key="index" :key="index"
@ -50,10 +50,12 @@
</div> </div>
</el-card> </el-card>
</div> </div>
<el-empty v-else description="暂无数据"/>
</template> </template>
<script> <script>
import { ProgressColors, ProgressFormat } from '@/utils/constants' import { ProgressColors, ProgressFormat } from '@/utils/constants'
import { isEmpty } from '@/utils/index'
export default { export default {
name: 'ProcessCardList', name: 'ProcessCardList',
@ -70,6 +72,7 @@ export default {
}, },
methods: { methods: {
ProgressFormat, ProgressFormat,
isEmpty
} }
} }
</script> </script>

View File

@ -3,7 +3,10 @@
<panel-group /> <panel-group />
<div class="order-flow-title">进行中的订单</div> <div class="group-title">
<i class="el-icon-s-order"></i>
进行中的订单
</div>
<order-flow-list /> <order-flow-list />
</div> </div>
@ -30,4 +33,11 @@ export default {
.dashboard-editor-container { .dashboard-editor-container {
padding: 32px; padding: 32px;
} }
.group-title {
background: linear-gradient(to right, #409EFF, #ffffff 50% );
color: #fff;
padding: 8px 16px;
border-radius: 4px;
margin-bottom: 16px;
}
</style> </style>