index.vue 23 KB

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