index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <template>
  2. <view class="receipt-form-page">
  3. <u-navbar
  4. placeholder
  5. :autoBack="true"
  6. :title="pageTitle"
  7. @rightClick="submitForm"
  8. >
  9. <view class="u-nav-slot" slot="right"> 保存 </view>
  10. </u-navbar>
  11. <!-- 表单内容 -->
  12. <view class="follow_form_wrap">
  13. <u--form
  14. labelPosition="left"
  15. labelWidth="100"
  16. :model="form"
  17. :rules="rules"
  18. ref="receiptFormRef"
  19. class="form_wrap"
  20. :errorType="'toast'"
  21. >
  22. <u-form-item label="最后修改时间" prop="updateTime" borderBottom>
  23. <u--input
  24. v-model="form.updateTime"
  25. placeholder="最后修改时间"
  26. disabled
  27. ></u--input>
  28. </u-form-item>
  29. <u-form-item label="订单类型" prop="customerServiceName" borderBottom>
  30. <ld-select
  31. :list="sendOrderTypeDict"
  32. label-key="dictLabel"
  33. value-key="dictValue"
  34. placeholder="请选择类别"
  35. v-model="form.customerServiceName"
  36. :border="false"
  37. ></ld-select>
  38. <u-icon slot="right" name="arrow-right"></u-icon>
  39. </u-form-item>
  40. <u-form-item
  41. label="物品名称"
  42. prop="item"
  43. borderBottom
  44. class="form_required"
  45. >
  46. <u--input
  47. v-model="form.item"
  48. placeholder="请输入物品名称"
  49. border="none"
  50. :disabled="!isEdit"
  51. ></u--input>
  52. </u-form-item>
  53. <u-form-item
  54. label="类别"
  55. prop="category"
  56. borderBottom
  57. class="form_required"
  58. >
  59. <ld-select
  60. :list="categoryDict"
  61. label-key="dictLabel"
  62. value-key="dictValue"
  63. placeholder="请选择类别"
  64. v-model="form.category"
  65. :border="false"
  66. ></ld-select>
  67. <u-icon slot="right" name="arrow-right"></u-icon>
  68. </u-form-item>
  69. <u-form-item label="品牌" prop="brand" borderBottom>
  70. <ld-select
  71. :list="brandDict"
  72. label-key="dictLabel"
  73. value-key="dictValue"
  74. placeholder="请选择品牌"
  75. v-model="form.brand"
  76. :border="false"
  77. ></ld-select>
  78. <u-icon slot="right" name="arrow-right"></u-icon>
  79. </u-form-item>
  80. <u-form-item
  81. label="是否需要查码"
  82. prop="needCheckCode"
  83. borderBottom
  84. class="form_required"
  85. >
  86. <u-radio-group
  87. v-model="form.needCheckCode"
  88. :disabled="!isEdit"
  89. @change="(val) => (form.needCheckCode = val)"
  90. >
  91. <u-radio
  92. v-for="item in needCheckCodeOptions"
  93. :key="item.value"
  94. :name="item.value"
  95. :label="item.label"
  96. style="margin-right: 10px"
  97. ></u-radio>
  98. </u-radio-group>
  99. </u-form-item>
  100. <u-form-item label="编码" prop="code" borderBottom>
  101. <u--input
  102. v-model="form.code"
  103. :disabled="form.needCheckCode !== 1 || !isEdit"
  104. placeholder="请输入编码"
  105. ></u--input>
  106. </u-form-item>
  107. <u-form-item label="查码费" prop="checkCodeFee" borderBottom>
  108. <u--input
  109. v-model.number="form.checkCodeFee"
  110. type="number"
  111. placeholder="请输入查码费"
  112. border="none"
  113. :disabled="!isEdit"
  114. ></u--input>
  115. </u-form-item>
  116. <u-form-item label="表款" prop="tableFee" borderBottom>
  117. <u--input
  118. v-model.number="form.tableFee"
  119. type="number"
  120. placeholder="请输入表款"
  121. border="none"
  122. :disabled="!isEdit"
  123. ></u--input>
  124. </u-form-item>
  125. <u-form-item label="好处费" prop="benefitFee" borderBottom>
  126. <u--input
  127. v-model.number="form.benefitFee"
  128. type="number"
  129. placeholder="请输入好处费"
  130. border="none"
  131. :disabled="!isEdit"
  132. ></u--input>
  133. </u-form-item>
  134. <u-form-item label="运费" prop="freight" borderBottom>
  135. <u--input
  136. v-model.number="form.freight"
  137. type="number"
  138. placeholder="请输入运费"
  139. border="none"
  140. :disabled="!isEdit"
  141. ></u--input>
  142. </u-form-item>
  143. <u-form-item label="维修金额" prop="repairAmount" borderBottom>
  144. <u--input
  145. v-model.number="form.repairAmount"
  146. type="number"
  147. placeholder="请输入维修金额"
  148. border="none"
  149. :disabled="!isEdit"
  150. ></u--input>
  151. </u-form-item>
  152. <u-form-item label="成本合计" prop="totalCost" borderBottom>
  153. <u--input
  154. v-model.number="form.totalCost"
  155. type="number"
  156. placeholder="自动计算成本合计"
  157. disabled
  158. ></u--input>
  159. </u-form-item>
  160. <u-form-item label="卖价" prop="sellingPrice" borderBottom>
  161. <u--input
  162. v-model.number="form.sellingPrice"
  163. type="number"
  164. placeholder="请输入卖价"
  165. border="none"
  166. :disabled="!isEdit"
  167. ></u--input>
  168. </u-form-item>
  169. <u-form-item label="业绩" prop="performance" borderBottom>
  170. <u--input
  171. v-model.number="form.performance"
  172. type="number"
  173. placeholder="自动计算业绩"
  174. disabled
  175. ></u--input>
  176. </u-form-item>
  177. <u-form-item label="分单比例" prop="splitRatio" borderBottom>
  178. <u--input
  179. v-model="form.splitRatio"
  180. type="number"
  181. step="0.01"
  182. placeholder="0-100"
  183. :disabled="!isEdit"
  184. @blur="validateSplitRatio"
  185. ></u--input>
  186. </u-form-item>
  187. <u-form-item label="毛业绩" prop="grossPerformance" borderBottom>
  188. <u--input
  189. v-model.number="form.grossPerformance"
  190. type="number"
  191. :placeholder="
  192. canEditGrossPerformance ? '请输入毛业绩' : '自动计算毛业绩'
  193. "
  194. :disabled="!canEditGrossPerformance"
  195. ></u--input>
  196. </u-form-item>
  197. <u-form-item label="开户人姓名" prop="customName" borderBottom>
  198. <u--input
  199. v-model="form.customName"
  200. placeholder="请输入开户人姓名"
  201. border="none"
  202. :disabled="!isEdit"
  203. ></u--input>
  204. </u-form-item>
  205. <u-form-item label="身份证号码" prop="idCard" borderBottom>
  206. <u--input
  207. v-model="form.idCard"
  208. placeholder="请输入客户身份证号码"
  209. border="none"
  210. :disabled="!isEdit"
  211. ></u--input>
  212. </u-form-item>
  213. <u-form-item label="银行卡号" prop="bankCardNumber" borderBottom>
  214. <u--input
  215. v-model="form.bankCardNumber"
  216. placeholder="请输入银行卡号"
  217. border="none"
  218. :disabled="!isEdit"
  219. ></u--input>
  220. </u-form-item>
  221. <u-form-item label="银行名称" prop="bankName" borderBottom>
  222. <u--input
  223. v-model="form.bankName"
  224. placeholder="请输入银行名称"
  225. border="none"
  226. :disabled="!isEdit"
  227. ></u--input>
  228. </u-form-item>
  229. <u-form-item label="快递单号" prop="expressOrderNo" borderBottom>
  230. <u--input
  231. v-model="form.expressOrderNo"
  232. placeholder="请输入快递单号"
  233. border="none"
  234. :disabled="!isEdit"
  235. ></u--input>
  236. </u-form-item>
  237. <u-form-item label="附件" prop="fileIds" borderBottom>
  238. <view class="file-list">
  239. <view
  240. v-for="file in fileOptions"
  241. :key="file.id"
  242. class="file-item"
  243. @click="handleFileSelect(file)"
  244. :class="{
  245. fileSelected: fileIds.includes(file.id.toString()),
  246. fileDisabled:
  247. file.receiptFormId !== null && file.receiptFormId !== form.id,
  248. }"
  249. :disabled="
  250. file.receiptFormId !== null && file.receiptFormId !== form.id
  251. "
  252. >
  253. <view class="file-info">
  254. <text class="file-name">{{ handleShowLabel(file) }}</text>
  255. <text
  256. v-if="
  257. file.receiptFormId !== null &&
  258. file.receiptFormId !== form.id
  259. "
  260. class="file-bound"
  261. >已绑定</text
  262. >
  263. </view>
  264. <u-icon
  265. v-if="fileIds.includes(file.id.toString())"
  266. name="checkmark"
  267. color="#409eff"
  268. size="18"
  269. class="fileSelectedIcon"
  270. ></u-icon>
  271. </view>
  272. </view>
  273. <view v-if="fileOptions.length === 0" class="no-files">
  274. <u-empty mode="data" text="暂无附件"></u-empty>
  275. </view>
  276. </u-form-item>
  277. <u-form-item label="收单备注" prop="receiptRemark" borderBottom>
  278. <u-textarea
  279. confirmType="done"
  280. v-model="form.receiptRemark"
  281. placeholder="请输入收单备注"
  282. :disabled="!isEdit"
  283. auto-height
  284. ></u-textarea>
  285. </u-form-item>
  286. </u--form>
  287. </view>
  288. </view>
  289. </template>
  290. <script>
  291. import { cloneDeep } from "lodash";
  292. import ldSelect from "@/components/ld-select/ld-select.vue";
  293. export default {
  294. name: "ReceiptForm",
  295. components: {
  296. ldSelect,
  297. },
  298. data() {
  299. return {
  300. loading: false,
  301. form: {
  302. id: undefined,
  303. sendFormId: '',
  304. clueId: '',
  305. item: '',
  306. brand: '',
  307. needCheckCode: 1,
  308. code: '',
  309. tableFee: 0,
  310. benefitFee: 0,
  311. freight: 0,
  312. checkCodeFee: 0,
  313. totalCost: 0,
  314. sellingPrice: 0,
  315. performance: 0,
  316. receiptRemark: '',
  317. repairAmount: 0,
  318. grossPerformance: 0,
  319. expressOrderNo: '',
  320. fileIds: '',
  321. category: '',
  322. customName: '',
  323. idCard: '',
  324. bankCardNumber: '',
  325. bankName: '',
  326. customerServiceName: '',
  327. updateTime: '',
  328. splitRatio: 0
  329. },
  330. fileOptions: [],
  331. orderId: "",
  332. clueId: "",
  333. receiptId: "",
  334. receiptDetail: {},
  335. needCheckCodeOptions: [
  336. { label: "是", value: 1 },
  337. { label: "否", value: 2 },
  338. ],
  339. categoryDict: [],
  340. sendOrderTypeDict: [],
  341. brandDict: [],
  342. labelStyle: {
  343. fontSize: "28rpx",
  344. color: "#606266",
  345. },
  346. // 表单验证规则
  347. rules: {
  348. item: [{ required: true, message: "请输入物品名称", trigger: "blur" }],
  349. category: [
  350. { required: true, message: "请选择类别", trigger: "change" },
  351. ],
  352. needCheckCode: [
  353. {
  354. validator: (rule, value, callback) => {
  355. if (!value) {
  356. callback(new Error("请选择是否查码"));
  357. } else {
  358. callback();
  359. }
  360. },
  361. trigger: "blur",
  362. },
  363. ],
  364. brand: [
  365. {
  366. required: true,
  367. message: "请选择品牌",
  368. trigger: ["blur", "change"]
  369. }
  370. ],
  371. code: [
  372. {
  373. validator: (rule, value, callback) => {
  374. if (this.form.needCheckCode === 1 && !value) {
  375. callback(new Error("编码不能为空"));
  376. } else {
  377. callback();
  378. }
  379. },
  380. trigger: "blur",
  381. },
  382. ],
  383. },
  384. };
  385. },
  386. computed: {
  387. pageTitle() {
  388. return this.form.id ? "编辑收单" : "新增收单";
  389. },
  390. fileIds: {
  391. get() {
  392. if (this.form.fileIds) {
  393. return this.form.fileIds.split(",").filter((id) => id);
  394. } else {
  395. return [];
  396. }
  397. },
  398. set(value) {
  399. this.form.fileIds = value.join(",");
  400. },
  401. },
  402. isEdit() {
  403. return true;
  404. },
  405. // 判断是否显示保存按钮:status=2时,只有接单人本人可以编辑
  406. canShowSaveButton() {
  407. if (!this.receiptDetail.status) return true;
  408. const { status, identification } = this.receiptDetail;
  409. const currentUserId = this.$store.state.user.userId;
  410. // 如果status不是"2",显示保存按钮
  411. if (status !== "2") {
  412. return true;
  413. }
  414. // 如果status是"2",只有接单人本人可以看到保存按钮
  415. return String(identification) === String(currentUserId);
  416. },
  417. // 自动计算成本合计 = 表款 + 好处费 + 运费 + 查码费 + 维修金额
  418. calculatedTotalCost() {
  419. const { tableFee, benefitFee, freight, checkCodeFee, repairAmount } =
  420. this.form;
  421. return (
  422. (tableFee || 0) +
  423. (benefitFee || 0) +
  424. (freight || 0) +
  425. (checkCodeFee || 0) +
  426. (repairAmount || 0)
  427. );
  428. },
  429. // 自动计算业绩 = 卖价 - 成本合计
  430. // 卖价为空时不计算,避免出现负数业绩
  431. calculatedPerformance() {
  432. const { sellingPrice } = this.form;
  433. // 卖价为空或为0时,不计算业绩
  434. if (!sellingPrice || sellingPrice <= 0) {
  435. return null;
  436. }
  437. return sellingPrice - this.calculatedTotalCost;
  438. },
  439. // 自动计算毛业绩 = 业绩 × 分单比例(仅当两者都不为空时)
  440. // 智能判断:<=1直接乘,>1先除以100再乘
  441. // 四舍五入保留两位小数
  442. calculatedGrossPerformance() {
  443. const { splitRatio } = this.form;
  444. if (
  445. this.calculatedPerformance &&
  446. splitRatio !== null &&
  447. splitRatio !== undefined &&
  448. splitRatio !== ""
  449. ) {
  450. // 智能判断:<=1直接乘,>1先除以100再乘
  451. const ratio = splitRatio <= 1 ? splitRatio : splitRatio / 100;
  452. const result = this.calculatedPerformance * ratio;
  453. // 四舍五入保留两位小数
  454. return Math.round(result * 100) / 100;
  455. }
  456. return null;
  457. },
  458. // 毛业绩是否可编辑:业绩不为空 且 比例为空
  459. canEditGrossPerformance() {
  460. return (
  461. this.calculatedPerformance &&
  462. !this.form.splitRatio &&
  463. this.form.splitRatio !== 0 &&
  464. this.isEdit
  465. );
  466. },
  467. },
  468. watch: {
  469. // 监听成本合计变化,自动更新form
  470. calculatedTotalCost(val) {
  471. this.form.totalCost = val;
  472. },
  473. // 监听业绩业绩变化,自动更新form
  474. calculatedPerformance(val) {
  475. this.form.performance = val;
  476. },
  477. // 监听毛业绩变化,仅当比例不为空时自动更新form
  478. calculatedGrossPerformance(val) {
  479. if (
  480. this.form.splitRatio !== null &&
  481. this.form.splitRatio !== undefined &&
  482. this.form.splitRatio !== "" &&
  483. val !== null
  484. ) {
  485. this.form.grossPerformance = val;
  486. }
  487. },
  488. },
  489. onLoad(options) {
  490. // 从路由参数获取orderId和clueId
  491. this.orderId = options.orderId;
  492. this.clueId = options.clueId;
  493. this.receiptId = options.receiptId;
  494. this.getDicts().then(() => {
  495. this.initForm();
  496. });
  497. },
  498. methods: {
  499. // 获取字典数据
  500. async getDicts() {
  501. try {
  502. const [categoryRes, brandRes,sendOrderTypeRes] = await Promise.all([
  503. this.$getDicts("crm_form_category"),
  504. this.$getDicts("crm_form_brand"),
  505. this.$getDicts("crm_sendOrder_type")
  506. ]);
  507. this.categoryDict = categoryRes;
  508. this.brandDict = brandRes;
  509. this.sendOrderTypeDict = sendOrderTypeRes;
  510. } catch (error) {
  511. console.error("获取字典数据失败:", error);
  512. }
  513. },
  514. handleBack() {
  515. uni.navigateBack();
  516. },
  517. /** 表单重置 */
  518. reset() {
  519. const form = Object.assign({
  520. id: undefined,
  521. sendFormId: this.orderId,
  522. clueId: this.clueId,
  523. item: undefined,
  524. brand: undefined,
  525. needCheckCode: 1,
  526. code: undefined,
  527. tableFee: undefined,
  528. benefitFee: undefined,
  529. freight: undefined,
  530. checkCodeFee: undefined,
  531. totalCost: undefined,
  532. sellingPrice: undefined,
  533. performance: undefined,
  534. receiptRemark: undefined,
  535. repairAmount: undefined,
  536. grossPerformance: undefined,
  537. expressOrderNo: undefined,
  538. fileIds: "",
  539. category: undefined,
  540. customName: undefined,
  541. idCard: undefined,
  542. bankCardNumber: undefined,
  543. bankName: undefined,
  544. customerServiceName: undefined,
  545. updateTime: undefined,
  546. splitRatio: undefined,
  547. });
  548. // 从订单详情预填充字段
  549. if (this.receiptDetail) {
  550. form.status = this.receiptDetail.status;
  551. form.identification = this.receiptDetail.identification;
  552. form.item = this.receiptDetail.item;
  553. form.category = this.receiptDetail.category;
  554. form.brand = this.receiptDetail.brand;
  555. }
  556. this.form = form;
  557. },
  558. handleShowLabel(file) {
  559. // 获取文件在fileIds数组中的索引,如果存在则显示序号
  560. const index = this.fileIds.indexOf(file.id.toString());
  561. let label = "";
  562. if (index !== -1) {
  563. label = `[${index + 1}] `;
  564. }
  565. label += file.fileName;
  566. if (file.remark) {
  567. label += `(${file.remark})`;
  568. }
  569. // 如果文件已绑定到其他收单,显示绑定信息
  570. if (file.receiptFormId !== null && file.receiptFormId !== this.form.id) {
  571. label += ` - 已绑定${file.item || ""}`;
  572. }
  573. return label;
  574. },
  575. initForm() {
  576. // 获取订单详情数据
  577. uni.$u.api
  578. .getClueSendFormVoByOrderId({
  579. id: this.orderId,
  580. })
  581. .then((res) => {
  582. this.receiptDetail = res.data;
  583. // 如果有receiptId,说明是编辑模式,获取收单详情
  584. if (this.receiptId) {
  585. return uni.$u.api
  586. .getReceiptForm(this.receiptId)
  587. .then((receiptRes) => {
  588. this.form = receiptRes.data;
  589. // 在数据初始化完成后获取附件列表
  590. this.fetchFileOptions();
  591. });
  592. } else {
  593. // 新增模式:初始化表单数据(需要receiptDetail数据才能预填充字段)
  594. this.reset();
  595. // 在数据初始化完成后获取附件列表
  596. this.fetchFileOptions();
  597. }
  598. })
  599. .catch((error) => {
  600. console.error("初始化表单失败:", error);
  601. uni.$u.toast("初始化表单失败");
  602. });
  603. },
  604. // 获取附件选项列表
  605. async fetchFileOptions() {
  606. try {
  607. const params = {
  608. clueId: this.receiptDetail.clueId,
  609. sourceId: this.receiptDetail.id,
  610. notBound: "1",
  611. orderFileType: "3",
  612. type: "2",
  613. };
  614. const { rows } = await uni.$u.api.selectClueFileByDto(params);
  615. this.fileOptions = rows || [];
  616. } catch (error) {
  617. console.error("获取附件列表失败:", error);
  618. uni.$u.toast("获取附件列表失败");
  619. }
  620. },
  621. // 文件选择
  622. handleFileSelect(file) {
  623. if (!this.isEdit) return;
  624. // 如果文件已经被绑定到其他收单,不允许选择
  625. if (file.receiptFormId !== null && file.receiptFormId !== this.form.id) {
  626. return;
  627. }
  628. const fileId = file.id.toString();
  629. const index = this.fileIds.indexOf(fileId);
  630. const newFileIds = [...this.fileIds]; // 创建数组副本
  631. if (index > -1) {
  632. // 已选中,取消选择
  633. newFileIds.splice(index, 1);
  634. } else {
  635. // 未选中,添加选择
  636. newFileIds.push(fileId);
  637. }
  638. // 重新赋值,触发set方法
  639. this.fileIds = newFileIds;
  640. },
  641. // 分单比例验证:非空时范围必须在0-100之间
  642. validateSplitRatio() {
  643. const { splitRatio } = this.form;
  644. if (
  645. splitRatio === null ||
  646. splitRatio === undefined ||
  647. splitRatio === ""
  648. ) {
  649. return; // 允许为空
  650. }
  651. const ratio = Number(splitRatio);
  652. // 范围验证:0-100
  653. if (ratio < 0) {
  654. uni.$u.toast("分单比例不能小于0");
  655. this.form.splitRatio = 0;
  656. } else if (ratio > 100) {
  657. uni.$u.toast("分单比例不能大于100");
  658. this.form.splitRatio = 100;
  659. }
  660. },
  661. // 提交表单
  662. submitForm() {
  663. this.$refs.receiptFormRef.validate().then(async (valid) => {
  664. if (valid) {
  665. await this.handleUpdate();
  666. } else {
  667. uni.$u.toast("请检查表单填写是否正确");
  668. }
  669. });
  670. },
  671. // 更新表单
  672. async handleUpdate() {
  673. try {
  674. this.loading = true;
  675. // 准备提交数据
  676. const submitData = {
  677. ...this.form,
  678. sendFormId: this.orderId,
  679. clueId: this.clueId,
  680. };
  681. // 根据是否有id判断是新增还是修改
  682. if (this.form.id) {
  683. // 修改
  684. await uni.$u.api.updateReceiptForm(submitData);
  685. uni.$emit('addReceiptFormSuccess');
  686. uni.$u.toast("修改成功");
  687. } else {
  688. // 新增
  689. await uni.$u.api.addReceiptForm(submitData);
  690. uni.$emit('addReceiptFormSuccess');
  691. uni.$u.toast("新增成功");
  692. }
  693. // 延迟返回上一页
  694. setTimeout(() => {
  695. uni.navigateBack();
  696. }, 1500);
  697. } catch (error) {
  698. console.error("保存失败:", error);
  699. uni.$u.toast("保存失败,请重试");
  700. } finally {
  701. this.loading = false;
  702. }
  703. },
  704. },
  705. };
  706. </script>
  707. <style lang="scss">
  708. .receipt-form-page {
  709. background-color: #fff;
  710. }
  711. @import "@/static/follow/index.scss";
  712. // 附件列表样式优化
  713. .file-list {
  714. display: flex;
  715. flex-direction: column;
  716. gap: 16rpx;
  717. padding: 10rpx 0;
  718. }
  719. .file-item {
  720. display: flex;
  721. align-items: center;
  722. justify-content: space-between;
  723. padding: 24rpx 30rpx;
  724. background-color: #f8f9fa;
  725. border-radius: 12rpx;
  726. border: 2rpx solid transparent;
  727. transition: all 0.3s ease;
  728. position: relative;
  729. cursor: pointer;
  730. &:active {
  731. background-color: #e9ecef;
  732. transform: scale(0.98);
  733. }
  734. // 选中状态样式
  735. &.fileSelected {
  736. background-color: #e6f4ff;
  737. border-color: #409eff;
  738. box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.2);
  739. }
  740. // 禁用状态样式
  741. &.fileDisabled {
  742. background-color: #f5f5f5;
  743. opacity: 0.6;
  744. cursor: not-allowed;
  745. &:active {
  746. transform: none;
  747. background-color: #f5f5f5;
  748. }
  749. }
  750. .file-info {
  751. flex: 1;
  752. display: flex;
  753. align-items: center;
  754. gap: 16rpx;
  755. .file-name {
  756. font-size: 28rpx;
  757. color: #303133;
  758. line-height: 40rpx;
  759. word-break: break-all;
  760. }
  761. .file-bound {
  762. font-size: 24rpx;
  763. color: #909399;
  764. background-color: #f5f7fa;
  765. padding: 2rpx 16rpx;
  766. border-radius: 16rpx;
  767. }
  768. }
  769. .u-icon {
  770. // 为选中图标添加动画效果
  771. &.fileSelectedIcon {
  772. animation: fadeInScale 0.3s ease;
  773. }
  774. }
  775. }
  776. // 动画效果
  777. @keyframes fadeInScale {
  778. 0% {
  779. opacity: 0;
  780. transform: scale(0.5);
  781. }
  782. 100% {
  783. opacity: 1;
  784. transform: scale(1);
  785. }
  786. }
  787. // 无附件时的样式
  788. .no-files {
  789. padding: 60rpx 0;
  790. text-align: center;
  791. }
  792. </style>