index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. <template>
  2. <view class="oder_form_wrap">
  3. <u-navbar placeholder :autoBack="true" title="发单表单">
  4. </u-navbar>
  5. <view class="form_wrap">
  6. <u--form labelPosition="top" labelWidth="100" :model="form" :rules="rules" ref="form" class="form_wrap"
  7. :errorType="'toast'">
  8. <u-form-item label="品牌" prop="brand" class="brand_wrap">
  9. <BrandSelect :list="brandDict" label-key="dictLabel" value-key="dictValue" placeholder="请选择物品品牌"
  10. v-model="form.brand" :border="false" border></BrandSelect>
  11. <u-icon slot="right" name="arrow-right"></u-icon>
  12. </u-form-item>
  13. <u-row gutter="5" justify="space-between">
  14. <u-col span="6">
  15. <u-form-item label="型号" prop="model">
  16. <u--input v-model="form.model" placeholder="例如:CF小号" border="surround"></u--input>
  17. </u-form-item>
  18. </u-col>
  19. <u-col span="6">
  20. <u-form-item label="实价" prop="price">
  21. <u--input v-model="form.price" placeholder="0.00" border="surround"></u--input>
  22. </u-form-item>
  23. </u-col>
  24. </u-row>
  25. <u-row gutter="5" justify="space-between">
  26. <u-col span="6">
  27. <u-form-item label="客户电话" prop="phone">
  28. <u--input v-model="form.phone" placeholder="电话号" border="surround"></u--input>
  29. </u-form-item>
  30. </u-col>
  31. <u-col span="6">
  32. <u-form-item label="客户微信" prop="wechat">
  33. <u--input v-model="form.wechat" placeholder="微信号" border="surround"></u--input>
  34. </u-form-item>
  35. </u-col>
  36. </u-row>
  37. <view class="address_wrap">
  38. <view class="address_title">
  39. <u-icon name="map" color="#3b82f6" size="16"></u-icon>
  40. <view>地址信息</view>
  41. </view>
  42. <u-form-item label="详细地址" prop="address" class='form_card'>
  43. <pick-regions :defaultRegion="defaultRegion" @getRegion="handleGetRegion" clearable
  44. class="region-picker">
  45. <view>
  46. <template v-if="form.province">
  47. <u--input :value="form.province + '/' + form.city + '/' + form.area"
  48. placeholder="点击选择" readonly suffixIcon="arrow-right"
  49. suffixIconStyle="color : #c0c4cc"></u--input>
  50. </template>
  51. <template v-else>
  52. <u--input placeholder="点击选择" readonly suffixIcon="arrow-right"
  53. suffixIconStyle="color : #c0c4cc"></u--input>
  54. </template>
  55. </view>
  56. </pick-regions>
  57. </u-form-item>
  58. <u--textarea v-model="form.address" placeholder="粘贴地址自动识别(例如:浙江省杭州市...)" confirmType="done"
  59. @blur="handleAddressBlur"></u--textarea>
  60. </view>
  61. <u-form-item label="上门时间" prop="visitTime" class="visit_time_wrap">
  62. <date-time-picker v-model="form.visitTime" :value-format="'YYYY-MM-DD HH:mm:ss'" :type="'datetime'">
  63. </date-time-picker>
  64. </u-form-item>
  65. <!-- <u-form-item label="类型" prop="category" class="category_wrap"> -->
  66. <u-form-item label="类型" prop="category" class="brand_wrap">
  67. <!-- <view class="category_select_wrap"> -->
  68. <ld-select :list="categoryDict" label-key="dictLabel" value-key="dictValue" placeholder="请选择类型"
  69. v-model="form.category" :border="false"></ld-select>
  70. <u-icon slot="right" name="arrow-right"></u-icon>
  71. <!-- </view> -->
  72. </u-form-item>
  73. <u-form-item label="价格范围" prop="priceRange">
  74. <u--input v-model="form.priceRange" placeholder="请输入价格范围" border="surround"></u--input>
  75. </u-form-item>
  76. <u-form-item label="战术选择" prop="tactic">
  77. <view class="tactic-buttons">
  78. <view v-for="item in tacticDict" :key="item.dictValue" class="tactic-button"
  79. :class="{ 'active': form.tactic === item.dictValue }" @click="form.tactic = item.dictValue">
  80. {{ item.dictLabel }}
  81. </view>
  82. </view>
  83. </u-form-item>
  84. <u-form-item label="备注信息" prop="remarks" class="remarks_wrap">
  85. <u--textarea v-model="form.remarks" placeholder="填写线索来源、客户特殊要求等..." count
  86. confirmType="done"></u--textarea>
  87. </u-form-item>
  88. </u--form>
  89. </view>
  90. <!-- 文件上传区域 -->
  91. <view class="upload_area" v-if="!sendFormId">
  92. <!-- 聊天记录附件 -->
  93. <order-file-upload title="聊天记录附件" orderFileType="1" fileType="image" :file-list="form.chatAttachmentList"
  94. @update:fileList="updateChatFiles" tip-text="上传聊天截图、录音等文件"></order-file-upload>
  95. <!-- 报价附件 -->
  96. <order-file-upload title="报价附件" orderFileType="2" fileType="image" :file-list="form.quoteAttachmentList"
  97. @update:fileList="updateQuoteFiles" tip-text="上传报价附件"></order-file-upload>
  98. <!-- 高清图附件 -->
  99. <order-file-upload title="高清图附件" orderFileType="3" fileType="image" :file-list="form.hdImageAttachmentList"
  100. @update:fileList="updateHdImageFiles" tip-text="上传物品高清图片"></order-file-upload>
  101. <!-- 其他附件 -->
  102. <order-file-upload title="其他附件" orderFileType="4" fileType="image" :file-list="form.otherAttachmentList"
  103. @update:fileList="updateOtherFiles" tip-text="上传其他相关文件"></order-file-upload>
  104. </view>
  105. <u-button @click="handleNavSaveClick" type="primary">提交订单</u-button>
  106. </view>
  107. </template>
  108. <script>
  109. import orderFileUpload from '@/components/order-file-upload/order-file-upload.vue'
  110. import dateTimePicker from "@/components/dateTimePicker/dateTimePicker.vue"
  111. import ldSelect from "@/components/ld-select/ld-select.vue"
  112. import BrandSelect from "@/components/brand-select/brand-select.vue"
  113. export default {
  114. components: {
  115. orderFileUpload,
  116. dateTimePicker,
  117. ldSelect,
  118. BrandSelect
  119. },
  120. data() {
  121. return {
  122. defaultRegion: ['广东省', '广州市', '番禺区'],
  123. categoryDict: [],
  124. tacticDict: [],
  125. brandDict: [],
  126. sendFormId: undefined,
  127. form: {
  128. clueId: '',
  129. sendDate: '',
  130. website: '',
  131. item: '',
  132. phone: '',
  133. wechat: '',
  134. address: '',
  135. visitTime: '',
  136. remarks: '',
  137. category: '1',
  138. brand: '',
  139. model: '',
  140. price: '',
  141. priceRange: '',
  142. tactic: '',
  143. // 地区信息
  144. province: '',
  145. city: '',
  146. area: '',
  147. // 附件列表
  148. chatAttachmentList: [], // 聊天记录附件
  149. quoteAttachmentList: [], // 报价附件
  150. hdImageAttachmentList: [], // 高清图附件
  151. otherAttachmentList: [] // 其他附件
  152. },
  153. rules: {
  154. sendDate: {
  155. type: 'string',
  156. required: true,
  157. message: '请输入发单日期',
  158. trigger: ['blur', 'change']
  159. },
  160. model: {
  161. type: 'string',
  162. required: true,
  163. message: '请输入型号',
  164. trigger: ['blur', 'change']
  165. },
  166. price: {
  167. type: 'string',
  168. required: true,
  169. message: '请输入实价',
  170. trigger: ['blur', 'change']
  171. },
  172. website: {
  173. type: 'url',
  174. message: '请输入正确的网址格式',
  175. trigger: ['blur', 'change']
  176. },
  177. item: {
  178. type: 'string',
  179. required: true,
  180. message: '请输入物品描述',
  181. trigger: ['blur', 'change']
  182. },
  183. wechat: {
  184. type: 'string',
  185. required: false,
  186. message: '请输入客户微信',
  187. trigger: ['blur', 'change']
  188. },
  189. phone: {
  190. type: 'string',
  191. required: false,
  192. message: '请输入联系电话',
  193. trigger: ['blur', 'change']
  194. },
  195. address: {
  196. type: 'string',
  197. required: true,
  198. message: '请输入详细地址',
  199. trigger: ['blur', 'change']
  200. },
  201. visitTime: {
  202. type: 'string',
  203. required: true,
  204. message: '请输入上门时间',
  205. trigger: ['blur', 'change']
  206. },
  207. category: {
  208. type: 'string',
  209. required: true,
  210. message: '请选择类型',
  211. trigger: ['blur', 'change']
  212. },
  213. brand: {
  214. type: 'string',
  215. required: true,
  216. message: '请选择物品品牌',
  217. trigger: ['blur', 'change']
  218. },
  219. tactic: {
  220. type: 'string',
  221. required: true,
  222. message: '请选择战术',
  223. trigger: ['blur', 'change']
  224. }
  225. }
  226. }
  227. },
  228. methods: {
  229. // 加载表单数据
  230. async loadFormData(sendFormId) {
  231. try {
  232. const res = await uni.$u.api.getClueSendFormVoByOrderId({
  233. id: sendFormId
  234. });
  235. if (res.code === 200) {
  236. this.form = res.data;
  237. }
  238. } catch (error) {
  239. console.error('加载表单数据失败:', error);
  240. uni.$u.toast('加载表单数据失败');
  241. }
  242. },
  243. // 获取地区选择
  244. handleGetRegion(region) {
  245. const [provinceData, cityData, areaData] = region;
  246. this.form.province = provinceData.name;
  247. this.form.city = cityData.name;
  248. this.form.area = areaData.name;
  249. // 构建完整的地区信息
  250. const regionText = this.form.province + this.form.city + this.form.area;
  251. // 检查当前地址内容是否为纯地区信息(没有具体街道等)
  252. const isPureRegion = !this.form.address ||
  253. this.form.address.trim() === '' ||
  254. this.form.address.trim().includes(this.form.province) ||
  255. this.form.address.trim().includes(this.form.city) ||
  256. this.form.address.trim().includes(this.form.area);
  257. // 如果是纯地区信息或者是空地址,则更新为选择的地区
  258. if (isPureRegion) {
  259. this.form.address = regionText;
  260. } else {
  261. // 如果有具体地址信息,保留原有地址,但更新地区部分
  262. // 这里可以保持原有地址不变,让用户自己选择是否更新
  263. // 或者可以选择性地更新地区信息
  264. }
  265. },
  266. // 地址失焦时自动解析地址信息
  267. handleAddressBlur() {
  268. },
  269. // 更新各类附件
  270. updateChatFiles(fileList) {
  271. this.form.chatAttachmentList = fileList;
  272. },
  273. updateQuoteFiles(fileList) {
  274. this.form.quoteAttachmentList = fileList;
  275. },
  276. updateHdImageFiles(fileList) {
  277. this.form.hdImageAttachmentList = fileList;
  278. },
  279. updateOtherFiles(fileList) {
  280. this.form.otherAttachmentList = fileList;
  281. },
  282. // 文件变化处理
  283. handleFileChange(data) {
  284. console.log('文件上传变化:', data);
  285. },
  286. // 获取字典数据
  287. async getDicts() {
  288. try {
  289. const [categoryRes, tacticRes, brandRes] = await Promise.all([
  290. this.$getDicts('crm_form_category'),
  291. this.$getDicts('crm_form_tactic'),
  292. this.$getDicts('crm_form_brand')
  293. ]);
  294. this.categoryDict = categoryRes;
  295. this.tacticDict = tacticRes;
  296. this.brandDict = brandRes;
  297. } catch (error) {
  298. console.error('获取字典数据失败:', error);
  299. }
  300. },
  301. // 处理保存
  302. async handleNavSaveClick() {
  303. try {
  304. // 表单验证
  305. await this.$refs.form.validate();
  306. if (this.form.id) {
  307. const updatedForm = {
  308. clueId: this.form.clueId,
  309. model: this.form.model,
  310. price: this.form.price,
  311. wechat: this.form.wechat,
  312. id: this.form.id,
  313. item: this.form.item,
  314. phone: this.form.phone,
  315. authenticateUserId: this.form.authenticateUserId,
  316. category: this.form.category,
  317. brand: this.form.brand,
  318. idCard: this.form.idCard,
  319. customName: this.form.customName,
  320. bankCardNumber: this.form.bankCardNumber,
  321. bankName: this.form.bankName,
  322. paymentMethod: this.form.paymentMethod,
  323. visitTime: this.form.visitTime,
  324. remarks: this.form.remarks,
  325. priceRange: this.form.priceRange,
  326. tactic: this.form.tactic
  327. }
  328. await uni.$u.api.updateClueOrderForm(updatedForm);
  329. uni.$emit('updateSendFormSuccess');
  330. uni.$u.toast('发单记录更新成功');
  331. } else {
  332. // 合并所有附件
  333. const allAttachments = [
  334. ...this.form.chatAttachmentList,
  335. ...this.form.quoteAttachmentList,
  336. ...this.form.hdImageAttachmentList,
  337. ...this.form.otherAttachmentList
  338. ];
  339. // 准备提交数据
  340. const submitData = {
  341. ...this.form,
  342. uploadList: allAttachments
  343. };
  344. await uni.$u.api.saveClueOrderForm(submitData);
  345. uni.$u.toast('发单记录添加成功');
  346. }
  347. // 延迟返回
  348. setTimeout(() => {
  349. uni.navigateBack();
  350. }, 1500);
  351. } catch (error) {
  352. console.error('保存失败:', error);
  353. if (error.message) {
  354. uni.$u.toast(error.message);
  355. }
  356. }
  357. }
  358. },
  359. onLoad(option) {
  360. console.log('option', option);
  361. const clueId = option.clueId;
  362. this.sendFormId = option.sendFormId;
  363. this.form.clueId = clueId;
  364. this.getDicts();
  365. if (option.sendFormId) {
  366. this.loadFormData(option.sendFormId);
  367. }
  368. }
  369. }
  370. </script>
  371. <style lang="scss" scoped>
  372. .oder_form_wrap {
  373. background-color: #f8f9fa;
  374. min-height: 100vh;
  375. padding-bottom: 10rpx;
  376. padding-top: 10px;
  377. padding-left: 20rpx;
  378. padding-right: 20rpx;
  379. }
  380. .upload_area {
  381. padding: 0 10rpx;
  382. }
  383. .bottom_btn {
  384. position: fixed;
  385. bottom: 0;
  386. left: 0;
  387. right: 0;
  388. padding: 20rpx;
  389. background: #fff;
  390. box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
  391. }
  392. ::v-deep .u-form-item {
  393. /* 移除底部边框 */
  394. border-bottom: none;
  395. /* 添加卡片样式 */
  396. // background-color: #fff;
  397. border-radius: 16rpx;
  398. /* 添加内边距 */
  399. padding: 10rpx;
  400. /* 确保内容不会溢出 */
  401. overflow: hidden;
  402. .u-input--square {
  403. border-radius: 20rpx;
  404. }
  405. .u-input--square:focus-within,
  406. .u-input--square.u-input--focus {
  407. border: 2rpx solid #2563eb !important;
  408. background-color: #fff !important;
  409. }
  410. }
  411. .brand_wrap {
  412. ::v-deep .u-form-item__body__right__content {
  413. border: 1px solid #dadbde;
  414. padding: 10rpx;
  415. border-radius: 20rpx;
  416. .inputWrap {
  417. border: none;
  418. }
  419. }
  420. }
  421. .address_wrap {
  422. border-radius: 20rpx;
  423. padding: 24rpx;
  424. display: flex;
  425. flex-direction: column;
  426. background-color: #fff;
  427. box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
  428. margin: 0 10rpx;
  429. .address_title {
  430. display: flex;
  431. align-items: center;
  432. background-color: #fff;
  433. padding-bottom: 12rpx;
  434. gap: 16rpx;
  435. font-size: 26rpx;
  436. color: #6b7280;
  437. font-weight: 600;
  438. }
  439. ::v-deep .u-form-item {
  440. margin-bottom: 0 !important;
  441. padding: 0;
  442. .u-input--square {
  443. background-color: #f9fafb;
  444. }
  445. }
  446. ::v-deep .u-textarea--radius {
  447. border-radius: 20rpx;
  448. }
  449. .region-picker {
  450. width: 100%;
  451. }
  452. ::v-deep .u-textarea {
  453. background-color: #f9fafb;
  454. }
  455. }
  456. .visit_time_wrap {
  457. ::v-deep .uni-date-x {
  458. border-radius: 10px;
  459. padding: 8rpx 10rpx;
  460. border: 1px solid #dadbde;
  461. background-color: #f8f9fa;
  462. }
  463. }
  464. .category_wrap {
  465. .category_select_wrap {
  466. border-radius: 20rpx;
  467. border: 1px solid #dadbde;
  468. padding: 6rpx;
  469. width: 100%;
  470. ::v-deep .ldSelectInput {
  471. font-size: 28rpx !important;
  472. }
  473. }
  474. .category_select_wrap:focus-within,
  475. .category_select_wrap:focus {
  476. border: 2rpx solid #2563eb !important;
  477. background-color: #fff !important;
  478. }
  479. }
  480. .remarks_wrap {
  481. .u-textarea {
  482. background-color: #fff;
  483. }
  484. ::v-deep .u-textarea--radius {
  485. border-radius: 20rpx;
  486. }
  487. }
  488. ::v-deep .u-form-item__body {
  489. padding: 10rpx 0rpx;
  490. }
  491. ::v-deep .u-form-item__body__left__content__label {
  492. color: rgb(107 114 128 / 1) !important;
  493. font-size: 28rpx;
  494. font-weight: 700;
  495. }
  496. .u-button {
  497. border-radius: 40rpx;
  498. background-color: #2563eb;
  499. border: none;
  500. color: #fff;
  501. font-weight: 700;
  502. font-size: 40rpx;
  503. height: 100rpx;
  504. }
  505. .tactic-buttons {
  506. display: flex;
  507. justify-content: space-between;
  508. margin-top: 10rpx;
  509. width: 100%;
  510. }
  511. .tactic-button {
  512. position: relative;
  513. border-width: 3rpx;
  514. border-style: solid;
  515. border-color: #e5e7eb;
  516. width: 30%;
  517. height: 80rpx;
  518. line-height: 80rpx;
  519. text-align: center;
  520. border-radius: 30rpx;
  521. background-color: #f9fafb;
  522. color: rgb(107 114 128 / 1);
  523. font-size: 28rpx;
  524. transition: all 0.3s ease;
  525. font-weight: 700;
  526. }
  527. .tactic-button.active {
  528. border-color: #3b82f6;
  529. color: #3b82f6;
  530. background-color: #eff6ff;
  531. }
  532. .tactic-button::after {
  533. content: '';
  534. position: absolute;
  535. border-width: 5rpx;
  536. border-style: solid;
  537. border-color: #ffffff;
  538. top: 4%;
  539. right: -12%;
  540. transform: translate(-50%, -50%);
  541. width: 25rpx;
  542. height: 25rpx;
  543. border-radius: 50%;
  544. background-color: #3b82f6;
  545. opacity: 0;
  546. transition: all 0.3s ease;
  547. }
  548. .tactic-button.active::after {
  549. opacity: 1;
  550. }
  551. .u-textarea {
  552. background-color: #f5f5f5;
  553. }
  554. </style>