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.
 
 
 
 
 
 

769 lines
19 KiB

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