index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. <template>
  2. <div class="add-inst-wrap">
  3. <el-dialog
  4. v-model="state.isShowDialog"
  5. :title="title"
  6. width="90%"
  7. :close-on-click-modal="false"
  8. >
  9. <el-form
  10. ref="editFormRef"
  11. :model="state.form"
  12. label-width="100px"
  13. size="default"
  14. label-position="top"
  15. :rules="rules"
  16. closeable
  17. >
  18. <el-row>
  19. <el-col
  20. :span="24"
  21. class="mb24"
  22. >
  23. <el-form-item
  24. label="姓名"
  25. prop="userName"
  26. >
  27. <el-input
  28. :disabled="dialogType != 'add'"
  29. v-model="state.form.userName"
  30. @focus="openSelectUser"
  31. placeholder="请选择"
  32. ></el-input>
  33. <!-- <el-select
  34. :disabled="dialogType != 'add'"
  35. filterable
  36. style="width: 100%"
  37. v-model.number="state.form.curUser"
  38. @change="selectUser"
  39. value-key="userId"
  40. class="w100"
  41. placeholder="请选择"
  42. clearable
  43. >
  44. <el-option :label="v.userName" :value="v" :key="v.userId" v-for="v in userOptions" />
  45. </el-select> -->
  46. </el-form-item>
  47. </el-col>
  48. <el-col
  49. :span="24"
  50. class="mb10"
  51. >
  52. <el-form-item
  53. label="预约资格有效开始时间"
  54. prop="startTime"
  55. >
  56. <el-date-picker
  57. style="width: 100%"
  58. v-model="state.form.startTime"
  59. type="date"
  60. placeholder="选择预约资格有效开始时间"
  61. value-format="YYYY-MM-DD"
  62. clearable
  63. :disabledDate="disabledDateFn"
  64. />
  65. </el-form-item>
  66. </el-col>
  67. <el-col
  68. :span="24"
  69. class="mb10"
  70. >
  71. <el-form-item
  72. label="预约资格有效结束时间"
  73. prop="endTime"
  74. >
  75. <el-date-picker
  76. style="width: 100%"
  77. v-model="state.form.endTime"
  78. type="date"
  79. placeholder="选择预约资格有效结束时间"
  80. value-format="YYYY-MM-DD"
  81. clearable
  82. :disabledDate="disabledDateFn"
  83. />
  84. </el-form-item>
  85. </el-col>
  86. <el-col :span="24">
  87. <el-form-item
  88. label="上传资格证书"
  89. prop="fileList"
  90. >
  91. <el-upload
  92. :class="{ disUoloadSty: state.form.fileList.length > 0 }"
  93. v-model:file-list="state.form.fileList"
  94. :action="uploadUrl"
  95. :limit="1"
  96. style="width: 100%"
  97. accept=".jpg,.png"
  98. list-type="picture-card"
  99. :on-preview="handlePictureCardPreview"
  100. :before-upload="beforeAvatarFileUpload"
  101. :on-remove="(res, uploadFile: UploadFile) => handleRemove()"
  102. :on-success="(res, uploadFile: UploadFile) => handleSuccess(res, uploadFile)"
  103. >
  104. <el-icon><Plus /></el-icon>
  105. </el-upload>
  106. </el-form-item>
  107. </el-col>
  108. </el-row>
  109. </el-form>
  110. <template #footer>
  111. <span class="dialog-footer">
  112. <el-button
  113. @click="onCancel"
  114. type="info"
  115. size="default"
  116. >
  117. 取 消
  118. </el-button>
  119. <el-button
  120. type="primary"
  121. @click="subAdd"
  122. size="default"
  123. >
  124. 提 交
  125. </el-button>
  126. </span>
  127. </template>
  128. </el-dialog>
  129. <el-dialog v-model="dialogVisible">
  130. <img
  131. w-full
  132. :src="dialogImageUrl"
  133. alt="Preview Image"
  134. />
  135. </el-dialog>
  136. <SelectUser
  137. ref="selectUserRef"
  138. :multiple="false"
  139. @selectedUserData="selectedUserData"
  140. />
  141. </div>
  142. </template>
  143. <script setup lang="ts" name="addInstrument">
  144. import { reactive, ref, nextTick, defineAsyncComponent } from 'vue'
  145. import { ElMessage } from 'element-plus'
  146. import to from 'await-to-js'
  147. import type { UploadFile } from 'element-plus'
  148. import { useSystemApi } from '/@/api/instr/system'
  149. const systemApi = useSystemApi()
  150. import { useGrantApi } from '/@/api/instr/inst/grant'
  151. import { useGrantApplyApi } from '/@/api/instr/inst/grantApply'
  152. const grantApi = useGrantApi()
  153. const grantApplyApi = useGrantApplyApi()
  154. import { deepClone } from '/@/utils/other'
  155. import { storeToRefs } from 'pinia'
  156. import mittBus from '/@/utils/mitt'
  157. import { useUserInfo } from '/@/stores/userInfo'
  158. const SelectUser = defineAsyncComponent(() => import('/@/views/instr/component/selectUserByDept/index.vue'))
  159. // 向父组件传值/事件
  160. const emit = defineEmits(['refresh'])
  161. // 定义变量内容
  162. const selectUserRef = ref()
  163. const stores = useUserInfo()
  164. const { userInfos } = storeToRefs(stores)
  165. const uploadUrl = import.meta.env.VITE_UPLOAD
  166. const editFormRef = ref()
  167. const title = ref('')
  168. const dialogVisible = ref(false) //查看大图
  169. const dialogImageUrl = ref('') //查看大图
  170. const userOptions = ref<RowUserType[]>([]) //用户列表
  171. const dialogType = ref('') //编辑、新增
  172. const disabledDateFn = (time: any) => {
  173. // 获取当前日期的0点时间戳
  174. const today = new Date()
  175. today.setHours(0, 0, 0, 0)
  176. // 如果传递进来的日期时间戳小于今天的0点时间戳,则禁止选择
  177. return time.getTime() < today.getTime()
  178. }
  179. const state = reactive({
  180. isShowDialog: false,
  181. form: {
  182. id: 0,
  183. userId: null,
  184. userName: '',
  185. instCode: '', // 仪器编码
  186. instId: 0, //仪器ID
  187. instName: '', //仪器名称
  188. date: null,
  189. startTime: '',
  190. endTime: '',
  191. curtName: '',
  192. curtFile: '',
  193. fileList: [],
  194. curUser: {},
  195. },
  196. })
  197. const rules = reactive({
  198. userId: [{ required: true, message: '人员不能为空', trigger: 'blur' }],
  199. startTime: [{ required: true, message: '预约资格有效开始时间不能为空', trigger: 'blur' }],
  200. endTime: [{ required: true, message: '预约资格有效结束时间不能为空', trigger: 'blur' }],
  201. // fileList: [{ required: true, message: '资格证书不能为空', trigger: 'blur' }],
  202. })
  203. const selectUser = (data) => {
  204. console.log(data)
  205. state.form.userId = data.userId
  206. state.form.userName = data.userName
  207. }
  208. // 打开弹窗
  209. const openDialog = (type: 'add' | 'edit', row) => {
  210. console.log(row)
  211. state.isShowDialog = true
  212. getDicts()
  213. nextTick(() => {
  214. editFormRef.value.clearValidate()
  215. editFormRef.value.resetFields()
  216. state.form.fileList = []
  217. state.form.date = null
  218. state.form.curUser = []
  219. dialogType.value = type
  220. if (type == 'edit') {
  221. getInstDetail(row.id)
  222. title.value = '编辑人员授权'
  223. } else if (type == 'add') {
  224. title.value = '新增人员授权'
  225. state.form.instCode = row.instCode
  226. state.form.instId = row.id
  227. state.form.instName = row.instName
  228. } else if (type == 'personal') {
  229. title.value = '申请预约资格授权'
  230. state.form.instCode = row.instCode
  231. state.form.instId = row.id
  232. state.form.instName = row.instName
  233. state.form.userId = userInfos.value.id
  234. state.form.userName = userInfos.value.nickName
  235. state.form.curUser = { userId: userInfos.value.id, userName: userInfos.value.nickName }
  236. }
  237. })
  238. }
  239. // 获取仪器详情
  240. const getInstDetail = async (id) => {
  241. const [err, res]: ToResponse = await to(grantApi.detail({ id }))
  242. if (err) return
  243. if (res?.code == 200) {
  244. console.log(res.data)
  245. state.form = res.data
  246. state.form.fileList = [{ name: res.data.curtName, url: res.data.curtFile }]
  247. state.form.curUser = { userId: res.data.userId, userName: res.data.userName }
  248. state.form.date = res.data.startTime ? [res.data.startTime, res.data.endTime] : null
  249. }
  250. }
  251. const getDicts = () => {
  252. Promise.all([systemApi.getUserList({ noPage: true })]).then(([user]) => {
  253. userOptions.value = user.data.list.map((item: any) => ({ userId: item.id, userName: item.nickName }))
  254. })
  255. }
  256. // 关闭弹窗
  257. const closeDialog = () => {
  258. // 清空表单,此项需加表单验证才能使用
  259. editFormRef.value.resetFields()
  260. state.form.fileList = []
  261. state.form.curtName = ''
  262. state.form.curtFile = ''
  263. state.form.userId = null
  264. state.isShowDialog = false
  265. }
  266. const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
  267. dialogImageUrl.value = uploadFile.url!
  268. dialogVisible.value = true
  269. }
  270. const handleSuccess = (res: any, uploadFile: UploadFile) => {
  271. state.form.curtName = uploadFile.name
  272. state.form.curtFile = res?.Data || ''
  273. }
  274. const handleRemove = () => {
  275. state.form.fileList = []
  276. state.form.curtName = ''
  277. state.form.curtFile = ''
  278. }
  279. const beforeAvatarFileUpload = (file) => {
  280. let isLt10m = file.size / 1024 / 1024 / 20 < 1
  281. if (!isLt10m) {
  282. ElMessage.error('上传文件大小不能超过 20MB!')
  283. return false
  284. }
  285. return true
  286. }
  287. const openSelectUser = () => {
  288. const param = state.form.userId
  289. ? { checkedUser: [{ id: state.form.userId, name: state.form.userName }] }
  290. : undefined
  291. selectUserRef.value.openDialog(param)
  292. }
  293. const selectedUserData = (data) => {
  294. state.form.userId = data.user[0].id
  295. state.form.userName = data.user[0].name
  296. }
  297. // 取消
  298. const onCancel = () => {
  299. closeDialog()
  300. }
  301. // 创建
  302. const subAdd = () => {
  303. editFormRef.value.validate(async (valid: any, error: any) => {
  304. if (valid) {
  305. let params = deepClone(state.form)
  306. let post = grantApi.add
  307. if (dialogType.value == 'edit') {
  308. post = grantApi.update
  309. } else if (dialogType.value == 'add') {
  310. post = grantApi.add
  311. } else if (dialogType.value == 'personal') {
  312. post = grantApplyApi.add
  313. }
  314. const [err, res]: ToResponse = await to(post({ ...params }))
  315. if (err) return
  316. if (res?.code == 200) {
  317. ElMessage.success('提交成功')
  318. closeDialog()
  319. mittBus.emit('refresAuthOrApplicationList', 'myAuth')
  320. mittBus.emit('refresAuthOrApplicationList', 'auth')
  321. emit('refresh')
  322. }
  323. } else {
  324. Object.keys(error).forEach((key, i) => {
  325. if (i == 0) {
  326. const firstFiled = error[key][0].field
  327. editFormRef.value.scrollToField(firstFiled)
  328. }
  329. })
  330. }
  331. })
  332. }
  333. // 暴露方法
  334. defineExpose({
  335. openDialog,
  336. })
  337. </script>
  338. <style scoped lang="scss">
  339. .add-inst-wrap {
  340. // :deep(.el-dialog) {
  341. // border-radius: 8px;
  342. // }
  343. :deep(.el-dialog__header) {
  344. border-radius: 8px 8px 0 0;
  345. margin: 0;
  346. font-size: 16px;
  347. font-weight: bold;
  348. color: #333333;
  349. padding: 12px 0 12px 22px;
  350. }
  351. :deep(.el-dialog__headerbtn) {
  352. top: 0;
  353. }
  354. .add-inst-content {
  355. .r-form {
  356. padding: 10px 0 0 5px;
  357. .w100 {
  358. width: 100%;
  359. }
  360. .upload-box {
  361. :deep(.el-form-item__content) {
  362. justify-content: center;
  363. }
  364. :deep(.el-form-item__error) {
  365. width: 100%;
  366. text-align: center;
  367. margin-top: 40px;
  368. }
  369. .upload-btn {
  370. width: 200px;
  371. height: 200px;
  372. border-radius: 4px;
  373. border: 1px solid #cdcdcd;
  374. display: flex;
  375. flex-direction: column;
  376. align-items: center;
  377. justify-content: center;
  378. > p {
  379. font-size: 14px;
  380. color: #969696;
  381. margin-top: 30px;
  382. }
  383. }
  384. }
  385. .file-size-tips {
  386. text-align: center;
  387. margin-top: 8px;
  388. font-size: 16px;
  389. color: #969696;
  390. }
  391. .form-group {
  392. // padding: 15px 0 0 15px;
  393. .form-item-tit {
  394. font-size: 14px;
  395. font-weight: bold;
  396. color: #333333;
  397. position: relative;
  398. .form-item-icon {
  399. position: absolute;
  400. top: -2px;
  401. left: -5px;
  402. width: 12px;
  403. height: 12px;
  404. background: linear-gradient(138deg, #4e82ff 0%, #2c46ff 100%);
  405. opacity: 0.3;
  406. border-radius: 50%;
  407. }
  408. }
  409. .form-row-wrap {
  410. // padding-left: 27px;
  411. }
  412. }
  413. :deep(.el-form-item__label) {
  414. font-size: 14px;
  415. // font-weight: bold;
  416. color: #343a3f;
  417. }
  418. .report-link {
  419. background: #eef3fe;
  420. border-radius: 12px;
  421. padding: 0 12px;
  422. line-height: 24px;
  423. }
  424. }
  425. :deep(.el-input-number) {
  426. width: 90px;
  427. }
  428. .add-btn {
  429. width: 113px;
  430. height: 32px;
  431. color: #2c78ff;
  432. }
  433. }
  434. .algin-center {
  435. align-items: center;
  436. }
  437. }
  438. .disUoloadSty {
  439. :deep(.el-upload--picture-card) {
  440. display: none; /* 上传按钮隐藏 */
  441. }
  442. }
  443. </style>