index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <!--
  2. * @Author: wanglj wanglijie@dashoo.cn
  3. * @Date: 2025-03-11 18:02:10
  4. * @LastEditors: wanglj wanglijie@dashoo.cn
  5. * @LastEditTime: 2025-03-28 10:55:46
  6. * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. -->
  9. <template>
  10. <div class="app-container">
  11. <!-- <header>
  12. <div class="search">
  13. <input v-model="state.form.keyWord" type="text" placeholder="输入关键词进行搜索" />
  14. <button @click="onSearch">搜索</button>
  15. </div>
  16. <ul>
  17. <li>仪器设备</li>
  18. <li>技术平台</li>
  19. <li>技术服务</li>
  20. <li>审批流程</li>
  21. </ul>
  22. </header> -->
  23. <div class="card">
  24. <h4>常用功能</h4>
  25. <ul class="nav">
  26. <li @click="onRouterPush('/instr-list')" v-auth="'h5-home-instr'">
  27. <img src="../../assets/img/仪器预约.png" alt="" />
  28. <p>仪器预约</p>
  29. </li>
  30. <li @click="onRouterPush('/entry')" v-auth="'h5-home-entry'">
  31. <img src="../../assets/img/入室申请.png" alt="" />
  32. <p>入室管理</p>
  33. </li>
  34. <!-- <li>
  35. <img src="../../assets/img/入室预约.png" alt="" />
  36. <p>入室预约</p>
  37. </li> -->
  38. <!-- <li v-auth="'h5-home-tech'">
  39. <img src="../../assets/img/技术委托.png" alt="" />
  40. <p>技术委托</p>
  41. </li> -->
  42. <li @click="onRouterPush('/training')" v-auth="'h5-home-training'">
  43. <img src="../../assets/img/培训考试.png" alt="" />
  44. <p>培训考试</p>
  45. </li>
  46. <li @click="onRouterPush('/my-cage')" v-auth="'h5-home-animal'">
  47. <img src="../../assets/img/动物笼位.png" alt="" />
  48. <p>动物笼位</p>
  49. </li>
  50. <!-- <li v-auth="'h5-home-reagent'">
  51. <img src="../../assets/img/试剂耗材.png" alt="" />
  52. <p>试剂耗材</p>
  53. </li> -->
  54. <!-- <li v-auth="'h5-home-more'">
  55. <img src="../../assets/img/更多应用.png" alt="" />
  56. <p>更多应用</p>
  57. </li> -->
  58. <li @click="onRouterPush('/inst/repairReport/home')" v-if="userInfos.userRoles !== 'project_group_member'">
  59. <img src="../../assets/img/更多应用.png" alt="" />
  60. <p>仪器报修</p>
  61. </li>
  62. <li @click="onRouterPush('/lab/inspection/home')" v-if="userInfos.userRoles !== 'project_group_member'">
  63. <img src="../../assets/img/更多应用.png" alt="" />
  64. <p>巡检任务</p>
  65. </li>
  66. </ul>
  67. </div>
  68. <div class="swipe-con flex justify-between mt10">
  69. <van-swipe class="my-swipe" :autoplay="5000" :show-indicators="false" vertical height="30">
  70. <van-swipe-item v-for="item in noticeList" :key="item.id"
  71. @click="onRouterPush('/notice/detail', { id: item.id })">
  72. <div class="flex justify-between">
  73. <span>{{ `【${item.noticeType === '10' ? '公告' : '通知'}】${item.noticeTitle}` }}</span>
  74. <span class="time">{{ formatDate(new Date(item.noticeTime), 'YYYY-mm-dd') }}</span>
  75. </div>
  76. </van-swipe-item>
  77. </van-swipe>
  78. <a href="javascript:void(0);" @click="onRouterPush('/notice')">更多</a>
  79. </div>
  80. <!-- <div class="card">
  81. <h4>
  82. 审批流程
  83. <span class="link" @click="onRouterPush('/todo', { type: 'approval' })">全部审批>></span>
  84. </h4>
  85. <ul class="approval">
  86. <li v-for="item in approvalList" :key="item.id">
  87. <div class="flex justify-between">
  88. <van-text-ellipsis :content="`${item.instTitle}`" />
  89. <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
  90. </div>
  91. <div class="flex justify-between">
  92. <van-text-ellipsis :content="`您提交的${item.defName}正在审核`" />
  93. <a href="javascript:void(0);" @click="onRouterPush('/todo/approval-detail', { id: item.id })">查看详情</a>
  94. </div>
  95. </li>
  96. </ul>
  97. </div> -->
  98. <div class="card mb20">
  99. <h4>
  100. 培训通知
  101. <span class="link" @click="onRouterPush('/training')">全部培训>></span>
  102. </h4>
  103. <ul class="approval">
  104. <li v-for="item in trainingList" :key="item.id" @click="onRouterPush('/training/enroll', { state: item.id })">
  105. <div class="flex justify-between">
  106. <van-text-ellipsis :content="`${item.title}`" />
  107. <van-tag v-if="item.status === '40'">已结束</van-tag>
  108. <van-tag v-if="item.status !== '40' && item.applyStatus === '10'" type="warning">未报名</van-tag>
  109. <van-tag v-else-if="item.status !== '40' && item.applyStatus === '20'" type="primary">已报名</van-tag>
  110. </div>
  111. <footer class="flex justify-between">
  112. <span>发布人:{{ item.createdName }}</span>
  113. <span>{{ formatDate(new Date(item.startTime), 'YYYY-mm-dd') }}</span>
  114. </footer>
  115. </li>
  116. </ul>
  117. </div>
  118. </div>
  119. </template>
  120. <script name="home" lang="ts" setup>
  121. import to from 'await-to-js'
  122. import { formatDate } from '/@/utils/formatTime'
  123. import { onMounted, reactive, ref } from 'vue'
  124. import { useDictApi } from '/@/api/system/dict'
  125. import { useProApi } from '/@/api/project'
  126. import { useDeptApi } from '/@/api/system/dept'
  127. import { useExecutionApi } from '/@/api/execution'
  128. import { useNewsApi } from '/@/api/system/news'
  129. import { useUserApi } from '/@/api/system/user'
  130. import { useTrainingApi } from '/@/api/training'
  131. import { useRouter } from 'vue-router'
  132. import { useUserInfo } from '/@/stores/userInfo'
  133. import { useUserInfos } from '/@/hooks/useUserInfos'
  134. const { userInfos } = useUserInfos()
  135. const newsApi = useNewsApi()
  136. const dictApi = useDictApi()
  137. const proApi = useProApi()
  138. const deptApi = useDeptApi()
  139. const executionApi = useExecutionApi()
  140. const trainingApi = useTrainingApi()
  141. const approvalList = ref<any[]>([])
  142. const noticeList = ref<any[]>([])
  143. const trainingList = ref<any[]>([])
  144. const userTypeList = ref(<RowDicDataType[]>[])
  145. const userSexList = ref(<RowDicDataType[]>[])
  146. const userCertList = ref(<RowDicDataType[]>[])
  147. const deptData = ref(<any[]>[])
  148. const pjtList = ref(<any[]>[])
  149. const pjtTypeList = ref(<any[]>[])
  150. const router = useRouter()
  151. const state = reactive({
  152. form: {
  153. keyWord: ''
  154. }
  155. })
  156. const getDicts = () => {
  157. Promise.all([
  158. dictApi.getDictDataByType('sys_user_type'),
  159. dictApi.getDictDataByType('sys_com_sex'),
  160. dictApi.getDictDataByType('sys_user_certificate'),
  161. deptApi.getDeptTree(),
  162. proApi.getProjectGroupListForApp({ noPage: true }),
  163. dictApi.getDictDataByType('sci_pjt_level'),
  164. ]).then(([type, sex, cert, dept, pjt, pjtType]) => {
  165. userTypeList.value = type.data.values || []
  166. userSexList.value = sex.data.values || []
  167. userCertList.value = cert.data.values || []
  168. deptData.value = dept.data || []
  169. pjtList.value = pjt.data.list || []
  170. pjtTypeList.value = pjtType.data.values || []
  171. })
  172. }
  173. const getApprovalList = async () => {
  174. // todo 平台id参数需要删除
  175. const [err, res]: ToResponse = await to(executionApi.getOwApproveList({ platformId: 1000103, pageNum: 1, pageSize: 3 }))
  176. if (err) return
  177. approvalList.value = res?.data?.list || []
  178. }
  179. const getNotice = async () => {
  180. const [err, res]: ToResponse = await to(newsApi.getNoticeList())
  181. if (err) return
  182. noticeList.value = res?.data?.list || []
  183. }
  184. const getTrainingList = async () => {
  185. const [err, res]: ToResponse = await to(trainingApi.getListForUser({ pageNum: 1, pageSize: 3, isAll: "10" }))
  186. if (err) return
  187. trainingList.value = res?.data?.list || []
  188. }
  189. const onSearch = () => { }
  190. const onRouterPush = (val: string, params?: any) => {
  191. router.push({
  192. path: val,
  193. query: { ...params }
  194. })
  195. }
  196. onMounted(() => {
  197. getDicts()
  198. getApprovalList()
  199. getNotice()
  200. getTrainingList()
  201. useUserApi().updateUserWxInfo({
  202. wechatOpenId: localStorage.getItem('openId'),
  203. wechatUnionId: localStorage.getItem('unionId')
  204. })
  205. })
  206. </script>
  207. <style lang="scss" scoped>
  208. .app-container {
  209. header {
  210. padding: 12px;
  211. border-radius: 8px;
  212. background-color: #1c9bfd;
  213. margin-top: 10px;
  214. .search {
  215. display: flex;
  216. border-radius: 4px;
  217. overflow: hidden;
  218. height: 28px;
  219. input {
  220. flex: 1;
  221. outline: none;
  222. border: none;
  223. padding: 0 10px;
  224. line-height: 28px;
  225. }
  226. button {
  227. border: none;
  228. border-left: 1px solid #d7d7d7;
  229. background-color: #fff;
  230. color: #3c78e5;
  231. padding: 0 20px;
  232. font-weight: bold;
  233. }
  234. }
  235. ul {
  236. display: flex;
  237. justify-content: space-between;
  238. margin-top: 10px;
  239. li {
  240. color: #fff;
  241. height: 14px;
  242. line-height: 14px;
  243. &::before {
  244. display: inline-block;
  245. content: '';
  246. height: 14px;
  247. width: 14px;
  248. border-radius: 9px;
  249. background-color: #ffff;
  250. vertical-align: top;
  251. margin-right: 2px;
  252. }
  253. }
  254. }
  255. }
  256. .card {
  257. min-height: 60px;
  258. margin-top: 10px;
  259. border-radius: 4px;
  260. background-color: #fff;
  261. padding: 10px;
  262. box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  263. h4 {
  264. height: 18px;
  265. line-height: 18px;
  266. display: flex;
  267. span {
  268. font-weight: normal;
  269. margin-left: auto;
  270. }
  271. &::before {
  272. display: inline-block;
  273. content: '';
  274. width: 3px;
  275. height: 18px;
  276. background-color: #1c9bfd;
  277. margin-right: 4px;
  278. vertical-align: middle;
  279. }
  280. }
  281. .time {
  282. color: #f69a4d;
  283. }
  284. .nav {
  285. display: flex;
  286. margin: 10px 0;
  287. flex-wrap: wrap;
  288. font-weight: normal;
  289. li {
  290. flex: 0 0 25%;
  291. display: flex;
  292. flex-direction: column;
  293. align-items: center;
  294. justify-content: center;
  295. &:nth-child(n + 5) {
  296. margin-top: 10px;
  297. }
  298. img {
  299. height: 48px;
  300. width: 48px;
  301. margin-bottom: 4px;
  302. }
  303. }
  304. }
  305. .approval {
  306. li {
  307. padding: 10px;
  308. background-color: #f9f9f9;
  309. margin-top: 8px;
  310. border-bottom: 1px solid #ecf5fa;
  311. .van-text-ellipsis {
  312. font-weight: bold;
  313. font-size: 16px;
  314. }
  315. footer {
  316. color: #969799;
  317. }
  318. a {
  319. color: #3c78e3;
  320. }
  321. }
  322. }
  323. }
  324. .swipe-con {
  325. display: flex;
  326. .my-swipe {
  327. flex: 1;
  328. height: 30px !important;
  329. line-height: 30px !important;
  330. :deep(.flex) {
  331. height: 30px;
  332. overflow: hidden;
  333. span {
  334. display: inline-block;
  335. height: 30px;
  336. line-height: 30px;
  337. }
  338. span:first-child {
  339. flex: 1;
  340. white-space: nowrap;
  341. overflow: hidden;
  342. text-overflow: ellipsis;
  343. }
  344. }
  345. }
  346. >a {
  347. flex: 0 0 28px;
  348. margin-left: 4px;
  349. color: #3c78e3;
  350. }
  351. }
  352. }
  353. </style>