index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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">
  9. <ld-select :list="brandDict" label-key="dictLabel" value-key="dictValue" placeholder="请选择物品品牌"
  10. v-model="form.brand" :border="false"></ld-select>
  11. <u-icon slot="right" name="arrow-right"></u-icon>
  12. </u-form-item>
  13. <u-row justify="space-between">
  14. <u-form-item label="型号" prop="model">
  15. <u-col span="6">
  16. <u--input v-model="form.model" placeholder="请输入型号" border="none"></u--input>
  17. </u-col>
  18. </u-form-item>
  19. <u-form-item label="实价" prop="price">
  20. <u-col span="6">
  21. <u--input v-model="form.price" placeholder="请输入实价" border="none"></u--input>
  22. </u-col>
  23. </u-form-item>
  24. </u-row>
  25. <u-row justify="space-between">
  26. <u-form-item label="客户电话" prop="phone">
  27. <u-col span="6">
  28. <u--input v-model="form.phone" placeholder="请输入客户电话" border="none"></u--input>
  29. </u-col>
  30. </u-form-item>
  31. <u-form-item label="客户微信" prop="wechat">
  32. <u-col span="6">
  33. <u--input v-model="form.wechat" placeholder="请输入客户微信" border="none"></u--input>
  34. </u-col>
  35. </u-form-item>
  36. </u-row>
  37. <u-form-item label="省市区">
  38. <pick-regions :defaultRegion="defaultRegion" @getRegion="handleGetRegion">
  39. <view class="region-picker">
  40. <template v-if="form.province">
  41. <u--input :value="form.province + '/' + form.city + '/' + form.area" placeholder="点击选择"
  42. readonly suffixIcon="arrow-right" suffixIconStyle="color : #c0c4cc"></u--input>
  43. </template>
  44. <template v-else>
  45. <u--input placeholder="点击选择" readonly suffixIcon="arrow-right"
  46. suffixIconStyle="color : #c0c4cc"></u--input>
  47. </template>
  48. </view>
  49. </pick-regions>
  50. </u-form-item>
  51. <u-form-item label="详细地址" prop="address" class='form_card'>
  52. <u--textarea v-model="form.address" placeholder="请输入详细地址" count confirmType="done"></u--textarea>
  53. </u-form-item>
  54. <u-form-item label="上门时间" prop="visitTime">
  55. <date-time-picker v-model="form.visitTime" :value-format="'YYYY-MM-DD HH:mm:ss'" :type="'datetime'">
  56. </date-time-picker>
  57. </u-form-item>
  58. <u-form-item label="类型" prop="category">
  59. <ld-select :list="categoryDict" label-key="dictLabel" value-key="dictValue" placeholder="请选择类型"
  60. v-model="form.category" :border="false"></ld-select>
  61. <u-icon slot="right" name="arrow-right"></u-icon>
  62. </u-form-item>
  63. <u-form-item label="价格范围" prop="priceRange">
  64. <u--input v-model="form.priceRange" placeholder="请输入价格范围" border="none"></u--input>
  65. </u-form-item>
  66. <u-form-item label="战术选择" prop="tactic">
  67. <view class="tactic-buttons">
  68. <view v-for="item in tacticDict" :key="item.dictValue" class="tactic-button"
  69. :class="{ 'active': form.tactic === item.dictValue }" @click="form.tactic = item.dictValue">
  70. {{ item.dictLabel }}
  71. </view>
  72. </view>
  73. </u-form-item>
  74. <u-form-item label="备注消息" prop="remarks">
  75. <u--textarea v-model="form.remarks" placeholder="请输入备注消息" count confirmType="done"></u--textarea>
  76. </u-form-item>
  77. </u--form>
  78. </view>
  79. <!-- 文件上传区域 -->
  80. <view class="upload_area" v-if="!sendFormId">
  81. <!-- 聊天记录附件 -->
  82. <order-file-upload title="聊天记录附件" orderFileType="1" :file-list="form.chatAttachmentList"
  83. @update:fileList="updateChatFiles" tip-text="上传聊天截图、录音等文件"></order-file-upload>
  84. <!-- 报价附件 -->
  85. <order-file-upload title="报价附件" orderFileType="2" :file-list="form.quoteAttachmentList"
  86. @update:fileList="updateQuoteFiles" tip-text="上传报价附件"></order-file-upload>
  87. <!-- 高清图附件 -->
  88. <order-file-upload title="高清图附件" orderFileType="3" :file-list="form.hdImageAttachmentList"
  89. @update:fileList="updateHdImageFiles" tip-text="上传物品高清图片"></order-file-upload>
  90. <!-- 其他附件 -->
  91. <order-file-upload title="其他附件" orderFileType="4" :file-list="form.otherAttachmentList"
  92. @update:fileList="updateOtherFiles" tip-text="上传其他相关文件"></order-file-upload>
  93. </view>
  94. <u-button @click="handleNavSaveClick" type="primary">提交订单</u-button>
  95. </view>
  96. </template>
  97. <script>
  98. import orderFileUpload from '@/components/order-file-upload/order-file-upload.vue'
  99. import dateTimePicker from "@/components/dateTimePicker/dateTimePicker.vue"
  100. import ldSelect from "@/components/ld-select/ld-select.vue"
  101. export default {
  102. components: {
  103. orderFileUpload,
  104. dateTimePicker,
  105. ldSelect
  106. },
  107. data() {
  108. return {
  109. defaultRegion: ['广东省', '广州市', '番禺区'],
  110. categoryDict: [],
  111. tacticDict: [],
  112. brandDict: [],
  113. sendFormId: undefined,
  114. form: {
  115. clueId: '',
  116. sendDate: '',
  117. website: '',
  118. item: '',
  119. phone: '',
  120. wechat: '',
  121. address: '',
  122. visitTime: '',
  123. remarks: '',
  124. category: '',
  125. brand: '',
  126. model: '',
  127. price: '',
  128. priceRange: '',
  129. tactic: '',
  130. // 地区信息
  131. province: '',
  132. city: '',
  133. area: '',
  134. // 附件列表
  135. chatAttachmentList: [], // 聊天记录附件
  136. quoteAttachmentList: [], // 报价附件
  137. hdImageAttachmentList: [], // 高清图附件
  138. otherAttachmentList: [] // 其他附件
  139. },
  140. rules: {
  141. sendDate: {
  142. type: 'string',
  143. required: true,
  144. message: '请输入发单日期',
  145. trigger: ['blur', 'change']
  146. },
  147. model: {
  148. type: 'string',
  149. required: true,
  150. message: '请输入型号',
  151. trigger: ['blur', 'change']
  152. },
  153. price: {
  154. type: 'string',
  155. required: true,
  156. message: '请输入实价',
  157. trigger: ['blur', 'change']
  158. },
  159. website: {
  160. type: 'url',
  161. message: '请输入正确的网址格式',
  162. trigger: ['blur', 'change']
  163. },
  164. item: {
  165. type: 'string',
  166. required: true,
  167. message: '请输入物品描述',
  168. trigger: ['blur', 'change']
  169. },
  170. wechat: {
  171. type: 'string',
  172. required: true,
  173. message: '请输入客户微信',
  174. trigger: ['blur', 'change']
  175. },
  176. phone: {
  177. type: 'string',
  178. required: true,
  179. message: '请输入联系电话',
  180. trigger: ['blur', 'change']
  181. },
  182. address: {
  183. type: 'string',
  184. required: true,
  185. message: '请输入详细地址',
  186. trigger: ['blur', 'change']
  187. },
  188. visitTime: {
  189. type: 'string',
  190. required: true,
  191. message: '请输入上门时间',
  192. trigger: ['blur', 'change']
  193. },
  194. category: {
  195. type: 'string',
  196. required: true,
  197. message: '请选择类型',
  198. trigger: ['blur', 'change']
  199. },
  200. brand: {
  201. type: 'string',
  202. required: true,
  203. message: '请选择物品品牌',
  204. trigger: ['blur', 'change']
  205. },
  206. tactic: {
  207. type: 'string',
  208. required: true,
  209. message: '请选择战术',
  210. trigger: ['blur', 'change']
  211. }
  212. }
  213. }
  214. },
  215. methods: {
  216. // 加载表单数据
  217. async loadFormData(sendFormId) {
  218. try {
  219. const res = await uni.$u.api.getClueSendFormVoByOrderId({
  220. id: sendFormId
  221. });
  222. if (res.code === 200) {
  223. this.form = res.data;
  224. }
  225. } catch (error) {
  226. console.error('加载表单数据失败:', error);
  227. uni.$u.toast('加载表单数据失败');
  228. }
  229. },
  230. // 获取地区选择
  231. handleGetRegion(region) {
  232. const [provinceData, cityData, areaData] = region;
  233. this.form.province = provinceData.name;
  234. this.form.city = cityData.name;
  235. this.form.area = areaData.name;
  236. // 只在详细地址为空时自动填充省市区
  237. if (!this.form.address || this.form.address.trim() === '') {
  238. this.form.address = this.form.province + this.form.city + this.form.area;
  239. }
  240. },
  241. // 更新各类附件
  242. updateChatFiles(fileList) {
  243. this.form.chatAttachmentList = fileList;
  244. },
  245. updateQuoteFiles(fileList) {
  246. this.form.quoteAttachmentList = fileList;
  247. },
  248. updateHdImageFiles(fileList) {
  249. this.form.hdImageAttachmentList = fileList;
  250. },
  251. updateOtherFiles(fileList) {
  252. this.form.otherAttachmentList = fileList;
  253. },
  254. // 文件变化处理
  255. handleFileChange(data) {
  256. console.log('文件上传变化:', data);
  257. },
  258. // 获取字典数据
  259. async getDicts() {
  260. try {
  261. const [categoryRes, tacticRes, brandRes] = await Promise.all([
  262. this.$getDicts('crm_form_category'),
  263. this.$getDicts('crm_form_tactic'),
  264. this.$getDicts('crm_form_brand')
  265. ]);
  266. this.categoryDict = categoryRes;
  267. this.tacticDict = tacticRes;
  268. this.brandDict = brandRes;
  269. } catch (error) {
  270. console.error('获取字典数据失败:', error);
  271. }
  272. },
  273. // 处理保存
  274. async handleNavSaveClick() {
  275. try {
  276. // 表单验证
  277. await this.$refs.form.validate();
  278. if (this.form.id) {
  279. const updatedForm = {
  280. clueId: this.form.clueId,
  281. model: this.form.model,
  282. price: this.form.price,
  283. wechat: this.form.wechat,
  284. id: this.form.id,
  285. item: this.form.item,
  286. phone: this.form.phone,
  287. authenticateUserId: this.form.authenticateUserId,
  288. category: this.form.category,
  289. brand: this.form.brand,
  290. idCard: this.form.idCard,
  291. customName: this.form.customName,
  292. bankCardNumber: this.form.bankCardNumber,
  293. bankName: this.form.bankName,
  294. paymentMethod: this.form.paymentMethod,
  295. visitTime: this.form.visitTime,
  296. remarks: this.form.remarks,
  297. priceRange: this.form.priceRange,
  298. tactic: this.form.tactic
  299. }
  300. await uni.$u.api.updateClueOrderForm(updatedForm);
  301. uni.$emit('updateSendFormSuccess');
  302. uni.$u.toast('发单记录更新成功');
  303. } else {
  304. // 合并所有附件
  305. const allAttachments = [
  306. ...this.form.chatAttachmentList,
  307. ...this.form.quoteAttachmentList,
  308. ...this.form.hdImageAttachmentList,
  309. ...this.form.otherAttachmentList
  310. ];
  311. // 准备提交数据
  312. const submitData = {
  313. ...this.form,
  314. uploadList: allAttachments
  315. };
  316. await uni.$u.api.saveClueOrderForm(submitData);
  317. uni.$u.toast('发单记录添加成功');
  318. }
  319. // 延迟返回
  320. setTimeout(() => {
  321. uni.navigateBack();
  322. }, 1500);
  323. } catch (error) {
  324. console.error('保存失败:', error);
  325. if (error.message) {
  326. uni.$u.toast(error.message);
  327. }
  328. }
  329. }
  330. },
  331. onLoad(option) {
  332. console.log('option', option);
  333. const clueId = option.clueId;
  334. this.sendFormId = option.sendFormId;
  335. this.form.clueId = clueId;
  336. this.getDicts();
  337. if (option.sendFormId) {
  338. this.loadFormData(option.sendFormId);
  339. }
  340. }
  341. }
  342. </script>
  343. <style lang="scss" scoped>
  344. .oder_form_wrap {
  345. background-color: #f5f5f5;
  346. min-height: 100vh;
  347. padding-bottom: 100rpx;
  348. padding-top: 10px;
  349. padding-left: 20rpx;
  350. padding-right: 20rpx;
  351. }
  352. .form_wrap {
  353. /* 移除整体背景和边框,让每个form-item成为独立卡片 */
  354. }
  355. .upload_area {
  356. padding: 0 0rpx;
  357. margin: 20rpx 0;
  358. }
  359. .bottom_btn {
  360. position: fixed;
  361. bottom: 0;
  362. left: 0;
  363. right: 0;
  364. padding: 20rpx;
  365. background: #fff;
  366. box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
  367. }
  368. .region-picker {
  369. width: 100%;
  370. }
  371. ::v-deep .u-form-item {
  372. /* 移除底部边框 */
  373. border-bottom: none;
  374. /* 添加卡片样式 */
  375. // background-color: #fff;
  376. border-radius: 16rpx;
  377. /* 添加阴影效果 */
  378. /* 添加外边距 */
  379. margin-bottom: 20rpx;
  380. /* 添加内边距 */
  381. padding: 20rpx;
  382. /* 确保内容不会溢出 */
  383. overflow: hidden;
  384. }
  385. .form_card {
  386. background-color: #fff;
  387. box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
  388. }
  389. ::v-deep .u-form-item__body {
  390. padding: 10rpx 0rpx;
  391. }
  392. ::v-deep .u-form-item__body__left__content__label {
  393. color: rgb(107 114 128 / 1) !important;
  394. font-size: 0.75rem;
  395. line-height: 1rem;
  396. font-weight: 700;
  397. }
  398. .u-button {
  399. border-radius: 40rpx;
  400. background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
  401. --tw-text-opacity: 1;
  402. color: rgb(255 255 255 / var(--tw-text-opacity, 1));
  403. font-weight: 700;
  404. font-size: 1rem;
  405. line-height: 1.5rem;
  406. height: 100rpx;
  407. }
  408. .tactic-buttons {
  409. display: flex;
  410. justify-content: space-between;
  411. margin-top: 10rpx;
  412. width: 100%;
  413. }
  414. .tactic-button {
  415. position: relative;
  416. border-width: 3rpx;
  417. border-style: solid;
  418. border-color: #e5e7eb;
  419. width: 30%;
  420. height: 80rpx;
  421. line-height: 80rpx;
  422. text-align: center;
  423. border-radius: 30rpx;
  424. background-color: #f5f5f5;
  425. color: rgb(107 114 128 / 1);
  426. font-size: 28rpx;
  427. transition: all 0.3s ease;
  428. font-weight: 700;
  429. }
  430. .tactic-button.active {
  431. border-color: rgb(29 78 216 / 1);
  432. color: rgb(29 78 216 / 1);
  433. }
  434. .tactic-button::after {
  435. content: '';
  436. position: absolute;
  437. border-width: 5rpx;
  438. border-style: solid;
  439. border-color: #ffffff;
  440. top: 4%;
  441. right: -12%;
  442. transform: translate(-50%, -50%);
  443. width: 25rpx;
  444. height: 25rpx;
  445. border-radius: 50%;
  446. background-color: rgb(29 78 216 / 1);
  447. opacity: 0;
  448. transition: all 0.3s ease;
  449. }
  450. .tactic-button.active::after {
  451. opacity: 1;
  452. }
  453. .u-textarea{
  454. background-color: #f5f5f5;
  455. }
  456. </style>