TransferDialog.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <template>
  2. <u-popup
  3. :show="visible"
  4. mode="bottom"
  5. :round="10"
  6. :closeOnClickOverlay="false"
  7. @close="close"
  8. class="transfer-popup"
  9. >
  10. <view class="popup-content">
  11. <!-- 头部 -->
  12. <view class="popup-header">
  13. <text class="popup-title">转派</text>
  14. <view class="close-btn" @click="close">×</view>
  15. </view>
  16. <!-- 表单 -->
  17. <scroll-view class="popup-body" scroll-y>
  18. <view class="card-a">
  19. <view class="card-section">
  20. <u--form
  21. labelPosition="left"
  22. labelWidth="160rpx"
  23. :model="form"
  24. :rules="rules"
  25. ref="formRef"
  26. >
  27. <!-- 执行部门 -->
  28. <u-form-item label="执行部门" prop="groupId" borderBottom required>
  29. <view class="picker-value" @click="showDeptPicker">
  30. {{ form.groupName || '请选择部门' }}
  31. </view>
  32. <u-icon slot="right" name="arrow-right" />
  33. </u-form-item>
  34. <!-- 执行人员 -->
  35. <u-form-item label="执行人员" prop="qualityId" borderBottom required>
  36. <view class="picker-value" @click="showUserPicker">
  37. {{ form.qualityName || '请选择人员' }}
  38. </view>
  39. <u-icon slot="right" name="arrow-right" />
  40. </u-form-item>
  41. </u--form>
  42. </view>
  43. </view>
  44. </scroll-view>
  45. <!-- 底部按钮 -->
  46. <view class="popup-footer">
  47. <u-button type="default" @click="close">取消</u-button>
  48. <u-button type="primary" @click="confirm" :loading="loading">确定</u-button>
  49. </view>
  50. </view>
  51. <!-- 部门树选择器 -->
  52. <ba-tree-picker
  53. ref="treePicker"
  54. key="dept"
  55. :multiple="false"
  56. @select-change="onDeptConfirm"
  57. title="选择部门"
  58. :localdata="deptList"
  59. valueKey="id"
  60. textKey="name"
  61. />
  62. <!-- 人员列表选择器(u-picker 单列) -->
  63. <u-picker
  64. :show="showUserPickerFlag"
  65. :columns="[userOptions]"
  66. keyName="label"
  67. @confirm="onUserConfirm"
  68. @cancel="showUserPickerFlag = false"
  69. />
  70. <u-toast ref="uToast" />
  71. </u-popup>
  72. </template>
  73. <script>
  74. import baTreePicker from '@/components/ba-tree-picker/ba-tree-picker.vue';
  75. import { transferQualityWork } from '@/api/inspectionWork';
  76. import { listOrganizations,getUserPage } from '@/api/common';
  77. export default {
  78. components: {
  79. baTreePicker,
  80. },
  81. data() {
  82. return {
  83. visible: false,
  84. loading: false,
  85. // 表单数据
  86. form: {
  87. id: '', // 质检工单ID
  88. groupId: '',
  89. groupName: '',
  90. qualityId: '',
  91. qualityName: '',
  92. },
  93. // 部门列表(树形)
  94. deptList: [],
  95. // 当前部门下的人员列表
  96. userList: [],
  97. // 人员选择器显示
  98. showUserPickerFlag: false,
  99. // 表单校验规则
  100. rules: {
  101. groupId: {
  102. type: 'string',
  103. required: true,
  104. message: '请选择执行部门',
  105. trigger: ['change'],
  106. },
  107. qualityId: {
  108. type: 'string',
  109. required: true,
  110. message: '请选择执行人员',
  111. trigger: ['change'],
  112. },
  113. },
  114. };
  115. },
  116. computed: {
  117. // 人员选择器选项
  118. userOptions() {
  119. return this.userList.map((item) => ({
  120. label: item.name,
  121. value: item.id,
  122. }));
  123. },
  124. },
  125. methods: {
  126. // ===== 外部调用 =====
  127. open(id) {
  128. this.visible = true;
  129. this.form.id = id || '';
  130. this.initData();
  131. },
  132. // ===== 关闭弹窗 =====
  133. close() {
  134. this.visible = false;
  135. this.resetForm();
  136. this.showUserPickerFlag = false;
  137. },
  138. // ===== 重置表单 =====
  139. resetForm() {
  140. this.form = {
  141. id: '',
  142. groupId: '',
  143. groupName: '',
  144. qualityId: '',
  145. qualityName: '',
  146. };
  147. this.userList = [];
  148. if (this.$refs.formRef) {
  149. this.$refs.formRef.clearValidate();
  150. }
  151. },
  152. // ===== 初始化数据 =====
  153. async initData() {
  154. try {
  155. // 1. 加载部门树
  156. const res = await listOrganizations({});
  157. this.deptList = res || [];
  158. // 2. 默认使用当前用户的部门
  159. const userInfo = uni.getStorageSync('userInfo') || {};
  160. const defaultGroupId = userInfo.groupId;
  161. if (defaultGroupId) {
  162. this.form.groupId = defaultGroupId;
  163. // 查找部门名称
  164. const findDept = (list) => {
  165. for (const item of list) {
  166. if (item.id === defaultGroupId) return item.name;
  167. if (item.children) {
  168. const found = findDept(item.children);
  169. if (found) return found;
  170. }
  171. }
  172. return null;
  173. };
  174. this.form.groupName = findDept(this.deptList) || '';
  175. // 加载该部门下的人员
  176. await this.loadUsers(defaultGroupId);
  177. }
  178. } catch (e) {
  179. console.error('初始化失败', e);
  180. }
  181. },
  182. // ===== 加载部门下的人员 =====
  183. async loadUsers(groupId) {
  184. if (!groupId) {
  185. this.userList = [];
  186. this.form.qualityId = '';
  187. this.form.qualityName = '';
  188. return;
  189. }
  190. try {
  191. const res = await getUserPage({
  192. pageNum: 1,
  193. size: -1,
  194. groupId: groupId,
  195. });
  196. this.userList = res.list || [];
  197. // 清空已选人员
  198. this.form.qualityId = '';
  199. this.form.qualityName = '';
  200. } catch (e) {
  201. console.error('加载人员失败', e);
  202. this.userList = [];
  203. }
  204. },
  205. // ===== 部门选择 =====
  206. showDeptPicker() {
  207. this.$refs.treePicker._show();
  208. },
  209. onDeptConfirm(data) {
  210. const id = data[0];
  211. if (!id) return;
  212. this.form.groupId = id;
  213. // 查找部门名称
  214. const findDept = (list) => {
  215. for (const item of list) {
  216. if (item.id === id) return item.name;
  217. if (item.children) {
  218. const found = findDept(item.children);
  219. if (found) return found;
  220. }
  221. }
  222. return null;
  223. };
  224. this.form.groupName = findDept(this.deptList) || '';
  225. // 加载人员
  226. this.loadUsers(id);
  227. // 触发校验
  228. if (this.$refs.formRef) {
  229. this.$refs.formRef.validateField('groupId');
  230. }
  231. },
  232. // ===== 人员选择 =====
  233. showUserPicker() {
  234. if (!this.form.groupId) {
  235. this.$refs.uToast.show({
  236. type: 'warning',
  237. message: '请先选择执行部门',
  238. });
  239. return;
  240. }
  241. if (this.userList.length === 0) {
  242. this.$refs.uToast.show({
  243. type: 'warning',
  244. message: '该部门暂无人员',
  245. });
  246. return;
  247. }
  248. this.showUserPickerFlag = true;
  249. },
  250. onUserConfirm(e) {
  251. const value = e.value[0]?.value;
  252. const label = e.value[0]?.label;
  253. if (value) {
  254. this.form.qualityId = value;
  255. this.form.qualityName = label;
  256. if (this.$refs.formRef) {
  257. this.$refs.formRef.validateField('qualityId');
  258. }
  259. }
  260. this.showUserPickerFlag = false;
  261. },
  262. // ===== 提交 =====
  263. async confirm() {
  264. try {
  265. await this.$refs.formRef.validate();
  266. this.loading = true;
  267. await transferQualityWork({
  268. id: this.form.id,
  269. groupId: this.form.groupId,
  270. groupName: this.form.groupName,
  271. qualityId: this.form.qualityId,
  272. qualityName: this.form.qualityName,
  273. });
  274. this.$refs.uToast.show({
  275. type: 'success',
  276. message: '转派成功',
  277. });
  278. this.$emit('success', this.form.id);
  279. this.close();
  280. } catch (e) {
  281. if (e && e.message) {
  282. this.$refs.uToast.show({
  283. type: 'error',
  284. message: e.message,
  285. });
  286. }
  287. } finally {
  288. this.loading = false;
  289. }
  290. },
  291. },
  292. };
  293. </script>
  294. <style lang="scss" scoped>
  295. .transfer-popup {
  296. /deep/ .u-popup__content {
  297. border-radius: 32rpx 32rpx 0 0;
  298. overflow: hidden;
  299. }
  300. }
  301. .popup-content {
  302. height: 50vh;
  303. display: flex;
  304. flex-direction: column;
  305. background: #eff2f7;
  306. }
  307. .popup-header {
  308. display: flex;
  309. justify-content: space-between;
  310. align-items: center;
  311. padding: 30rpx 32rpx;
  312. background: #fff;
  313. border-bottom: 2rpx solid #eef2f6;
  314. flex-shrink: 0;
  315. .popup-title {
  316. font-size: 36rpx;
  317. font-weight: bold;
  318. color: #1f2b3c;
  319. }
  320. .close-btn {
  321. width: 60rpx;
  322. height: 60rpx;
  323. display: flex;
  324. align-items: center;
  325. justify-content: center;
  326. font-size: 52rpx;
  327. color: #8e9aae;
  328. line-height: 1;
  329. }
  330. }
  331. .popup-body {
  332. flex: 1;
  333. overflow-y: auto;
  334. padding: 24rpx;
  335. }
  336. .card-a {
  337. background: #fff;
  338. border-radius: 48rpx;
  339. box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.05);
  340. overflow: hidden;
  341. }
  342. .card-section {
  343. padding: 30rpx 32rpx;
  344. }
  345. /deep/ .u-form-item {
  346. padding: 12rpx 0;
  347. .u-form-item__body__left__label {
  348. font-size: 26rpx !important;
  349. color: #6c7a91 !important;
  350. }
  351. .u-form-item__body__right__content {
  352. .u-input__content__field-wrapper__field {
  353. font-size: 26rpx !important;
  354. text-align: right;
  355. }
  356. }
  357. }
  358. .picker-value {
  359. font-size: 26rpx;
  360. color: #1e2a3a;
  361. padding: 8rpx 0;
  362. text-align: right;
  363. flex: 1;
  364. min-height: 44rpx;
  365. }
  366. .popup-footer {
  367. display: flex;
  368. padding: 16rpx 24rpx;
  369. background: #fff;
  370. border-top: 2rpx solid #eef2f6;
  371. gap: 16rpx;
  372. flex-shrink: 0;
  373. /deep/ .u-button {
  374. flex: 1;
  375. border-radius: 48rpx;
  376. height: 72rpx;
  377. }
  378. }
  379. </style>