addCustomer.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. <template>
  2. <div v-if="showModal" class="modal-overlay" @click.self="handleClose">
  3. <div class="modal modal-lg">
  4. <div class="modal-header">
  5. <div class="modal-title"><i class="fas fa-user-plus"></i> 添加客户</div>
  6. <button class="modal-close" @click="handleClose"><i class="fas fa-times"></i></button>
  7. </div>
  8. <div class="modal-body">
  9. <!-- 基础信息 -->
  10. <div class="section-title">基础信息</div>
  11. <div class="form-row ">
  12. <div class="lef">
  13. <div class="form-group">
  14. <label class="form-label">客户代码</label>
  15. <input type="text" class="form-input" v-model="formData.customerCode" placeholder="客户代码">
  16. </div>
  17. <div class="form-group">
  18. <label class="form-label">归属人员 <span class="required">*</span></label>
  19. <select class="form-select" v-model="formData.ownerUser">
  20. <option value="">请选择用户</option>
  21. <option value="user1">用户1</option>
  22. <option value="user2">用户2</option>
  23. </select>
  24. </div>
  25. <div class="form-group">
  26. <label class="form-label">客户状态 <span class="required">*</span></label>
  27. <select class="form-select" v-model="formData.customerStatus">
  28. <option value="no_need">暂无需</option>
  29. <option value="intent">意向客户</option>
  30. <option value="deal">成交客户</option>
  31. </select>
  32. </div>
  33. <div class="form-group">
  34. <label class="form-label">联系人姓名 <span class="required">*</span></label>
  35. <input type="text" class="form-input" v-model="formData.contactName" placeholder="请输入联系人姓名">
  36. </div>
  37. <div class="form-group">
  38. <label class="form-label">意向产品</label>
  39. <select class="form-select" v-model="formData.intentProduct">
  40. <option value="">意向产品</option>
  41. </select>
  42. </div>
  43. <div class="form-group">
  44. <label class="form-label">客户来源</label>
  45. <select class="form-select" v-model="formData.source">
  46. <option value="">客户来源</option>
  47. </select>
  48. </div>
  49. <div class="form-group">
  50. <label class="form-label">客户等级</label>
  51. <div class="radio-group">
  52. <label class="radio-item">
  53. <input type="radio" v-model="formData.level" value="A"> A级
  54. </label>
  55. <label class="radio-item">
  56. <input type="radio" v-model="formData.level" value="B"> B级
  57. </label>
  58. <label class="radio-item">
  59. <input type="radio" v-model="formData.level" value="C"> C级
  60. </label>
  61. <label class="radio-item">
  62. <input type="radio" v-model="formData.level" value="D"> D级
  63. </label>
  64. </div>
  65. </div>
  66. <div class="form-group">
  67. <label class="form-label">助记名称</label>
  68. <input type="text" class="form-input" v-model="formData.alias" placeholder="请输入助记名称">
  69. </div>
  70. </div>
  71. <div class="right">
  72. <div class="form-group">
  73. <label class="form-label">区域名称 <span class="required">*</span></label>
  74. <select class="form-select" v-model="formData.regionName">
  75. <option value="">请选择</option>
  76. <option value="A">区域A</option>
  77. <option value="B">区域B</option>
  78. </select>
  79. </div>
  80. <div class="form-group">
  81. <label class="form-label">客户名称 <span class="required">*</span></label>
  82. <input type="text" class="form-input" v-model="formData.customerName" placeholder="请输入客户名称">
  83. </div>
  84. <!-- 企业网址 紧凑多行布局 -->
  85. <div class="form-group multi-input-group">
  86. <label class="form-label">企业网址 <span class="required">*</span></label>
  87. <div class="input-item" v-for="(item, idx) in formData.websiteList" :key="idx">
  88. <div class="input-with-icon">
  89. <input
  90. type="text"
  91. class="form-input"
  92. v-model="formData.websiteList[idx]"
  93. placeholder="请输入企业网址"
  94. >
  95. <span class="icon-btn" @click="addWebsite" v-if="idx === 0">+</span>
  96. <span class="icon-btn minus" @click="delWebsite(idx)" v-if="idx > 0">-</span>
  97. </div>
  98. </div>
  99. </div>
  100. <!-- 联系人邮箱 紧凑多行布局 -->
  101. <div class="form-group multi-input-group">
  102. <label class="form-label">联系人邮箱 <span class="required">*</span></label>
  103. <div class="input-item" v-for="(item, idx) in formData.emailList" :key="idx">
  104. <div class="input-with-icon">
  105. <input
  106. type="text"
  107. class="form-input"
  108. v-model="formData.emailList[idx]"
  109. placeholder="请输入联系人邮箱"
  110. >
  111. <span class="icon-btn" @click="addEmail" v-if="idx === 0">+</span>
  112. <span class="icon-btn minus" @click="delEmail(idx)" v-if="idx > 0">-</span>
  113. </div>
  114. </div>
  115. </div>
  116. <div class="form-group">
  117. <label class="form-label">客户类型</label>
  118. <select class="form-select" v-model="formData.customerType">
  119. <option value="">客户类型</option>
  120. </select>
  121. </div>
  122. <div class="form-group">
  123. <label class="form-label">经济性质</label>
  124. <select class="form-select" v-model="formData.economicNature">
  125. <option value="">客户经济性质</option>
  126. </select>
  127. </div>
  128. <div class="form-group">
  129. <label class="form-label">信用等级</label>
  130. <div class="radio-group">
  131. <label class="radio-item">
  132. <input type="radio" v-model="formData.creditLevel" value="good"> 良好
  133. </label>
  134. <label class="radio-item">
  135. <input type="radio" v-model="formData.creditLevel" value="normal"> 一般
  136. </label>
  137. <label class="radio-item">
  138. <input type="radio" v-model="formData.creditLevel" value="poor"> 较差
  139. </label>
  140. <label class="radio-item">
  141. <input type="radio" v-model="formData.creditLevel" value="worse"> 比较差
  142. </label>
  143. </div>
  144. </div>
  145. <div class="form-group">
  146. <label class="form-label">备注信息</label>
  147. <textarea class="form-textarea" v-model="formData.remark" placeholder="请输入内容"></textarea>
  148. </div>
  149. </div>
  150. </div>
  151. <!-- 企业信息 -->
  152. <div class="section-title">企业信息</div>
  153. <div class="form-row">
  154. <div class="form-group">
  155. <label class="form-label">企业规模</label>
  156. <select class="form-select" v-model="formData.scale">
  157. <option value="">意向产品</option>
  158. </select>
  159. </div>
  160. <div class="form-group">
  161. <label class="form-label">所属行业</label>
  162. <input type="text" class="form-input" v-model="formData.industry" placeholder="请输入所属行业">
  163. </div>
  164. </div>
  165. <div class="form-row">
  166. <div class="form-group">
  167. <label class="form-label">通信地址</label>
  168. <input type="text" class="form-input" v-model="formData.address" placeholder="请输入通信地址">
  169. </div>
  170. <div class="form-group">
  171. <label class="form-label">邮政编码</label>
  172. <input type="text" class="form-input" v-model="formData.postcode" placeholder="请输入邮政编码">
  173. </div>
  174. </div>
  175. <div class="form-row">
  176. <div class="form-group">
  177. <label class="form-label">传真号码</label>
  178. <input type="text" class="form-input" v-model="formData.fax" placeholder="请输入传真号码">
  179. </div>
  180. <div class="form-group">
  181. <label class="form-label">企业邮箱</label>
  182. <div class="input-with-icon">
  183. <input type="text" class="form-input" v-model="formData.companyEmail" placeholder="请输入企业邮箱">
  184. <span class="icon-btn">+</span>
  185. </div>
  186. </div>
  187. </div>
  188. <div class="form-row">
  189. <div class="form-group">
  190. <label class="form-label">企业备用邮箱</label>
  191. <div class="input-with-icon">
  192. <input type="text" class="form-input" v-model="formData.backupEmail" placeholder="请输入企业备用邮箱">
  193. <span class="icon-btn minus">-</span>
  194. </div>
  195. </div>
  196. <div class="form-group">
  197. <label class="form-label">联系号码</label>
  198. <input type="text" class="form-input" v-model="formData.contactPhone" placeholder="请输入联系号码">
  199. </div>
  200. </div>
  201. <!-- 首要联系人 -->
  202. <div class="section-title">首要联系人</div>
  203. <div class="form-row">
  204. <div class="form-group">
  205. <label class="form-label">称呼</label>
  206. <div class="radio-group">
  207. <label class="radio-item">
  208. <input type="radio" v-model="formData.gender" value="unknown"> 未知
  209. </label>
  210. <label class="radio-item">
  211. <input type="radio" v-model="formData.gender" value="mr"> 先生
  212. </label>
  213. <label class="radio-item">
  214. <input type="radio" v-model="formData.gender" value="mrs"> 女士
  215. </label>
  216. </div>
  217. </div>
  218. <div class="form-group">
  219. <label class="form-label">角色</label>
  220. <select class="form-select" v-model="formData.role">
  221. <option value="">请选择角色</option>
  222. </select>
  223. </div>
  224. </div>
  225. <div class="form-row">
  226. <div class="form-group">
  227. <label class="form-label">生日</label>
  228. <select class="form-select" v-model="formData.birthday">
  229. <option value="">请选择生日</option>
  230. </select>
  231. </div>
  232. <div class="form-group">
  233. <label class="form-label">部门职务</label>
  234. <input type="text" class="form-input" v-model="formData.position" placeholder="请输入部门职务">
  235. </div>
  236. </div>
  237. <div class="form-row">
  238. <div class="form-group">
  239. <label class="form-label">手机号码</label>
  240. <input type="text" class="form-input" v-model="formData.mobile" placeholder="请输入手机号码">
  241. </div>
  242. <div class="form-group">
  243. <label class="form-label">联系人备用邮箱</label>
  244. <div class="input-with-icon">
  245. <input type="text" class="form-input" v-model="formData.contactBackupEmail" placeholder="请输入联系人备用邮箱">
  246. <span class="icon-btn minus">-</span>
  247. </div>
  248. </div>
  249. </div>
  250. <div class="form-row">
  251. <div class="form-group">
  252. <label class="form-label">固定电话</label>
  253. <input type="text" class="form-input" v-model="formData.tel" placeholder="请输入固定电话">
  254. </div>
  255. <div class="form-group">
  256. <label class="form-label">传真号码</label>
  257. <input type="text" class="form-input" v-model="formData.contactFax" placeholder="请输入传真号码">
  258. </div>
  259. </div>
  260. <div class="form-row">
  261. <div class="form-group">
  262. <label class="form-label">宅电</label>
  263. <input type="text" class="form-input" v-model="formData.homeTel" placeholder="请输入内容">
  264. </div>
  265. <div class="form-group">
  266. <label class="form-label">QQ</label>
  267. <input type="text" class="form-input" v-model="formData.qq" placeholder="请输入QQ">
  268. </div>
  269. </div>
  270. <div class="form-row">
  271. <div class="form-group">
  272. <label class="form-label">SKYPE</label>
  273. <input type="text" class="form-input" v-model="formData.skype" placeholder="请输入SKYPE">
  274. </div>
  275. <div class="form-group">
  276. <label class="form-label">旺旺</label>
  277. <input type="text" class="form-input" v-model="formData.ww" placeholder="请输入旺旺">
  278. </div>
  279. </div>
  280. <div class="form-row">
  281. <div class="form-group">
  282. <label class="form-label">微信</label>
  283. <input type="text" class="form-input" v-model="formData.wechat" placeholder="请输入微信">
  284. </div>
  285. <div class="form-group">
  286. <label class="form-label">Twitter</label>
  287. <input type="text" class="form-input" v-model="formData.twitter" placeholder="请输入Twitter">
  288. </div>
  289. </div>
  290. <div class="form-row">
  291. <div class="form-group">
  292. <label class="form-label">LinkedIn</label>
  293. <input type="text" class="form-input" v-model="formData.linkedin" placeholder="请输入LinkedIn">
  294. </div>
  295. <div class="form-group">
  296. <label class="form-label">Facebook</label>
  297. <input type="text" class="form-input" v-model="formData.facebook" placeholder="请输入Facebook">
  298. </div>
  299. </div>
  300. <div class="form-row">
  301. <div class="form-group">
  302. <label class="form-label">Whatsapp</label>
  303. <input type="text" class="form-input" v-model="formData.whatsapp" placeholder="请输入Whatsapp">
  304. </div>
  305. <div class="form-group">
  306. <label class="form-label">所在地区</label>
  307. <input type="text" class="form-input" v-model="formData.contactRegion" placeholder="请输入所在地区">
  308. </div>
  309. </div>
  310. </div>
  311. <div class="modal-footer">
  312. <button class="btn btn-secondary" @click="handleClose">取消</button>
  313. <button class="btn btn-primary" @click="handleAdd"><i class="fas fa-plus"></i> 添加</button>
  314. </div>
  315. </div>
  316. </div>
  317. </template>
  318. <script setup>
  319. import { ref, defineProps, defineEmits } from 'vue'
  320. const props = defineProps({
  321. showModal: {
  322. type: Boolean,
  323. default: false
  324. }
  325. })
  326. const emit = defineEmits(['close', 'add'])
  327. // 表单数据
  328. const formData = ref({
  329. customerCode: '',
  330. regionName: '',
  331. ownerUser: '',
  332. customerName: '',
  333. customerStatus: 'no_need',
  334. websiteList: [''], // 最多4条(主+3备用)
  335. emailList: [''], // 最多4条(主+3备用)
  336. intentProduct: '',
  337. customerType: '',
  338. source: '',
  339. economicNature: '',
  340. level: 'A',
  341. creditLevel: 'good',
  342. alias: '',
  343. remark: '',
  344. scale: '',
  345. industry: '',
  346. address: '',
  347. postcode: '',
  348. fax: '',
  349. companyEmail: '',
  350. backupEmail: '',
  351. contactPhone: '',
  352. gender: 'unknown',
  353. role: '',
  354. birthday: '',
  355. position: '',
  356. mobile: '',
  357. contactBackupEmail: '',
  358. tel: '',
  359. contactFax: '',
  360. homeTel: '',
  361. qq: '',
  362. skype: '',
  363. ww: '',
  364. wechat: '',
  365. twitter: '',
  366. linkedin: '',
  367. facebook: '',
  368. whatsapp: '',
  369. contactRegion: ''
  370. })
  371. // 企业网址 新增
  372. const addWebsite = () => {
  373. if (formData.value.websiteList.length >= 4) return
  374. formData.value.websiteList.push('')
  375. }
  376. // 企业网址 删除
  377. const delWebsite = (index) => {
  378. formData.value.websiteList.splice(index, 1)
  379. }
  380. // 联系人邮箱 新增
  381. const addEmail = () => {
  382. if (formData.value.emailList.length >= 4) return
  383. formData.value.emailList.push('')
  384. }
  385. // 联系人邮箱 删除
  386. const delEmail = (index) => {
  387. formData.value.emailList.splice(index, 1)
  388. }
  389. // 关闭弹窗 & 重置表单
  390. const handleClose = () => {
  391. formData.value = {
  392. customerCode: '',
  393. regionName: '',
  394. ownerUser: '',
  395. customerName: '',
  396. customerStatus: 'no_need',
  397. websiteList: [''],
  398. emailList: [''],
  399. intentProduct: '',
  400. customerType: '',
  401. source: '',
  402. economicNature: '',
  403. level: 'A',
  404. creditLevel: 'good',
  405. alias: '',
  406. remark: '',
  407. scale: '',
  408. industry: '',
  409. address: '',
  410. postcode: '',
  411. fax: '',
  412. companyEmail: '',
  413. backupEmail: '',
  414. contactPhone: '',
  415. gender: 'unknown',
  416. role: '',
  417. birthday: '',
  418. position: '',
  419. mobile: '',
  420. contactBackupEmail: '',
  421. tel: '',
  422. contactFax: '',
  423. homeTel: '',
  424. qq: '',
  425. skype: '',
  426. ww: '',
  427. wechat: '',
  428. twitter: '',
  429. linkedin: '',
  430. facebook: '',
  431. whatsapp: '',
  432. contactRegion: ''
  433. }
  434. emit('close')
  435. }
  436. // 提交表单
  437. const handleAdd = () => {
  438. const { websiteList, emailList, regionName, ownerUser, customerName, customerStatus } = formData.value
  439. if (!regionName || !ownerUser || !customerName || !customerStatus) {
  440. alert('请填写所有必填项!')
  441. return
  442. }
  443. // 过滤空值
  444. const params = {
  445. ...formData.value,
  446. websiteList: websiteList.filter(item => item.trim()),
  447. emailList: emailList.filter(item => item.trim())
  448. }
  449. emit('add', params)
  450. handleClose()
  451. }
  452. </script>
  453. <style scoped>
  454. .modal-overlay {
  455. position: fixed;
  456. top: 0;
  457. left: 0;
  458. right: 0;
  459. bottom: 0;
  460. background: rgba(0, 0, 0, 0.7);
  461. display: flex;
  462. align-items: center;
  463. justify-content: center;
  464. z-index: 1000;
  465. padding: 20px;
  466. }
  467. .modal {
  468. background: var(--bg-card);
  469. border-radius: 16px;
  470. width: 100%;
  471. max-width: 800px;
  472. max-height: 90vh;
  473. overflow: hidden;
  474. display: flex;
  475. flex-direction: column;
  476. }
  477. .modal-header {
  478. display: flex;
  479. justify-content: space-between;
  480. align-items: center;
  481. padding: 20px;
  482. border-bottom: 1px solid var(--border);
  483. }
  484. .modal-title {
  485. display: flex;
  486. align-items: center;
  487. gap: 12px;
  488. font-size: 16px;
  489. font-weight: 600;
  490. }
  491. .modal-close {
  492. width: 32px;
  493. height: 32px;
  494. background: var(--bg-hover);
  495. border: none;
  496. border-radius: 8px;
  497. color: var(--text-secondary);
  498. cursor: pointer;
  499. display: flex;
  500. align-items: center;
  501. justify-content: center;
  502. }
  503. .modal-close:hover {
  504. background: var(--border);
  505. color: var(--text-primary);
  506. }
  507. .modal-body {
  508. flex: 1;
  509. overflow-y: auto;
  510. padding: 20px;
  511. }
  512. .modal-footer {
  513. display: flex;
  514. justify-content: flex-end;
  515. gap: 12px;
  516. padding: 20px;
  517. border-top: 1px solid var(--border);
  518. }
  519. .section-title {
  520. font-size: 14px;
  521. font-weight: 600;
  522. color: var(--text-secondary);
  523. background: var(--bg-hover);
  524. padding: 8px 12px;
  525. border-radius: 4px;
  526. margin-bottom: 16px;
  527. }
  528. /* 行布局:保留两列,统一间距 */
  529. .form-row {
  530. display: grid;
  531. grid-template-columns: 1fr 1fr;
  532. gap: 16px;
  533. margin-bottom: 16px;
  534. align-items: flex-start; /* 关键:多行输入时顶部对齐,避免留白 */
  535. }
  536. .form-row .left,.form-row .right{
  537. display: flex;
  538. flex-direction: column;
  539. }
  540. .form-group {
  541. }
  542. /* 多行输入专属样式 */
  543. .multi-input-group {
  544. gap: 4px; /* 标签与输入组间距缩小 */
  545. }
  546. .form-group.full-width {
  547. grid-column: 1 / -1;
  548. }
  549. .form-label {
  550. font-size: 12px;
  551. color: var(--text-secondary);
  552. margin-bottom: 6px;
  553. }
  554. .form-input,
  555. .form-select,
  556. .form-textarea {
  557. width: 100%;
  558. padding: 10px 14px;
  559. background: var(--bg-dark);
  560. border: 1px solid var(--border);
  561. border-radius: 8px;
  562. color: var(--text-primary);
  563. font-size: 13px;
  564. box-sizing: border-box;
  565. }
  566. .form-input:focus,
  567. .form-select:focus,
  568. .form-textarea:focus {
  569. outline: none;
  570. border-color: var(--primary);
  571. }
  572. .form-textarea {
  573. min-height: 80px;
  574. resize: vertical;
  575. }
  576. .required {
  577. color: var(--danger, #f56c6c);
  578. }
  579. /* 单条输入容器,紧凑间距 */
  580. .input-item {
  581. margin-bottom: 6px;
  582. }
  583. .input-item:last-child {
  584. margin-bottom: 0;
  585. }
  586. .input-with-icon {
  587. position: relative;
  588. width: 100%;
  589. }
  590. .icon-btn {
  591. position: absolute;
  592. right: 12px;
  593. top: 50%;
  594. transform: translateY(-50%);
  595. color: var(--primary);
  596. cursor: pointer;
  597. font-size: 16px;
  598. user-select: none;
  599. }
  600. .icon-btn.minus {
  601. color: var(--danger, #f56c6c);
  602. }
  603. .radio-group {
  604. display: flex;
  605. gap: 16px;
  606. align-items: center;
  607. }
  608. .radio-item {
  609. display: flex;
  610. align-items: center;
  611. gap: 4px;
  612. font-size: 13px;
  613. color: var(--text-secondary);
  614. cursor: pointer;
  615. }
  616. .btn {
  617. padding: 10px 20px;
  618. border-radius: 8px;
  619. border: none;
  620. cursor: pointer;
  621. font-size: 13px;
  622. display: flex;
  623. align-items: center;
  624. gap: 6px;
  625. }
  626. .btn-secondary {
  627. background: var(--bg-hover);
  628. color: var(--text-secondary);
  629. }
  630. .btn-primary {
  631. background: var(--primary);
  632. color: #fff;
  633. }
  634. </style>