|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <el-dialog title="结果预定与审核" width="80%" custom-class="reservation-approval-dialog" :visible.sync="dialogVisible">
|
|
|
|
|
|
|
+ <el-dialog title="结果预定与审核" width="80%" custom-class="reservation-approval-dialog" :visible.sync="dialogVisible" :close-on-click-modal="false">
|
|
|
<div class="reservation-approval-block">
|
|
<div class="reservation-approval-block">
|
|
|
<div class="reservation-approval-header">
|
|
<div class="reservation-approval-header">
|
|
|
<div class="crop-type-block" v-if="pages == 'crop' && statusPages == 4">
|
|
<div class="crop-type-block" v-if="pages == 'crop' && statusPages == 4">
|
|
@@ -17,7 +17,11 @@
|
|
|
<span class="label" style="white-space: nowrap;">颜色代码:</span>
|
|
<span class="label" style="white-space: nowrap;">颜色代码:</span>
|
|
|
<el-input v-model="form.colorCode" size="small"></el-input>
|
|
<el-input v-model="form.colorCode" size="small"></el-input>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="reservation-approval__status">
|
|
|
|
|
|
|
+ <div class="crop-colorCode-block" style="display: flex;align-items: center;" v-if="cropPreview && statusPages == 4">
|
|
|
|
|
+ <span class="label" style="white-space: nowrap;">SKU:</span>
|
|
|
|
|
+ <el-input v-model="form.sku" size="small"></el-input>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="reservation-approval__status" v-if="!cropPreview">
|
|
|
<ul v-if="pages == 'review'">
|
|
<ul v-if="pages == 'review'">
|
|
|
<li :class="{'active': statusPages == 2}">待审核图</li>
|
|
<li :class="{'active': statusPages == 2}">待审核图</li>
|
|
|
<li :class="{'active': statusPages == 3}">待复核</li>
|
|
<li :class="{'active': statusPages == 3}">待复核</li>
|
|
@@ -29,10 +33,10 @@
|
|
|
<li :class="{'active': statusPages >= 6}">已完成</li>
|
|
<li :class="{'active': statusPages >= 6}">已完成</li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="reservation-approval__save" v-if="pages == 'review' || (pages == 'crop' && statusPages != 4)">
|
|
|
|
|
|
|
+ <div class="reservation-approval__save" v-if="!cropPreview && (pages == 'review' || (pages == 'crop' && statusPages >= 4))">
|
|
|
<el-button
|
|
<el-button
|
|
|
size="small"
|
|
size="small"
|
|
|
- :disabled="(pages == 'review' && statusPages == 4) || (pages == 'crop' && statusPages >= 6)"
|
|
|
|
|
|
|
+ :disabled="(pages == 'review' && statusPages == 4) || (pages == 'crop' && statusPages >= 6) || !imagesList.length"
|
|
|
:loading="saveLoading"
|
|
:loading="saveLoading"
|
|
|
@click="saveHandle"
|
|
@click="saveHandle"
|
|
|
>
|
|
>
|
|
@@ -43,29 +47,81 @@
|
|
|
<el-button
|
|
<el-button
|
|
|
type="primary"
|
|
type="primary"
|
|
|
size="small"
|
|
size="small"
|
|
|
- :disabled="(pages == 'review' && statusPages == 4) || (pages == 'crop' && statusPages >= 6)"
|
|
|
|
|
|
|
+ :disabled="(pages == 'review' && statusPages == 4) || (pages == 'crop' && statusPages >= 6) || !imagesList.length"
|
|
|
:loading="reviewLoading"
|
|
:loading="reviewLoading"
|
|
|
@click="handleConfirm"
|
|
@click="handleConfirm"
|
|
|
>
|
|
>
|
|
|
{{ pages == 'crop' && statusPages == 4 ? '确认裁图' : '审核通过' }}
|
|
{{ pages == 'crop' && statusPages == 4 ? '确认裁图' : '审核通过' }}
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="reservation-approval__submit" v-if="statusPages == 8">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ :loading="submitLoading"
|
|
|
|
|
+ @click="uploadGalleryList"
|
|
|
|
|
+ >
|
|
|
|
|
+ 上传图库
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="reservation-approval-body" :class="{'full': !form.regenerateImage}">
|
|
<div class="reservation-approval-body" :class="{'full': !form.regenerateImage}">
|
|
|
<el-row>
|
|
<el-row>
|
|
|
<el-col :span="24">
|
|
<el-col :span="24">
|
|
|
<div class="images-list-wapper" v-if="statusPages >= 5">
|
|
<div class="images-list-wapper" v-if="statusPages >= 5">
|
|
|
- <div class="images-list__one" v-for="(item, count) in cropImages" :key="count">
|
|
|
|
|
- <h2>{{ item.title }}</h2>
|
|
|
|
|
|
|
+ <div class="images-list__one">
|
|
|
|
|
+ <h2>主图1:1</h2>
|
|
|
|
|
+ <draggable
|
|
|
|
|
+ tag="div"
|
|
|
|
|
+ class="images-list-block"
|
|
|
|
|
+ v-model="imagesList"
|
|
|
|
|
+ @start="onDragStart"
|
|
|
|
|
+ @end="onDragEnd"
|
|
|
|
|
+ :options="{ animation: 150 }">
|
|
|
|
|
+ <div class="images-items" v-for="(acc, index) in imagesList" v-if="acc.position === 'main' && acc.width === acc.height" :key="index">
|
|
|
|
|
+ <images-item :images-list="cropPreview ? imagesList : originalImagesList" :pages="pages" :item="acc" :index="index" :status-pages="statusPages" @delImage="delImage" @needRegenerate="needRegenerate"></images-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </draggable>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="images-list__one">
|
|
|
|
|
+ <h2>主图3:4</h2>
|
|
|
|
|
+ <draggable
|
|
|
|
|
+ tag="div"
|
|
|
|
|
+ class="images-list-block"
|
|
|
|
|
+ v-model="imagesList"
|
|
|
|
|
+ @start="onDragStart"
|
|
|
|
|
+ @end="onDragEnd"
|
|
|
|
|
+ :options="{ animation: 150 }">
|
|
|
|
|
+ <div class="images-items" v-for="(acc, index) in imagesList" v-if="acc.position === 'main' && acc.width !== acc.height" :key="index">
|
|
|
|
|
+ <images-item :images-list="cropPreview ? imagesList : originalImagesList" :pages="pages" :item="acc" :index="index" :status-pages="statusPages" @delImage="delImage" @needRegenerate="needRegenerate"></images-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </draggable>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="images-list__one">
|
|
|
|
|
+ <h2>竖图2:3</h2>
|
|
|
<draggable
|
|
<draggable
|
|
|
tag="div"
|
|
tag="div"
|
|
|
class="images-list-block"
|
|
class="images-list-block"
|
|
|
- v-model="item.images"
|
|
|
|
|
|
|
+ v-model="imagesList"
|
|
|
@start="onDragStart"
|
|
@start="onDragStart"
|
|
|
@end="onDragEnd"
|
|
@end="onDragEnd"
|
|
|
:options="{ animation: 150 }">
|
|
:options="{ animation: 150 }">
|
|
|
- <div class="images-items" v-for="(acc, index) in item.images" :key="acc.id">
|
|
|
|
|
- <images-item :pages="pages" :item="acc" :index="index" :status-pages="statusPages" @delImage="delImage" @needRegenerate="needRegenerate"></images-item>
|
|
|
|
|
|
|
+ <div class="images-items" v-for="(acc, index) in imagesList" v-if="acc.position === 'list'" :key="index">
|
|
|
|
|
+ <images-item :images-list="cropPreview ? imagesList : originalImagesList" :pages="pages" :item="acc" :index="index" :status-pages="statusPages" @delImage="delImage" @needRegenerate="needRegenerate"></images-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </draggable>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="images-list__one">
|
|
|
|
|
+ <h2>颜色图1:1</h2>
|
|
|
|
|
+ <draggable
|
|
|
|
|
+ tag="div"
|
|
|
|
|
+ class="images-list-block"
|
|
|
|
|
+ v-model="imagesList"
|
|
|
|
|
+ @start="onDragStart"
|
|
|
|
|
+ @end="onDragEnd"
|
|
|
|
|
+ :options="{ animation: 150 }">
|
|
|
|
|
+ <div class="images-items" v-for="(acc, index) in imagesList" v-if="acc.position === 'color'" :key="index">
|
|
|
|
|
+ <images-item :images-list="cropPreview ? imagesList : originalImagesList" :pages="pages" :item="acc" :index="index" :status-pages="statusPages" @delImage="delImage" @needRegenerate="needRegenerate"></images-item>
|
|
|
</div>
|
|
</div>
|
|
|
</draggable>
|
|
</draggable>
|
|
|
</div>
|
|
</div>
|
|
@@ -77,9 +133,34 @@
|
|
|
v-model="imagesList"
|
|
v-model="imagesList"
|
|
|
@start="onDragStart"
|
|
@start="onDragStart"
|
|
|
@end="onDragEnd"
|
|
@end="onDragEnd"
|
|
|
|
|
+ :move="checkMove"
|
|
|
:options="{ animation: 150 }">
|
|
:options="{ animation: 150 }">
|
|
|
- <div class="images-items" v-for="(acc, index) in imagesList" :key="acc.id">
|
|
|
|
|
- <images-item :pages="pages" :item="acc" :index="index" :status-pages="statusPages" @delImage="delImage" @needRegenerate="needRegenerate"></images-item>
|
|
|
|
|
|
|
+ <div class="images-items" v-for="(acc, index) in imagesList" :key="index">
|
|
|
|
|
+ <images-item :images-list="cropPreview ? imagesList : originalImagesList" :crop-preview="cropPreview" :pages="pages" :item="acc" :index="index" :status-pages="statusPages" @delImage="delImage" @needRegenerate="needRegenerate"></images-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="images-items fixed-item" v-if="cropPreview">
|
|
|
|
|
+ <div class="images-items__img">
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ ref="uploadRefCrop"
|
|
|
|
|
+ class="upload-demo"
|
|
|
|
|
+ drag
|
|
|
|
|
+ :action="action"
|
|
|
|
|
+ :show-file-list="false"
|
|
|
|
|
+ :headers="{
|
|
|
|
|
+ 'Authorization': 'Bearer ' + token
|
|
|
|
|
+ }"
|
|
|
|
|
+ multiple
|
|
|
|
|
+ :on-success="handleVideoSuccess"
|
|
|
|
|
+ :before-upload="beforeUploadVideo">
|
|
|
|
|
+ <div v-loading="uploading">
|
|
|
|
|
+ <div class="el-upload__info">
|
|
|
|
|
+ <i class="el-icon-upload"></i>
|
|
|
|
|
+ <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
|
|
|
+ <div class="el-upload__tip" slot="tip">图片要求:支持JPG、PNG和WEBP,最大为20M</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</draggable>
|
|
</draggable>
|
|
|
</div>
|
|
</div>
|
|
@@ -105,8 +186,7 @@
|
|
|
:show-file-list="false"
|
|
:show-file-list="false"
|
|
|
:headers="{
|
|
:headers="{
|
|
|
'Authorization': 'Bearer ' + token
|
|
'Authorization': 'Bearer ' + token
|
|
|
- }"
|
|
|
|
|
- multiple
|
|
|
|
|
|
|
+ }"
|
|
|
:on-success="handleVideoSuccess"
|
|
:on-success="handleVideoSuccess"
|
|
|
:before-upload="beforeUploadVideo">
|
|
:before-upload="beforeUploadVideo">
|
|
|
<div v-loading="uploading">
|
|
<div v-loading="uploading">
|
|
@@ -135,9 +215,10 @@
|
|
|
</div>
|
|
</div>
|
|
|
</el-upload>
|
|
</el-upload>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ <span class="tips">重生成之后,需要保存!</span>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
<span class="regenerate-footer">
|
|
<span class="regenerate-footer">
|
|
|
- <el-button size="small" @click="resetForm">取消</el-button>
|
|
|
|
|
|
|
+ <el-button size="small" @click="colseRegenerateImage">取消</el-button>
|
|
|
<el-button type="primary" size="small" :loading="regenerateLoading" @click="handleRegenerate">重生成</el-button>
|
|
<el-button type="primary" size="small" :loading="regenerateLoading" @click="handleRegenerate">重生成</el-button>
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
@@ -171,6 +252,7 @@ export default {
|
|
|
return {
|
|
return {
|
|
|
action: api.fileUrl,
|
|
action: api.fileUrl,
|
|
|
uploading: false,
|
|
uploading: false,
|
|
|
|
|
+ cropPreview: false,
|
|
|
pages: '',
|
|
pages: '',
|
|
|
statusPages: '',
|
|
statusPages: '',
|
|
|
paramsId: '',
|
|
paramsId: '',
|
|
@@ -197,43 +279,30 @@ export default {
|
|
|
],
|
|
],
|
|
|
imagesList: [],
|
|
imagesList: [],
|
|
|
originalOrders: [],
|
|
originalOrders: [],
|
|
|
|
|
+ originalImagesList: [],
|
|
|
dialogVisible: false,
|
|
dialogVisible: false,
|
|
|
saveLoading: false,
|
|
saveLoading: false,
|
|
|
|
|
+ submitLoading: false,
|
|
|
reviewLoading: false,
|
|
reviewLoading: false,
|
|
|
regenerateLoading: false,
|
|
regenerateLoading: false,
|
|
|
- comfirmLoading: false
|
|
|
|
|
|
|
+ isCropDone: false
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
|
token() {
|
|
token() {
|
|
|
return getToken();
|
|
return getToken();
|
|
|
- },
|
|
|
|
|
- cropImages() {
|
|
|
|
|
- if (this.statusPages >= 5) {
|
|
|
|
|
- return [
|
|
|
|
|
- {
|
|
|
|
|
- title: '主图1:1',
|
|
|
|
|
- images: this.imagesList.filter(acc => acc.position == 'main' && acc.width == acc.height)
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- title: '主图3:4',
|
|
|
|
|
- images: this.imagesList.filter(acc => acc.position == 'main' && acc.width != acc.height)
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- title: '竖图2:3',
|
|
|
|
|
- images: this.imagesList.filter(acc => acc.position == 'list')
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
- } else {
|
|
|
|
|
- return []
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
watch: {
|
|
watch: {
|
|
|
dialogVisible(val) {
|
|
dialogVisible(val) {
|
|
|
if (!val) {
|
|
if (!val) {
|
|
|
- this.comfirmLoading = false;
|
|
|
|
|
this.regenerateLoading = false;
|
|
this.regenerateLoading = false;
|
|
|
|
|
+ if (this.reviewLoading) {
|
|
|
|
|
+ // 说明裁图还没有执行完毕
|
|
|
|
|
+ this.isCropDone = true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.isCropDone = false;
|
|
|
|
|
+ }
|
|
|
this.resetForm();
|
|
this.resetForm();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -242,8 +311,26 @@ export default {
|
|
|
show(row, type) {
|
|
show(row, type) {
|
|
|
this.dialogVisible = true;
|
|
this.dialogVisible = true;
|
|
|
this.$nextTick(() => {
|
|
this.$nextTick(() => {
|
|
|
|
|
+ // if (localStorage.getItem('cropInfo')) {
|
|
|
|
|
+ // try {
|
|
|
|
|
+ // const cropInfo = JSON.parse(localStorage.getItem('cropInfo'));
|
|
|
|
|
+ // this.form.sku = cropInfo.sku || null;
|
|
|
|
|
+ // this.form.cropType = cropInfo.cropType || null;
|
|
|
|
|
+ // this.form.colorCode = cropInfo.colorCode || '';
|
|
|
|
|
+ // } catch (e) {}
|
|
|
|
|
+ // }
|
|
|
|
|
+ this.cropPreview = false;
|
|
|
|
|
+ this.saveLoading = false;
|
|
|
this.pages = type;
|
|
this.pages = type;
|
|
|
this.paramsId = row.id;
|
|
this.paramsId = row.id;
|
|
|
|
|
+ if (row.type == 1) {
|
|
|
|
|
+ this.originalImagesList = [...row.referenceImagesList, ...row.originalImagesList];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.originalImagesList = row.aiGeneratedImageVOList.filter(acc => acc.imageType == 1).map(acc => {
|
|
|
|
|
+ return acc.imageUrl
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ // this.originalImagesList = [...row.referenceImagesList, ...row.originalImagesList];
|
|
|
this.taskId = row.aiGeneratedImageVOList && row.aiGeneratedImageVOList[0].taskId;
|
|
this.taskId = row.aiGeneratedImageVOList && row.aiGeneratedImageVOList[0].taskId;
|
|
|
this.statusPages = row.status * 1;
|
|
this.statusPages = row.status * 1;
|
|
|
this.form.sku = row.sku;
|
|
this.form.sku = row.sku;
|
|
@@ -261,6 +348,35 @@ export default {
|
|
|
console.log(this.imagesList, 444)
|
|
console.log(this.imagesList, 444)
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
+ init() {
|
|
|
|
|
+ this.dialogVisible = true;
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.saveLoading = false;
|
|
|
|
|
+ this.reviewLoading = false;
|
|
|
|
|
+ this.taskId = '';
|
|
|
|
|
+ this.cropPreview = true;
|
|
|
|
|
+ this.pages = 'crop';
|
|
|
|
|
+ this.statusPages = 4;
|
|
|
|
|
+ this.originalImagesList = []
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ uploadGalleryList() {
|
|
|
|
|
+ this.submitLoading = true;
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ taskId: this.taskId
|
|
|
|
|
+ }
|
|
|
|
|
+ request({
|
|
|
|
|
+ url: '/imageTask/uploadGallery',
|
|
|
|
|
+ method: 'post',
|
|
|
|
|
+ data: params
|
|
|
|
|
+ }).then(res => {
|
|
|
|
|
+ if (res.code == 200) {
|
|
|
|
|
+ this.$message.success(res.msg || '操作成功!');
|
|
|
|
|
+ }
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+ this.submitLoading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
saveHandle() {
|
|
saveHandle() {
|
|
|
this.saveLoading = true;
|
|
this.saveLoading = true;
|
|
|
request({
|
|
request({
|
|
@@ -282,87 +398,177 @@ export default {
|
|
|
this.saveLoading = false;
|
|
this.saveLoading = false;
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
+ async handleCrop() {
|
|
|
|
|
+ // 参数校验
|
|
|
|
|
+ if (!this.validateCropParams()) return;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.reviewLoading = true;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是裁图预览模式,先直传图片
|
|
|
|
|
+ if (this.cropPreview && !this.taskId) {
|
|
|
|
|
+ const res = await this.directSubmitImages();
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ this.isCropDone = false;
|
|
|
|
|
+ this.dialogVisible = false
|
|
|
|
|
+ this.$emit('update-success');
|
|
|
|
|
+ this.$message.success('裁图已提交');
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 执行裁图
|
|
|
|
|
+ const res = await this.cropImagesHandle();
|
|
|
|
|
+ if (res.code === 200 && this.taskId && !this.isCropDone) {
|
|
|
|
|
+ this.isCropDone = false;
|
|
|
|
|
+ this.afterCropSuccess(res);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (res.code === 500) {
|
|
|
|
|
+ this.isCropDone = false;
|
|
|
|
|
+ this.$emit('update-success');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error(err);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.reviewLoading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ // 参数校验
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ validateCropParams() {
|
|
|
|
|
+ if (this.cropPreview && !this.form.sku) {
|
|
|
|
|
+ this.$message.error('请填写SKU!');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.form.cropType || !this.form.colorCode) {
|
|
|
|
|
+ this.$message.error('请选择裁切类型或填写颜色代码!');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ // 直传图片(directSubmit)
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ directSubmitImages() {
|
|
|
|
|
+ return request({
|
|
|
|
|
+ url: '/imageTask/directSubmit',
|
|
|
|
|
+ method: 'post',
|
|
|
|
|
+ data: {
|
|
|
|
|
+ sessionId: `${Date.now() + 10}_${genCidHex16()}`,
|
|
|
|
|
+ applicationId: 6,
|
|
|
|
|
+ sizes: this.sizeList.map(acc => ({
|
|
|
|
|
+ position: acc.positionCode,
|
|
|
|
|
+ height: acc.height,
|
|
|
|
|
+ width: acc.width,
|
|
|
|
|
+ sort: acc.sort
|
|
|
|
|
+ })),
|
|
|
|
|
+ tasks: [
|
|
|
|
|
+ {
|
|
|
|
|
+ sku: this.form.sku,
|
|
|
|
|
+ type: this.form.cropType,
|
|
|
|
|
+ colorCode: this.form.colorCode,
|
|
|
|
|
+ images: this.imagesList.map((acc, index) => ({
|
|
|
|
|
+ imageUrl: acc.imageUrl,
|
|
|
|
|
+ imageOrder: index
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ // 裁图接口(crop)
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ cropImagesHandle() {
|
|
|
|
|
+ return request({
|
|
|
|
|
+ url: '/imageTask/crop',
|
|
|
|
|
+ method: 'post',
|
|
|
|
|
+ data: {
|
|
|
|
|
+ sessionId: `${Date.now() + 10}_${genCidHex16()}`,
|
|
|
|
|
+ applicationId: 6,
|
|
|
|
|
+ sku: this.form.sku,
|
|
|
|
|
+ type: this.form.cropType,
|
|
|
|
|
+ taskId: this.taskId,
|
|
|
|
|
+ colorCode: this.form.colorCode,
|
|
|
|
|
+ sizes: this.sizeList.map(acc => ({
|
|
|
|
|
+ position: acc.positionCode,
|
|
|
|
|
+ height: acc.height,
|
|
|
|
|
+ width: acc.width,
|
|
|
|
|
+ sort: acc.sort
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ // 裁图成功后的统一处理
|
|
|
|
|
+ // ======================
|
|
|
|
|
+ afterCropSuccess(res) {
|
|
|
|
|
+ this.statusPages += 1;
|
|
|
|
|
+
|
|
|
|
|
+ this.$emit('update-success');
|
|
|
|
|
+ this.$emit('update-status', this.paramsId, this.statusPages);
|
|
|
|
|
+
|
|
|
|
|
+ this.imagesList = res.data.map(acc => ({
|
|
|
|
|
+ id: acc.id,
|
|
|
|
|
+ imageUrl: acc.imageUrl,
|
|
|
|
|
+ imageOrder: acc.imageOrder,
|
|
|
|
|
+ position: acc.position,
|
|
|
|
|
+ width: acc.width,
|
|
|
|
|
+ height: acc.height
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ this.$message.success(res.msg || '操作成功!');
|
|
|
|
|
+ },
|
|
|
handleConfirm() {
|
|
handleConfirm() {
|
|
|
if (this.pages == 'crop' && this.statusPages == 4) {
|
|
if (this.pages == 'crop' && this.statusPages == 4) {
|
|
|
- // 裁图
|
|
|
|
|
- if (!this.form.cropType || !this.form.colorCode) {
|
|
|
|
|
- this.$message.error('请选择裁切类型或填写颜色代码!');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ this.handleCrop();
|
|
|
|
|
+ } else {
|
|
|
this.reviewLoading = true;
|
|
this.reviewLoading = true;
|
|
|
request({
|
|
request({
|
|
|
- url: '/imageTask/crop',
|
|
|
|
|
|
|
+ url: `/imageTask/audit/${this.paramsId}`,
|
|
|
method: 'post',
|
|
method: 'post',
|
|
|
data: {
|
|
data: {
|
|
|
- sessionId: `${Date.now() + 10}_${genCidHex16()}`,
|
|
|
|
|
- applicationId: 6,
|
|
|
|
|
- sku: this.form.sku,
|
|
|
|
|
- type: this.form.cropType,
|
|
|
|
|
- taskId: this.taskId,
|
|
|
|
|
- colorCode: this.form.colorCode,
|
|
|
|
|
- sizes: this.sizeList.map(acc => {
|
|
|
|
|
|
|
+ id: this.paramsId,
|
|
|
|
|
+ aiGeneratedImageUpdateParams: this.imagesList.map(acc => {
|
|
|
return {
|
|
return {
|
|
|
- position: acc.positionCode,
|
|
|
|
|
- height: acc.height,
|
|
|
|
|
- width: acc.width,
|
|
|
|
|
- sort: acc.sort
|
|
|
|
|
|
|
+ id: acc.id,
|
|
|
|
|
+ imageUrl: acc.imageUrl,
|
|
|
|
|
+ imageOrder: acc.imageOrder,
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
}).then(res => {
|
|
}).then(res => {
|
|
|
if (res.code == 200) {
|
|
if (res.code == 200) {
|
|
|
this.statusPages += 1;
|
|
this.statusPages += 1;
|
|
|
- this.$emit('update-success');
|
|
|
|
|
|
|
+ if (this.statusPages >= 6) {
|
|
|
|
|
+ this.form.regenerateImage = null;
|
|
|
|
|
+ }
|
|
|
this.$emit('update-status', this.paramsId, this.statusPages);
|
|
this.$emit('update-status', this.paramsId, this.statusPages);
|
|
|
- // 更新imagesList
|
|
|
|
|
- this.imagesList = res.data.map(acc => {
|
|
|
|
|
- return {
|
|
|
|
|
- id: acc.id,
|
|
|
|
|
- imageUrl: acc.imageUrl,
|
|
|
|
|
- imageOrder: acc.imageOrder,
|
|
|
|
|
- width: acc.width,
|
|
|
|
|
- height: acc.height
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
this.$message.success(res.msg || '操作成功!');
|
|
this.$message.success(res.msg || '操作成功!');
|
|
|
}
|
|
}
|
|
|
}).finally(() => {
|
|
}).finally(() => {
|
|
|
this.reviewLoading = false;
|
|
this.reviewLoading = false;
|
|
|
})
|
|
})
|
|
|
- return;
|
|
|
|
|
}
|
|
}
|
|
|
- this.reviewLoading = true;
|
|
|
|
|
- request({
|
|
|
|
|
- url: `/imageTask/audit/${this.paramsId}`,
|
|
|
|
|
- method: 'post',
|
|
|
|
|
- data: {
|
|
|
|
|
- id: this.paramsId,
|
|
|
|
|
- aiGeneratedImageUpdateParams: this.imagesList.map(acc => {
|
|
|
|
|
- return {
|
|
|
|
|
- id: acc.id,
|
|
|
|
|
- imageUrl: acc.imageUrl,
|
|
|
|
|
- imageOrder: acc.imageOrder,
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- }).then(res => {
|
|
|
|
|
- if (res.code == 200) {
|
|
|
|
|
- this.statusPages += 1;
|
|
|
|
|
- if (this.statusPages >= 6) {
|
|
|
|
|
- this.form.regenerateImage = null;
|
|
|
|
|
- }
|
|
|
|
|
- this.$emit('update-status', this.paramsId, this.statusPages);
|
|
|
|
|
- this.$message.success(res.msg || '操作成功!');
|
|
|
|
|
- }
|
|
|
|
|
- }).finally(() => {
|
|
|
|
|
- this.reviewLoading = false;
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
},
|
|
},
|
|
|
// 拖动开始:记录原始 imageOrder 顺序
|
|
// 拖动开始:记录原始 imageOrder 顺序
|
|
|
onDragStart() {
|
|
onDragStart() {
|
|
|
this.originalOrders = this.imagesList.map(item => item.imageOrder)
|
|
this.originalOrders = this.imagesList.map(item => item.imageOrder)
|
|
|
},
|
|
},
|
|
|
-
|
|
|
|
|
|
|
+ checkMove(evt) {
|
|
|
|
|
+ // 禁止 fixed-item 被拖动
|
|
|
|
|
+ if (evt.dragged.classList.contains('fixed-item')) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ },
|
|
|
// 拖动结束:按当前位置重新赋值 imageOrder
|
|
// 拖动结束:按当前位置重新赋值 imageOrder
|
|
|
onDragEnd() {
|
|
onDragEnd() {
|
|
|
this.imagesList = this.imagesList.map((item, index) => {
|
|
this.imagesList = this.imagesList.map((item, index) => {
|
|
@@ -379,30 +585,50 @@ export default {
|
|
|
cancelButtonText: "取消",
|
|
cancelButtonText: "取消",
|
|
|
type: "warning"
|
|
type: "warning"
|
|
|
}).then(() => {
|
|
}).then(() => {
|
|
|
- request({
|
|
|
|
|
- url: '/image/delete',
|
|
|
|
|
- method: 'post',
|
|
|
|
|
- data: [row.id]
|
|
|
|
|
- }).then(res => {
|
|
|
|
|
- if (res.code == 200) {
|
|
|
|
|
- this.imagesList.splice(itemIndex, 1);
|
|
|
|
|
- this.$emit('update-images', this.paramsId, row.id);
|
|
|
|
|
- if (row.id == this.form.imageId) {
|
|
|
|
|
- this.form.regenerateImage = null;
|
|
|
|
|
|
|
+ if (this.cropPreview) {
|
|
|
|
|
+ this.imagesList.splice(itemIndex, 1);
|
|
|
|
|
+ this.$notify({
|
|
|
|
|
+ title: "成功",
|
|
|
|
|
+ message: "删除成功",
|
|
|
|
|
+ type: "success",
|
|
|
|
|
+ duration: 3000
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ request({
|
|
|
|
|
+ url: '/image/delete',
|
|
|
|
|
+ method: 'post',
|
|
|
|
|
+ data: [row.id]
|
|
|
|
|
+ }).then(res => {
|
|
|
|
|
+ if (res.code == 200) {
|
|
|
|
|
+ this.imagesList.forEach((acc, itemIndex) => {
|
|
|
|
|
+ if (acc.id == row.id) {
|
|
|
|
|
+ this.imagesList.splice(itemIndex, 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ this.$emit('update-images', this.paramsId, row.id);
|
|
|
|
|
+ if (row.id == this.form.imageId) {
|
|
|
|
|
+ this.form.regenerateImage = null;
|
|
|
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ this.$notify({
|
|
|
|
|
+ title: "成功",
|
|
|
|
|
+ message: "删除成功",
|
|
|
|
|
+ type: "success",
|
|
|
|
|
+ duration: 3000
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
- this.$notify({
|
|
|
|
|
- title: "成功",
|
|
|
|
|
- message: "删除成功",
|
|
|
|
|
- type: "success",
|
|
|
|
|
- duration: 3000
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- }).finally(() => {
|
|
|
|
|
-
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
|
|
+ colseRegenerateImage() {
|
|
|
|
|
+ this.form.regenerateImage = null;
|
|
|
|
|
+ this.form.prompt = '';
|
|
|
|
|
+ this.form.imageUrl = '';
|
|
|
|
|
+ },
|
|
|
async handleRegenerate() {
|
|
async handleRegenerate() {
|
|
|
if (this.pages == 'review' && !this.form.prompt) {
|
|
if (this.pages == 'review' && !this.form.prompt) {
|
|
|
this.$message.error('请填写内容!');
|
|
this.$message.error('请填写内容!');
|
|
@@ -412,69 +638,78 @@ export default {
|
|
|
this.$message.error('请上传裁图图片!');
|
|
this.$message.error('请上传裁图图片!');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- this.regenerateLoading = true;
|
|
|
|
|
- try {
|
|
|
|
|
- const payload = {
|
|
|
|
|
- sessionId: `${Date.now() + 10}_${genCidHex16()}`,
|
|
|
|
|
- applicationId: 2,
|
|
|
|
|
- images: this.pages == 'review' ? [this.form.regenerateImage] : [this.form.imageUrl]
|
|
|
|
|
- }
|
|
|
|
|
- if (this.pages == 'review') {
|
|
|
|
|
- payload.prompt = this.form.prompt;
|
|
|
|
|
- }
|
|
|
|
|
- const result = await fetchStreamText(`/app/ai/send/imageMessage`, payload, {
|
|
|
|
|
- onChunk: (chunk) => {
|
|
|
|
|
- console.log('实时分片:', chunk)
|
|
|
|
|
- // this.replyText += chunk // Vue 可实时更新聊天内容
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ if (this.pages == 'review') {
|
|
|
|
|
+ this.regenerateLoading = true;
|
|
|
try {
|
|
try {
|
|
|
- this.regenerateLoading = false;
|
|
|
|
|
- if (result && typeof result == 'string') {
|
|
|
|
|
- console.log('完整文本:', result)
|
|
|
|
|
- const data = JSON.parse(result)
|
|
|
|
|
- if (result.code == 200) {
|
|
|
|
|
- this.imagesList.forEach(acc => {
|
|
|
|
|
- if (acc.id == this.form.imageId) {
|
|
|
|
|
- acc.imageUrl = data.image
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- } else {
|
|
|
|
|
- if (result.code == 401) {
|
|
|
|
|
- this.$router.push(`/login`);
|
|
|
|
|
- } else {
|
|
|
|
|
- this.$message.error(result.msg || result.errorContent || '请求出错,请联系管理员!');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const payload = {
|
|
|
|
|
+ sessionId: `${Date.now() + 10}_${genCidHex16()}`,
|
|
|
|
|
+ applicationId: 2,
|
|
|
|
|
+ images: this.pages == 'review' ? [this.form.regenerateImage] : [this.form.imageUrl]
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.pages == 'review') {
|
|
|
|
|
+ payload.prompt = this.form.prompt;
|
|
|
|
|
+ }
|
|
|
|
|
+ const result = await fetchStreamText(`/app/ai/send/imageMessage`, payload, {
|
|
|
|
|
+ onChunk: (chunk) => {
|
|
|
|
|
+ console.log('实时分片:', chunk)
|
|
|
|
|
+ // this.replyText += chunk // Vue 可实时更新聊天内容
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- console.log('JSON 对象12', result)
|
|
|
|
|
- if (result.code == 200) {
|
|
|
|
|
- this.imagesList.forEach(acc => {
|
|
|
|
|
- if (acc.id == this.form.imageId) {
|
|
|
|
|
- acc.imageUrl = result.image
|
|
|
|
|
|
|
+ })
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.regenerateLoading = false;
|
|
|
|
|
+ if (result && typeof result == 'string') {
|
|
|
|
|
+ console.log('完整文本:', result)
|
|
|
|
|
+ const data = JSON.parse(result)
|
|
|
|
|
+ if (result.code == 200) {
|
|
|
|
|
+ this.imagesList.forEach(acc => {
|
|
|
|
|
+ if (acc.id == this.form.imageId) {
|
|
|
|
|
+ acc.imageUrl = data.image
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (result.code == 401) {
|
|
|
|
|
+ this.$router.push(`/login`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.$message.error(result.msg || result.errorContent || '请求出错,请联系管理员!');
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
- if (result.code == 401) {
|
|
|
|
|
- this.$router.push(`/login`);
|
|
|
|
|
|
|
+ console.log('JSON 对象12', result)
|
|
|
|
|
+ if (result.code == 200) {
|
|
|
|
|
+ this.imagesList.forEach(acc => {
|
|
|
|
|
+ if (acc.id == this.form.imageId) {
|
|
|
|
|
+ acc.imageUrl = result.image
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
} else {
|
|
} else {
|
|
|
- this.$message.error(result.msg || result.errorContent || '请求出错,请联系管理员!');
|
|
|
|
|
|
|
+ if (result.code == 401) {
|
|
|
|
|
+ this.$router.push(`/login`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.$message.error(result.msg || result.errorContent || '请求出错,请联系管理员!');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('报错:', e)
|
|
|
}
|
|
}
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
- console.error('报错:', e)
|
|
|
|
|
|
|
+ console.error('请求错误:', e)
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.regenerateLoading = false;
|
|
|
}
|
|
}
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('请求错误:', e)
|
|
|
|
|
- } finally {
|
|
|
|
|
- this.regenerateLoading = false;
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.imagesList.forEach(acc => {
|
|
|
|
|
+ if (acc.id == this.form.imageId) {
|
|
|
|
|
+ acc.imageUrl = this.form.imageUrl;
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- needRegenerate(index) {
|
|
|
|
|
- this.form.imageId = this.imagesList[index].id;
|
|
|
|
|
- this.form.regenerateImage = this.imagesList[index].imageUrl;
|
|
|
|
|
|
|
+ needRegenerate(item) {
|
|
|
|
|
+ this.form.imageUrl = '';
|
|
|
|
|
+ this.form.imageId = this.imagesList.filter(acc => acc.id == item.id)[0].id;
|
|
|
|
|
+ this.form.regenerateImage = this.imagesList.filter(acc => acc.id == item.id)[0].imageUrl;
|
|
|
},
|
|
},
|
|
|
replaceFile() {
|
|
replaceFile() {
|
|
|
if (this.$refs.uploadRef) {
|
|
if (this.$refs.uploadRef) {
|
|
@@ -505,7 +740,14 @@ export default {
|
|
|
handleVideoSuccess(res) {
|
|
handleVideoSuccess(res) {
|
|
|
this.uploading = false
|
|
this.uploading = false
|
|
|
if (res.code == 200) {
|
|
if (res.code == 200) {
|
|
|
- this.form.imageUrl = res.data.url
|
|
|
|
|
|
|
+ if (this.cropPreview) {
|
|
|
|
|
+ this.imagesList.push({
|
|
|
|
|
+ imageUrl: res.data.url
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.form.imageUrl = res.data.url;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
} else {
|
|
} else {
|
|
|
this.$message.error('上传失败,请重新上传!');
|
|
this.$message.error('上传失败,请重新上传!');
|
|
|
}
|
|
}
|
|
@@ -527,6 +769,15 @@ export default {
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
resetForm() {
|
|
resetForm() {
|
|
|
|
|
+ this.cropPreview = false;
|
|
|
|
|
+ this.pages = '';
|
|
|
|
|
+ this.statusPages = '';
|
|
|
|
|
+ this.paramsId = '';
|
|
|
|
|
+ this.taskId = '';
|
|
|
|
|
+ this.imagesList = [];
|
|
|
|
|
+ this.originalOrders = [];
|
|
|
|
|
+ const data = { sku: this.form.sku, cropType: this.form.cropType, colorCode: this.form.colorCode };
|
|
|
|
|
+ localStorage.setItem('cropInfo', JSON.stringify(data));
|
|
|
this.form = {
|
|
this.form = {
|
|
|
imageId: '',
|
|
imageId: '',
|
|
|
regenerateImage: null,
|
|
regenerateImage: null,
|
|
@@ -585,6 +836,7 @@ export default {
|
|
|
border-radius: 200px;
|
|
border-radius: 200px;
|
|
|
font-size: 12px;
|
|
font-size: 12px;
|
|
|
line-height: 32px;
|
|
line-height: 32px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
|
|
|
&.active {
|
|
&.active {
|
|
|
background: #ae8877;
|
|
background: #ae8877;
|
|
@@ -604,6 +856,11 @@ export default {
|
|
|
width: 75%;
|
|
width: 75%;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ .tips {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: red;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
&.full .el-row {
|
|
&.full .el-row {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
}
|
|
}
|
|
@@ -661,6 +918,25 @@ export default {
|
|
|
display: block;
|
|
display: block;
|
|
|
z-index: 10;
|
|
z-index: 10;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ .upload-demo {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .el-upload-dragger {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ padding: 0 20px;
|
|
|
|
|
+
|
|
|
|
|
+ .el-icon-upload {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ margin-bottom: 30px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
.btns-group {
|
|
.btns-group {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
@@ -801,6 +1077,14 @@ export default {
|
|
|
width: 25%;
|
|
width: 25%;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ @media screen and (max-width: 1540px) {
|
|
|
|
|
+ .reservation-approval-block .reservation-approval-header {
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .reservation-approval-dialog {
|
|
|
|
|
+ width: 98% !important;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|