| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- <template>
- <view class="receipt-form-page">
- <u-navbar placeholder :autoBack="true" :title="pageTitle" @rightClick="submitForm">
- <view class="u-nav-slot" slot="right"> 保存 </view>
- </u-navbar>
- <!-- 表单内容 -->
- <view class="follow_form_wrap">
- <u--form labelPosition="left" labelWidth="100" :model="form" :rules="rules" ref="receiptFormRef" class="form_wrap"
- :errorType="'toast'">
- <u-form-item label="最后修改时间" prop="updateTime" borderBottom>
- <u--input v-model="form.updateTime" placeholder="最后修改时间" disabled></u--input>
- </u-form-item>
- <u-form-item label="订单类型" prop="customerServiceName" borderBottom>
- <ld-select :list="sendOrderTypeDict" label-key="dictLabel" value-key="dictValue" placeholder="请选择类别"
- v-model="form.customerServiceName" :border="false"></ld-select>
- <u-icon slot="right" name="arrow-right"></u-icon>
- </u-form-item>
- <u-form-item label="物品名称" prop="item" borderBottom class="form_required">
- <u--input v-model="form.item" placeholder="请输入物品名称" border="none" :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="类别" prop="category" borderBottom class="form_required">
- <ld-select :list="categoryDict" label-key="dictLabel" value-key="dictValue" placeholder="请选择类别"
- v-model="form.category" :border="false"></ld-select>
- <u-icon slot="right" name="arrow-right"></u-icon>
- </u-form-item>
- <u-form-item label="品牌" prop="brand" borderBottom>
- <ld-select :list="brandDict" label-key="dictLabel" value-key="dictValue" placeholder="请选择品牌"
- v-model="form.brand" :border="false"></ld-select>
- <u-icon slot="right" name="arrow-right"></u-icon>
- </u-form-item>
- <u-form-item label="是否需要查码" prop="needCheckCode" borderBottom class="form_required">
- <u-radio-group v-model="form.needCheckCode" :disabled="!isEdit" @change="(val) => (form.needCheckCode = val)">
- <u-radio v-for="item in needCheckCodeOptions" :key="item.value" :name="item.value" :label="item.label"
- style="margin-right: 10px"></u-radio>
- </u-radio-group>
- </u-form-item>
- <u-form-item label="编码" prop="code" borderBottom>
- <u--input v-model="form.code" :disabled="form.needCheckCode !== 1 || !isEdit" placeholder="请输入编码"></u--input>
- </u-form-item>
- <u-form-item label="查码费" prop="checkCodeFee" borderBottom>
- <u--input v-model.number="form.checkCodeFee" type="number" placeholder="请输入查码费" border="none"
- :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="表款" prop="tableFee" borderBottom>
- <u--input v-model.number="form.tableFee" type="number" placeholder="请输入表款" border="none"
- :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="好处费" prop="benefitFee" borderBottom>
- <u--input v-model.number="form.benefitFee" type="number" placeholder="请输入好处费" border="none"
- :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="运费" prop="freight" borderBottom>
- <u--input v-model.number="form.freight" type="number" placeholder="请输入运费" border="none"
- :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="维修金额" prop="repairAmount" borderBottom>
- <u--input v-model.number="form.repairAmount" type="number" placeholder="请输入维修金额" border="none"
- :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="成本合计" prop="totalCost" borderBottom>
- <u--input v-model.number="form.totalCost" type="number" placeholder="自动计算成本合计" disabled></u--input>
- </u-form-item>
- <u-form-item label="卖价" prop="sellingPrice" borderBottom>
- <u--input v-model.number="form.sellingPrice" type="number" placeholder="请输入卖价" border="none"
- :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="业绩" prop="performance" borderBottom>
- <u--input v-model.number="form.performance" type="number" placeholder="自动计算业绩" disabled></u--input>
- </u-form-item>
- <u-form-item label="分单比例" prop="splitRatio" borderBottom>
- <u--input v-model="form.splitRatio" type="number" step="0.01" placeholder="0-100" :disabled="!isEdit"
- @blur="validateSplitRatio"></u--input>
- </u-form-item>
- <u-form-item label="毛业绩" prop="grossPerformance" borderBottom>
- <u--input v-model.number="form.grossPerformance" type="number" :placeholder="canEditGrossPerformance ? '请输入毛业绩' : '自动计算毛业绩'
- " :disabled="!canEditGrossPerformance"></u--input>
- </u-form-item>
- <u-form-item label="开户人姓名" prop="customName" borderBottom>
- <u--input v-model="form.customName" placeholder="请输入开户人姓名" border="none" :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="身份证号码" prop="idCard" borderBottom>
- <u--input v-model="form.idCard" placeholder="请输入客户身份证号码" border="none" :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="银行卡号" prop="bankCardNumber" borderBottom>
- <u--input v-model="form.bankCardNumber" placeholder="请输入银行卡号" border="none" :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="银行名称" prop="bankName" borderBottom>
- <u--input v-model="form.bankName" placeholder="请输入银行名称" border="none" :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="快递单号" prop="expressOrderNo" borderBottom>
- <u--input v-model="form.expressOrderNo" placeholder="请输入快递单号" border="none" :disabled="!isEdit"></u--input>
- </u-form-item>
- <u-form-item label="附件" prop="fileIds" borderBottom>
- <view class="file-list">
- <view v-for="file in fileOptions" :key="file.id" class="file-item" @click="handleFileSelect(file)" :class="{
- fileSelected: fileIds.includes(file.id.toString()),
- fileDisabled:
- file.receiptFormId !== null && file.receiptFormId !== form.id,
- }" :disabled="file.receiptFormId !== null && file.receiptFormId !== form.id
- ">
- <view class="file-info">
- <text class="file-name">{{ handleShowLabel(file) }}</text>
- <text v-if="
- file.receiptFormId !== null &&
- file.receiptFormId !== form.id
- " class="file-bound">已绑定</text>
- </view>
- <u-icon v-if="fileIds.includes(file.id.toString())" name="checkmark" color="#409eff" size="18"
- class="fileSelectedIcon"></u-icon>
- </view>
- </view>
- <view v-if="fileOptions.length === 0" class="no-files">
- <u-empty mode="data" text="暂无附件"></u-empty>
- </view>
- </u-form-item>
- <u-form-item label="收单备注" prop="receiptRemark" borderBottom>
- <u-textarea confirmType="done" v-model="form.receiptRemark" placeholder="请输入收单备注" :disabled="!isEdit"
- auto-height></u-textarea>
- </u-form-item>
- </u--form>
- </view>
- </view>
- </template>
- <script>
- import { cloneDeep } from "lodash";
- import ldSelect from "@/components/ld-select/ld-select.vue";
- export default {
- name: "ReceiptForm",
- components: {
- ldSelect,
- },
- data() {
- return {
- loading: false,
- form: {
- id: undefined,
- sendFormId: '',
- clueId: '',
- item: '',
- brand: '',
- needCheckCode: 1,
- code: '',
- tableFee: 0,
- benefitFee: 0,
- freight: 0,
- checkCodeFee: 0,
- totalCost: 0,
- sellingPrice: 0,
- performance: 0,
- receiptRemark: '',
- repairAmount: 0,
- grossPerformance: 0,
- expressOrderNo: '',
- fileIds: '',
- category: '',
- customName: '',
- idCard: '',
- bankCardNumber: '',
- bankName: '',
- customerServiceName: '',
- updateTime: '',
- splitRatio: 0
- },
- fileOptions: [],
- orderId: "",
- clueId: "",
- receiptId: "",
- receiptDetail: {},
- needCheckCodeOptions: [
- { label: "是", value: 1 },
- { label: "否", value: 2 },
- ],
- categoryDict: [],
- sendOrderTypeDict: [],
- brandDict: [],
- labelStyle: {
- fontSize: "28rpx",
- color: "#606266",
- },
- // 表单验证规则
- rules: {
- item: [{ required: true, message: "请输入物品名称", trigger: "blur" }],
- category: [
- { required: true, message: "请选择类别", trigger: "change" },
- ],
- needCheckCode: [
- {
- validator: (rule, value, callback) => {
- if (!value) {
- callback(new Error("请选择是否查码"));
- } else {
- callback();
- }
- },
- trigger: "blur",
- },
- ],
- brand: [
- {
- required: true,
- message: "请选择品牌",
- trigger: ["blur", "change"]
- }
- ],
- code: [
- {
- validator: (rule, value, callback) => {
- if (this.form.needCheckCode === 1 && !value) {
- callback(new Error("编码不能为空"));
- } else {
- callback();
- }
- },
- trigger: "blur",
- },
- ],
- },
- };
- },
- computed: {
- pageTitle() {
- return this.form.id ? "编辑收单" : "新增收单";
- },
- fileIds: {
- get() {
- if (this.form.fileIds) {
- return this.form.fileIds.split(",").filter((id) => id);
- } else {
- return [];
- }
- },
- set(value) {
- this.form.fileIds = value.join(",");
- },
- },
- isEdit() {
- return true;
- },
- // 判断是否显示保存按钮:status=2时,只有接单人本人可以编辑
- canShowSaveButton() {
- if (!this.receiptDetail.status) return true;
- const { status, identification } = this.receiptDetail;
- const currentUserId = this.$store.state.user.userId;
- // 如果status不是"2",显示保存按钮
- if (status !== "2") {
- return true;
- }
- // 如果status是"2",只有接单人本人可以看到保存按钮
- return String(identification) === String(currentUserId);
- },
- // 自动计算成本合计 = 表款 + 好处费 + 运费 + 查码费 + 维修金额
- calculatedTotalCost() {
- const { tableFee, benefitFee, freight, checkCodeFee, repairAmount } =
- this.form;
- return (
- (tableFee || 0) +
- (benefitFee || 0) +
- (freight || 0) +
- (checkCodeFee || 0) +
- (repairAmount || 0)
- );
- },
- // 自动计算业绩 = 卖价 - 成本合计
- // 卖价为空时不计算,避免出现负数业绩
- calculatedPerformance() {
- const { sellingPrice } = this.form;
- // 卖价为空或为0时,不计算业绩
- if (!sellingPrice || sellingPrice <= 0) {
- return null;
- }
- return sellingPrice - this.calculatedTotalCost;
- },
- // 自动计算毛业绩 = 业绩 × 分单比例(仅当两者都不为空时)
- // 智能判断:<=1直接乘,>1先除以100再乘
- // 四舍五入保留两位小数
- calculatedGrossPerformance() {
- const { splitRatio } = this.form;
- if (
- this.calculatedPerformance &&
- splitRatio !== null &&
- splitRatio !== undefined &&
- splitRatio !== ""
- ) {
- // 智能判断:<=1直接乘,>1先除以100再乘
- const ratio = splitRatio <= 1 ? splitRatio : splitRatio / 100;
- const result = this.calculatedPerformance * ratio;
- // 四舍五入保留两位小数
- return Math.round(result * 100) / 100;
- }
- return null;
- },
- // 毛业绩是否可编辑:业绩不为空 且 比例为空
- canEditGrossPerformance() {
- return (
- this.calculatedPerformance &&
- !this.form.splitRatio &&
- this.form.splitRatio !== 0 &&
- this.isEdit
- );
- },
- },
- watch: {
- // 监听成本合计变化,自动更新form
- calculatedTotalCost(val) {
- this.form.totalCost = val;
- },
- // 监听业绩业绩变化,自动更新form
- calculatedPerformance(val) {
- this.form.performance = val;
- },
- // 监听毛业绩变化,仅当比例不为空时自动更新form
- calculatedGrossPerformance(val) {
- if (
- this.form.splitRatio !== null &&
- this.form.splitRatio !== undefined &&
- this.form.splitRatio !== "" &&
- val !== null
- ) {
- this.form.grossPerformance = val;
- }
- },
- },
- onLoad(options) {
- // 从路由参数获取orderId和clueId
- this.orderId = options.orderId;
- this.clueId = options.clueId;
- this.receiptId = options.receiptId;
- this.getDicts().then(() => {
- this.initForm();
- });
- },
- methods: {
- // 获取字典数据
- async getDicts() {
- try {
- const [categoryRes, brandRes, sendOrderTypeRes] = await Promise.all([
- this.$getDicts("crm_form_category"),
- this.$getDicts("crm_form_brand"),
- this.$getDicts("crm_sendOrder_type")
- ]);
- this.categoryDict = categoryRes;
- this.brandDict = brandRes;
- this.sendOrderTypeDict = sendOrderTypeRes;
- } catch (error) {
- console.error("获取字典数据失败:", error);
- }
- },
- handleBack() {
- uni.navigateBack();
- },
- /** 表单重置 */
- reset() {
- const form = Object.assign({
- id: undefined,
- sendFormId: this.orderId,
- clueId: this.clueId,
- item: undefined,
- brand: undefined,
- needCheckCode: 1,
- code: undefined,
- tableFee: undefined,
- benefitFee: undefined,
- freight: undefined,
- checkCodeFee: undefined,
- totalCost: undefined,
- sellingPrice: undefined,
- performance: undefined,
- receiptRemark: undefined,
- repairAmount: undefined,
- grossPerformance: undefined,
- expressOrderNo: undefined,
- fileIds: "",
- category: undefined,
- customName: undefined,
- idCard: undefined,
- bankCardNumber: undefined,
- bankName: undefined,
- customerServiceName: undefined,
- updateTime: undefined,
- splitRatio: undefined,
- });
- // 从订单详情预填充字段
- if (this.receiptDetail) {
- form.status = this.receiptDetail.status;
- form.identification = this.receiptDetail.identification;
- form.item = this.receiptDetail.item;
- form.category = this.receiptDetail.category;
- form.brand = this.receiptDetail.brand;
- }
- this.form = form;
- },
- handleShowLabel(file) {
- // 获取文件在fileIds数组中的索引,如果存在则显示序号
- const index = this.fileIds.indexOf(file.id.toString());
- let label = "";
- if (index !== -1) {
- label = `[${index + 1}] `;
- }
- label += file.fileName;
- if (file.remark) {
- label += `(${file.remark})`;
- }
- // 如果文件已绑定到其他收单,显示绑定信息
- if (file.receiptFormId !== null && file.receiptFormId !== this.form.id) {
- label += ` - 已绑定${file.item || ""}`;
- }
- return label;
- },
- initForm() {
- // 获取订单详情数据
- uni.$u.api
- .getClueSendFormVoByOrderId({
- id: this.orderId,
- })
- .then((res) => {
- this.receiptDetail = res.data;
- // 如果有receiptId,说明是编辑模式,获取收单详情
- if (this.receiptId) {
- return uni.$u.api
- .getReceiptForm(this.receiptId)
- .then((receiptRes) => {
- this.form = receiptRes.data;
- // 在数据初始化完成后获取附件列表
- this.fetchFileOptions();
- });
- } else {
- // 新增模式:初始化表单数据(需要receiptDetail数据才能预填充字段)
- this.reset();
- // 在数据初始化完成后获取附件列表
- this.fetchFileOptions();
- }
- })
- .catch((error) => {
- console.error("初始化表单失败:", error);
- uni.$u.toast("初始化表单失败");
- });
- },
- // 获取附件选项列表
- async fetchFileOptions() {
- try {
- const params = {
- clueId: this.receiptDetail.clueId,
- sourceId: this.receiptDetail.id,
- notBound: "1",
- orderFileType: "3",
- type: "2",
- };
- const { rows } = await uni.$u.api.selectClueFileByDto(params);
- this.fileOptions = rows || [];
- } catch (error) {
- console.error("获取附件列表失败:", error);
- uni.$u.toast("获取附件列表失败");
- }
- },
- // 文件选择
- handleFileSelect(file) {
- if (!this.isEdit) return;
- // 如果文件已经被绑定到其他收单,不允许选择
- if (file.receiptFormId !== null && file.receiptFormId !== this.form.id) {
- return;
- }
- const fileId = file.id.toString();
- const index = this.fileIds.indexOf(fileId);
- const newFileIds = [...this.fileIds]; // 创建数组副本
- if (index > -1) {
- // 已选中,取消选择
- newFileIds.splice(index, 1);
- } else {
- // 未选中,添加选择
- newFileIds.push(fileId);
- }
- // 重新赋值,触发set方法
- this.fileIds = newFileIds;
- },
- // 分单比例验证:非空时范围必须在0-100之间
- validateSplitRatio() {
- const { splitRatio } = this.form;
- if (
- splitRatio === null ||
- splitRatio === undefined ||
- splitRatio === ""
- ) {
- return; // 允许为空
- }
- const ratio = Number(splitRatio);
- // 范围验证:0-100
- if (ratio < 0) {
- uni.$u.toast("分单比例不能小于0");
- this.form.splitRatio = 0;
- } else if (ratio > 100) {
- uni.$u.toast("分单比例不能大于100");
- this.form.splitRatio = 100;
- }
- },
- // 提交表单
- submitForm() {
- this.$refs.receiptFormRef.validate().then(async (valid) => {
- if (valid) {
- await this.handleUpdate();
- } else {
- uni.$u.toast("请检查表单填写是否正确");
- }
- });
- },
- // 更新表单
- async handleUpdate() {
- try {
- this.loading = true;
- // 准备提交数据
- const submitData = {
- ...this.form,
- sendFormId: this.orderId,
- clueId: this.clueId,
- };
- // 根据是否有id判断是新增还是修改
- if (this.form.id) {
- // 修改
- await uni.$u.api.updateReceiptForm(submitData);
- uni.$emit('addReceiptFormSuccess');
- uni.$u.toast("修改成功");
- } else {
- // 新增
- await uni.$u.api.addReceiptForm(submitData);
- uni.$emit('addReceiptFormSuccess');
- uni.$u.toast("新增成功");
- }
- // 延迟返回上一页
- setTimeout(() => {
- uni.navigateBack();
- }, 1500);
- } catch (error) {
- console.error("保存失败:", error);
- uni.$u.toast("保存失败,请重试");
- } finally {
- this.loading = false;
- }
- },
- },
- };
- </script>
- <style lang="scss">
- .receipt-form-page {
- background-color: #fff;
- }
- @import "@/static/follow/index.scss";
- // 附件列表样式优化
- .file-list {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
- padding: 10rpx 0;
- }
- .file-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 24rpx 30rpx;
- background-color: #f8f9fa;
- border-radius: 12rpx;
- border: 2rpx solid transparent;
- transition: all 0.3s ease;
- position: relative;
- cursor: pointer;
- &:active {
- background-color: #e9ecef;
- transform: scale(0.98);
- }
- // 选中状态样式
- &.fileSelected {
- background-color: #e6f4ff;
- border-color: #409eff;
- box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.2);
- }
- // 禁用状态样式
- &.fileDisabled {
- background-color: #f5f5f5;
- opacity: 0.6;
- cursor: not-allowed;
- &:active {
- transform: none;
- background-color: #f5f5f5;
- }
- }
- .file-info {
- flex: 1;
- display: flex;
- align-items: center;
- gap: 16rpx;
- .file-name {
- font-size: 28rpx;
- color: #303133;
- line-height: 40rpx;
- word-break: break-all;
- }
- .file-bound {
- font-size: 24rpx;
- color: #909399;
- background-color: #f5f7fa;
- padding: 2rpx 16rpx;
- border-radius: 16rpx;
- }
- }
- .u-icon {
- // 为选中图标添加动画效果
- &.fileSelectedIcon {
- animation: fadeInScale 0.3s ease;
- }
- }
- }
- // 动画效果
- @keyframes fadeInScale {
- 0% {
- opacity: 0;
- transform: scale(0.5);
- }
- 100% {
- opacity: 1;
- transform: scale(1);
- }
- }
- // 无附件时的样式
- .no-files {
- padding: 60rpx 0;
- text-align: center;
- }
- </style>
|