userInfo.vue 23 KB

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