You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
crmeb/app/pages/supply_chain/wish_menu/index.vue

732 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<z-paging ref="paging" v-model="records" @query="queryList" :layout-only="activeTab === 'form'">
<template #top>
<!-- 顶部标签:我要祈愿 / 祈愿记录 -->
<view class="tabs">
<view
class="tab"
:class="activeTab === 'form' ? 'active' : ''"
@click="activeTab = 'form'"
>
我要祈愿
</view>
<view
class="tab"
:class="activeTab === 'list' ? 'active' : ''"
@click="switchToList"
>
祈愿记录
</view>
</view>
</template>
<!-- 表单区域 -->
<view class="tab-content" v-show="activeTab === 'form'">
<view class="form-card">
<view class="form-item">
<text class="label">祈愿菜品</text>
<textarea
class="textarea"
v-model="form.csContent"
placeholder="请详细描述您想吃的菜品..."
maxlength="300"
:auto-height="true"
/>
<text class="count">{{ form.csContent.length }}/300</text>
</view>
<view class="form-item">
<text class="label">联系方式</text>
<input
class="input"
type="text"
v-model="form.phone"
placeholder="请输入手机号(方便联系)"
maxlength="20"
/>
</view>
<view class="form-item">
<text class="label">上传图片(选填)</text>
<view class="upload-section">
<view class="upload-list">
<view
class="upload-item"
v-for="(image, index) in images"
:key="index"
>
<image :src="HTTP_ADMIN_URL + '/' + image.url" mode="aspectFill"></image>
<text class="delete-btn" @click="deleteImage(index)">×</text>
</view>
<view class="upload-btn" @click="chooseImage" v-if="images.length < 9">
<text class="iconfont icon-tianjia"></text>
<text>添加图片</text>
</view>
</view>
<text class="hint">最多上传9张图片</text>
</view>
</view>
<view class="form-item remark-item">
<text class="label">备注(选填)</text>
<input
class="input"
type="text"
v-model="form.remark"
placeholder="如有补充说明可在此填写"
maxlength="100"
/>
</view>
</view>
<view class="submit-section">
<view class="submit-btn" @click="submitWish">
提交祈愿
</view>
</view>
</view>
<!-- 列表区域 -->
<view class="tab-content" v-show="activeTab === 'list'">
<view
class="record-card"
v-for="item in records"
:key="item.id"
>
<view class="card-header">
<view class="left">
<text class="type">祈愿</text>
<text
class="status"
:class="statusClassMap[item.status] || statusClassMap['__default__']"
>
{{ statusText(item.status) }}
</text>
</view>
<view class="header-right">
<text class="time">
{{ item.submitTime || item.createTime || '' }}
</text>
<text class="delete-btn" @click="handleDelete(item.id)">删除</text>
</view>
</view>
<view class="card-body">
<view class="row">
<text class="label">祈愿人</text>
<text class="value">
{{ getOwnerName(item) }}
</text>
</view>
<view class="row">
<text class="label">祈愿菜品</text>
<text class="value">
{{ item.csContent || '—' }}
</text>
</view>
<view class="row" v-if="item.handlerReslut">
<text class="label">处理结果</text>
<text class="value">
{{ item.handlerReslut }}
</text>
</view>
<view class="row" v-if="item.handlerName">
<text class="label">处理人</text>
<text class="value">
{{ item.handlerName }}
</text>
</view>
<view class="row" v-if="item.handlerDate">
<text class="label">处理时间</text>
<text class="value">
{{ item.handlerDate }}
</text>
</view>
<view
class="image-section"
v-if="item.beforeProcessFiles && item.beforeProcessFiles.length"
>
<text class="image-title">祈愿图片</text>
<view class="image-grid">
<view
class="image-item"
v-for="(file, imgIndex) in item.beforeProcessFiles"
:key="imgIndex"
@click="previewRecordImages(item.beforeProcessFiles, imgIndex)"
>
<image
:src="HTTP_ADMIN_URL + '/' + (file.url || file.attDir)"
mode="aspectFill"
></image>
</view>
</view>
</view>
<view
class="image-section"
v-if="item.afterProcessFiles && item.afterProcessFiles.length"
>
<text class="image-title">处理图片</text>
<view class="image-grid">
<view
class="image-item"
v-for="(file, imgIndex) in item.afterProcessFiles"
:key="imgIndex"
@click="previewRecordImages(item.afterProcessFiles, imgIndex)"
>
<image
:src="HTTP_ADMIN_URL + '/' + (file.url || file.attDir)"
mode="aspectFill"
></image>
</view>
</view>
</view>
</view>
</view>
</view>
</z-paging>
</template>
<script>
import {
createComplaintSuggestion,
wishMenuList,
deleteComplaintSuggestion
} from '@/api/property.js';
import request from '@/utils/request.js';
import { HTTP_ADMIN_URL } from '@/config/app';
import store from '@/store';
export default {
data() {
return {
HTTP_ADMIN_URL,
activeTab: 'form',
form: {
csType: '99',
submitChannel: '1',
csContent: '',
remark: '',
phone: '',
},
images: [],
records: [],
statusClassMap: {
0: 'status-pending',
1: 'status-pending',
2: 'status-processing',
3: 'status-done',
'__default__': 'status-pending'
}
};
},
onLoad() {
if (this.activeTab === 'list') {
this.refreshList();
}
},
methods: {
switchToList() {
this.activeTab = 'list';
if (!this.records.length) {
this.$refs.paging.reload();
}
},
chooseImage() {
// #ifndef MP-WEIXIN
uni.chooseImage({
count: 9 - this.images.length,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.uploadImages(res.tempFilePaths);
},
fail: (err) => {
console.error('选择图片失败:', err);
}
});
// #endif
// #ifdef MP-WEIXIN
uni.chooseMedia({
count: 9 - this.images.length,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: (res) => {
const tempFilePaths = res.tempFiles.map(file => file.tempFilePath);
this.uploadImages(tempFilePaths);
},
fail: (err) => {
console.error('选择图片失败:', err);
}
});
// #endif
},
async uploadImages(filePaths) {
for (let i = 0; i < filePaths.length; i++) {
try {
uni.showLoading({ title: '上传中...', mask: true });
const result = await request.uploadFile(filePaths[i], 'multipart', {}, {
params: {
model: 'app-wish',
pid: 10
}
});
this.images.push(result);
} catch (e) {
console.error('上传图片失败:', e);
uni.showToast({
title: '上传失败:' + (typeof e === 'string' ? e : '未知错误'),
icon: 'none'
});
} finally {
uni.hideLoading();
}
}
},
deleteImage(index) {
this.images.splice(index, 1);
},
async submitWish() {
if (!this.form.csContent.trim()) {
uni.showToast({
title: '请填写祈愿菜品',
icon: 'none'
});
return;
}
const imageFiles = this.images.map(file => {
let attDir = file.url;
return {
attId: file.id + '',
name: file.fileName,
attDir,
attSize: file.fileSize,
attType: file.type,
fileName: file.fileName,
filePath: attDir,
url: attDir,
originalFileName: file.fileName
};
});
// 获取当前时间的yyyy-mm-dd hh:mm:ss格式
const getCurrentTime = () => {
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
const payload = {
csType: this.form.csType,
submitChannel: this.form.submitChannel,
csContent: this.form.csContent.trim(),
remark: this.form.remark.trim(),
phone: this.form.phone.trim(),
status: '1',
beforeProcessFiles: imageFiles,
submitTime: getCurrentTime(),
};
try {
uni.showLoading({ title: '提交中...', mask: true });
await createComplaintSuggestion(payload);
uni.hideLoading();
uni.showToast({
title: '提交成功',
icon: 'success'
});
this.form.csContent = '';
this.form.remark = '';
this.form.phone = '';
this.images = [];
if (this.activeTab === 'list') {
this.$refs.paging.reload();
}
} catch (e) {
uni.hideLoading();
uni.showToast({
title: typeof e === 'string' ? e : '提交失败,请稍后重试',
icon: 'none'
});
}
},
// 获取祈愿记录列表
queryList(pageNo, pageSize) {
// 调用API
wishMenuList({
page: pageNo,
limit: pageSize
}).then((res) => {
const data = res?.data;
const list = data?.list || [];
// 将请求结果通过complete传给z-paging处理
this.$refs.paging.complete(list);
}).catch((err) => {
console.error('获取祈愿记录失败:', err);
// 如果请求失败调用complete(false)
this.$refs.paging.complete(false);
});
},
statusText(status) {
if (!status || status === '0') return '待处理';
if (status === '1') return '未处理';
if (status === '2') return '处理中';
if (status === '3') return '已处理';
return status;
},
previewRecordImages(files, index) {
if (!files || !files.length) return;
const urls = files.map(file => {
const path = file.url || file.attDir || file.filePath;
return this.HTTP_ADMIN_URL + '/' + path;
});
uni.previewImage({
current: index,
urls
});
},
async handleDelete(id) {
uni.showModal({
title: '确认删除',
content: '确定要删除这条祈愿记录吗?',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({ title: '删除中...', mask: true });
await deleteComplaintSuggestion([id]);
uni.hideLoading();
uni.showToast({
title: '删除成功',
icon: 'success'
});
this.$refs.paging.reload();
} catch (e) {
uni.hideLoading();
uni.showToast({
title: typeof e === 'string' ? e : '删除失败,请稍后重试',
icon: 'none'
});
}
}
}
});
},
getOwnerName(item) {
if (item.ownerId === store.getters.uid) {
return store.getters.userInfo.nickname || '—';
}
return item.ownerName || '—';
},
}
};
</script>
<style lang="scss">
.tabs {
padding: 0 30rpx;
display: flex;
margin-bottom: 30rpx;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
.tab {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #666;
position: relative;
&.active {
color: #409EFF;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background-color: #409EFF;
border-radius: 2rpx;
}
}
}
}
.tab-content {
padding: 0 30rpx;
.form-card {
background-color: #fff;
border-radius: 10rpx;
padding: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.08);
.form-item {
margin-bottom: 30rpx;
.label {
display: block;
font-size: 26rpx;
color: #333;
margin-bottom: 16rpx;
font-weight: 600;
}
.input {
width: 100%;
height: 72rpx;
border-radius: 10rpx;
border: 1rpx solid #e5e5e5;
padding: 0 20rpx;
font-size: 24rpx;
box-sizing: border-box;
}
.textarea {
width: 100%;
min-height: 160rpx;
border-radius: 10rpx;
border: 1rpx solid #e5e5e5;
padding: 20rpx;
font-size: 24rpx;
box-sizing: border-box;
line-height: 1.6;
}
.count {
display: block;
margin-top: 10rpx;
text-align: right;
font-size: 20rpx;
color: #999;
}
.upload-section {
.upload-list {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
.upload-item {
width: 180rpx;
height: 180rpx;
position: relative;
border-radius: 10rpx;
overflow: hidden;
border: 1rpx solid #e5e5e5;
image {
width: 100%;
height: 100%;
}
.delete-btn {
position: absolute;
top: 10rpx;
right: 10rpx;
width: 36rpx;
height: 36rpx;
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
line-height: 1;
}
}
.upload-btn {
width: 180rpx;
height: 180rpx;
border: 1rpx dashed #d9d9d9;
border-radius: 10rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fafafa;
.iconfont {
font-size: 40rpx;
color: #bfbfbf;
margin-bottom: 10rpx;
}
text {
font-size: 24rpx;
color: #999;
}
}
}
.hint {
display: block;
margin-top: 16rpx;
font-size: 20rpx;
color: #999;
}
}
}
.remark-item {
margin-bottom: 0;
}
}
.submit-section {
margin-top: 40rpx;
margin-bottom: 30rpx;
.submit-btn {
height: 88rpx;
background-color: #409EFF;
color: #fff;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-weight: 600;
box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.35);
}
}
.record-card {
background-color: #fff;
border-radius: 10rpx;
padding: 24rpx 26rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.06);
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
.left {
display: flex;
align-items: center;
.type {
font-size: 26rpx;
font-weight: 600;
color: #333;
margin-right: 16rpx;
}
.status {
padding: 6rpx 18rpx;
border-radius: 20rpx;
font-size: 20rpx;
}
.status-pending {
background-color: #E6F7FF;
color: #409EFF;
}
.status-done {
background-color: #F6FFED;
color: #52C41A;
}
.status-processing {
background-color: #FFF7E6;
color: #FA8C16;
}
}
.header-right {
display: flex;
align-items: center;
flex-direction: column;
align-items: flex-end;
.time {
font-size: 22rpx;
color: #999;
margin-bottom: 8rpx;
}
.delete-btn {
font-size: 22rpx;
color: #FF4D4F;
padding: 4rpx 12rpx;
border-radius: 16rpx;
background-color: #FFF1F0;
}
}
}
.card-body {
.row {
display: flex;
margin-bottom: 10rpx;
.label {
width: 140rpx;
font-size: 24rpx;
color: #666;
}
.value {
flex: 1;
font-size: 24rpx;
color: #333;
}
}
.image-section {
margin-top: 12rpx;
.image-title {
display: block;
font-size: 22rpx;
color: #666;
margin-bottom: 10rpx;
}
.image-grid {
margin-left: 0;
display: flex;
flex-wrap: wrap;
gap: 12rpx;
.image-item {
width: calc((100% - 24rpx) / 3);
aspect-ratio: 1 / 1;
border-radius: 8rpx;
overflow: hidden;
background-color: #f5f5f5;
image {
width: 100%;
height: 100%;
display: block;
}
}
}
}
}
}
}
</style>
No newline at end of file