<template>
|
<view class="after-sales-edit">
|
<PageHeader :title="pageTitle" @back="goBack" />
|
|
<view class="form-container">
|
<up-form ref="formRef" :model="form" :rules="isReadonly ? {} : rules" label-width="100">
|
<up-form-item label="客户名称" prop="customerName" required @click="openCustomerPicker">
|
<up-input v-model="form.customerName" readonly placeholder="请选择客户" />
|
<template #right>
|
<up-icon v-if="!isReadonly" name="arrow-right" @click="openCustomerPicker"></up-icon>
|
</template>
|
</up-form-item>
|
|
<up-form-item label="售后类型" prop="serviceType" required @click="openServiceTypePicker">
|
<up-input v-model="serviceTypeLabel" readonly placeholder="请选择售后类型" />
|
<template #right>
|
<up-icon v-if="!isReadonly" name="arrow-right" @click="openServiceTypePicker"></up-icon>
|
</template>
|
</up-form-item>
|
|
<up-form-item label="关联销售单" prop="salesContractNo" required @click="openSalesOrderPicker">
|
<up-input v-model="form.salesContractNo" readonly placeholder="请选择销售单号" />
|
<template #right>
|
<up-icon v-if="!isReadonly" name="arrow-right" @click="openSalesOrderPicker"></up-icon>
|
</template>
|
</up-form-item>
|
|
<up-form-item label="紧急程度" prop="urgency" required @click="openUrgencyPicker">
|
<up-input v-model="urgencyLabel" readonly placeholder="请选择紧急程度" />
|
<template #right>
|
<up-icon v-if="!isReadonly" name="arrow-right" @click="openUrgencyPicker"></up-icon>
|
</template>
|
</up-form-item>
|
|
<up-form-item label="问题描述" prop="disRes">
|
<up-textarea v-model="form.disRes" :disabled="isReadonly" placeholder="请输入问题描述" count autoHeight></up-textarea>
|
</up-form-item>
|
</up-form>
|
|
<!-- 关联产品区域 -->
|
<view class="product-section">
|
<view class="section-header">
|
<up-button type="primary" size="small" @click="showProductSelect = true" v-if="!isReadonly && form.salesContractNo">选择产品</up-button>
|
</view>
|
|
<view class="product-list" v-if="tableData.length > 0">
|
<view v-for="(item, index) in tableData" :key="index" class="product-item">
|
<view class="product-info">
|
<view class="info-row">
|
<text class="info-label">产品分类:</text>
|
<text class="info-value">{{ item.productCategory }}</text>
|
</view>
|
<view class="info-row">
|
<text class="info-label">规格型号:</text>
|
<text class="info-value">{{ item.specificationModel }}</text>
|
</view>
|
<view class="info-row">
|
<text class="info-label">单位:</text>
|
<text class="info-value">{{ item.unit }}</text>
|
</view>
|
<view class="info-row">
|
<text class="info-label">数量:</text>
|
<text class="info-value">{{ item.quantity }}</text>
|
</view>
|
</view>
|
<view class="product-action" v-if="!isReadonly">
|
<up-icon name="trash" color="#fa3534" size="20" @click="removeProduct(index)"></up-icon>
|
</view>
|
</view>
|
</view>
|
<view v-else class="no-product">
|
<text>暂无关联产品</text>
|
</view>
|
</view>
|
</view>
|
|
<!-- 各种选择器 -->
|
<up-action-sheet :show="showCustomerPicker" :actions="customerActions" title="选择客户" @select="onCustomerSelect" @close="showCustomerPicker = false" />
|
<up-action-sheet :show="showServiceTypePicker" :actions="serviceTypeActions" title="选择售后类型" @select="onServiceTypeSelect" @close="showServiceTypePicker = false" />
|
<up-action-sheet :show="showSalesOrderPicker" :actions="salesOrderActions" title="选择关联销售单" @select="onSalesOrderSelect" @close="showSalesOrderPicker = false" />
|
<up-action-sheet :show="showUrgencyPicker" :actions="urgencyActions" title="选择紧急程度" @select="onUrgencySelect" @close="showUrgencyPicker = false" />
|
|
<!-- 产品选择弹窗 -->
|
<up-popup :show="showProductSelect" mode="bottom" @close="showProductSelect = false" round="10">
|
<view class="product-select-popup">
|
<view class="popup-header">
|
<text class="popup-title">选择产品</text>
|
<up-icon name="close" size="20" @click="showProductSelect = false"></up-icon>
|
</view>
|
<scroll-view scroll-y class="product-scroll">
|
<view v-for="(item, index) in availableProducts" :key="index" class="selectable-product" @click="toggleProduct(item)">
|
<view class="product-checkbox">
|
<up-icon :name="isProductSelected(item) ? 'checkbox-mark' : 'minus-circle'" :color="isProductSelected(item) ? '#2979ff' : '#ccc'" size="20"></up-icon>
|
</view>
|
<view class="product-details">
|
<text class="p-name">{{ item.productCategory }}</text>
|
<text class="p-model">{{ item.specificationModel }} | {{ item.unit }}</text>
|
</view>
|
</view>
|
</scroll-view>
|
<view class="popup-footer">
|
<up-button type="primary" @click="showProductSelect = false">确定</up-button>
|
</view>
|
</view>
|
</up-popup>
|
|
<FooterButtons :show="!isReadonly" @cancel="goBack" @confirm="submitForm" />
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, onMounted } from 'vue';
|
import { getAllCustomerList, getSalesLedger, afterSalesServiceAdd, afterSalesServiceUpdate } from '@/api/customerService/index';
|
import PageHeader from '@/components/PageHeader.vue';
|
import FooterButtons from '@/components/FooterButtons.vue';
|
import { useDict } from '@/utils/dict';
|
import useUserStore from '@/store/modules/user';
|
|
const userStore = useUserStore();
|
const { post_sale_waiting_list, degree_of_urgency } = useDict('post_sale_waiting_list', 'degree_of_urgency');
|
|
const operationType = ref('add');
|
const isReadonly = computed(() => operationType.value === 'view');
|
const pageTitle = computed(() => {
|
if (operationType.value === 'view') return '售后单详情';
|
if (operationType.value === 'add') return '新增售后单';
|
return '编辑售后单';
|
});
|
const formRef = ref(null);
|
const form = reactive({
|
id: null,
|
customerName: '',
|
customerId: null,
|
serviceType: '',
|
salesContractNo: '',
|
salesLedgerId: null,
|
urgency: '',
|
disRes: '',
|
productModelIds: '',
|
checkUserId: null,
|
feedbackDate: '',
|
});
|
|
const rules = {
|
customerName: [{ required: true, message: '请选择客户', trigger: 'change' }],
|
serviceType: [{ required: true, message: '请选择售后类型', trigger: 'change' }],
|
salesContractNo: [{ required: true, message: '请选择关联销售单', trigger: 'change' }],
|
urgency: [{ required: true, message: '请选择紧急程度', trigger: 'change' }],
|
};
|
|
const tableData = ref([]);
|
const customerList = ref([]);
|
const salesOrderList = ref([]);
|
const availableProducts = ref([]);
|
|
const showCustomerPicker = ref(false);
|
const showServiceTypePicker = ref(false);
|
const showSalesOrderPicker = ref(false);
|
const showUrgencyPicker = ref(false);
|
const showProductSelect = ref(false);
|
|
const customerActions = computed(() => customerList.value.map(item => ({ name: item.customerName, value: item.id })));
|
const serviceTypeActions = computed(() => (post_sale_waiting_list.value || []).map(item => ({ name: item.label, value: item.value })));
|
const salesOrderActions = computed(() => salesOrderList.value.map(item => ({ name: item.salesContractNo, value: item.salesContractNo, id: item.id, products: item.productData })));
|
const urgencyActions = computed(() => (degree_of_urgency.value || []).map(item => ({ name: item.label, value: item.value })));
|
|
const serviceTypeLabel = computed(() => {
|
const item = (post_sale_waiting_list.value || []).find(i => i.value == form.serviceType);
|
return item ? item.label : '';
|
});
|
|
const urgencyLabel = computed(() => {
|
const item = (degree_of_urgency.value || []).find(i => i.value == form.urgency);
|
return item ? item.label : '';
|
});
|
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
const openCustomerPicker = () => {
|
if (isReadonly.value) return;
|
showCustomerPicker.value = true;
|
};
|
const openServiceTypePicker = () => {
|
if (isReadonly.value) return;
|
showServiceTypePicker.value = true;
|
};
|
const openSalesOrderPicker = () => {
|
if (isReadonly.value) return;
|
if (!form.customerName) {
|
uni.showToast({ title: '请先选择客户', icon: 'none' });
|
return;
|
}
|
showSalesOrderPicker.value = true;
|
};
|
const openUrgencyPicker = () => {
|
if (isReadonly.value) return;
|
showUrgencyPicker.value = true;
|
};
|
|
const onCustomerSelect = (item) => {
|
form.customerName = item.name;
|
form.customerId = item.value;
|
form.salesContractNo = '';
|
form.salesLedgerId = null;
|
tableData.value = [];
|
availableProducts.value = [];
|
fetchSalesOrders(item.name);
|
};
|
|
const onServiceTypeSelect = (item) => {
|
form.serviceType = item.value;
|
};
|
|
const onSalesOrderSelect = (item) => {
|
form.salesContractNo = item.name;
|
form.salesLedgerId = item.id;
|
setProductsFromSalesOrder(item, true);
|
};
|
|
const onUrgencySelect = (item) => {
|
form.urgency = item.value;
|
};
|
|
const normalizeProduct = (p) => {
|
return {
|
...p,
|
id: p.id || p.productModelId || p.modelId,
|
productCategory: p.productCategory || p.productName || '',
|
specificationModel: p.specificationModel || p.model || '',
|
};
|
};
|
|
const setProductsFromSalesOrder = (orderAction, selectAll = false) => {
|
const products = Array.isArray(orderAction?.products) ? orderAction.products : [];
|
const normalizedProducts = products.map(p => normalizeProduct(p));
|
availableProducts.value = normalizedProducts;
|
if (selectAll) {
|
tableData.value = normalizedProducts.slice();
|
return;
|
}
|
const ids = String(form.productModelIds || '')
|
.split(',')
|
.map(s => s.trim())
|
.filter(Boolean);
|
if (ids.length === 0) {
|
tableData.value = normalizedProducts.slice();
|
return;
|
}
|
const idSet = new Set(ids.map(String));
|
tableData.value = normalizedProducts.filter(p => idSet.has(String(p.id)));
|
};
|
|
const fetchCustomers = () => {
|
getAllCustomerList({ current: 1, size: 1000 }).then(res => {
|
const records = res?.records ?? res?.data?.records ?? [];
|
customerList.value = Array.isArray(records) ? records : [];
|
});
|
};
|
|
const fetchSalesOrders = (customerName) => {
|
getSalesLedger({ customerName }).then(res => {
|
const records = res?.records ?? res?.data?.records ?? [];
|
salesOrderList.value = Array.isArray(records) ? records : [];
|
if (form.salesContractNo) {
|
const match = salesOrderList.value.find(i => String(i.salesContractNo) === String(form.salesContractNo));
|
if (match) {
|
setProductsFromSalesOrder({ id: match.id, name: match.salesContractNo, products: match.productData }, false);
|
form.salesLedgerId = match.id;
|
}
|
}
|
});
|
};
|
|
const removeProduct = (index) => {
|
tableData.value.splice(index, 1);
|
};
|
|
const isProductSelected = (product) => {
|
const id = product.id || product.productModelId || product.modelId;
|
return tableData.value.some(p => (p.id || p.productModelId || p.modelId) === id);
|
};
|
|
const toggleProduct = (product) => {
|
if (isReadonly.value) return;
|
const id = product.id || product.productModelId || product.modelId;
|
const index = tableData.value.findIndex(p => (p.id || p.productModelId || p.modelId) === id);
|
if (index > -1) {
|
tableData.value.splice(index, 1);
|
} else {
|
tableData.value.push(normalizeProduct(product));
|
}
|
};
|
|
const submitForm = () => {
|
if (isReadonly.value) return;
|
formRef.value.validate().then(valid => {
|
if (valid) {
|
form.productModelIds = tableData.value.map(p => p.id || p.productModelId || p.modelId).join(',');
|
if (!form.checkUserId) {
|
form.checkUserId = userStore.id;
|
}
|
if (!form.feedbackDate) {
|
const now = new Date();
|
form.feedbackDate = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
}
|
const api = operationType.value === 'add' ? afterSalesServiceAdd : afterSalesServiceUpdate;
|
|
api(form).then((res) => {
|
if (res?.code === 200 || res?.code === undefined) {
|
uni.showToast({ title: operationType.value === 'add' ? '新增成功' : '修改成功', icon: 'success' });
|
setTimeout(() => goBack(), 500);
|
return;
|
}
|
uni.showToast({ title: res?.msg || '操作失败', icon: 'none' });
|
}).catch(() => {});
|
}
|
});
|
};
|
|
onMounted(() => {
|
operationType.value = uni.getStorageSync('afterSalesOperationType') || 'add';
|
const editDataStr = uni.getStorageSync('afterSalesEditData');
|
|
fetchCustomers();
|
|
if (editDataStr) {
|
const editData = JSON.parse(editDataStr);
|
Object.assign(form, editData);
|
if (form.customerName) {
|
fetchSalesOrders(form.customerName);
|
}
|
} else {
|
form.checkUserId = userStore.id;
|
const now = new Date();
|
form.feedbackDate = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
}
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.after-sales-edit {
|
min-height: 100vh;
|
background: #f8f9fa;
|
padding-bottom: 100px;
|
}
|
|
.form-container {
|
background: #ffffff;
|
padding: 10px 20px;
|
margin-top: 10px;
|
}
|
|
.product-section {
|
margin-top: 20px;
|
border-top: 1px solid #f0f0f0;
|
padding-top: 20px;
|
}
|
|
.section-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 15px;
|
}
|
|
.section-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #333;
|
}
|
|
.product-list {
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
}
|
|
.product-item {
|
background: #f8f9fa;
|
border-radius: 8px;
|
padding: 12px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.product-info {
|
flex: 1;
|
}
|
|
.info-row {
|
display: flex;
|
margin-bottom: 4px;
|
font-size: 13px;
|
}
|
|
.info-label {
|
color: #909399;
|
width: 70px;
|
}
|
|
.info-value {
|
color: #303133;
|
flex: 1;
|
}
|
|
.no-product {
|
text-align: center;
|
padding: 30px 0;
|
color: #999;
|
font-size: 14px;
|
}
|
|
.product-select-popup {
|
height: 60vh;
|
display: flex;
|
flex-direction: column;
|
background: #fff;
|
}
|
|
.popup-header {
|
padding: 15px 20px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
border-bottom: 1px solid #f0f0f0;
|
}
|
|
.popup-title {
|
font-size: 16px;
|
font-weight: 600;
|
}
|
|
.product-scroll {
|
flex: 1;
|
padding: 10px 20px;
|
}
|
|
.selectable-product {
|
display: flex;
|
align-items: center;
|
padding: 15px 0;
|
border-bottom: 1px solid #f5f5f5;
|
}
|
|
.product-checkbox {
|
margin-right: 15px;
|
}
|
|
.product-details {
|
display: flex;
|
flex-direction: column;
|
}
|
|
.p-name {
|
font-size: 14px;
|
color: #333;
|
margin-bottom: 4px;
|
}
|
|
.p-model {
|
font-size: 12px;
|
color: #999;
|
}
|
|
.popup-footer {
|
padding: 20px;
|
border-top: 1px solid #f0f0f0;
|
}
|
</style>
|