You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

803 lines
20 KiB

  1. <template>
  2. <view id="home" class="page">
  3. <!-- 顶部搜索栏 -->
  4. <view class="header text-white">
  5. <!-- #ifndef H5 -->
  6. <l-icon
  7. @click="scanClick"
  8. type="scan"
  9. color="white"
  10. class="header-left text-xxl margin-left-sm"
  11. />
  12. <!-- #endif -->
  13. <view @click="moreClick(1)" class="search header-mid margin-lr-sm">
  14. <l-icon type="search" color="white" class="margin-lr-sm" />
  15. 搜索更多应用
  16. </view>
  17. <!-- <l-icon
  18. @click="msgClick"
  19. type="mail"
  20. color="white"
  21. class="header-right text-xxl margin-right-sm"
  22. /> -->
  23. </view>
  24. <!-- 轮播图片 -->
  25. <swiper v-if="imgCount > 0" style="height: 120px">
  26. <swiper-item
  27. v-for="(item, index) of imgData"
  28. :key="index"
  29. style="height: 120px"
  30. >
  31. <image
  32. :src="item"
  33. mode="aspectFill"
  34. style="height: 100%; width: 100%"
  35. ></image>
  36. </swiper-item>
  37. </swiper>
  38. <!-- 功能宫格列表 -->
  39. <view class="col-4 function-list cu-list grid no-border">
  40. <view
  41. v-for="(item, index) in funcListDisplay"
  42. @click="funcListClick(item)"
  43. :key="index"
  44. class="cu-item text-center flex flex-wrap justify-center align-center"
  45. >
  46. <view
  47. class="app-item align-center flex flex-wrap justify-center align-center"
  48. style="position: relative;"
  49. >
  50. <l-icon :type="item.icon" color="white" class="text-sl" />
  51. <view v-if="item.F_Name == '我的审批'&&CornerMarker" class="CornerMarker">
  52. {{CornerMarker}}
  53. </view>
  54. </view>
  55. <text>{{ item.F_Name }}</text>
  56. </view>
  57. </view>
  58. <view class="text-center bg-white margin-bottom"
  59. ><text
  60. @click="moreClick(0)"
  61. class="function-more-btn margin-tb-sm text-gray"
  62. >更多应用</text
  63. ></view
  64. >
  65. <!-- 统计数据宫格 -->
  66. <l-title>统计数据</l-title>
  67. <view class="count-list cu-list grid col-3 margin-bottom">
  68. <view
  69. v-for="(item, index) in countData"
  70. :key="index"
  71. class="cu-item text-center"
  72. >
  73. <text class="margin-bottom-xs">{{ item.title || "(未命名)" }}</text>
  74. <text class="count-item-value">{{ item.value || "-" }}</text>
  75. </view>
  76. </view>
  77. <!-- 通知列表区块 -->
  78. <view
  79. v-for="(block, blockIndex) of noticeData"
  80. :key="blockIndex"
  81. class="margin-bottom"
  82. >
  83. <!-- <view class="margin-top"></view> -->
  84. <l-title class="hasmoreBtn" @click="informClick(block.title)">
  85. {{ block.title }}
  86. <l-icon
  87. type="more"
  88. color="black"
  89. class="moreBtn header-left text-xxl margin-left"
  90. />
  91. </l-title>
  92. <l-list>
  93. <l-list-item
  94. v-for="(item, i) of block.content"
  95. @click="noticeClick(item)"
  96. :key="i"
  97. arrow
  98. >
  99. {{ item.f_title || "(无标题)" }}
  100. <l-tag slot="action" line="gray">{{
  101. postDateTime(item.f_time)
  102. }}</l-tag>
  103. </l-list-item>
  104. </l-list>
  105. </view>
  106. <!-- 图表区块 -->
  107. <view v-for="item of chartData" :key="item.id" class="margin-bottom">
  108. <l-title>{{ item.title }}</l-title>
  109. <view
  110. :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  111. class="chart-list"
  112. >
  113. <!--#ifndef MP-ALIPAY -->
  114. <canvas
  115. v-if="item.type === 1"
  116. @tap="chartTap(item, $event)"
  117. @touchstart="touchStart(item.id, $event)"
  118. @touchmove="touchMove(item.id, $event)"
  119. @touchend="touchEnd(item.id, $event)"
  120. :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  121. :canvas-id="item.id"
  122. :id="item.id"
  123. class="charts"
  124. ></canvas>
  125. <canvas
  126. v-else
  127. @tap="chartTap(item, $event)"
  128. :style="{ width: cWidth + 'px', height: cHeight + 'px' }"
  129. :canvas-id="item.id"
  130. :id="item.id"
  131. class="charts"
  132. ></canvas>
  133. <!--#endif -->
  134. <!-- 阿里小程序,需要以2倍尺寸显示,然后缩放为50% -->
  135. <!--#ifdef MP-ALIPAY -->
  136. <canvas
  137. v-if="item.type === 1"
  138. @tap="chartTap(item, $event)"
  139. @touchstart="touchStart(item.id, $event)"
  140. @touchmove="touchMove(item.id, $event)"
  141. @touchend="touchEnd(item.id, $event)"
  142. :style="{
  143. width: cWidth * pixelRatio + 'px',
  144. height: cHeight * pixelRatio + 'px',
  145. transform: 'scale(' + 1 / pixelRatio + ')',
  146. marginLeft: (-cWidth * (pixelRatio - 1)) / 2 + 'px',
  147. marginTop: (-cHeight * (pixelRatio - 1)) / 2 + 'px',
  148. }"
  149. :canvas-id="item.id"
  150. :id="item.id"
  151. class="charts"
  152. ></canvas>
  153. <canvas
  154. v-else
  155. @tap="chartTap(item, $event)"
  156. :style="{
  157. width: cWidth * pixelRatio + 'px',
  158. height: cHeight * pixelRatio + 'px',
  159. transform: 'scale(' + 1 / pixelRatio + ')',
  160. marginLeft: (-cWidth * (pixelRatio - 1)) / 2 + 'px',
  161. marginTop: (-cHeight * (pixelRatio - 1)) / 2 + 'px',
  162. }"
  163. :canvas-id="item.id"
  164. :id="item.id"
  165. class="charts"
  166. ></canvas>
  167. <!--#endif -->
  168. </view>
  169. </view>
  170. <tabBar selectedIndex='/pages/home'></tabBar>
  171. </view>
  172. </template>
  173. <script>
  174. import moment from "moment";
  175. import mapValues from "lodash/mapValues";
  176. import uCharts from "@/common/u-charts.js";
  177. // 用于保存图表操作对象
  178. let chartsObject = {};
  179. let chartsConfig = {};
  180. export default {
  181. data() {
  182. return {
  183. imgData: [],
  184. listData: [],
  185. myList: [],
  186. countData: [],
  187. noticeData: [],
  188. chartData: [],
  189. pixelRatio: 1,
  190. cWidth: "",
  191. cHeight: "",
  192. chartsFontSize: 10,
  193. ready:false,
  194. CornerMarker:''
  195. };
  196. },
  197. async onLoad(param) {
  198. await this.init(param);
  199. },
  200. async onShow() {
  201. uni.hideTabBar()
  202. if(this.ready){
  203. this.ready = false
  204. await this.refresh()
  205. this.ready = true
  206. }
  207. },
  208. // 本页面开启下拉刷新,用于刷新首页数据
  209. onPullDownRefresh() {
  210. this.refresh().then(() => {
  211. this.TOAST("已更新首页数据");
  212. uni.stopPullDownRefresh();
  213. });
  214. },
  215. methods: {
  216. // 页面初始化
  217. async init(param) {
  218. this.HIDE_LOADING();
  219. // 有参数表示可能是打开分享消息;将数据存入全局变量以备后续跳转
  220. if (param && param.learun && param.pagePath) {
  221. this.SET_GLOBAL("jumpParam", param);
  222. }
  223. // 登录状态无效,则跳转到登录页
  224. const stateValid = await this.checkLoginState();
  225. if (!stateValid) {
  226. this.RELAUNCH_TO("/pages/login");
  227. return;
  228. }
  229. // 图表相关参数初始化
  230. this.initCharts();
  231. // 加载页面数据和全局数据
  232. await this.FETCH_CLIENTDATA();
  233. await this.refresh();
  234. this.ready = true
  235. // 监听「我的应用」列表修改
  236. this.ON("home-list-change", () => {
  237. this.HTTP_GET("learun/adms/function/mylist").then((newList) => {
  238. this.myList = newList;
  239. });
  240. });
  241. this.SET_STORAGE("nextTime", null);
  242. // 处理小程序分享消息跳转
  243. // #ifdef MP
  244. const jumpParam = this.GET_GLOBAL("jumpParam");
  245. if (jumpParam) {
  246. this.SET_GLOBAL("jumpParam", null);
  247. this.MP_SHARE_DECODE(jumpParam);
  248. }
  249. // #endif
  250. //微信推送跳转
  251. if (!!param.page) {
  252. //通知公告
  253. if (param.page == "notice") {
  254. this.NAV_TO("/pages/LR_OAModule/list");
  255. }
  256. //邮件
  257. if (param.page == "mail") {
  258. this.NAV_TO("/pages/EducationalAdministration/SYS_ReceiveMessage/list");
  259. }
  260. //OA待办
  261. if (param.page == "oa") {
  262. this.NAV_TO("/pages/nworkflow/myflow/list");
  263. }
  264. //公文下发
  265. if (param.page == "file") {
  266. this.NAV_TO("/pages/EducationalAdministration/Sys_ReceiveFile/list");
  267. }
  268. return;
  269. }
  270. },
  271. // 验证登录状态
  272. async checkLoginState() {
  273. const token = this.GET_GLOBAL("token") || uni.getStorageSync("token");
  274. if (!token || token === "null" || token === "undefined") {
  275. this.RELAUNCH_TO("/pages/login");
  276. this.HIDE_LOADING();
  277. return false;
  278. }
  279. this.SET_GLOBAL("token", token);
  280. // 判断是否有 loginUser 对象
  281. if (this.GET_GLOBAL("loginUser")) {
  282. return true;
  283. }
  284. // 拉取用户信息验证登录态;如果失败则跳转至登录页
  285. const userInfo = await this.HTTP_GET("learun/adms/user/info");
  286. if (!userInfo) {
  287. this.SET_GLOBAL("token", null);
  288. this.SET_STORAGE("token", null);
  289. return false;
  290. }
  291. // 有登录态,则设置用户信息和全局数据
  292. const { baseinfo, mpinfo, post, role } = userInfo;
  293. const user = { ...baseinfo, post, role };
  294. if (mpinfo && Array.isArray(mpinfo) && mpinfo.includes(this.PLATFORM)) {
  295. user.miniProgram = true;
  296. }
  297. this.SET_GLOBAL("loginUser", user);
  298. return true;
  299. },
  300. // 刷新首页数据
  301. async refresh() {
  302. // 清空页面数据
  303. chartsObject = {};
  304. this.imgData = [];
  305. this.listData = [];
  306. this.myList = [];
  307. this.countData = [];
  308. this.noticeData = [];
  309. this.chartData = [];
  310. // 同时发出请求,获取轮播图、所有功能列表、我的功能列表、商机通知提醒图表数据
  311. // 商机、通知提醒图表数据,获取的是数据ID,所以还需要进一步请求
  312. const [imgData, listData, myList, settingData] = await Promise.all([
  313. this.HTTP_GET(
  314. "learun/adms/desktop/imgid" +
  315. (this.CONFIG("isDistributed") == true ? "?isDistributed=true" : "")
  316. ),
  317. this.HTTP_GET("learun/adms/function/list").then(
  318. (result) => result.data
  319. ),
  320. this.HTTP_GET("learun/adms/function/mylist"),
  321. this.HTTP_GET("learun/adms/desktop/setting").then(
  322. (result) => result.data
  323. ),
  324. ]);
  325. this.imgData = imgData;
  326. if (!this.CONFIG("isDistributed"))
  327. // 轮播图需要加上 url 前缀
  328. this.imgData = imgData.map((t) => this.API + `/desktop/img?data=${t}`);
  329. // 功能区按钮需要处理 icon
  330. this.listData = listData.map((item) => {
  331. const icon = item.F_Icon
  332. ? item.F_Icon.replace(`iconfont icon-`, ``)
  333. : "";
  334. const existsIcon = this.getUiIcons().some((t) => t === icon);
  335. return {
  336. ...item,
  337. icon: existsIcon ? icon : "roundright",
  338. };
  339. });
  340. // 我的应用列表需要过滤掉不存在的按钮
  341. this.myList = myList.filter((t) =>
  342. listData.find((item) => item.F_Id === t)
  343. );
  344. // 发出请求,获取商机信息、消息通知信息、图表信息;三类数据全部同时请求
  345. await Promise.all([
  346. ...settingData.target.map((item) =>
  347. this.HTTP_GET("learun/adms/desktop/data", {
  348. type: "Target",
  349. id: item.F_Id,
  350. }).then((data) => {
  351. if (data && data.value) {
  352. const { value } = data;
  353. this.countData.push({
  354. title: item.F_Name,
  355. value,
  356. });
  357. }
  358. })
  359. ),
  360. ...settingData.list.map((item) =>
  361. this.HTTP_GET("learun/adms/desktop/data", {
  362. type: "list",
  363. id: item.F_Id,
  364. }).then((data) => {
  365. if (data && data.value) {
  366. let { value } = data;
  367. if(value&&value.length){
  368. value = value.sort((a,b)=>{
  369. return new Date(b.f_time).valueOf() - new Date(a.f_time).valueOf()
  370. })
  371. }
  372. if(item.F_Name == "待办事项"){
  373. this.CornerMarker = value.length||'0'
  374. }
  375. this.noticeData.push({
  376. title: item.F_Name,
  377. F_Sort:item.F_Sort,
  378. content: value,
  379. });
  380. this.noticeData = this.noticeData.sort((a,b)=>a.F_Sort - b.F_Sort)
  381. }
  382. })
  383. ),
  384. ...settingData.chart.map((item) =>
  385. this.HTTP_GET("learun/adms/desktop/data", {
  386. type: "chart",
  387. id: item.F_Id,
  388. }).then((data) => {
  389. if (data && data.value) {
  390. const { value } = data;
  391. this.chartData.push({
  392. title: item.F_Name,
  393. value,
  394. id: item.F_Id,
  395. type: item.F_Type,
  396. });
  397. }
  398. })
  399. ),
  400. ]);
  401. // 渲染图表
  402. this.chartData.forEach((item) => {
  403. // 根据 item.type 的值选用对应的图表初始化配置项
  404. // 0=环形图;1=折线图;2=柱状图
  405. const charts = new uCharts(
  406. [
  407. {
  408. ...chartsConfig,
  409. canvasId: item.id,
  410. type: "ring",
  411. series: item.value.map((t) => ({
  412. name: t.name,
  413. data: t.value,
  414. })),
  415. extra: {
  416. pie: {
  417. offsetAngle: -45,
  418. ringWidth: 20,
  419. labelWidth: 15,
  420. },
  421. },
  422. legend: {
  423. lineHeight: 20,
  424. },
  425. },
  426. {
  427. ...chartsConfig,
  428. canvasId: item.id,
  429. type: "line",
  430. series: [
  431. {
  432. name: item.title,
  433. data: item.value.map((t) => t.value),
  434. },
  435. ],
  436. categories: item.value.map((t) => t.name),
  437. extra: {
  438. line: {
  439. type: "straight",
  440. },
  441. },
  442. xAxis: {
  443. rotateLabel: true,
  444. fontSize: this.chartsFontSize,
  445. itemCount: 8,
  446. },
  447. enableScroll: true,
  448. },
  449. {
  450. ...chartsConfig,
  451. canvasId: item.id,
  452. type: "column",
  453. series: [
  454. {
  455. name: item.title,
  456. data: item.value.map((t) => t.value),
  457. },
  458. ],
  459. categories: item.value.map((t) => t.name),
  460. xAxis: {
  461. rotateLabel: true,
  462. fontSize: this.chartsFontSize,
  463. },
  464. },
  465. ][item.type]
  466. );
  467. chartsObject[item.id] = charts;
  468. });
  469. },
  470. // 初始化图表
  471. initCharts() {
  472. // (阿里小程序)设置图表尺寸为2倍
  473. // #ifdef MP-ALIPAY
  474. const pixelRatio = uni.getSystemInfoSync().pixelRatio;
  475. if (pixelRatio > 1) {
  476. this.pixelRatio = 2;
  477. this.chartsFontSize = 15;
  478. }
  479. // #endif
  480. this.cWidth = uni.upx2px(750);
  481. this.cHeight = uni.upx2px(500);
  482. chartsConfig = {
  483. $this: this,
  484. pixelRatio: this.pixelRatio,
  485. width: this.cWidth * this.pixelRatio,
  486. height: this.cHeight * this.pixelRatio,
  487. background: "#FFFFFF",
  488. dataLabel: true,
  489. padding: [20, 15, 5, 15],
  490. };
  491. },
  492. // 图表点击事件
  493. chartTap(chartItem, e) {
  494. const format = ({ name, data }, category) =>
  495. `${category || ""} ${name}: ${data}`;
  496. // #ifdef MP-DINGTALK
  497. const detail = e.detail;
  498. const item = detail;
  499. e.mp.changedTouches = [item];
  500. // #endif
  501. chartsObject[chartItem.id].showToolTip(e, {
  502. format,
  503. });
  504. },
  505. // 图表拖动事件(开始)
  506. touchStart(chartId, e) {
  507. chartsObject[chartId].scrollStart(e);
  508. },
  509. // 图表拖动事件(拖动)
  510. touchMove(chartId, e) {
  511. chartsObject[chartId].scroll(e);
  512. },
  513. // 图表拖动事件(结束)
  514. touchEnd(chartId, e) {
  515. chartsObject[chartId].scrollEnd(e);
  516. },
  517. // 格式化日期的显示(新闻列表、通知公告等)
  518. postDateTime(timeStr) {
  519. return moment(timeStr).fromNow();
  520. },
  521. // 点击功能按钮
  522. funcListClick(item) {
  523. if (item.F_IsSystem === 2) {
  524. this.NAV_TO(
  525. `/pages/customapp/list?formId=${item.F_FormId}`,
  526. item,
  527. true
  528. );
  529. return;
  530. }
  531. this.NAV_TO(`/pages/${item.F_Url}/list`);
  532. },
  533. // 点击通知公告的标题
  534. async noticeClick(item) {
  535. if(item.f_category!==undefined){
  536. this.NAV_TO("/pages/home/notice", item, true);
  537. }else{
  538. // const result = await this.HTTP_GET(
  539. // 'learun/adms/newwf/mytask',
  540. // {
  541. // pagination:{"rows":100,"page":1,"sidx":"F_CreateDate","sord":"DESC"},
  542. // queryJson: JSON.stringify({keyword:item.f_title})
  543. // },
  544. // '加载任务时出错'
  545. // )
  546. // if(!result){
  547. // return
  548. // }
  549. // this.processId = this.currentTask.F_Id
  550. // this.taskId = this.currentTask.F_TaskId
  551. let params = {
  552. F_Id:item.f_processid,
  553. F_TaskId:item.f_id,
  554. mark:"pre",
  555. F_Title:item.f_title,
  556. }
  557. this.NAV_TO('/pages/nworkflow/myflow/single', params, true)
  558. }
  559. },
  560. informClick(titleName) {
  561. let categoryId = null;
  562. if (titleName == "待办事项") {
  563. this.NAV_TO("/pages/nworkflow/myflow/list", {tab:1}, true);
  564. }else{
  565. if(titleName == "通知公告")categoryId= 1;
  566. if(titleName == "校园新闻")categoryId= 2;
  567. if(titleName == "教学工作")categoryId= 6;
  568. if(titleName == "常用下载")categoryId= 12;
  569. if(titleName == "规章制度")categoryId= 10;
  570. this.NAV_TO("/pages/LR_OAModule/list",{categoryId}, true,);
  571. }
  572. },
  573. // #ifndef H5
  574. // 点击左上角扫码图标,H5 无此功能
  575. scanClick() {
  576. uni.scanCode({
  577. scanType: ["qrCode", "barCode"],
  578. success: ({ result, charSet }) => {
  579. // 您可以在这里自行定制扫码后的功能
  580. },
  581. });
  582. },
  583. // #endif
  584. // 点击更多功能按钮
  585. moreClick(openSearch) {
  586. this.NAV_TO(`/pages/home/more${openSearch ? "?search=1" : ""}`);
  587. },
  588. // 点击右上角的消息按钮
  589. msgClick() {
  590. this.TAB_TO("/pages/msg");
  591. },
  592. },
  593. computed: {
  594. // 「我的功能」区域按钮列表
  595. funcListDisplay() {
  596. const { myList, listData } = this;
  597. const myFuncList = myList.reduce((list, id) => {
  598. if (listData.find((t) => t.F_Id === id)) {
  599. return [...list, listData.find((t) => t.F_Id === id)];
  600. }
  601. return list;
  602. }, []);
  603. return myFuncList;
  604. },
  605. // imgData 数组的长度
  606. imgCount() {
  607. return this.imgData.length;
  608. },
  609. },
  610. };
  611. </script>
  612. <style lang="less" scoped>
  613. .page {
  614. background-color: #f3f3f3;
  615. padding-bottom: 48px;
  616. .header {
  617. height: 100rpx;
  618. background-color: #0c86d8;
  619. display: flex;
  620. align-items: center;
  621. .header-left {
  622. flex-grow: 0;
  623. }
  624. .header-mid {
  625. flex-grow: 1;
  626. background-color: #3d9ee0;
  627. line-height: 60rpx;
  628. height: 60rpx;
  629. font-size: 24rpx;
  630. color: #fff;
  631. border-radius: 6rpx;
  632. }
  633. .header-right {
  634. flex-grow: 0;
  635. }
  636. }
  637. .content {
  638. background-color: #fff;
  639. }
  640. .function-list {
  641. padding-bottom: 0;
  642. .cu-item {
  643. .app-item {
  644. border-radius: 50%;
  645. height: 45px;
  646. width: 45px;
  647. }
  648. &:nth-child(7n + 1) > .app-item {
  649. background-color: #62bbff;
  650. }
  651. &:nth-child(7n + 2) > .app-item {
  652. background-color: #7bd2ff;
  653. }
  654. &:nth-child(7n + 3) > .app-item {
  655. background-color: #ffd761;
  656. }
  657. &:nth-child(7n + 4) > .app-item {
  658. background-color: #fe955c;
  659. }
  660. &:nth-child(7n + 5) > .app-item {
  661. background-color: #ff6283;
  662. }
  663. &:nth-child(7n + 6) > .app-item {
  664. background-color: #60e3f3;
  665. }
  666. &:nth-child(7n) > .app-item {
  667. background-color: #acc8fe;
  668. }
  669. }
  670. }
  671. .function-more-btn {
  672. display: inline-block;
  673. border: currentColor 1px solid;
  674. border-radius: 2px;
  675. padding: 10rpx 50rpx;
  676. }
  677. .count-list {
  678. &:after {
  679. content: "";
  680. clear: both;
  681. display: table;
  682. }
  683. .count-item-value {
  684. color: #0188d2;
  685. font-size: 24px;
  686. }
  687. }
  688. .chart-list {
  689. background-color: #fff;
  690. overflow: hidden;
  691. }
  692. }
  693. </style>
  694. <style lang="less">
  695. #home {
  696. width: 750rpx;
  697. overflow-x: hidden;
  698. .function-list .cu-item text[class*="cuIcon"] {
  699. margin-top: 0 !important;
  700. }
  701. }
  702. .CornerMarker{
  703. padding: 1px 2px;
  704. position: absolute;
  705. right: 0px;
  706. top: 0px;
  707. background-color: red;
  708. min-width: 14px;
  709. border-radius: 7px;
  710. font-size: 12px;
  711. color: #fff;
  712. }
  713. .hasmoreBtn{
  714. .action{
  715. width: 100%;
  716. }
  717. .text-xl{
  718. flex: 1;
  719. position: relative;
  720. }
  721. .moreBtn{
  722. position: absolute;
  723. right: 15px;
  724. }
  725. }
  726. </style>