userInfo.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. <template>
  2. <view class="container">
  3. <view class="content">
  4. <view class="summary-section">
  5. <view class="summary-header">
  6. <view class="summary-indicator"></view>
  7. <text class="summary-title">基础信息</text>
  8. </view>
  9. <view class="list">
  10. <view class="info-item">
  11. <view class="label">头像</view>
  12. <view class="right">
  13. <image class="head" :src="formData.avatar||'/static/image/my_heads_icon.png'"></image>
  14. <button class="wx-head" type="balanced" open-type="chooseAvatar"
  15. @chooseavatar="onChooseAvatar"></button>
  16. </view>
  17. </view>
  18. <view class="info-item">
  19. <view class="label">姓名</view>
  20. <view class="right">
  21. <input type="nickname" @input="onNicknameInput" v-model="formData.nickname"
  22. placeholder="请输入昵称" class="input" />
  23. </view>
  24. </view>
  25. <view class="info-item" @click="toSelectGender">
  26. <view class="label">性别</view>
  27. <view class="right">
  28. <text class="txt">{{formData.sex==1?'男':'女'}}</text>
  29. <image class="icon" src="/static/image/icon_my_more.png" mode=""></image>
  30. </view>
  31. </view>
  32. </view>
  33. <view class="summary-header">
  34. <view class="summary-indicator"></view>
  35. <text class="summary-title">岗位信息</text>
  36. </view>
  37. <view class="list">
  38. <view class="info-item" @click="toSelect('dept')">
  39. <view class="label">部门</view>
  40. <view class="right">
  41. <text class="txt" :class="{ash:!formData.deptName}">{{formData.deptName||'请选择部门'}}</text>
  42. <image class="icon" src="/static/image/icon_my_more.png" mode=""></image>
  43. </view>
  44. </view>
  45. <view class="info-item" @click="toSelect('post')">
  46. <view class="label">岗位</view>
  47. <view class="right">
  48. <text class="txt" :class="{ash:!formData.postNames}">{{formData.postNames||'请选择岗位'}}</text>
  49. <image class="icon" src="/static/image/icon_my_more.png" mode=""></image>
  50. </view>
  51. </view>
  52. <view class="info-item" @click="toSelect('product')">
  53. <view class="label">产品</view>
  54. <view class="right">
  55. <text class="txt" :class="{ash:!formData.productNames}">{{formData.productNames||'请选择产品'}}</text>
  56. <image class="icon" src="/static/image/icon_my_more.png" mode=""></image>
  57. </view>
  58. </view>
  59. <view class="info-item" @click="toSelect('superior')">
  60. <view class="label">直属上级</view>
  61. <view class="right">
  62. <text class="txt"
  63. :class="{ash:!formData.supervisorName}">{{formData.supervisorName||'请选择'}}</text>
  64. <image class="icon" src="/static/image/icon_my_more.png" mode=""></image>
  65. </view>
  66. </view>
  67. <view class="form-section">
  68. <view class="form-label">
  69. <text>证明材料</text>
  70. <text class="tip">工作证、OA信息截图、个人名片等</text>
  71. </view>
  72. <view class="upload-proof" @click="chooseProofImages">
  73. <view v-if="formData.proofImages && formData.proofImages.length > 0" class="uploaded-images">
  74. <view v-for="(image, index) in formData.proofImages" :key="index"
  75. class="uploaded-image-item">
  76. <image :src="image" mode="aspectFill"></image>
  77. <view class="image-delete" @click.stop="deleteProofImage(index)">×</view>
  78. </view>
  79. <view class="upload-placeholder">
  80. <image src="/static/image/icon_camera.png" mode="aspectFill"></image>
  81. </view>
  82. </view>
  83. <view v-else class="upload-placeholder">
  84. <image src="/static/image/icon_camera.png" mode="aspectFill"></image>
  85. </view>
  86. </view>
  87. </view>
  88. </view>
  89. </view>
  90. </view>
  91. <view class="btn-box">
  92. <view class="sub-btn" @click="submit()">保存</view>
  93. </view>
  94. <u-picker :show="showPicker" :columns="[pickerList]" keyName="title" @confirm="onPickerConfirm"
  95. @cancel="showPicker=false"></u-picker>
  96. <!-- 多选弹窗 -->
  97. <u-popup :show="showMultiSelectPopup" mode="bottom" round="20rpx" :closeable="false">
  98. <view class="u-popup-content">
  99. <view class="popup-header">
  100. <text class="popup-title">{{ multiSelectTitle }}</text>
  101. <text class="popup-close" @click="closeMultiSelectPopup">×</text>
  102. </view>
  103. <view class="popup-body">
  104. <view class="tag-group">
  105. <view v-for="(item, index) in multiSelectData" :key="index" class="tag-item"
  106. :class="{ active: selectedItems.includes(item) }" @click="toggleItem(item)">
  107. {{ item }}
  108. </view>
  109. </view>
  110. </view>
  111. <view class="popup-footer">
  112. <view class="popup-btn cancel-btn" @click="closeMultiSelectPopup">取消</view>
  113. <view class="popup-btn confirm-btn" @click="confirmMultiSelect">确定</view>
  114. </view>
  115. </view>
  116. </u-popup>
  117. </view>
  118. </template>
  119. <script>
  120. import {
  121. getUserInfo,
  122. updateUserInfo,
  123. getDeptList,
  124. getPostList,
  125. getProductList,
  126. getCompanyUserList
  127. } from '@/api/user'
  128. export default {
  129. data() {
  130. return {
  131. userInfo: {},
  132. formData: {
  133. proofImages: [],
  134. avatar: '',
  135. nickname: '',
  136. sex: '',
  137. deptId: '',
  138. deptName: '',
  139. postId: '',
  140. postName: '',
  141. postIds: [],
  142. productId: '',
  143. productName: '',
  144. productCode: '',
  145. productCodes: [],
  146. productNames: '',
  147. supervisorId: '',
  148. supervisorName: ''
  149. },
  150. showPicker: false,
  151. pickerList: [],
  152. pickerType: '',
  153. // 多选相关
  154. showMultiSelectPopup: false,
  155. multiSelectTitle: '',
  156. multiSelectData: [],
  157. selectedItems: [],
  158. currentMultiSelectType: '',
  159. dataCache: {
  160. deptList: null,
  161. postList: null,
  162. productList: null,
  163. userList: null
  164. }
  165. }
  166. },
  167. onLoad() {
  168. // 先获取用户信息
  169. this.getUserInfo();
  170. // 统一请求所有需要的数据
  171. this.loadAllData();
  172. },
  173. onShow() {
  174. },
  175. methods: {
  176. // 统一加载所有数据
  177. loadAllData() {
  178. // 加载部门列表
  179. this.loadData('dept', getDeptList, 'deptId', 'deptName', '');
  180. // 加载岗位列表
  181. this.loadData('post', getPostList, 'postId', 'postName', '');
  182. // 加载上级列表
  183. this.loadData('superior', getCompanyUserList, 'userId', 'nickName', '');
  184. // 加载产品列表(如果userInfo已加载)
  185. if (this.userInfo && this.userInfo.productList) {
  186. this.loadData('product', null, 'code', 'name', 'code');
  187. }
  188. },
  189. // 加载指定类型的数据
  190. async loadData(type, api, idField, nameField, codeField) {
  191. try {
  192. if (type === 'product' && this.userInfo && this.userInfo.productList) {
  193. // 产品数据从userInfo.productList中获取
  194. const list = this.userInfo.productList.map(item => ({
  195. id: item.code,
  196. title: item.name,
  197. code: item.code
  198. }));
  199. this.dataCache[type + 'List'] = list;
  200. } else if (type === 'post' && this.post) {
  201. // 岗位数据从this.post中获取
  202. const list = this.post.map((item, index) => ({
  203. id: index + 1,
  204. title: item
  205. }));
  206. this.dataCache[type + 'List'] = list;
  207. } else if (api) {
  208. const res = await api();
  209. if (res.code === 200 && res.data && res.data.length > 0) {
  210. let filteredData = res.data;
  211. // 剔除当前用户本人
  212. if (type === 'superior' && this.userInfo && this.userInfo.userId) {
  213. filteredData = res.data.filter(item => item.userId !== this.userInfo.userId);
  214. }
  215. const list = filteredData.map(item => ({
  216. id: item[idField],
  217. title: item[nameField],
  218. code: item[codeField] || ''
  219. }));
  220. this.dataCache[type + 'List'] = list;
  221. }
  222. }
  223. } catch (err) {
  224. console.error(`获取${type}列表失败:`, err);
  225. }
  226. },
  227. // 从缓存中获取数据,不再发起接口请求
  228. getDataList(type) {
  229. return this.dataCache[type + 'List'] || [];
  230. },
  231. onNicknameInput(e) {
  232. this.formData.nickname = e.detail.value;
  233. },
  234. onChooseAvatar(e) {
  235. const {
  236. avatarUrl
  237. } = e.detail;
  238. uni.showLoading({
  239. title: '上传中...'
  240. });
  241. const baseUrl = uni.getStorageSync('requestPath');
  242. uni.uploadFile({
  243. url: `${baseUrl}/system/file/upload`,
  244. filePath: avatarUrl,
  245. name: 'file',
  246. formData: {
  247. bizType: 'USER_INFO'
  248. },
  249. header: {
  250. 'AppToken': uni.getStorageSync('AppToken')
  251. },
  252. success: (uploadFileRes) => {
  253. const data = JSON.parse(uploadFileRes.data);
  254. if (data.code === 200) {
  255. this.formData.avatar = data.data.fileUrl;
  256. } else {
  257. uni.showToast({
  258. icon: 'none',
  259. title: data.message || '上传失败'
  260. });
  261. }
  262. uni.hideLoading();
  263. },
  264. fail: () => {
  265. uni.hideLoading();
  266. uni.showToast({
  267. icon: 'none',
  268. title: '上传失败'
  269. });
  270. }
  271. });
  272. },
  273. submit() {
  274. // 打印 postIds 的值和类型
  275. console.log('postIds 值:', this.formData.postIds);
  276. console.log('postIds 类型:', typeof this.formData.postIds);
  277. console.log('postIds 是否为数组:', Array.isArray(this.formData.postIds));
  278. // 确保 postIds 是数组
  279. let postIdsArray = this.formData.postIds;
  280. if (!Array.isArray(postIdsArray)) {
  281. if (postIdsArray) {
  282. postIdsArray = [postIdsArray];
  283. } else {
  284. postIdsArray = [];
  285. }
  286. }
  287. console.log('转换后 postIds 值:', postIdsArray);
  288. console.log('转换后 postIds 类型:', typeof postIdsArray);
  289. console.log('转换后 postIds 是否为数组:', Array.isArray(postIdsArray));
  290. // 确保 productCodes 是数组
  291. let productCodesArray = this.formData.productCodes;
  292. if (!Array.isArray(productCodesArray)) {
  293. if (productCodesArray) {
  294. productCodesArray = [productCodesArray];
  295. } else {
  296. productCodesArray = [];
  297. }
  298. }
  299. console.log('转换后 productCodes 值:', productCodesArray);
  300. const params = {
  301. avatar: this.formData.avatar,
  302. nicknName: this.formData.nickname,
  303. sex: this.formData.sex,
  304. deptId: this.formData.deptId,
  305. postIds: postIdsArray,
  306. productCode: productCodesArray.join(','),
  307. supervisorId: this.formData.supervisorId,
  308. certificationMaterials: this.formData.proofImages.join(',') || ''
  309. };
  310. uni.showLoading({
  311. title: '保存中...'
  312. });
  313. updateUserInfo(params).then(res => {
  314. uni.hideLoading();
  315. if (res.code === 200) {
  316. uni.showToast({
  317. icon: 'success',
  318. title: '修改成功'
  319. });
  320. setTimeout(() => {
  321. uni.navigateBack({
  322. delta: 1
  323. });
  324. }, 1500);
  325. } else {
  326. uni.showToast({
  327. icon: 'none',
  328. title: res.msg || '保存失败'
  329. });
  330. }
  331. }).catch(err => {
  332. uni.hideLoading();
  333. uni.showToast({
  334. icon: 'none',
  335. title: '网络错误,请重试'
  336. });
  337. });
  338. },
  339. async getUserInfo() {
  340. try {
  341. const res = await getUserInfo();
  342. if (res.code === 200 && res.user) {
  343. const user = res.user;
  344. // 设置用户信息
  345. this.userInfo = user;
  346. this.post = res.post;
  347. // 设置表单数据
  348. this.formData.avatar = user.avatar || '';
  349. this.formData.nickname = user.userName || '';
  350. this.formData.sex = user.sex || '';
  351. this.formData.deptId = user.deptId || '';
  352. this.formData.deptName = user.deptName || '';
  353. this.formData.deptIds = user.deptId ? [user.deptId] : [];
  354. this.formData.deptNames = user.deptName || '';
  355. // 处理岗位数据
  356. this.formData.postIds = user.postIds || [];
  357. this.formData.postNames = res.post ? res.post.join(', ') : '';
  358. // 处理产品数据
  359. this.formData.productCode = user.productCode || '';
  360. this.formData.productCodes = user.productCode ? user.productCode.split(',') : [];
  361. // 从productList中获取产品名称
  362. if (user.productList && user.productList.length > 0) {
  363. const productNames = user.productList.map(item => item.name).join(', ');
  364. this.formData.productNames = productNames;
  365. // 设置第一个产品作为默认值
  366. this.formData.productId = user.productList[0].code;
  367. this.formData.productName = user.productList[0].name;
  368. } else {
  369. this.formData.productNames = '';
  370. this.formData.productId = '';
  371. this.formData.productName = '';
  372. }
  373. this.formData.supervisorId = user.supervisorId || '';
  374. this.formData.supervisorName = user.supervisorName || '';
  375. this.formData.proofImages = user.certificationMaterials ? user.certificationMaterials.split(
  376. ',') : [];
  377. uni.setStorageSync('userInfo', user);
  378. // 加载产品数据
  379. this.loadData('product', null, 'code', 'name', 'code');
  380. // 加载岗位数据
  381. this.loadData('post', null, 'id', 'title', '');
  382. }
  383. } catch (err) {
  384. console.error('获取用户信息失败:', err);
  385. uni.showToast({
  386. icon: 'none',
  387. title: '获取用户信息失败'
  388. });
  389. }
  390. },
  391. chooseProofImages() {
  392. uni.chooseImage({
  393. count: 9,
  394. sizeType: ['compressed'],
  395. sourceType: ['album', 'camera'],
  396. success: (res) => {
  397. this.uploadImages(res.tempFilePaths);
  398. }
  399. });
  400. },
  401. async uploadImages(tempFilePaths) {
  402. try {
  403. uni.showLoading({
  404. title: '上传中...'
  405. });
  406. const uploadedImages = [];
  407. const baseUrl = uni.getStorageSync('requestPath');
  408. for (const tempFilePath of tempFilePaths) {
  409. const uploadRes = await new Promise((resolve, reject) => {
  410. uni.uploadFile({
  411. url: `${baseUrl}/system/file/upload`,
  412. filePath: tempFilePath,
  413. name: 'file',
  414. formData: {
  415. bizType: 'USER_INFO'
  416. },
  417. header: {
  418. 'AppToken': uni.getStorageSync('AppToken')
  419. },
  420. success: (res) => {
  421. const data = JSON.parse(res.data);
  422. if (data.code === 200) {
  423. resolve(data.data);
  424. } else {
  425. reject(new Error(data.message || '上传失败'));
  426. }
  427. },
  428. fail: (err) => {
  429. reject(err);
  430. }
  431. });
  432. });
  433. uploadedImages.push(uploadRes.fileUrl);
  434. }
  435. this.formData.proofImages = [...this.formData.proofImages, ...uploadedImages];
  436. uni.hideLoading();
  437. uni.showToast({
  438. icon: 'success',
  439. title: '上传成功'
  440. });
  441. } catch (e) {
  442. console.error('上传失败:', e);
  443. uni.hideLoading();
  444. uni.showToast({
  445. icon: 'none',
  446. title: '上传失败'
  447. });
  448. }
  449. },
  450. deleteProofImage(index) {
  451. this.formData.proofImages.splice(index, 1);
  452. },
  453. toSelect(type) {
  454. const typeNames = {
  455. dept: '部门',
  456. post: '岗位',
  457. product: '产品',
  458. superior: '业务员'
  459. };
  460. const list = this.getDataList(type);
  461. if (list && list.length > 0) {
  462. if (type === 'dept' || type === 'post' || type === 'product') {
  463. // 部门、岗位和产品使用多选弹窗
  464. this.multiSelectData = list.map(item => item.title);
  465. this.multiSelectTitle = typeNames[type];
  466. this.currentMultiSelectType = type;
  467. // 初始化已选项
  468. if (type === 'dept') {
  469. this.selectedItems = this.formData.deptNames ? this.formData.deptNames.split(', ') : [];
  470. } else if (type === 'post') {
  471. this.selectedItems = this.formData.postNames ? this.formData.postNames.split(', ') : [];
  472. } else if (type === 'product') {
  473. this.selectedItems = this.formData.productNames ? this.formData.productNames.split(', ') : [];
  474. }
  475. // 显示多选弹窗
  476. this.showMultiSelectPopup = true;
  477. } else {
  478. // 其他使用单选picker
  479. this.pickerList = list;
  480. this.pickerType = type;
  481. this.showPicker = true;
  482. }
  483. } else {
  484. uni.showToast({
  485. icon: 'none',
  486. title: `暂无${typeNames[type]}数据`
  487. });
  488. }
  489. },
  490. onPickerConfirm(e) {
  491. const selectedItem = e.value[0];
  492. console.log('selectedItem 值:', selectedItem);
  493. const fieldMap = {
  494. product: {
  495. id: 'productId',
  496. name: 'productName',
  497. code: 'productCode'
  498. },
  499. superior: {
  500. id: 'supervisorId',
  501. name: 'supervisorName'
  502. },
  503. gender: {
  504. id: 'sex'
  505. }
  506. };
  507. if (this.pickerType === 'gender') {
  508. this.formData.sex = selectedItem.id;
  509. } else {
  510. const fields = fieldMap[this.pickerType];
  511. this.formData[fields.id] = selectedItem.id;
  512. this.formData[fields.name] = selectedItem.title;
  513. if (fields.code) {
  514. this.formData[fields.code] = selectedItem.code || selectedItem.id;
  515. }
  516. }
  517. this.showPicker = false;
  518. },
  519. toSelectGender() {
  520. this.pickerList = [{
  521. id: '1',
  522. title: '男'
  523. },
  524. {
  525. id: '0',
  526. title: '女'
  527. }
  528. ];
  529. this.pickerType = 'gender';
  530. this.showPicker = true;
  531. },
  532. // 多选相关方法
  533. // 切换选项
  534. toggleItem(item) {
  535. const index = this.selectedItems.indexOf(item);
  536. if (index > -1) {
  537. // 取消选择
  538. this.selectedItems.splice(index, 1);
  539. } else {
  540. // 添加选择
  541. this.selectedItems.push(item);
  542. }
  543. },
  544. // 关闭多选弹窗
  545. closeMultiSelectPopup() {
  546. this.showMultiSelectPopup = false;
  547. },
  548. // 确认多选结果
  549. confirmMultiSelect() {
  550. const selectedNames = this.selectedItems.join(', ');
  551. const list = this.getDataList(this.currentMultiSelectType);
  552. // 根据选中的名称获取对应的id
  553. const selectedIds = list
  554. .filter(item => this.selectedItems.includes(item.title))
  555. .map(item => item.id);
  556. if (this.currentMultiSelectType === 'dept') {
  557. this.formData.deptNames = selectedNames;
  558. this.formData.deptIds = selectedIds;
  559. if (selectedIds.length > 0) {
  560. this.formData.deptId = selectedIds[0];
  561. this.formData.deptName = this.selectedItems[0];
  562. } else {
  563. this.formData.deptId = '';
  564. this.formData.deptName = '';
  565. }
  566. } else if (this.currentMultiSelectType === 'post') {
  567. this.formData.postNames = selectedNames;
  568. // 岗位数据直接使用名称数组
  569. this.formData.postIds = this.selectedItems;
  570. } else if (this.currentMultiSelectType === 'product') {
  571. this.formData.productNames = selectedNames;
  572. // 获取选中的产品代码
  573. const selectedCodes = list
  574. .filter(item => this.selectedItems.includes(item.title))
  575. .map(item => item.code || item.id);
  576. this.formData.productCodes = selectedCodes;
  577. if (selectedIds.length > 0) {
  578. this.formData.productId = selectedIds[0];
  579. this.formData.productName = this.selectedItems[0];
  580. this.formData.productCode = selectedCodes[0];
  581. } else {
  582. this.formData.productId = '';
  583. this.formData.productName = '';
  584. this.formData.productCode = '';
  585. }
  586. }
  587. this.showMultiSelectPopup = false;
  588. }
  589. }
  590. }
  591. </script>
  592. <style lang="scss" scoped>
  593. .container {
  594. background-color: #ffffff;
  595. min-height: 100vh;
  596. display: flex;
  597. flex-direction: column;
  598. position: relative;
  599. .content {
  600. width: 100%;
  601. height: 100%;
  602. position: relative;
  603. z-index: 2;
  604. .summary-section {
  605. padding: 32rpx 24rpx;
  606. display: flex;
  607. flex-direction: column;
  608. .summary-header {
  609. display: flex;
  610. align-items: center;
  611. margin-bottom: 24rpx;
  612. .summary-indicator {
  613. width: 6rpx;
  614. height: 32rpx;
  615. background: #388BFF;
  616. border-radius: 40rpx;
  617. margin-right: 16rpx;
  618. }
  619. .summary-title {
  620. font-size: 36rpx;
  621. font-weight: bold;
  622. color: #333;
  623. }
  624. }
  625. }
  626. .list {
  627. margin-bottom: 40rpx;
  628. .info-item {
  629. height: 104upx;
  630. padding: 0 30upx;
  631. display: flex;
  632. align-items: center;
  633. justify-content: space-between;
  634. border-bottom: 1px solid #F5F6FA;
  635. &:last-child {
  636. border-bottom: none;
  637. }
  638. .label {
  639. font-size: 30upx;
  640. font-family: PingFang SC;
  641. font-weight: 400;
  642. color: #0F1826;
  643. }
  644. .right {
  645. display: flex;
  646. align-items: center;
  647. justify-content: center;
  648. .txt {
  649. font-size: 28rpx;
  650. color: #333333;
  651. margin-right: 8rpx;
  652. }
  653. .ash {
  654. color: #C8C9CC;
  655. }
  656. .icon {
  657. width: 36rpx;
  658. height: 36rpx;
  659. }
  660. .head {
  661. border-radius: 50%;
  662. width: 80upx;
  663. height: 80upx;
  664. }
  665. .wx-head {
  666. position: absolute;
  667. width: 80upx;
  668. height: 80upx;
  669. opacity: 0;
  670. }
  671. .input {
  672. text-align: right;
  673. font-size: 30upx;
  674. font-family: PingFang SC;
  675. font-weight: 400;
  676. color: #0F1826;
  677. }
  678. }
  679. }
  680. .form-section {
  681. background: #fff;
  682. border-radius: 16rpx;
  683. padding: 24rpx;
  684. margin-bottom: 24rpx;
  685. .form-label {
  686. display: flex;
  687. align-items: center;
  688. font-size: 28rpx;
  689. color: #333;
  690. .tip {
  691. font-size: 24rpx;
  692. color: #999999;
  693. margin-left: 24rpx;
  694. }
  695. }
  696. .upload-proof {
  697. margin-top: 16rpx;
  698. .uploaded-images {
  699. display: flex;
  700. flex-wrap: wrap;
  701. gap: 20rpx;
  702. .uploaded-image-item {
  703. width: 160rpx;
  704. height: 160rpx;
  705. border-radius: 16rpx;
  706. position: relative;
  707. image {
  708. width: 100%;
  709. height: 100%;
  710. border-radius: 12rpx;
  711. }
  712. .image-delete {
  713. position: absolute;
  714. top: -12rpx;
  715. right: -12rpx;
  716. width: 40rpx;
  717. height: 40rpx;
  718. background: rgba(0, 0, 0, 0.6);
  719. color: #FFFFFF;
  720. border-radius: 50%;
  721. display: flex;
  722. align-items: center;
  723. justify-content: center;
  724. font-size: 32rpx;
  725. font-weight: bold;
  726. z-index: 1;
  727. }
  728. }
  729. }
  730. .upload-placeholder {
  731. width: 160rpx;
  732. height: 160rpx;
  733. background: #F7F8FA;
  734. border-radius: 16rpx;
  735. display: flex;
  736. flex-direction: column;
  737. align-items: center;
  738. justify-content: center;
  739. image {
  740. width: 48rpx;
  741. height: 48rpx;
  742. }
  743. }
  744. }
  745. }
  746. }
  747. }
  748. }
  749. .btn-box {
  750. margin-top: 20rpx;
  751. height: 120upx;
  752. padding: 0 30upx;
  753. display: flex;
  754. align-items: center;
  755. justify-content: center;
  756. .sub-btn {
  757. width: 100%;
  758. height: 88upx;
  759. line-height: 88upx;
  760. text-align: center;
  761. font-size: 30upx;
  762. font-family: PingFang SC;
  763. font-weight: bold;
  764. color: #FFFFFF;
  765. background: #388BFF;
  766. border-radius: 200rpx;
  767. }
  768. }
  769. /* 多选弹窗样式 */
  770. .u-popup-content {
  771. background: #fff;
  772. border-radius: 20rpx 20rpx 0 0;
  773. max-height: 80vh;
  774. display: flex;
  775. flex-direction: column;
  776. width: 100%;
  777. }
  778. .popup-header {
  779. display: flex;
  780. justify-content: space-between;
  781. align-items: center;
  782. padding: 32rpx;
  783. border-bottom: 1px solid #EBEDF0;
  784. }
  785. .popup-title {
  786. font-size: 32rpx;
  787. font-weight: bold;
  788. color: #333;
  789. }
  790. .popup-close {
  791. font-size: 40rpx;
  792. color: #999;
  793. cursor: pointer;
  794. }
  795. .popup-body {
  796. padding: 32rpx;
  797. flex: 1;
  798. overflow-y: auto;
  799. }
  800. .tag-group {
  801. display: flex;
  802. flex-wrap: wrap;
  803. gap: 16rpx;
  804. }
  805. .tag-item {
  806. padding: 16rpx 32rpx;
  807. border: 2rpx solid #DCDFE6;
  808. border-radius: 40rpx;
  809. font-size: 28rpx;
  810. color: #666;
  811. cursor: pointer;
  812. transition: all 0.3s;
  813. white-space: nowrap;
  814. }
  815. .tag-item.active {
  816. background: #388BFF;
  817. border-color: #388BFF;
  818. color: #fff;
  819. }
  820. .popup-footer {
  821. display: flex;
  822. }
  823. .popup-btn {
  824. flex: 1;
  825. gap: 20rpx;
  826. text-align: center;
  827. height: 88rpx;
  828. line-height: 88rpx;
  829. border: none;
  830. font-size: 28rpx;
  831. cursor: pointer;
  832. border-radius: 40rpx;
  833. }
  834. .cancel-btn {
  835. background: #fff;
  836. color: #666;
  837. border: 2rpx solid #DCDFE6;
  838. }
  839. .confirm-btn {
  840. background: #388BFF;
  841. color: #fff;
  842. }
  843. </style>